forked from jsnbuchanan/crowd-funder-for-time-pwa
Changes: - Move v-model directives before other attributes - Move v-bind directives before event handlers - Reorder attributes for better readability - Fix template attribute ordering across components - Improve eslint rules - add default vite config for testing (handles nostr error too) This follows Vue.js style guide recommendations for attribute ordering and improves template consistency.
183 lines
4.9 KiB
Vue
183 lines
4.9 KiB
Vue
<template>
|
|
<div v-if="visible" class="dialog-overlay z-[60]">
|
|
<div class="dialog relative">
|
|
<div class="text-lg text-center font-light relative z-50">
|
|
<div
|
|
id="ViewHeading"
|
|
class="text-center font-bold absolute top-0 left-0 right-0 px-4 py-0.5 bg-black/50 text-white leading-none"
|
|
>
|
|
Add Photo
|
|
</div>
|
|
<div
|
|
class="text-lg text-center px-2 py-0.5 leading-none absolute right-0 top-0 text-white"
|
|
@click="close()"
|
|
>
|
|
<font-awesome icon="xmark" class="w-[1em]"></font-awesome>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<div class="text-center mt-8">
|
|
<div>
|
|
<font-awesome
|
|
icon="camera"
|
|
class="bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-2 rounded-md"
|
|
@click="openPhotoDialog()"
|
|
/>
|
|
</div>
|
|
<div class="mt-4">
|
|
<input type="file" @change="uploadImageFile" />
|
|
</div>
|
|
<div class="mt-4">
|
|
<span class="mt-2">
|
|
... or paste a URL:
|
|
<input v-model="imageUrl" type="text" class="border-2" />
|
|
</span>
|
|
<span class="ml-2">
|
|
<font-awesome
|
|
v-if="imageUrl"
|
|
icon="check"
|
|
class="bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-2 rounded-md cursor-pointer"
|
|
@click="acceptUrl"
|
|
/>
|
|
<!-- so that there's no shifting when it becomes visible -->
|
|
<font-awesome
|
|
v-else
|
|
icon="check"
|
|
class="text-white bg-white px-2 py-2"
|
|
/>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<PhotoDialog ref="photoDialog" />
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import axios from "axios";
|
|
import { ref } from "vue";
|
|
import { Component, Vue } from "vue-facing-decorator";
|
|
|
|
import PhotoDialog from "../components/PhotoDialog.vue";
|
|
import { NotificationIface } from "../constants/app";
|
|
|
|
const inputImageFileNameRef = ref<Blob>();
|
|
|
|
@Component({
|
|
components: { PhotoDialog },
|
|
})
|
|
export default class ImageMethodDialog extends Vue {
|
|
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
|
|
|
claimType: string;
|
|
crop: boolean = false;
|
|
imageCallback: (imageUrl?: string) => void = () => {};
|
|
imageUrl?: string;
|
|
visible = false;
|
|
|
|
open(setImageFn: (arg: string) => void, claimType: string, crop?: boolean) {
|
|
this.claimType = claimType;
|
|
this.crop = !!crop;
|
|
this.imageCallback = setImageFn;
|
|
|
|
this.visible = true;
|
|
}
|
|
|
|
openPhotoDialog(blob?: Blob, fileName?: string) {
|
|
this.visible = false;
|
|
|
|
(this.$refs.photoDialog as PhotoDialog).open(
|
|
this.imageCallback,
|
|
this.claimType,
|
|
this.crop,
|
|
blob,
|
|
fileName,
|
|
);
|
|
}
|
|
|
|
async uploadImageFile(event: Event) {
|
|
this.visible = false;
|
|
|
|
inputImageFileNameRef.value = event.target.files[0];
|
|
// https://developer.mozilla.org/en-US/docs/Web/API/File
|
|
// ... plus it has a `type` property from my testing
|
|
const file = inputImageFileNameRef.value;
|
|
if (file != null) {
|
|
const reader = new FileReader();
|
|
reader.onload = async (e) => {
|
|
const data = e.target?.result as ArrayBuffer;
|
|
if (data) {
|
|
const blob = new Blob([new Uint8Array(data)], {
|
|
type: file.type,
|
|
});
|
|
this.openPhotoDialog(blob, file.name as string);
|
|
}
|
|
};
|
|
reader.readAsArrayBuffer(file as Blob);
|
|
}
|
|
}
|
|
|
|
async acceptUrl() {
|
|
this.visible = false;
|
|
if (this.crop) {
|
|
try {
|
|
const urlBlobResponse: Blob = await axios.get(this.imageUrl as string, {
|
|
responseType: "blob", // This ensures the data is returned as a Blob
|
|
});
|
|
const fullUrl = new URL(this.imageUrl as string);
|
|
const fileName = fullUrl.pathname.split("/").pop() as string;
|
|
(this.$refs.photoDialog as PhotoDialog).open(
|
|
this.imageCallback,
|
|
this.claimType,
|
|
this.crop,
|
|
urlBlobResponse.data as Blob,
|
|
fileName,
|
|
);
|
|
} catch (error) {
|
|
this.$notify(
|
|
{
|
|
group: "alert",
|
|
type: "danger",
|
|
title: "Error",
|
|
text: "There was an error retrieving that image.",
|
|
},
|
|
5000,
|
|
);
|
|
}
|
|
} else {
|
|
this.imageCallback(this.imageUrl);
|
|
}
|
|
}
|
|
|
|
close() {
|
|
this.visible = false;
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.dialog-overlay {
|
|
z-index: 50;
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
padding: 1.5rem;
|
|
}
|
|
|
|
.dialog {
|
|
background-color: white;
|
|
padding: 1rem;
|
|
border-radius: 0.5rem;
|
|
width: 100%;
|
|
max-width: 700px;
|
|
}
|
|
</style>
|