Skip to content

Commit f20412c

Browse files
committed
separate debounced autosave
1 parent a366acf commit f20412c

1 file changed

Lines changed: 41 additions & 28 deletions

File tree

frontend/src/pages/Editor.tsx

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ export const Editor: React.FC = () => {
124124
const latestFilesRef = useRef<any>(null);
125125
const lastSyncedFilesRef = useRef<Record<string, any>>({});
126126
const latestAppStateRef = useRef<any>(null);
127-
const debouncedSaveRef = useRef<((elements: readonly any[], appState: any) => void) | null>(null);
127+
const debouncedSaveRef = useRef<((drawingId: string, elements: readonly any[], appState: any) => void) | null>(null);
128128

129129
const emitFilesDeltaIfNeeded = useCallback(
130130
(nextFiles: Record<string, any>) => {
@@ -361,13 +361,13 @@ export const Editor: React.FC = () => {
361361
const didEmit = emitFilesDeltaIfNeeded(nextFiles);
362362

363363
// Persist after file data becomes available so new tabs (tab3) load correctly.
364-
if (didEmit && latestAppStateRef.current && debouncedSaveRef.current) {
365-
debouncedSaveRef.current(latestElementsRef.current, latestAppStateRef.current);
364+
if (didEmit && id && latestAppStateRef.current && debouncedSaveRef.current) {
365+
debouncedSaveRef.current(id, latestElementsRef.current, latestAppStateRef.current);
366366
}
367367
};
368368
}
369369
setIsReady(true);
370-
}, [emitFilesDeltaIfNeeded]);
370+
}, [emitFilesDeltaIfNeeded, id]);
371371

372372
// Handle #addLibrary URL hash parameter for importing libraries from links
373373
useEffect(() => {
@@ -428,12 +428,12 @@ export const Editor: React.FC = () => {
428428
scrollToContent: true,
429429
}), []);
430430

431-
const saveDataRef = useRef<((elements: readonly any[], appState: any) => Promise<void>) | null>(null);
432-
const savePreviewRef = useRef<((elements: readonly any[], appState: any, files: any) => Promise<void>) | null>(null);
431+
const saveDataRef = useRef<((drawingId: string, elements: readonly any[], appState: any) => Promise<void>) | null>(null);
432+
const savePreviewRef = useRef<((drawingId: string, elements: readonly any[], appState: any, files: any) => Promise<void>) | null>(null);
433433
const saveLibraryRef = useRef<((items: any[]) => Promise<void>) | null>(null);
434434

