import { useCallback, useEffect, useRef, type Dispatch, type SetStateAction, } from "react"; import { readCanvasSnapshot, writeCanvasSnapshot, } from "@/lib/canvas-local-persistence"; type UseCanvasLocalSnapshotPersistenceParams = { canvasId: string; nodes: TNode[]; edges: TEdge[]; setNodes: Dispatch>; setEdges: Dispatch>; debounceMs?: number; }; type PendingSnapshot = { canvasId: string; nodes: TNode[]; edges: TEdge[]; }; const DEFAULT_SNAPSHOT_DEBOUNCE_MS = 350; export function useCanvasLocalSnapshotPersistence({ canvasId, nodes, edges, setNodes, setEdges, debounceMs = DEFAULT_SNAPSHOT_DEBOUNCE_MS, }: UseCanvasLocalSnapshotPersistenceParams): void { const hasHydratedLocalSnapshotRef = useRef(false); const timeoutRef = useRef | null>(null); const pendingSnapshotRef = useRef | null>(null); const flushPendingSnapshot = useCallback(() => { if (timeoutRef.current) { clearTimeout(timeoutRef.current); timeoutRef.current = null; } const pendingSnapshot = pendingSnapshotRef.current; if (!pendingSnapshot) return; writeCanvasSnapshot(pendingSnapshot.canvasId, { nodes: pendingSnapshot.nodes, edges: pendingSnapshot.edges, }); pendingSnapshotRef.current = null; }, []); useEffect(() => { hasHydratedLocalSnapshotRef.current = false; flushPendingSnapshot(); const snapshot = readCanvasSnapshot(canvasId); if (snapshot) { setNodes(snapshot.nodes); setEdges(snapshot.edges); } hasHydratedLocalSnapshotRef.current = true; }, [canvasId, flushPendingSnapshot, setEdges, setNodes]); useEffect(() => { if (!hasHydratedLocalSnapshotRef.current) return; pendingSnapshotRef.current = { canvasId, nodes, edges }; if (timeoutRef.current) { clearTimeout(timeoutRef.current); } timeoutRef.current = setTimeout(() => { const pendingSnapshot = pendingSnapshotRef.current; if (!pendingSnapshot) return; writeCanvasSnapshot(pendingSnapshot.canvasId, { nodes: pendingSnapshot.nodes, edges: pendingSnapshot.edges, }); pendingSnapshotRef.current = null; timeoutRef.current = null; }, debounceMs); }, [canvasId, debounceMs, edges, nodes]); useEffect(() => { return () => flushPendingSnapshot(); }, [flushPendingSnapshot]); useEffect(() => { window.addEventListener("beforeunload", flushPendingSnapshot); window.addEventListener("pagehide", flushPendingSnapshot); return () => { window.removeEventListener("beforeunload", flushPendingSnapshot); window.removeEventListener("pagehide", flushPendingSnapshot); }; }, [flushPendingSnapshot]); }