SPB Git

Repository Public

use-layout-persistence.ts 2.0 KB 57 lines typescript

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
Files main
Menu

Theme