Repository Public
1'use client';2import { useEffect, useRef } from 'react';3import { useQueryClient } from '@tanstack/react-query';4import { saveSettings, useSettingsQuery } from '@/lib/queries';5import { LAYOUT_KEY, parseLayout, pickLayout, presetLayout, useUi } from '@/stores/ui';67/**8 * Layout (preset, panel sizes, collapsed state) is loaded from the server settings, falling9 * back to localStorage, and saved back debounced. localStorage holds layout only.10 */11export function useLayoutPersistence(): void {12 const qc = useQueryClient();13 const settings = useSettingsQuery();14 const hydrated = useUi((s) => s.hydrated);15 const lastSaved = useRef('');1617 useEffect(() => {18 if (hydrated || settings.isPending) return;19 let local: unknown = null;20 try {21 local = JSON.parse(localStorage.getItem(LAYOUT_KEY) ?? 'null');22 } catch {23 /* ignore */24 }25 const server = settings.data?.layout;26 const fromServer = server && Object.keys(server).length > 0 ? parseLayout(server) : null;27 const base = settings.data ? presetLayout(settings.data.defaultLayout) : {};28 const chosen = fromServer ?? parseLayout(local);29 useUi.getState().hydrate({ ...base, ...chosen });30 lastSaved.current = JSON.stringify(pickLayout(useUi.getState()));31 }, [hydrated, settings.isPending, settings.data]);3233 useEffect(() => {34 if (!hydrated) return;35 let timer: ReturnType<typeof setTimeout> | null = null;36 const unsub = useUi.subscribe((s) => {37 const layout = pickLayout(s);38 const json = JSON.stringify(layout);39 if (json === lastSaved.current) return;40 try {41 localStorage.setItem(LAYOUT_KEY, json);42 } catch {43 /* storage unavailable */44 }45 if (timer) clearTimeout(timer);46 timer = setTimeout(() => {47 lastSaved.current = json;48 void saveSettings(qc, { layout: layout as unknown as Record<string, unknown> }).catch(() => undefined);49 }, 1200);50 });51 return () => {52 unsub();53 if (timer) clearTimeout(timer);54 };55 }, [hydrated, qc]);56}57