From 1188662067c23e6f118c0311895e19a2d2a14d54 Mon Sep 17 00:00:00 2001 From: Tomas Dvorak Date: Fri, 18 Sep 2026 21:37:51 +0200 Subject: [PATCH] fix(uploader): accumulate file-input change events before resolving (#3571) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some mobile pickers deliver a multi-file selection across several 'change' events; select() resolved its promise on the first one, so every later file was silently dropped — matching the '3 selected, 1 uploaded' report. The handler now accumulates files and resolves once the picker goes quiet for 400ms. Also binds only the input actually being clicked instead of rebinding both on every select. --- frontend/src/component/Uploader/core/index.ts | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/frontend/src/component/Uploader/core/index.ts b/frontend/src/component/Uploader/core/index.ts index 3d3021eb..5ccbeddf 100644 --- a/frontend/src/component/Uploader/core/index.ts +++ b/frontend/src/component/Uploader/core/index.ts @@ -169,11 +169,22 @@ export default class UploadManager { throw new UploaderError(UploaderErrorName.NoPolicySelected, "No policy selected."); } - this.fileInput.onchange = (ev: Event) => this.addFiles(ev, dst, resolve, reject); - this.directoryInput.onchange = (ev: Event) => this.addFiles(ev, dst, resolve, reject); - this.fileInput.value = ""; - this.directoryInput.value = ""; - type == SelectType.File ? this.fileInput.click() : this.directoryInput.click(); + const input = type == SelectType.File ? this.fileInput : this.directoryInput; + // Some mobile pickers deliver multi-selections via several `change` + // events; the first resolve would swallow the rest. Accumulate and + // resolve once the picker goes quiet (#3571). + let files: File[] = []; + let timer: ReturnType | undefined; + input.onchange = (ev: Event) => { + const target = ev.target as HTMLInputElement; + if (target?.files?.length) { + files = files.concat(Array.from(target.files)); + } + clearTimeout(timer); + timer = setTimeout(() => this.addFiles(files, dst, resolve, reject), 400); + }; + input.value = ""; + input.click(); }); };