64 lines
2.3 KiB
TypeScript
64 lines
2.3 KiB
TypeScript
/**
|
|
* Hook for canvas graph state and persistence. Wraps useGraphStateWithHistory
|
|
* with initial graph from recollection storage (or example). Save is explicit via save().
|
|
*/
|
|
|
|
import { useCallback, useMemo, useRef, useState } from 'react'
|
|
import { useGraphStateWithHistory } from '@/hooks/useGraphStateWithHistory'
|
|
import { getInitialGraph } from '@/app/canvas/canvasGraphUtils'
|
|
import { saveGraphToStorage, RECOLLECTION_VERSION } from '@/app/recollections/recollectionGraphStorage'
|
|
import type { AppNode, AppEdge } from '@/lib/graph/nodeTypes'
|
|
|
|
export type SaveStatus = 'saved' | 'unsaved' | 'saving'
|
|
|
|
export type UseCanvasGraphResult = ReturnType<typeof useGraphStateWithHistory> & {
|
|
/** Persist current nodes/edges to storage. No-op when recollectionId is missing. */
|
|
save: () => void
|
|
/** For menubar: show "Unsaved changes" | "Saving…" | "All changes saved". */
|
|
saveStatus: SaveStatus
|
|
}
|
|
|
|
export function useCanvasGraph(recollectionId: string | undefined): UseCanvasGraphResult {
|
|
const initialGraph = useMemo(() => getInitialGraph(recollectionId), [recollectionId])
|
|
const result = useGraphStateWithHistory(initialGraph.nodes, initialGraph.edges)
|
|
const { nodes, edges } = result
|
|
|
|
const nodesRef = useRef(nodes)
|
|
const edgesRef = useRef(edges)
|
|
nodesRef.current = nodes
|
|
edgesRef.current = edges
|
|
|
|
const [isSaving, setIsSaving] = useState(false)
|
|
const [lastSavedSerialized, setLastSavedSerialized] = useState(() =>
|
|
JSON.stringify({ nodes: initialGraph.nodes, edges: initialGraph.edges })
|
|
)
|
|
|
|
const currentSerialized = useMemo(
|
|
() => JSON.stringify({ nodes, edges }),
|
|
[nodes, edges]
|
|
)
|
|
const isDirty = currentSerialized !== lastSavedSerialized
|
|
const saveStatus: SaveStatus = isSaving ? 'saving' : isDirty ? 'unsaved' : 'saved'
|
|
|
|
const save = useCallback(() => {
|
|
if (!recollectionId) return
|
|
const snapshot = JSON.stringify({
|
|
nodes: nodesRef.current,
|
|
edges: edgesRef.current,
|
|
})
|
|
setIsSaving(true)
|
|
saveGraphToStorage(recollectionId, {
|
|
version: RECOLLECTION_VERSION,
|
|
nodes: nodesRef.current,
|
|
edges: edgesRef.current,
|
|
})
|
|
const SAVING_DISPLAY_MS = 360
|
|
setTimeout(() => {
|
|
setLastSavedSerialized(snapshot)
|
|
setIsSaving(false)
|
|
}, SAVING_DISPLAY_MS)
|
|
}, [recollectionId])
|
|
|
|
return { ...result, save, saveStatus }
|
|
}
|