import { useCallback, useMemo, useState, useSyncExternalStore } from "react"; const UPDATED = "paperclip:pinned-navigation-updated"; const EMPTY = "[]"; function read(key: string | null) { try { return key ? localStorage.getItem(key) ?? EMPTY : EMPTY; } catch { return EMPTY; } } function parse(value: string): string[] { try { const entries: unknown = JSON.parse(value); return Array.isArray(entries) ? [...new Set(entries.filter((entry): entry is string => typeof entry === "string"))] : []; } catch { return []; } } /** Personal browser navigation preference. Stores route names, never work data. */ export function usePinnedNavigation(userId: string | null | undefined) { const key = userId ? `paperclip.coordinator.pinned-nav.v1:${encodeURIComponent(userId)}` : null; const [failure, setFailure] = useState<{ key: string; message: string } | null>(null); const subscribe = useCallback((notify: () => void) => { const storage = (event: StorageEvent) => { if (event.key === key || event.key === null) notify(); }; const updated = (event: Event) => { if ((event as CustomEvent).detail === key) notify(); }; window.addEventListener("storage", storage); window.addEventListener(UPDATED, updated); return () => { window.removeEventListener("storage", storage); window.removeEventListener(UPDATED, updated); }; }, [key]); const snapshot = useSyncExternalStore(subscribe, () => read(key), () => EMPTY); const pins = useMemo(() => parse(snapshot), [snapshot]); function save(next: string[]) { if (!key) return; try { localStorage.setItem(key, JSON.stringify(next)); setFailure(null); window.dispatchEvent(new CustomEvent(UPDATED, { detail: key })); } catch { setFailure({ key, message: "Couldn’t save pinned items. Allow browser storage and try again." }); } } function toggle(to: string) { if (!key) return; const current = parse(read(key)); save(current.includes(to) ? current.filter(entry => entry !== to) : [...current, to]); } function reorder(visibleOrder: string[]) { if (!key) return; const current = parse(read(key)); const currentKeys = new Set(current); const ordered = [...new Set(visibleOrder)].filter(entry => currentKeys.has(entry)); const visible = new Set(ordered); let index = 0; save(current.map(entry => visible.has(entry) ? ordered[index++] : entry)); } return { pins, toggle, reorder, canPin: !!key, error: failure?.key === key ? failure.message : null }; }