diff options
| author | かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> | 2025-05-21 21:13:19 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-05-21 21:13:19 +0900 |
| commit | ccf5bd337e98a470f30639ceedc16501f57aee68 (patch) | |
| tree | 1dfe0f192f2a98eaee8c9482b6e45f496fbde8d8 /packages/frontend/src/components/MkUploaderDialog.vue | |
| parent | fix(frontend): 誤植を修正 (#16075) (diff) | |
| download | misskey-ccf5bd337e98a470f30639ceedc16501f57aee68.tar.gz misskey-ccf5bd337e98a470f30639ceedc16501f57aee68.tar.bz2 misskey-ccf5bd337e98a470f30639ceedc16501f57aee68.zip | |
enhance(frontend): ファイルのアップロードを中止できるように (#16069)
* enhance(frontend): ファイルのアップロードを中止できるように
* Update Changelog
* fix: ダイアログを閉じたり、中断ボタンが押されたりしたときはその後のアップロードをすべて中止するように
* fix
Diffstat (limited to 'packages/frontend/src/components/MkUploaderDialog.vue')
| -rw-r--r-- | packages/frontend/src/components/MkUploaderDialog.vue | 93 |
1 files changed, 76 insertions, 17 deletions
diff --git a/packages/frontend/src/components/MkUploaderDialog.vue b/packages/frontend/src/components/MkUploaderDialog.vue index 3a83247d4b..b171546854 100644 --- a/packages/frontend/src/components/MkUploaderDialog.vue +++ b/packages/frontend/src/components/MkUploaderDialog.vue @@ -18,8 +18,8 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="$style.root"> <div :class="[$style.overallProgress, canRetry ? $style.overallProgressError : null]" :style="{ '--op': `${overallProgress}%` }"></div> - <div :class="$style.main" class="_gaps_s"> - <div :class="$style.items" class="_gaps_s"> + <div class="_gaps_s _spacer"> + <div class="_gaps_s"> <div v-for="ctx in items" :key="ctx.id" @@ -74,7 +74,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #footer> <div class="_buttonsCenter"> - <MkButton v-if="isUploading" rounded @click="cancel()"><i class="ti ti-x"></i> {{ i18n.ts.cancel }}</MkButton> + <MkButton v-if="isUploading" rounded @click="abortWithConfirm()"><i class="ti ti-x"></i> {{ i18n.ts.abort }}</MkButton> <MkButton v-else-if="!firstUploadAttempted" primary rounded @click="upload()"><i class="ti ti-upload"></i> {{ i18n.ts.upload }}</MkButton> <MkButton v-if="canRetry" rounded @click="upload()"><i class="ti ti-reload"></i> {{ i18n.ts.retry }}</MkButton> @@ -96,10 +96,9 @@ import { i18n } from '@/i18n.js'; import { prefer } from '@/preferences.js'; import MkButton from '@/components/MkButton.vue'; import bytes from '@/filters/bytes.js'; -import MkSwitch from '@/components/MkSwitch.vue'; import MkSelect from '@/components/MkSelect.vue'; import { isWebpSupported } from '@/utility/isWebpSupported.js'; -import { uploadFile } from '@/utility/drive.js'; +import { uploadFile, UploadAbortedError } from '@/utility/drive.js'; import * as os from '@/os.js'; import { ensureSignin } from '@/i.js'; @@ -138,7 +137,7 @@ const emit = defineEmits<{ (ev: 'closed'): void; }>(); -const items = ref([] as { +const items = ref<{ id: string; name: string; progress: { max: number; value: number } | null; @@ -147,10 +146,12 @@ const items = ref([] as { uploading: boolean; uploaded: Misskey.entities.DriveFile | null; uploadFailed: boolean; + aborted: boolean; compressedSize?: number | null; compressedImage?: Blob | null; file: File; -}[]); + abort?: (() => void) | null; +}[]>([]); const dialog = useTemplateRef('dialog'); @@ -213,10 +214,23 @@ async function cancel() { }); if (canceled) return; + abortAll(); emit('canceled'); dialog.value?.close(); } +async function abortWithConfirm() { + const { canceled } = await os.confirm({ + type: 'question', + text: i18n.ts._uploader.abortConfirm, + okText: i18n.ts.yes, + cancelText: i18n.ts.no, + }); + if (canceled) return; + + abortAll(); +} + async function done() { if (items.value.some(item => item.uploaded == null)) { const { canceled } = await os.confirm({ @@ -258,6 +272,17 @@ function showMenu(ev: MouseEvent, item: typeof items.value[0]) { items.value.splice(items.value.indexOf(item), 1); }, }); + } else if (item.uploading) { + menu.push({ + icon: 'ti ti-cloud-pause', + text: i18n.ts.abort, + danger: true, + action: () => { + if (item.abort != null) { + item.abort(); + } + } + }); } os.popupMenu(menu, ev.currentTarget ?? ev.target); @@ -266,7 +291,20 @@ function showMenu(ev: MouseEvent, item: typeof items.value[0]) { async function upload() { // エラーハンドリングなどを考慮してシーケンシャルにやる firstUploadAttempted.value = true; + items.value = items.value.map(item => ({ + ...item, + aborted: false, + uploadFailed: false, + waiting: false, + uploading: false, + })); + for (const item of items.value.filter(item => item.uploaded == null)) { + // アップロード処理途中で値が変わる場合(途中で全キャンセルされたりなど)もあるので、Array filterではなくここでチェック + if (item.aborted) { + continue; + } + item.waiting = true; item.uploadFailed = false; @@ -296,7 +334,7 @@ async function upload() { // エラーハンドリングなどを考慮してシ item.uploading = true; - const driveFile = await uploadFile(item.compressedImage ?? item.file, { + const { filePromise, abort } = uploadFile(item.compressedImage ?? item.file, { name: item.name, folderId: props.folderId, onProgress: (progress) => { @@ -308,16 +346,43 @@ async function upload() { // エラーハンドリングなどを考慮してシ item.progress.max = progress.total; } }, + }); + + item.abort = () => { + item.abort = null; + abort(); + item.uploading = false; + item.waiting = false; + item.uploadFailed = true; + }; + + await filePromise.then((file) => { + item.uploaded = file; + item.abort = null; }).catch(err => { item.uploadFailed = true; item.progress = null; - throw err; + if (!(err instanceof UploadAbortedError)) { + throw err; + } }).finally(() => { item.uploading = false; item.waiting = false; }); + } +} - item.uploaded = driveFile; +function abortAll() { + for (const item of items.value) { + if (item.uploaded != null) { + continue; + } + + if (item.abort != null) { + item.abort(); + } + item.aborted = true; + item.uploadFailed = true; } } @@ -340,6 +405,7 @@ function initializeFile(file: File) { thumbnail: window.URL.createObjectURL(file), waiting: false, uploading: false, + aborted: false, uploaded: null, uploadFailed: false, file: markRaw(file), @@ -373,13 +439,6 @@ onMounted(() => { } } -.main { - padding: 12px; -} - -.items { -} - .item { position: relative; border-radius: 10px; |