Files
crowd-funder-for-time-pwa/src/components/ImageMethodDialog.vue
Matthew Raymer e5518cd47c fix: update Vue template syntax and improve Vite config
- Fix Vue template syntax in App.vue by using proper event handler format
- Update Vite config to properly handle ESM imports and crypto modules
- Add manual chunks for better code splitting
- Improve environment variable handling in vite-env.d.ts
- Fix TypeScript linting errors in App.vue
2025-04-18 09:59:33 +00:00

182 lines
4.8 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]" />
</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>