"use client"; /** * useViewPrefs — per-view persistence of sort state and row order. * * Uses a separate localStorage key ("viewprefs_") from useColumnConfig * ("colvis_") to avoid format conflicts. Server-side the data is merged * into the same User.columnPreferences JSONB via trpc.user.setColumnPreferences. * * Each user's sort + row order is independent (stored on their own User record). */ import { useState, useCallback, useRef } from "react"; import type { ViewKey } from "@planarchy/shared"; import { trpc } from "~/lib/trpc/client.js"; interface SavedSort { field: string; dir: "asc" | "desc"; } interface LocalViewPrefs { sort?: SavedSort; rowOrder?: string[]; } const LS_KEY = (view: ViewKey) => `viewprefs_${view}`; function readLocal(view: ViewKey): LocalViewPrefs { if (typeof window === "undefined") return {}; try { const raw = localStorage.getItem(LS_KEY(view)); return raw ? (JSON.parse(raw) as LocalViewPrefs) : {}; } catch { return {}; } } function writeLocal(view: ViewKey, patch: { sort?: SavedSort | null; rowOrder?: string[] | null }) { if (typeof window === "undefined") return; try { const current = readLocal(view); const next = { ...current, ...patch }; // Remove keys that are explicitly set to null/undefined if (next.sort == null) delete next.sort; if (!next.rowOrder?.length) delete next.rowOrder; localStorage.setItem(LS_KEY(view), JSON.stringify(next)); } catch { // ignore write errors (private browsing, quota, etc.) } } export function useViewPrefs(view: ViewKey) { // Initialise synchronously from localStorage so there's no flash const init = readLocal(view); const [savedSort, setSavedSortState] = useState(init.sort ?? null); const [rowOrder, setRowOrderState] = useState(init.rowOrder ?? []); const setMutation = trpc.user.setColumnPreferences.useMutation(); // Debounce server sync so rapid drags don't spam mutations const debounceRef = useRef | null>(null); function scheduleSave(patch: Parameters[0]) { if (debounceRef.current) clearTimeout(debounceRef.current); debounceRef.current = setTimeout(() => { setMutation.mutate(patch); }, 600); } const setSavedSort = useCallback( (sort: SavedSort | null) => { setSavedSortState(sort); writeLocal(view, { sort: sort ?? null }); scheduleSave({ view, sort: sort ?? null }); }, // eslint-disable-next-line react-hooks/exhaustive-deps [view], ); const setRowOrder = useCallback( (ids: string[]) => { setRowOrderState(ids); writeLocal(view, { rowOrder: ids }); scheduleSave({ view, rowOrder: ids.length > 0 ? ids : null }); }, // eslint-disable-next-line react-hooks/exhaustive-deps [view], ); return { savedSort, setSavedSort, rowOrder, setRowOrder }; } export type ViewPrefsHandle = ReturnType;