diff --git a/packages/excalidraw/data/filesystem.ts b/packages/excalidraw/data/filesystem.ts index 186d58734..d9880a700 100644 --- a/packages/excalidraw/data/filesystem.ts +++ b/packages/excalidraw/data/filesystem.ts @@ -22,7 +22,6 @@ export const fileOpen = (opts: { const mimeTypes = opts.extensions?.reduce((mimeTypes, type) => { mimeTypes.push(MIME_TYPES[type]); - return mimeTypes; }, [] as string[]); @@ -39,34 +38,60 @@ export const fileOpen = (opts: { mimeTypes, multiple: opts.multiple ?? false, legacySetup: (resolve, reject, input) => { + let isResolved = false; + let checkInterval: number | null = null; + + // Increased delay for iOS to ensure file selection is complete + const CHECK_INTERVAL = 100; // 100ms + const MAX_CHECKS = 50; // 5 seconds total + let checkCount = 0; + const scheduleRejection = debounce(reject, INPUT_CHANGE_INTERVAL_MS); - const focusHandler = () => { - checkForFile(); - document.addEventListener(EVENT.KEYUP, scheduleRejection); - document.addEventListener(EVENT.POINTER_UP, scheduleRejection); - scheduleRejection(); - }; + const checkForFile = () => { - // this hack might not work when expecting multiple files + if (isResolved) return; + if (input.files?.length) { + isResolved = true; const ret = opts.multiple ? [...input.files] : input.files[0]; resolve(ret as RetType); + return true; } + + checkCount++; + if (checkCount >= MAX_CHECKS) { + scheduleRejection(); + return true; + } + return false; }; + + const focusHandler = () => { + // Start checking for files only after focus event + checkInterval = window.setInterval(() => { + if (checkForFile()) { + clearInterval(checkInterval!); + } + }, CHECK_INTERVAL); + + document.addEventListener(EVENT.KEYUP, scheduleRejection); + document.addEventListener(EVENT.POINTER_UP, scheduleRejection); + }; + requestAnimationFrame(() => { window.addEventListener(EVENT.FOCUS, focusHandler); }); - const interval = window.setInterval(() => { - checkForFile(); - }, INPUT_CHANGE_INTERVAL_MS); + return (rejectPromise) => { - clearInterval(interval); + if (checkInterval) { + clearInterval(checkInterval); + } scheduleRejection.cancel(); window.removeEventListener(EVENT.FOCUS, focusHandler); document.removeEventListener(EVENT.KEYUP, scheduleRejection); document.removeEventListener(EVENT.POINTER_UP, scheduleRejection); - if (rejectPromise) { - // so that something is shown in console if we need to debug this + + if (rejectPromise && !isResolved) { console.warn("Opening the file was canceled (legacy-fs)."); rejectPromise(new AbortError()); }