store.ts 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. import { create } from "zustand";
  2. import { combine, persist, createJSONStorage } from "zustand/middleware";
  3. import { Updater } from "../typing";
  4. import { deepClone } from "./clone";
  5. import { indexedDBStorage } from "@/app/utils/indexedDB-storage";
  6. type SecondParam<T> = T extends (
  7. _f: infer _F,
  8. _s: infer S,
  9. ...args: infer _U
  10. ) => any
  11. ? S
  12. : never;
  13. type MakeUpdater<T> = {
  14. lastUpdateTime: number;
  15. _hasHydrated: boolean;
  16. markUpdate: () => void;
  17. update: Updater<T>;
  18. setHasHydrated: (state: boolean) => void;
  19. };
  20. type SetStoreState<T> = (
  21. partial: T | Partial<T> | ((state: T) => T | Partial<T>),
  22. replace?: boolean | undefined,
  23. ) => void;
  24. export function createPersistStore<T extends object, M>(
  25. state: T,
  26. methods: (
  27. set: SetStoreState<T & MakeUpdater<T>>,
  28. get: () => T & MakeUpdater<T>,
  29. ) => M,
  30. persistOptions: SecondParam<typeof persist<T & M & MakeUpdater<T>>>,
  31. ) {
  32. persistOptions.storage = createJSONStorage(() => indexedDBStorage);
  33. const oldOonRehydrateStorage = persistOptions?.onRehydrateStorage;
  34. persistOptions.onRehydrateStorage = (state) => {
  35. oldOonRehydrateStorage?.(state);
  36. return () => state.setHasHydrated(true);
  37. };
  38. return create(
  39. persist(
  40. combine(
  41. {
  42. ...state,
  43. lastUpdateTime: 0,
  44. _hasHydrated: false,
  45. },
  46. (set, get) => {
  47. return {
  48. ...methods(set, get as any),
  49. markUpdate() {
  50. set({ lastUpdateTime: Date.now() } as Partial<
  51. T & M & MakeUpdater<T>
  52. >);
  53. },
  54. update(updater) {
  55. const state = deepClone(get());
  56. updater(state);
  57. set({
  58. ...state,
  59. lastUpdateTime: Date.now(),
  60. });
  61. },
  62. setHasHydrated: (state: boolean) => {
  63. set({ _hasHydrated: state } as Partial<T & M & MakeUpdater<T>>);
  64. },
  65. } as M & MakeUpdater<T>;
  66. },
  67. ),
  68. persistOptions as any,
  69. ),
  70. );
  71. }