show upload status when uploading images

This commit is contained in:
Zutatensuppe 2021-06-20 13:40:28 +02:00
parent 4b10fbc01b
commit b410f400fa
6 changed files with 106 additions and 10 deletions

View file

@ -49,10 +49,21 @@ gallery", if possible!
</div>
<div class="area-buttons">
<button class="btn" :disabled="!canPostToGallery" @click="postToGallery">🖼 Post to gallery</button>
<button class="btn" :disabled="!canSetupGameClick" @click="setupGameClick">🧩 Post to gallery <br /> + set up game</button>
<button class="btn"
:disabled="!canPostToGallery"
@click="postToGallery"
>
<template v-if="uploading === 'postToGallery'">Uploading ({{uploadProgressPercent}}%)</template>
<template v-else>🖼 Post to gallery</template>
</button>
<button class="btn"
:disabled="!canSetupGameClick"
@click="setupGameClick"
>
<template v-if="uploading === 'setupGame'">Uploading ({{uploadProgressPercent}}%)</template>
<template v-else>🧩 Post to gallery <br /> + set up game</template>
</button>
</div>
</div>
</div>
</template>
@ -75,6 +86,12 @@ export default defineComponent({
autocompleteTags: {
type: Function,
},
uploadProgress: {
type: Number,
},
uploading: {
type: String,
},
},
emits: {
bgclick: null,
@ -91,10 +108,19 @@ export default defineComponent({
}
},
computed: {
uploadProgressPercent (): number {
return this.uploadProgress ? Math.round(this.uploadProgress * 100) : 0
},
canPostToGallery (): boolean {
if (this.uploading) {
return false
}
return !!(this.previewUrl && this.file)
},
canSetupGameClick (): boolean {
if (this.uploading) {
return false
}
return !!(this.previewUrl && this.file)
},
},

View file

@ -44,8 +44,11 @@ in jigsawpuzzles.io
v-if="dialog==='new-image'"
:autocompleteTags="autocompleteTags"
@bgclick="dialog=''"
:uploadProgress="uploadProgress"
:uploading="uploading"
@postToGalleryClick="postToGalleryClick"
@setupGameClick="setupGameClick" />
@setupGameClick="setupGameClick"
/>
<edit-image-dialog
v-if="dialog==='edit-image'"
:autocompleteTags="autocompleteTags"
@ -61,7 +64,7 @@ in jigsawpuzzles.io
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue'
import { defineComponent } from 'vue'
import ImageLibrary from './../components/ImageLibrary.vue'
import NewImageDialog from './../components/NewImageDialog.vue'
@ -69,6 +72,7 @@ import EditImageDialog from './../components/EditImageDialog.vue'
import NewGameDialog from './../components/NewGameDialog.vue'
import { GameSettings, Image, Tag } from '../../common/Types'
import Util from '../../common/Util'
import xhr from '../xhr'
export default defineComponent({
components: {
@ -97,6 +101,9 @@ export default defineComponent({
} as Image,
dialog: '',
uploading: '',
uploadProgress: 0,
}
},
async created() {
@ -143,15 +150,18 @@ export default defineComponent({
this.dialog = 'edit-image'
},
async uploadImage (data: any) {
this.uploadProgress = 0
const formData = new FormData();
formData.append('file', data.file, data.file.name);
formData.append('title', data.title)
formData.append('tags', data.tags)
const res = await fetch('/api/upload', {
method: 'post',
const res = await xhr.post('/api/upload', {
body: formData,
onUploadProgress: (evt: ProgressEvent<XMLHttpRequestEventTarget>): void => {
this.uploadProgress = evt.loaded / evt.total
},
})
this.uploadProgress = 1
return await res.json()
},
async saveImage (data: any) {
@ -175,12 +185,16 @@ export default defineComponent({
await this.loadImages()
},
async postToGalleryClick(data: any) {
this.uploading = 'postToGallery'
await this.uploadImage(data)
this.uploading = ''
this.dialog = ''
await this.loadImages()
},
async setupGameClick (data: any) {
this.uploading = 'setupGame'
const image = await this.uploadImage(data)
this.uploading = ''
this.loadImages() // load images in background
this.image = image
this.dialog = 'new-game'

56
src/frontend/xhr.ts Normal file
View file

@ -0,0 +1,56 @@
export interface Response {
status: number,
text: string,
json: () => Promise<any>,
}
export interface Options {
body: FormData|string,
headers?: any,
onUploadProgress?: (ev: ProgressEvent<XMLHttpRequestEventTarget>) => any,
}
const request = async (
method: string,
url: string,
options: Options
): Promise<Response> => {
return new Promise((resolve, reject) => {
const xhr = new window.XMLHttpRequest()
xhr.open(method, url, true)
xhr.withCredentials = true
for (const k in options.headers || {}) {
xhr.setRequestHeader(k, options.headers[k])
}
xhr.addEventListener('load', function (ev: ProgressEvent<XMLHttpRequestEventTarget>
) {
resolve({
status: this.status,
text: this.responseText,
json: async () => JSON.parse(this.responseText),
})
})
xhr.addEventListener('error', function (ev: ProgressEvent<XMLHttpRequestEventTarget>) {
reject(new Error('xhr error'))
})
if (xhr.upload && options.onUploadProgress) {
xhr.upload.addEventListener('progress', function (ev: ProgressEvent<XMLHttpRequestEventTarget>) {
// typescript complains without this extra check
if (options.onUploadProgress) {
options.onUploadProgress(ev)
}
})
}
xhr.send(options.body)
})
}
export default {
request,
get: (url: string, options: any): Promise<Response> => {
return request('get', url, options)
},
post: (url: string, options: any): Promise<Response> => {
return request('post', url, options)
},
}