| "use client"; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| import { useEffect, useRef } from "react"; |
| import { useChatStore } from "./chatStore"; |
| import { loadConversations, saveConversations } from "./sync"; |
|
|
| const SAVE_DEBOUNCE_MS = 1500; |
|
|
| export function useBackendSync() { |
| const hydrated = useChatStore((s) => s.hydrated); |
| const conversations = useChatStore((s) => s.conversations); |
| const activeId = useChatStore((s) => s.activeId); |
|
|
| const initialFetchStarted = useRef(false); |
| const saveTimer = useRef<ReturnType<typeof setTimeout> | null>(null); |
| const inFlight = useRef<AbortController | null>(null); |
|
|
| |
| useEffect(() => { |
| if (initialFetchStarted.current) return; |
| initialFetchStarted.current = true; |
|
|
| let cancelled = false; |
| (async () => { |
| const remote = await loadConversations(); |
| if (cancelled) return; |
| const store = useChatStore.getState(); |
| if (remote && remote.conversations) { |
| store._hydrateFromRemote({ |
| conversations: remote.conversations, |
| activeId: remote.activeId ?? null, |
| }); |
| } else { |
| |
| store._setHydrated(true); |
| } |
| })(); |
|
|
| return () => { |
| cancelled = true; |
| }; |
| }, []); |
|
|
| |
| useEffect(() => { |
| if (!hydrated) return; |
|
|
| if (saveTimer.current) clearTimeout(saveTimer.current); |
| saveTimer.current = setTimeout(() => { |
| |
| if (inFlight.current) inFlight.current.abort(); |
| const ctrl = new AbortController(); |
| inFlight.current = ctrl; |
|
|
| |
| |
| const sanitized: Record<string, typeof conversations[string]> = {}; |
| for (const [id, conv] of Object.entries(conversations)) { |
| sanitized[id] = { |
| ...conv, |
| turns: conv.turns.filter((t) => !t.pending), |
| }; |
| } |
|
|
| const setStatus = useChatStore.getState()._setSyncStatus; |
| setStatus("saving"); |
| saveConversations( |
| { |
| conversations: sanitized, |
| activeId, |
| }, |
| ctrl.signal |
| ).then((ok) => { |
| if (ctrl.signal.aborted) return; |
| setStatus(ok ? "idle" : "error"); |
| if (ok) { |
| useChatStore.setState({ lastSavedAt: Date.now() }); |
| } |
| }); |
| }, SAVE_DEBOUNCE_MS); |
|
|
| return () => { |
| if (saveTimer.current) clearTimeout(saveTimer.current); |
| }; |
| }, [hydrated, conversations, activeId]); |
| } |
|
|