import { useSyncExternalStore } from 'react'; // Local-only friend nickname overrides. Stored in localStorage keyed by the // peer's user-id. Empty/missing value = use the real display name. // Local-only by design — friends never see your nickname for them. const STORAGE_KEY = 'chatapp.friendNicknames.v1'; let cache: Record | null = null; const listeners = new Set<() => void>(); function load(): Record { if (cache) return cache; try { const raw = window.localStorage.getItem(STORAGE_KEY); if (!raw) { cache = {}; return cache; } const parsed = JSON.parse(raw) as unknown; if (parsed && typeof parsed === 'object') { cache = {}; for (const [k, v] of Object.entries(parsed as Record)) { if (typeof v === 'string' && v.trim().length > 0) cache[k] = v; } return cache; } } catch { /* corrupted; fall through */ } cache = {}; return cache; } function persist(): void { try { window.localStorage.setItem(STORAGE_KEY, JSON.stringify(cache ?? {})); } catch { /* quota / private mode */ } for (const l of listeners) l(); } export function getNickname(userId: string): string | null { return load()[userId] ?? null; } export function setNickname(userId: string, nickname: string | null): void { const store = load(); const trimmed = nickname?.trim() ?? ''; if (trimmed.length === 0) { if (!(userId in store)) return; delete store[userId]; } else { if (store[userId] === trimmed) return; store[userId] = trimmed; } persist(); } // Reactive hook: returns the current nickname for a user, or `fallback` // when no nickname is set. Re-renders when ANY nickname changes (cheap, // the set is small). export function useNickname(userId: string | null | undefined, fallback: string): string { const subscribe = (cb: () => void) => { listeners.add(cb); return () => { listeners.delete(cb); }; }; const getSnapshot = () => (userId ? getNickname(userId) : null); const nickname = useSyncExternalStore(subscribe, getSnapshot, getSnapshot); return nickname ?? fallback; }