show upload status when uploading images
This commit is contained in:
parent
4b10fbc01b
commit
b410f400fa
6 changed files with 106 additions and 10 deletions
File diff suppressed because one or more lines are too long
1
build/public/assets/index.e0726e0c.js
Normal file
1
build/public/assets/index.e0726e0c.js
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -4,7 +4,7 @@
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
|
|
||||||
<title>🧩 jigsaw.hyottoko.club</title>
|
<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="modulepreload" href="/assets/vendor.684f7bc8.js">
|
||||||
<link rel="stylesheet" href="/assets/index.5c03949d.css">
|
<link rel="stylesheet" href="/assets/index.5c03949d.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
|
||||||
|
|
@ -49,10 +49,21 @@ gallery", if possible!
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="area-buttons">
|
<div class="area-buttons">
|
||||||
<button class="btn" :disabled="!canPostToGallery" @click="postToGallery">🖼️ Post to gallery</button>
|
<button class="btn"
|
||||||
<button class="btn" :disabled="!canSetupGameClick" @click="setupGameClick">🧩 Post to gallery <br /> + set up game</button>
|
: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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -75,6 +86,12 @@ export default defineComponent({
|
||||||
autocompleteTags: {
|
autocompleteTags: {
|
||||||
type: Function,
|
type: Function,
|
||||||
},
|
},
|
||||||
|
uploadProgress: {
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
uploading: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
emits: {
|
emits: {
|
||||||
bgclick: null,
|
bgclick: null,
|
||||||
|
|
@ -91,10 +108,19 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
uploadProgressPercent (): number {
|
||||||
|
return this.uploadProgress ? Math.round(this.uploadProgress * 100) : 0
|
||||||
|
},
|
||||||
canPostToGallery (): boolean {
|
canPostToGallery (): boolean {
|
||||||
|
if (this.uploading) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return !!(this.previewUrl && this.file)
|
return !!(this.previewUrl && this.file)
|
||||||
},
|
},
|
||||||
canSetupGameClick (): boolean {
|
canSetupGameClick (): boolean {
|
||||||
|
if (this.uploading) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return !!(this.previewUrl && this.file)
|
return !!(this.previewUrl && this.file)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,11 @@ in jigsawpuzzles.io
|
||||||
v-if="dialog==='new-image'"
|
v-if="dialog==='new-image'"
|
||||||
:autocompleteTags="autocompleteTags"
|
:autocompleteTags="autocompleteTags"
|
||||||
@bgclick="dialog=''"
|
@bgclick="dialog=''"
|
||||||
|
:uploadProgress="uploadProgress"
|
||||||
|
:uploading="uploading"
|
||||||
@postToGalleryClick="postToGalleryClick"
|
@postToGalleryClick="postToGalleryClick"
|
||||||
@setupGameClick="setupGameClick" />
|
@setupGameClick="setupGameClick"
|
||||||
|
/>
|
||||||
<edit-image-dialog
|
<edit-image-dialog
|
||||||
v-if="dialog==='edit-image'"
|
v-if="dialog==='edit-image'"
|
||||||
:autocompleteTags="autocompleteTags"
|
:autocompleteTags="autocompleteTags"
|
||||||
|
|
@ -61,7 +64,7 @@ in jigsawpuzzles.io
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, PropType } from 'vue'
|
import { defineComponent } from 'vue'
|
||||||
|
|
||||||
import ImageLibrary from './../components/ImageLibrary.vue'
|
import ImageLibrary from './../components/ImageLibrary.vue'
|
||||||
import NewImageDialog from './../components/NewImageDialog.vue'
|
import NewImageDialog from './../components/NewImageDialog.vue'
|
||||||
|
|
@ -69,6 +72,7 @@ import EditImageDialog from './../components/EditImageDialog.vue'
|
||||||
import NewGameDialog from './../components/NewGameDialog.vue'
|
import NewGameDialog from './../components/NewGameDialog.vue'
|
||||||
import { GameSettings, Image, Tag } from '../../common/Types'
|
import { GameSettings, Image, Tag } from '../../common/Types'
|
||||||
import Util from '../../common/Util'
|
import Util from '../../common/Util'
|
||||||
|
import xhr from '../xhr'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -97,6 +101,9 @@ export default defineComponent({
|
||||||
} as Image,
|
} as Image,
|
||||||
|
|
||||||
dialog: '',
|
dialog: '',
|
||||||
|
|
||||||
|
uploading: '',
|
||||||
|
uploadProgress: 0,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async created() {
|
async created() {
|
||||||
|
|
@ -143,15 +150,18 @@ export default defineComponent({
|
||||||
this.dialog = 'edit-image'
|
this.dialog = 'edit-image'
|
||||||
},
|
},
|
||||||
async uploadImage (data: any) {
|
async uploadImage (data: any) {
|
||||||
|
this.uploadProgress = 0
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', data.file, data.file.name);
|
formData.append('file', data.file, data.file.name);
|
||||||
formData.append('title', data.title)
|
formData.append('title', data.title)
|
||||||
formData.append('tags', data.tags)
|
formData.append('tags', data.tags)
|
||||||
|
const res = await xhr.post('/api/upload', {
|
||||||
const res = await fetch('/api/upload', {
|
|
||||||
method: 'post',
|
|
||||||
body: formData,
|
body: formData,
|
||||||
|
onUploadProgress: (evt: ProgressEvent<XMLHttpRequestEventTarget>): void => {
|
||||||
|
this.uploadProgress = evt.loaded / evt.total
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
this.uploadProgress = 1
|
||||||
return await res.json()
|
return await res.json()
|
||||||
},
|
},
|
||||||
async saveImage (data: any) {
|
async saveImage (data: any) {
|
||||||
|
|
@ -175,12 +185,16 @@ export default defineComponent({
|
||||||
await this.loadImages()
|
await this.loadImages()
|
||||||
},
|
},
|
||||||
async postToGalleryClick(data: any) {
|
async postToGalleryClick(data: any) {
|
||||||
|
this.uploading = 'postToGallery'
|
||||||
await this.uploadImage(data)
|
await this.uploadImage(data)
|
||||||
|
this.uploading = ''
|
||||||
this.dialog = ''
|
this.dialog = ''
|
||||||
await this.loadImages()
|
await this.loadImages()
|
||||||
},
|
},
|
||||||
async setupGameClick (data: any) {
|
async setupGameClick (data: any) {
|
||||||
|
this.uploading = 'setupGame'
|
||||||
const image = await this.uploadImage(data)
|
const image = await this.uploadImage(data)
|
||||||
|
this.uploading = ''
|
||||||
this.loadImages() // load images in background
|
this.loadImages() // load images in background
|
||||||
this.image = image
|
this.image = image
|
||||||
this.dialog = 'new-game'
|
this.dialog = 'new-game'
|
||||||
|
|
|
||||||
56
src/frontend/xhr.ts
Normal file
56
src/frontend/xhr.ts
Normal 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)
|
||||||
|
},
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue