import { useEffect, useRef } from 'react'; import { useLiveQuery } from 'dexie-react-hooks'; import { db } from '@/lib/db/db'; import { cloudUsername, pushBackup } from '@/lib/cloud/client'; import { useConnectivityStore } from '@/stores/connectivityStore'; import { useDebouncedCallback } from '@/lib/useDebouncedCallback'; /** * Autosave to the cloud while signed in: whenever durable data changes, debounce * a full backup push. A fingerprint of (id:updatedAt) over the durable tables is * watched via liveQuery, so it reacts to edits, adds, and deletes alike. High-churn * tables (dice rolls, the session recap) are excluded so a roll doesn't trigger a * save. No-op when signed out. Mounted once in the app shell. */ export function useCloudAutosave(): void { const fingerprint = useLiveQuery(async () => { const stamp = async (rows: Promise>) => (await rows).map((r) => `${r.id ?? r.campaignId ?? ''}:${r.updatedAt ?? ''}`).join(','); const [c, ch, e, n, np, q, m, h, cal] = await Promise.all([ stamp(db.campaigns.toArray()), stamp(db.characters.toArray()), stamp(db.encounters.toArray()), stamp(db.notes.toArray()), stamp(db.npcs.toArray()), stamp(db.quests.toArray()), stamp(db.maps.toArray()), stamp(db.homebrew.toArray()), stamp(db.calendars.toArray()), ]); return [c, ch, e, n, np, q, m, h, cal].join('|'); }, [], undefined); const save = useDebouncedCallback(() => { if (!cloudUsername()) return; const conn = useConnectivityStore.getState(); conn.setCloudSync('saving'); void pushBackup() .then(() => useConnectivityStore.getState().setCloudSync('saved')) .catch(() => useConnectivityStore.getState().setCloudSync('error')); // offline / transient — next change retries }, 8000); // Skip the initial load; only push once data actually changes after mount. const prev = useRef(undefined); useEffect(() => { if (fingerprint === undefined) return; if (prev.current === undefined) { prev.current = fingerprint; return; } if (prev.current !== fingerprint) { prev.current = fingerprint; save(); } }, [fingerprint, save]); }