435-
saveDataRef.current = async (elements: readonly any[], appState: any) => {
436-
if (!id) return;
435+
saveDataRef.current = async (drawingId: string, elements: readonly any[], appState: any) => {
436+
if (!drawingId) return;
437437

438438
try {
439439
const persistableAppState = {
@@ -446,27 +446,27 @@ export const Editor: React.FC = () => {
446446
const persistableElements = Array.isArray(snapshot) ? snapshot : [];
447447

448448
console.log("[Editor] Saving drawing", {
449-
drawingId: id,
449+
drawingId,
450450
elementCount: persistableElements.length,
451451
hasRenderableElements: persistableElements.some((el: any) => !el?.isDeleted),
452452
appState: persistableAppState,
453453
});
454454

455-
await api.updateDrawing(id, {
455+
await api.updateDrawing(drawingId, {
456456
elements: persistableElements,
457457
appState: persistableAppState,
458458
files: latestFilesRef.current || {},
459459
});
460460

461-
console.log("[Editor] Save complete", { drawingId: id });
461+
console.log("[Editor] Save complete", { drawingId });
462462
} catch (err) {
463463
console.error('Failed to save drawing', err);
464464
toast.error("Failed to save changes");
465465
}
466466
};
467467

468-
savePreviewRef.current = async (elements: readonly any[], appState: any, files: any) => {
469-
if (!id) return;
468+
savePreviewRef.current = async (drawingId: string, elements: readonly any[], appState: any, files: any) => {
469+
if (!drawingId) return;
470470

471471
try {
472472
const currentSnapshot = latestElementsRef.current ?? elements;
@@ -484,13 +484,13 @@ export const Editor: React.FC = () => {
484484
const preview = svg.outerHTML;
485485

486486
console.log("[Editor] Saving preview", {
487-
drawingId: id,
487+
drawingId,
488488
elementCount: currentSnapshot.length,
489489
});
490490

491-
await api.updateDrawing(id, { preview });
491+
await api.updateDrawing(drawingId, { preview });
492492

493-
console.log("[Editor] Preview save complete", { drawingId: id });
493+
console.log("[Editor] Preview save complete", { drawingId });
494494
} catch (err) {
495495
console.error('Failed to save preview', err);
496496
}
@@ -509,19 +509,19 @@ export const Editor: React.FC = () => {
509509

510510

511511
const debouncedSave = useCallback(
512-
debounce((elements, appState) => {
512+
debounce((drawingId, elements, appState) => {
513513
if (saveDataRef.current) {
514-
saveDataRef.current(elements, appState);
514+
saveDataRef.current(drawingId, elements, appState);
515515
}
516516
}, 1000),
517517
[] // Empty dependency array = Stable across renders
518518
);
519519
// Allow non-hook code (e.g., Excalidraw API wrappers) to trigger debounced saves.
520520
debouncedSaveRef.current = debouncedSave;
521521
const debouncedSavePreview = useCallback(
522-
debounce((elements, appState, files) => {
522+
debounce((drawingId, elements, appState, files) => {
523523
if (savePreviewRef.current) {
524-
savePreviewRef.current(elements, appState, files);
524+
savePreviewRef.current(drawingId, elements, appState, files);
525525
}
526526
}, 10000),
527527
[]
@@ -536,6 +536,13 @@ export const Editor: React.FC = () => {
536536
[]
537537
);
538538

539+
useEffect(() => {
540+
return () => {
541+
debouncedSave.cancel();
542+
debouncedSavePreview.cancel();
543+
};
544+
}, [debouncedSave, debouncedSavePreview]);
545+
539546
const broadcastChanges = useCallback(
540547
throttle((elements: readonly any[], currentFiles?: Record<string, any>) => {
541548
if (!socketRef.current || !id) return;
@@ -670,8 +677,9 @@ export const Editor: React.FC = () => {
670677
const files = excalidrawAPI.current.getFiles() || {};
671678
latestElementsRef.current = elements;
672679
latestFilesRef.current = files;
673-
await saveDataRef.current(elements, appState);
674-
savePreviewRef.current(elements, appState, files);
680+
if (!id) return;
681+
await saveDataRef.current(id, elements, appState);
682+
savePreviewRef.current(id, elements, appState, files);
675683
toast.success("Saved changes to server");
676684
}
677685
}
@@ -739,7 +747,9 @@ export const Editor: React.FC = () => {
739747
elementCount: allElements.length,
740748
hasRenderableElements,
741749
});
742-
debouncedSave(allElements, appState);
750+
if (id) {
751+
debouncedSave(id, allElements, appState);
752+
}
743753

744754
// Trigger Slow Preview Gen
745755
const filesSnapshot = currentFiles;
@@ -748,8 +758,10 @@ export const Editor: React.FC = () => {
748758
drawingId: id,
749759
fileCount: Object.keys(filesSnapshot).length,
750760
});
751-
debouncedSavePreview(allElements, appState, filesSnapshot);
752-
}, [debouncedSave, debouncedSavePreview, broadcastChanges]);
761+
if (id) {
762+
debouncedSavePreview(id, allElements, appState, filesSnapshot);
763+
}
764+
}, [debouncedSave, debouncedSavePreview, broadcastChanges, id]);
753765

754766
// Ensure file-only updates (e.g. pasted image dataURL arriving asynchronously)
755767
// are still broadcast to collaborators AND persisted to the server.
@@ -767,7 +779,7 @@ export const Editor: React.FC = () => {
767779

768780
// Persist after file data becomes available (covers the "tab 3" case).
769781
if (didEmit && latestAppStateRef.current && debouncedSaveRef.current) {
770-
debouncedSaveRef.current(latestElementsRef.current, latestAppStateRef.current);
782+
debouncedSaveRef.current(id, latestElementsRef.current, latestAppStateRef.current);
771783
}
772784
}, 1000);
773785

@@ -803,15 +815,16 @@ export const Editor: React.FC = () => {
803815
// Save drawing and generate preview before navigating
804816
try {
805817
if (excalidrawAPI.current && saveDataRef.current && savePreviewRef.current) {
818+
if (!id) return;
806819
const elements = excalidrawAPI.current.getSceneElementsIncludingDeleted();
807820
const appState = excalidrawAPI.current.getAppState();
808821
const files = excalidrawAPI.current.getFiles() || {};
809822
latestElementsRef.current = elements;
810823
latestFilesRef.current = files;
811824

812825
await Promise.all([
813-
saveDataRef.current(elements, appState),
814-
savePreviewRef.current(elements, appState, files)
826+
saveDataRef.current(id, elements, appState),
827+
savePreviewRef.current(id, elements, appState, files)
815828
]);
816829
console.log("[Editor] Saved on back navigation", { drawingId: id });
817830
}

0 commit comments

Comments
 (0)