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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -4,7 +4,7 @@
<meta charset="UTF-8">
<title>🧩 jigsaw.hyottoko.club</title>
<script type="module" crossorigin src="/assets/index.1a1747a7.js"></script>
<script type="module" crossorigin src="/assets/index.e0726e0c.js"></script>
<link rel="modulepreload" href="/assets/vendor.684f7bc8.js">
<link rel="stylesheet" href="/assets/index.5c03949d.css">
</head>

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)
},
}