import { FiltersState } from '@cloud-ru/ds-chips'; import { DataPersistOptions, useDataPersist } from '@cloud-ru/ds-utils'; import { MutableRefObject, useEffect, useMemo, useRef } from 'react'; import { PersistedFilterState, ToolbarPersistConfig } from '../../types'; import { defaultParser, defaultSerializer, prepareDataForFilter } from './utils'; type UsePersistStateProps> = { persist?: ToolbarPersistConfig; filter?: TState; search?: string; }; type RunPersistStateHydrationOnceParams = { hasHydratedRef: MutableRefObject; defaultState: PersistedFilterState | undefined; onLoad?: ToolbarPersistConfig['onLoad']; }; function runPersistStateHydrationOnce({ hasHydratedRef, defaultState, onLoad, }: RunPersistStateHydrationOnceParams) { if (hasHydratedRef.current) { return; } if (defaultState) { onLoad?.({ ...defaultState, filter: prepareDataForFilter(defaultState.filter ?? {}), }); } hasHydratedRef.current = true; } export function usePersistState>({ persist, filter, search, }: UsePersistStateProps) { const hasHydratedRef = useRef(false); const skipInitialPersistRef = useRef(true); const onLoadRef = useRef(persist?.onLoad); onLoadRef.current = persist?.onLoad; const dataPersistOptions = useMemo> | undefined>(() => { if (!persist?.filterQueryKey || !persist?.id) return undefined; const defaultValidate = (value: PersistedFilterState): boolean => { const filterOk = value?.filter === undefined || (typeof value.filter === 'object' && value.filter !== null); const searchOk = value?.search === undefined || typeof value.search === 'string'; return Boolean(filterOk && searchOk); }; const combinedValidate = (value: PersistedFilterState): boolean => { const baseValid = defaultValidate(value); return persist?.validateData ? baseValid && persist.validateData(value) : baseValid; }; return { queryKey: persist.filterQueryKey, localStorageKey: `${persist.id}_filter`, validateData: combinedValidate as unknown as (value: unknown) => value is PersistedFilterState, }; }, [persist]); const { getDefaultData, setDataToStorages } = useDataPersist>({ options: dataPersistOptions, serializer: value => (persist?.serializer ? persist.serializer(value) : defaultSerializer(value)), parser: (value: string) => (persist?.parser ? persist.parser(value) : defaultParser(value)), }); const defaultState = useMemo(getDefaultData, [getDefaultData]); useEffect(() => { runPersistStateHydrationOnce({ hasHydratedRef, defaultState, onLoad: onLoadRef.current, }); }, [defaultState]); useEffect(() => { if (!persist?.id || !persist?.filterQueryKey || !hasHydratedRef.current) { return; } if (skipInitialPersistRef.current) { skipInitialPersistRef.current = false; return; } const snapshot: PersistedFilterState = { ...(persist.state ?? getDefaultData() ?? {}), ...(filter ? { filter } : {}), ...(search !== undefined ? { search } : {}), }; if (Object.keys(snapshot).length === 0) return; setDataToStorages(snapshot); }, [persist?.id, persist?.filterQueryKey, persist?.state, filter, search, setDataToStorages, getDefaultData]); }