"use client"; import { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react"; import { subscribeQueueRemove } from "./watchQueueEvents"; const STORAGE_KEY = "pinkudex.watch-queue"; type Ctx = { ids: number[]; has: (id: number) => boolean; add: (id: number) => void; addMany: (ids: number[]) => void; remove: (id: number) => void; removeMany: (ids: number[]) => void; toggle: (id: number) => void; clear: () => void; }; const WatchQueueCtx = createContext(null); function readStorage(): number[] { if (typeof window === "undefined") return []; try { const raw = localStorage.getItem(STORAGE_KEY); if (!raw) return []; const parsed = JSON.parse(raw); if (!Array.isArray(parsed)) return []; return parsed.filter((n): n is number => typeof n === "number"); } catch { return []; } } export function WatchQueueProvider({ children }: { children: React.ReactNode }) { const [ids, setIds] = useState([]); const [hydrated, setHydrated] = useState(false); // Hydrate from localStorage. SSR renders an empty queue; this fills it // on mount so the server-rendered HTML always matches initial paint. useEffect(() => { setIds(readStorage()); setHydrated(true); }, []); // Persist on change (after hydration, so we don't blow away storage with // the empty-array initial state). useEffect(() => { if (!hydrated) return; try { localStorage.setItem(STORAGE_KEY, JSON.stringify(ids)); } catch {} }, [ids, hydrated]); // Cross-tab sync. useEffect(() => { function onStorage(e: StorageEvent) { if (e.key !== STORAGE_KEY) return; setIds(readStorage()); } window.addEventListener("storage", onStorage); return () => window.removeEventListener("storage", onStorage); }, []); // External signal — covers fire this after their watched flag flips true. useEffect(() => subscribeQueueRemove((detail) => { const drop = new Set(detail); setIds((cur) => cur.filter((id) => !drop.has(id))); }), []); const add = useCallback((id: number) => { setIds((cur) => (cur.includes(id) ? cur : [...cur, id])); }, []); const addMany = useCallback((newIds: number[]) => { setIds((cur) => { const have = new Set(cur); const merged = [...cur]; for (const id of newIds) if (!have.has(id)) { merged.push(id); have.add(id); } return merged; }); }, []); const remove = useCallback((id: number) => { setIds((cur) => cur.filter((x) => x !== id)); }, []); const removeMany = useCallback((dropIds: number[]) => { const drop = new Set(dropIds); setIds((cur) => cur.filter((id) => !drop.has(id))); }, []); const toggle = useCallback((id: number) => { setIds((cur) => (cur.includes(id) ? cur.filter((x) => x !== id) : [...cur, id])); }, []); const clear = useCallback(() => setIds([]), []); const value = useMemo(() => ({ ids, has: (id) => ids.includes(id), add, addMany, remove, removeMany, toggle, clear, }), [ids, add, addMany, remove, removeMany, toggle, clear]); return {children}; } export function useWatchQueue(): Ctx { const ctx = useContext(WatchQueueCtx); if (!ctx) throw new Error("useWatchQueue must be used within WatchQueueProvider"); return ctx; }