fix(zim): stabilize uploader state to avoid cancel on tab refocus

Also improves surfacing of upload error messages
This commit is contained in:
jakeaturner 2026-06-20 03:36:40 +00:00
parent a7d05859d4
commit fad9e30ddc
No known key found for this signature in database
GPG Key ID: B1072EBDEECE328D
3 changed files with 23 additions and 9 deletions

View File

@ -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<unknown>
}) => {
// 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 (
<Dashboard

View File

@ -30,6 +30,7 @@ export default function ZimPage() {
const { data, isLoading } = useQuery<ZimFileWithMetadata[]>({
queryKey: ['zim-files'],
queryFn: getFiles,
refetchOnWindowFocus: false,
})
async function getFiles() {

View File

@ -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": [