From: Alexander Ebert Date: Fri, 21 Jun 2024 12:46:27 +0000 (+0200) Subject: Add a chunk-based progress tracking X-Git-Tag: 6.1.0_Alpha_1~55 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=4ad932a4bb757403d0be87aed68dfe50839ea011;p=GitHub%2FWoltLab%2FWCF.git Add a chunk-based progress tracking There will be no progress bar if there is only a single chunk to be uploaded. --- diff --git a/ts/WoltLabSuite/Core/Component/Attachment/Entry.ts b/ts/WoltLabSuite/Core/Component/Attachment/Entry.ts index fc0ae3da68..408b113141 100644 --- a/ts/WoltLabSuite/Core/Component/Attachment/Entry.ts +++ b/ts/WoltLabSuite/Core/Component/Attachment/Entry.ts @@ -188,6 +188,38 @@ function markElementAsErroneous(element: HTMLElement, errorMessage: string): voi element.append(errorElement); } +function trackUploadProgress(element: HTMLElement, file: WoltlabCoreFileElement): void { + const progress = document.createElement("progress"); + progress.classList.add("attachment__item__progress__bar"); + progress.max = 100; + const readout = document.createElement("span"); + readout.classList.add("attachment__item__progress__readout"); + + file.addEventListener("uploadProgress", (event: CustomEvent) => { + progress.value = event.detail; + readout.textContent = `${event.detail}%`; + + if (progress.parentNode === null) { + element.classList.add("attachment__item--uploading"); + + const wrapper = document.createElement("div"); + wrapper.classList.add("attachment__item__progress"); + wrapper.append(progress, readout); + + element.append(wrapper); + } + }); +} + +function removeUploadProgress(element: HTMLElement): void { + if (!element.classList.contains("attachment__item--uploading")) { + return; + } + + element.classList.remove("attachment__item--uploading"); + element.querySelector(".attachment__item__progress")?.remove(); +} + export function createAttachmentFromFile(file: WoltlabCoreFileElement, editor: HTMLElement) { const element = document.createElement("li"); element.classList.add("attachment__item"); @@ -212,7 +244,12 @@ export function createAttachmentFromFile(file: WoltlabCoreFileElement, editor: H }) .catch((reason) => { fileInitializationFailed(element, file, reason); + }) + .finally(() => { + removeUploadProgress(element); }); + trackUploadProgress(element, file); + return element; } diff --git a/ts/WoltLabSuite/Core/Component/File/Upload.ts b/ts/WoltLabSuite/Core/Component/File/Upload.ts index 0f88d23376..babee01432 100644 --- a/ts/WoltLabSuite/Core/Component/File/Upload.ts +++ b/ts/WoltLabSuite/Core/Component/File/Upload.ts @@ -65,7 +65,7 @@ async function upload(element: WoltlabCoreFileUploadElement, file: File): Promis const chunkSize = Math.ceil(file.size / numberOfChunks); - // TODO: Can we somehow report any meaningful upload progress? + notifyChunkProgress(fileElement, 0, numberOfChunks); for (let i = 0; i < numberOfChunks; i++) { const start = i * chunkSize; @@ -81,6 +81,8 @@ async function upload(element: WoltlabCoreFileUploadElement, file: File): Promis throw response.error; } + notifyChunkProgress(fileElement, i + 1, numberOfChunks); + await chunkUploadCompleted(fileElement, response.value); if (response.value.completed) { @@ -89,6 +91,19 @@ async function upload(element: WoltlabCoreFileUploadElement, file: File): Promis } } +function notifyChunkProgress(element: WoltlabCoreFileElement, currentChunk: number, numberOfChunks: number): void { + // Suppress the progress bar for uploads that are processed in a single + // request, because we cannot track the upload progress within a chunk. + if (numberOfChunks === 1) { + return; + } + + const event = new CustomEvent("uploadProgress", { + detail: Math.floor((currentChunk / numberOfChunks) * 100), + }); + element.dispatchEvent(event); +} + async function chunkUploadCompleted(fileElement: WoltlabCoreFileElement, result: UploadChunkResponse): Promise { if (!result.completed) { return; diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Attachment/Entry.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Attachment/Entry.js index 67e741e765..a158f39795 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Attachment/Entry.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Attachment/Entry.js @@ -141,6 +141,31 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/FileUtil", "WoltLabSui errorElement.textContent = errorMessage; element.append(errorElement); } + function trackUploadProgress(element, file) { + const progress = document.createElement("progress"); + progress.classList.add("attachment__item__progress__bar"); + progress.max = 100; + const readout = document.createElement("span"); + readout.classList.add("attachment__item__progress__readout"); + file.addEventListener("uploadProgress", (event) => { + progress.value = event.detail; + readout.textContent = `${event.detail}%`; + if (progress.parentNode === null) { + element.classList.add("attachment__item--uploading"); + const wrapper = document.createElement("div"); + wrapper.classList.add("attachment__item__progress"); + wrapper.append(progress, readout); + element.append(wrapper); + } + }); + } + function removeUploadProgress(element) { + if (!element.classList.contains("attachment__item--uploading")) { + return; + } + element.classList.remove("attachment__item--uploading"); + element.querySelector(".attachment__item__progress")?.remove(); + } function createAttachmentFromFile(file, editor) { const element = document.createElement("li"); element.classList.add("attachment__item"); @@ -160,7 +185,11 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/FileUtil", "WoltLabSui }) .catch((reason) => { fileInitializationFailed(element, file, reason); + }) + .finally(() => { + removeUploadProgress(element); }); + trackUploadProgress(element, file); return element; } exports.createAttachmentFromFile = createAttachmentFromFile; diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/File/Upload.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/File/Upload.js index 06612c3e33..c05f517015 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/File/Upload.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/File/Upload.js @@ -23,7 +23,7 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Helper/Selector", "Wol } const { identifier, numberOfChunks } = response.value; const chunkSize = Math.ceil(file.size / numberOfChunks); - // TODO: Can we somehow report any meaningful upload progress? + notifyChunkProgress(fileElement, 0, numberOfChunks); for (let i = 0; i < numberOfChunks; i++) { const start = i * chunkSize; const end = start + chunkSize; @@ -34,12 +34,24 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Helper/Selector", "Wol fileElement.uploadFailed(response.error); throw response.error; } + notifyChunkProgress(fileElement, i + 1, numberOfChunks); await chunkUploadCompleted(fileElement, response.value); if (response.value.completed) { return response.value; } } } + function notifyChunkProgress(element, currentChunk, numberOfChunks) { + // Suppress the progress bar for uploads that are processed in a single + // request, because we cannot track the upload progress within a chunk. + if (numberOfChunks === 1) { + return; + } + const event = new CustomEvent("uploadProgress", { + detail: Math.floor((currentChunk / numberOfChunks) * 100), + }); + element.dispatchEvent(event); + } async function chunkUploadCompleted(fileElement, result) { if (!result.completed) { return; diff --git a/wcfsetup/install/files/style/ui/attachment.scss b/wcfsetup/install/files/style/ui/attachment.scss index 0af744b190..fdce55b367 100644 --- a/wcfsetup/install/files/style/ui/attachment.scss +++ b/wcfsetup/install/files/style/ui/attachment.scss @@ -200,6 +200,17 @@ html[data-color-scheme="dark"] { justify-content: end; } +.attachment__item__progress { + align-items: center; + column-gap: 10px; + display: flex; + grid-area: buttons; +} + +.attachment__item__progress__readout { + @include wcfFontSmall; +} + .formAttachmentContent { .formAttachmentList { display: flex;