Export `clearPreviousErrors()`
authorCyperghost <olaf_schmitz_1@t-online.de>
Wed, 3 Jul 2024 08:50:29 +0000 (10:50 +0200)
committerCyperghost <olaf_schmitz_1@t-online.de>
Wed, 3 Jul 2024 08:50:29 +0000 (10:50 +0200)
Use a Promise to recognize the beginning of the upload to replace the context

ts/WoltLabSuite/Core/Component/File/Upload.ts
ts/WoltLabSuite/Core/Form/Builder/Field/Controller/FileProcessor.ts
wcfsetup/install/files/js/WoltLabSuite/Core/Component/File/Upload.js
wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/Controller/FileProcessor.js
wcfsetup/install/files/lib/system/file/processor/FileProcessor.class.php

index babee01432a589aeab3c6d981950ed2b5c19fbe0..19e2504769e6af6ddfbe7d3e6b279afd10b53723 100644 (file)
@@ -125,7 +125,7 @@ async function getSha256Hash(data: BufferSource): Promise<string> {
     .join("");
 }
 
-function clearPreviousErrors(element: WoltlabCoreFileUploadElement): void {
+export function clearPreviousErrors(element: WoltlabCoreFileUploadElement): void {
   element.parentElement?.querySelectorAll(".innerError").forEach((x) => x.remove());
 }
 
index 85e9adc20efba8c4a33fef935afcc95f6b192ced..b4fd565ca6eef3fbf5659f41f9e82df8527b2e77 100644 (file)
@@ -15,6 +15,7 @@ import {
   removeUploadProgress,
   trackUploadProgress,
 } from "WoltLabSuite/Core/Component/File/File";
+import { clearPreviousErrors } from "WoltLabSuite/Core/Component/File/Upload";
 
 const _data = new Map<string, FileProcessor>();
 
@@ -33,6 +34,7 @@ export class FileProcessor {
   readonly #imageOnly: boolean;
   readonly #singleFileUpload: boolean;
   readonly #extraButtons: ExtraButton[];
+  #uploadResolve: undefined | (() => void);
 
   constructor(
     fieldId: string,
@@ -52,6 +54,10 @@ export class FileProcessor {
 
     this.#uploadButton = this.#container.querySelector("woltlab-core-file-upload") as WoltlabCoreFileUploadElement;
     this.#uploadButton.addEventListener("uploadStart", (event: CustomEvent<WoltlabCoreFileElement>) => {
+      if (this.#uploadResolve !== undefined) {
+        this.#uploadResolve();
+      }
+
       this.#registerFile(event.detail);
     });
     this.#fileInput = this.#uploadButton.shadowRoot!.querySelector<HTMLInputElement>('input[type="file"]')!;
@@ -148,15 +154,23 @@ export class FileProcessor {
       context.__replace = true;
       this.#uploadButton.dataset.context = JSON.stringify(context);
 
-      // Remove the element and all buttons from the dom, but keep them stored in a variable.
-      // If the user cancels the dialog or the upload fails, reinsert the old elements and show an error message.
-      // If the upload is successful, delete the old file.
       this.#replaceElement = element;
       this.#unregisterFile(element);
 
+      clearPreviousErrors(this.#uploadButton);
+
       const changeEventListener = () => {
-        this.#uploadButton.dataset.context = oldContext;
         this.#fileInput.removeEventListener("cancel", cancelEventListener);
+
+        // Wait until the upload starts,
+        // the request to the server is not synchronized with the end of the `change` event.
+        // Otherwise, we would swap the context too soon.
+        void new Promise<void>((resolve) => {
+          this.#uploadResolve = resolve;
+        }).then(() => {
+          this.#uploadResolve = undefined;
+          this.#uploadButton.dataset.context = oldContext;
+        });
       };
       const cancelEventListener = () => {
         this.#uploadButton.dataset.context = oldContext;
index c05f517015f957000c78a690d1759887d944ff7f..55de8f1c337928f7d656281096eddd649ad81eb0 100644 (file)
@@ -1,7 +1,7 @@
 define(["require", "exports", "tslib", "WoltLabSuite/Core/Helper/Selector", "WoltLabSuite/Core/Api/Files/Upload", "WoltLabSuite/Core/Api/Files/Chunk/Chunk", "WoltLabSuite/Core/Api/Files/GenerateThumbnails", "WoltLabSuite/Core/Image/Resizer", "WoltLabSuite/Core/Dom/Util", "WoltLabSuite/Core/Language"], function (require, exports, tslib_1, Selector_1, Upload_1, Chunk_1, GenerateThumbnails_1, Resizer_1, Util_1, Language_1) {
     "use strict";
     Object.defineProperty(exports, "__esModule", { value: true });
-    exports.setup = void 0;
+    exports.setup = exports.clearPreviousErrors = void 0;
     Resizer_1 = tslib_1.__importDefault(Resizer_1);
     async function upload(element, file) {
         const objectType = element.dataset.objectType;
@@ -71,6 +71,7 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Helper/Selector", "Wol
     function clearPreviousErrors(element) {
         element.parentElement?.querySelectorAll(".innerError").forEach((x) => x.remove());
     }
+    exports.clearPreviousErrors = clearPreviousErrors;
     async function resizeImage(element, file) {
         switch (file.type) {
             case "image/jpeg":
index 722b6d83fd5e4db6c1ea6c8887b69465ee6f9bbf..abcafe6364b368314e4c8908f097e58f4cee65e4 100644 (file)
@@ -4,7 +4,7 @@
  * @license   GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @since     6.1
  */
-define(["require", "exports", "tslib", "WoltLabSuite/Core/Language", "WoltLabSuite/Core/Api/Files/DeleteFile", "WoltLabSuite/Core/Dom/Change/Listener", "WoltLabSuite/Core/Component/File/File"], function (require, exports, tslib_1, Language_1, DeleteFile_1, Listener_1, File_1) {
+define(["require", "exports", "tslib", "WoltLabSuite/Core/Language", "WoltLabSuite/Core/Api/Files/DeleteFile", "WoltLabSuite/Core/Dom/Change/Listener", "WoltLabSuite/Core/Component/File/File", "WoltLabSuite/Core/Component/File/Upload"], function (require, exports, tslib_1, Language_1, DeleteFile_1, Listener_1, File_1, Upload_1) {
     "use strict";
     Object.defineProperty(exports, "__esModule", { value: true });
     exports.getValues = exports.FileProcessor = void 0;
@@ -19,6 +19,7 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Language", "WoltLabSui
         #imageOnly;
         #singleFileUpload;
         #extraButtons;
+        #uploadResolve;
         constructor(fieldId, singleFileUpload = false, imageOnly = false, extraButtons = []) {
             this.#fieldId = fieldId;
             this.#imageOnly = imageOnly;
@@ -30,6 +31,9 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Language", "WoltLabSui
             }
             this.#uploadButton = this.#container.querySelector("woltlab-core-file-upload");
             this.#uploadButton.addEventListener("uploadStart", (event) => {
+                if (this.#uploadResolve !== undefined) {
+                    this.#uploadResolve();
+                }
                 this.#registerFile(event.detail);
             });
             this.#fileInput = this.#uploadButton.shadowRoot.querySelector('input[type="file"]');
@@ -108,14 +112,20 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Language", "WoltLabSui
                 const context = JSON.parse(oldContext);
                 context.__replace = true;
                 this.#uploadButton.dataset.context = JSON.stringify(context);
-                // Remove the element and all buttons from the dom, but keep them stored in a variable.
-                // If the user cancels the dialog or the upload fails, reinsert the old elements and show an error message.
-                // If the upload is successful, delete the old file.
                 this.#replaceElement = element;
                 this.#unregisterFile(element);
+                (0, Upload_1.clearPreviousErrors)(this.#uploadButton);
                 const changeEventListener = () => {
-                    this.#uploadButton.dataset.context = oldContext;
                     this.#fileInput.removeEventListener("cancel", cancelEventListener);
+                    // Wait until the upload starts,
+                    // the request to the server is not synchronized with the end of the `change` event.
+                    // Otherwise, we would swap the context too soon.
+                    void new Promise((resolve) => {
+                        this.#uploadResolve = resolve;
+                    }).then(() => {
+                        this.#uploadResolve = undefined;
+                        this.#uploadButton.dataset.context = oldContext;
+                    });
                 };
                 const cancelEventListener = () => {
                     this.#uploadButton.dataset.context = oldContext;
index 283e81e86d091df774be537549a9b1a69986db61..d770ef4ff8c0af6dc34b84753d1073954091a2ae 100644 (file)
@@ -240,8 +240,8 @@ final class FileProcessor extends SingletonFactory
 
     public function hasReachedUploadLimit(IFileProcessor $fileProcessor, array $context): bool
     {
-        // Replace button from `FileProcessorFormField` called
-        if (isset($context["__replace"]) && $context["__replace"] === true) {
+        // Replace button from `FileProcessorFormField` is clicked.
+        if (!empty($context["__replace"])) {
             return false;
         }