diff --git a/admin/inertia/components/ZimUploader/index.tsx b/admin/inertia/components/ZimUploader/index.tsx index a734c0f..7f08398 100644 --- a/admin/inertia/components/ZimUploader/index.tsx +++ b/admin/inertia/components/ZimUploader/index.tsx @@ -16,6 +16,16 @@ export default function ZimUploader({ onUploadComplete, existingFilenames }: Zim existingFilenamesRef.current = existingFilenames }, [existingFilenames]) + // Keep the latest callback in a ref so the Uppy lifecycle effect below does + // not depend on it. The parent passes a new inline function on every render, + // and if that identity were in the effect deps, a re-render mid-upload (e.g. + // a window-focus refetch) would tear down and `uppy.destroy()` the instance, + // aborting the in-flight upload. + const onUploadCompleteRef = useRef(onUploadComplete) + useEffect(() => { + onUploadCompleteRef.current = onUploadComplete + }, [onUploadComplete]) + const [uppy] = useState(() => new Uppy({ restrictions: { @@ -53,9 +63,17 @@ export default function ZimUploader({ onUploadComplete, existingFilenames }: Zim } } } - const handleComplete = (result: { successful: Array<{ response?: { body?: { added?: number } } }> }) => { + const handleComplete = (result: { + successful: Array<{ response?: { body?: { added?: number } } }> + failed: Array + }) => { + // Uppy emits `complete` even when uploads fail or are aborted. Only treat + // the run as a success when nothing failed — otherwise leave the uploader + // open so the Dashboard can surface the error instead of closing it with a + // false "Upload complete" toast. + if (result.failed.length > 0) return const added = result.successful.reduce((sum, f) => sum + (f.response?.body?.added ?? 0), 0) - onUploadComplete(added) + onUploadCompleteRef.current(added) } uppy.on('file-added', handleFileAdded) uppy.on('complete', handleComplete) @@ -64,7 +82,7 @@ export default function ZimUploader({ onUploadComplete, existingFilenames }: Zim uppy.off('complete', handleComplete) uppy.destroy() } - }, [uppy, onUploadComplete]) + }, [uppy]) return ( ({ queryKey: ['zim-files'], queryFn: getFiles, + refetchOnWindowFocus: false, }) async function getFiles() { diff --git a/admin/package-lock.json b/admin/package-lock.json index fe48e34..0a18db6 100644 --- a/admin/package-lock.json +++ b/admin/package-lock.json @@ -35,6 +35,7 @@ "@uppy/core": "5.2.0", "@uppy/dashboard": "5.1.0", "@uppy/react": "5.1.1", + "@uppy/xhr-upload": "5.2.0", "@vinejs/vine": "3.0.1", "@vitejs/plugin-react": "4.7.0", "autoprefixer": "10.5.0", @@ -4589,7 +4590,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -4606,7 +4606,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -4623,7 +4622,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -4716,7 +4714,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -4733,7 +4730,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -4750,7 +4746,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [