import { useEffect, useLayoutEffect, useRef } from "react"; import type { Dump } from "../model.ts"; import { useWS } from "./useWS.ts"; interface DumpListSyncOptions { /** Keep private dumps visible (caller is the owner). */ isOwner?: boolean; /** * Only re-insert dumps created by this user when they become visible again. * Leave undefined to never re-insert (caller handles it, or no re-insertion needed). */ ownerId?: string; /** * When true, don't re-add a dump that isn't in the list (idx === -1). * Use this when the caller handles re-insertion itself (e.g. with a position map). */ skipReinsert?: boolean; } /** * Keeps a dump list in sync with real-time WS events: * - deletedDumpIds growing → filter * - lastDumpEvent "updated" → update in-place, or remove if now private * - lastDumpEvent for a not-in-list dump → prepend if ownerId matches and visible */ export function useDumpListSync( setDumps: (fn: (prev: Dump[]) => Dump[]) => void, options?: DumpListSyncOptions, ): void { const { deletedDumpIds, lastDumpEvent } = useWS(); // Keep refs up-to-date so closures in effects are never stale. const setDumpsRef = useRef(setDumps); const optionsRef = useRef(options); useLayoutEffect(() => { setDumpsRef.current = setDumps; optionsRef.current = options; }); useEffect(() => { if (deletedDumpIds.size === 0) return; setDumpsRef.current((prev) => prev.filter((d) => !deletedDumpIds.has(d.id)) ); }, [deletedDumpIds]); useEffect(() => { if (!lastDumpEvent) return; const { isOwner, ownerId, skipReinsert } = optionsRef.current ?? {}; const dump = lastDumpEvent; setDumpsRef.current((prev) => { const idx = prev.findIndex((d) => d.id === dump.id); if (idx !== -1) { // Remove if it became private and the viewer can't see private dumps. if (dump.isPrivate && !isOwner) { return prev.filter((d) => d.id !== dump.id); } const next = [...prev]; next[idx] = dump; return next; } // Dump not in list: only re-insert when ownerId is set and matches. if (skipReinsert || !ownerId) return prev; if (dump.userId !== ownerId) return prev; if (dump.isPrivate && !isOwner) return prev; return [dump, ...prev]; }); }, [lastDumpEvent]); }