"use client"; // Which archives the hub's search covers, per browser. Every archive is in by // default; the visitor takes one out with its scope chip. Stored as the set of // origins switched OFF, so an archive the browser has never seen (a new // official instance, a site added later) is searched until someone says not to. // localStorage is a convenience here: when it is unavailable every archive is // simply searched. import { useCallback, useEffect, useRef, useState } from "react"; const STORAGE_KEY = "ytdlp-tb:hub-scope"; function readOff(): ReadonlySet { if (typeof window === "undefined") return new Set(); try { const raw = window.localStorage.getItem(STORAGE_KEY); const parsed: unknown = raw ? JSON.parse(raw) : null; const off = parsed && typeof parsed === "object" && Array.isArray((parsed as { off?: unknown }).off) ? (parsed as { off: unknown[] }).off : []; return new Set(off.filter((o): o is string => typeof o === "string")); } catch { return new Set(); } } function writeOff(off: ReadonlySet) { try { window.localStorage.setItem( STORAGE_KEY, JSON.stringify({ off: Array.from(off) }), ); } catch { // Storage full / disabled — the choice still holds for this visit. } } export function useHubScope() { // Read on the client's first render. The registry's server snapshot is empty, // so nothing that depends on this renders during hydration. const [off, setOff] = useState>(readOff); const first = useRef(true); useEffect(() => { if (first.current) { first.current = false; return; } writeOff(off); }, [off]); const toggle = useCallback((origin: string) => { setOff((prev) => { const next = new Set(prev); if (next.has(origin)) next.delete(origin); else next.add(origin); return next; }); }, []); const isOn = useCallback((origin: string) => !off.has(origin), [off]); return { isOn, toggle }; }