"use client"; // Favoris et comparateur sans compte : stockés localement (localStorage), supprimables par l'utilisateur. Aucune donnée n'est envoyée au serveur. import { useCallback, useEffect, useState } from "react"; const KEY_FAV = "toveli:favorites:v1"; const KEY_CMP = "toveli:compare:v1"; const KEY_SEARCH = "toveli:searches:v1"; function read(k: string, fallback: T): T { if (typeof window === "undefined") return fallback; try { const v = localStorage.getItem(k); return v ? (JSON.parse(v) as T) : fallback; } catch { return fallback; } } function write(k: string, v: unknown) { try { localStorage.setItem(k, JSON.stringify(v)); window.dispatchEvent(new Event("toveli:storage")); } catch { /* quota */ } } function useLocalList(key: string, max: number) { const [items, setItems] = useState([]); useEffect(() => { const load = () => setItems(read(key, [])); load(); window.addEventListener("toveli:storage", load); window.addEventListener("storage", load); return () => { window.removeEventListener("toveli:storage", load); window.removeEventListener("storage", load); }; }, [key]); const toggle = useCallback((uid: string) => { const cur = read(key, []); const next = cur.includes(uid) ? cur.filter((x) => x !== uid) : [uid, ...cur].slice(0, max); write(key, next); setItems(next); }, [key, max]); const has = useCallback((uid: string) => items.includes(uid), [items]); const clear = useCallback(() => { write(key, []); setItems([]); }, [key]); return { items, toggle, has, clear }; } /** Favoris : locaux par défaut ; si l'utilisateur est connecté, synchronisés avec le serveur (les favoris locaux sont poussés une fois, puis le serveur fait foi). */ export function useFavorites() { const local = useLocalList(KEY_FAV, 200); const [server, setServer] = useState(null); useEffect(() => { let alive = true; fetch("/api/me").then((r) => r.json()).then(async (d) => { if (!alive || !d?.user) return; const cur = read(KEY_FAV, []); if (cur.length) { await fetch("/api/me/favorites", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ uids: cur }) }).catch(() => {}); write(KEY_FAV, []); } const r = await fetch("/api/me/favorites").then((r) => r.json()).catch(() => null); if (alive && r?.items) setServer(r.items.map((i: { uid: string }) => i.uid)); }).catch(() => {}); return () => { alive = false; }; }, []); if (server === null) return local; return { items: server, has: (uid: string) => server.includes(uid), toggle: (uid: string) => { const next = server.includes(uid) ? server.filter((x) => x !== uid) : [uid, ...server]; setServer(next); fetch("/api/me/favorites", { method: server.includes(uid) ? "DELETE" : "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ uids: [uid] }) }).catch(() => {}); }, clear: () => { fetch("/api/me/favorites", { method: "DELETE", headers: { "content-type": "application/json" }, body: JSON.stringify({ uids: server }) }).catch(() => {}); setServer([]); }, }; } export function useCompare() { return useLocalList(KEY_CMP, 4); } export interface SavedSearch { id: string; label: string; href: string; createdAt: string; } export function useSavedSearches() { const [items, setItems] = useState([]); useEffect(() => { const load = () => setItems(read(KEY_SEARCH, [])); load(); window.addEventListener("toveli:storage", load); return () => window.removeEventListener("toveli:storage", load); }, []); const save = useCallback((label: string, href: string) => { const cur = read(KEY_SEARCH, []); if (cur.some((s) => s.href === href)) return; const next = [{ id: Math.random().toString(36).slice(2), label, href, createdAt: new Date().toISOString() }, ...cur].slice(0, 50); write(KEY_SEARCH, next); setItems(next); }, []); const remove = useCallback((id: string) => { const next = read(KEY_SEARCH, []).filter((s) => s.id !== id); write(KEY_SEARCH, next); setItems(next); }, []); const clearAll = useCallback(() => { [KEY_FAV, KEY_CMP, KEY_SEARCH].forEach((k) => localStorage.removeItem(k)); window.dispatchEvent(new Event("toveli:storage")); }, []); return { items, save, remove, clearAll }; }