refactor: deta histroy
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Hook for canvas graph state and persistence. Wraps useGraphStateWithHistory
|
||||
* with initial graph from project storage (or example) and debounced save.
|
||||
* with initial graph from project storage (or example) and debounced + idle-based save.
|
||||
* Keeps CanvasPage focused on composition and layout.
|
||||
*/
|
||||
|
||||
@@ -12,20 +12,65 @@ import type { AppNode, AppEdge } from '@/lib/graph/nodeTypes'
|
||||
|
||||
export type UseCanvasGraphResult = ReturnType<typeof useGraphStateWithHistory>
|
||||
|
||||
/** Debounce delay (ms) before we schedule a save. */
|
||||
const SAVE_DEBOUNCE_MS = 800
|
||||
/** Max wait (ms) for requestIdleCallback before falling back to setTimeout. */
|
||||
const SAVE_IDLE_TIMEOUT_MS = 2000
|
||||
|
||||
export function useCanvasGraph(projectId: string | undefined): UseCanvasGraphResult {
|
||||
const initialGraph = useMemo(() => getInitialGraph(projectId), [projectId])
|
||||
const result = useGraphStateWithHistory(initialGraph.nodes, initialGraph.edges)
|
||||
const { nodes, edges } = result
|
||||
|
||||
const saveTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||
const idleCallbackRef = useRef<number | null>(null)
|
||||
const pendingSaveRef = useRef<{ projectId: string; nodes: AppNode[]; edges: AppEdge[] } | null>(
|
||||
null
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (!projectId) return
|
||||
const save = () => {
|
||||
saveGraphToStorage(projectId, { version: PROJECT_VERSION, nodes, edges })
|
||||
|
||||
const scheduleSave = () => {
|
||||
pendingSaveRef.current = { projectId, nodes, edges }
|
||||
|
||||
const doSave = () => {
|
||||
const pending = pendingSaveRef.current
|
||||
pendingSaveRef.current = null
|
||||
if (pending && pending.projectId === projectId) {
|
||||
saveGraphToStorage(pending.projectId, {
|
||||
version: PROJECT_VERSION,
|
||||
nodes: pending.nodes,
|
||||
edges: pending.edges,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof requestIdleCallback !== 'undefined') {
|
||||
idleCallbackRef.current = requestIdleCallback(doSave, {
|
||||
timeout: SAVE_IDLE_TIMEOUT_MS,
|
||||
})
|
||||
} else {
|
||||
idleCallbackRef.current = window.setTimeout(doSave, 0) as unknown as number
|
||||
}
|
||||
}
|
||||
saveTimeoutRef.current = setTimeout(save, 500)
|
||||
|
||||
if (saveTimeoutRef.current) clearTimeout(saveTimeoutRef.current)
|
||||
saveTimeoutRef.current = setTimeout(scheduleSave, SAVE_DEBOUNCE_MS)
|
||||
|
||||
return () => {
|
||||
if (saveTimeoutRef.current) clearTimeout(saveTimeoutRef.current)
|
||||
if (saveTimeoutRef.current) {
|
||||
clearTimeout(saveTimeoutRef.current)
|
||||
saveTimeoutRef.current = null
|
||||
}
|
||||
if (idleCallbackRef.current != null) {
|
||||
if (typeof cancelIdleCallback !== 'undefined') {
|
||||
cancelIdleCallback(idleCallbackRef.current)
|
||||
} else {
|
||||
clearTimeout(idleCallbackRef.current)
|
||||
}
|
||||
idleCallbackRef.current = null
|
||||
}
|
||||
}
|
||||
}, [projectId, nodes, edges])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user