import { useEffect, useCallback, useRef, useState } from 'react' import { shallow } from 'zustand/shallow' import { saveDomiBoardContent, getDomiBoardContent, } from '../service/api' import { useDominoStoreInstance } from '../components/canvas' import type { SceneElement, DominoCanvasData } from '../components/canvas' export function usePersistence(options: { taskId: string setLoading: (loading: boolean) => void }) { const { taskId, setLoading } = options const store = useDominoStoreInstance() const isFirstLoad = useRef(true) const [initialized, setInitialized] = useState(false) // 任务切换时重置状态 useEffect(() => { isFirstLoad.current = true setInitialized(false) }, [taskId]) const loadBoard = useCallback(async () => { if (!taskId) { setLoading(false) setInitialized(true) return } const currentTaskId = taskId setLoading(true) try { const res = (await getDomiBoardContent(taskId)) as | DominoCanvasData | string if (taskId !== currentTaskId) return if (res) { try { const parsed = ( typeof res === 'string' ? JSON.parse(res) : res ) as DominoCanvasData if (parsed && typeof parsed === 'object') { const { elements, elementOrder, createdAt, updatedAt } = parsed const state = store.getState() state.clearElements() Object.values(elements).forEach(el => { state.addElement(el) }) // addElement might have added them in different order, so we overwrite it. store.setState({ elementOrder, metadata: { createdAt: createdAt || Date.now(), updatedAt: updatedAt || Date.now(), }, }) state.resetHistory() setLoading(false) isFirstLoad.current = false setInitialized(true) } } catch (e) { console.error('Failed to parse saved DomiBoard content', e) } } } catch (e) { console.error('Failed to load DomiBoard content', e) } finally { if (taskId === currentTaskId) { if (isFirstLoad.current) { isFirstLoad.current = false } setLoading(false) setInitialized(true) store.getState().resetHistory() } } }, [taskId, setLoading]) const saveTimerRef = useRef(null) // Auto-save logic useEffect(() => { let lastState = { elements: store.getState().elements, elementOrder: store.getState().elementOrder, } const unsubscribe = store.subscribe(state => { const curr = { elements: state.elements, elementOrder: state.elementOrder, } // Check if relevant state changed using shallow comparison if (shallow(lastState, curr)) return lastState = curr if (isFirstLoad.current || !taskId) return if (saveTimerRef.current) { clearTimeout(saveTimerRef.current) } saveTimerRef.current = setTimeout(async () => { try { const currentMetadata = store.getState().metadata || {} const createdAt = currentMetadata.createdAt || Date.now() const persistentElements: Record = {} const persistentOrder: string[] = [] Object.values(curr.elements).forEach(el => { if (el.type !== 'placeholder') { persistentElements[el.id] = el } }) curr.elementOrder.forEach((id: string) => { if (curr.elements[id] && curr.elements[id].type !== 'placeholder') { persistentOrder.push(id) } }) const persistenceData: DominoCanvasData = { elements: persistentElements, elementOrder: persistentOrder, createdAt, updatedAt: Date.now(), } await saveDomiBoardContent({ task_id: taskId, data: persistenceData, }) } catch (e) { console.error('Failed to auto-save DomiBoard content', e) } }, 2000) // 2 second debounce }) return () => { unsubscribe() if (saveTimerRef.current) { clearTimeout(saveTimerRef.current) } } }, [taskId, store]) return { loadBoard, initialized } }