repo_description: ✨ Light and Fast AI Assistant. Support: Web | iOS | MacOS | Android |  Linux | Windows
repo_topics: calclaude chatgpt claude cross-platform desktop fe gemini gemini-pro gemini-server gemini-ultra gpt-4o groq nextjs ollama react tauri tauri-app vercel webui
code: import { create } from "zustand";
  import { combine, persist, createJSONStorage } from "zustand/middleware";
  import { Updater } from "../typing";
  import { deepClone } from "./clone";
  import { indexedDBStorage } from "@/app/utils/indexedDB-storage";
  
  type SecondParam<T> = T extends (
    _f: infer _F,
    _s: infer S,
    ...args: infer _U
  ) => any
    ? S
    : never;
  
  type MakeUpdater<T> = {
    lastUpdateTime: number;
    _hasHydrated: boolean;
  
    markUpdate: () => void;
    update: Updater<T>;
    setHasHydrated: (state: boolean) => void;
  };
  
  type SetStoreState<T> = (
    partial: T | Partial<T> | ((state: T) => T | Partial<T>),
    replace?: boolean | undefined,
  ) => void;
  
  export function createPersistStore<T extends object, M>(
    state: T,
    methods: (
      set: SetStoreState<T & MakeUpdater<T>>,
      get: () => T & MakeUpdater<T>,
    ) => M,
    persistOptions: SecondParam<typeof persist<T & M & MakeUpdater<T>>>,
  ) {
    persistOptions.storage = createJSONStorage(() => indexedDBStorage);
    const oldOonRehydrateStorage = persistOptions?.onRehydrateStorage;
    persistOptions.onRehydrateStorage = (state) => {
      oldOonRehydrateStorage?.(state);
      return () => state.setHasHydrated(true);
    };
  
    return create(
      persist(
        combine(
          {
            ...state,
            lastUpdateTime: 0,
            _hasHydrated: false,
          },
          (set, get) => {
            return {
              ...methods(set, get as any),
  
              markUpdate() {
                set({ lastUpdateTime: Date.now() } as Partial<
                  T & M & MakeUpdater<T>
                >);
              },
              update(updater) {
                const state = deepClone(get());
                updater(state);
                set({
                  ...state,
                  lastUpdateTime: Date.now(),
                });
              },
              setHasHydrated: (state: boolean) => {
                set({ _hasHydrated: state } as Partial<T & M & MakeUpdater<T>>);
              },
            } as M & MakeUpdater<T>;
          },
        ),
        persistOptions as any,
      ),
    );
  }
  