| ... | @@ -14,6 +14,7 @@ import { | ... | @@ -14,6 +14,7 @@ import { |
| 14 | DEFAULT_RUNTIME_MODE, | 14 | DEFAULT_RUNTIME_MODE, |
| 15 | type ChatImageAttachment, | 15 | type ChatImageAttachment, |
| 16 | } from "./types"; | 16 | } from "./types"; |
| | 17 | import { Debouncer } from "@tanstack/react-pacer"; |
| 17 | import { create } from "zustand"; | 18 | import { create } from "zustand"; |
| 18 | import { createJSONStorage, persist, type StateStorage } from "zustand/middleware"; | 19 | import { createJSONStorage, persist, type StateStorage } from "zustand/middleware"; |
| 19 | | 20 | |
| ... | @@ -27,41 +28,24 @@ interface DebouncedStorage extends StateStorage { | ... | @@ -27,41 +28,24 @@ interface DebouncedStorage extends StateStorage { |
| 27 | } | 28 | } |
| 28 | | 29 | |
| 29 | export function createDebouncedStorage(baseStorage: StateStorage): DebouncedStorage { | 30 | export function createDebouncedStorage(baseStorage: StateStorage): DebouncedStorage { |
| 30 | let timer: ReturnType<typeof setTimeout> | null = null; | 31 | const debouncedSetItem = new Debouncer( |
| 31 | let pendingName: string | null = null; | 32 | (name: string, value: string) => { |
| 32 | let pendingValue: string | null = null; | 33 | baseStorage.setItem(name, value); |
| | 34 | }, |
| | 35 | { wait: COMPOSER_PERSIST_DEBOUNCE_MS }, |
| | 36 | ); |
| | 37 | |
| 33 | return { | 38 | return { |
| 34 | getItem: (name) => baseStorage.getItem(name), | 39 | getItem: (name) => baseStorage.getItem(name), |
| 35 | setItem: (name, value) => { | 40 | setItem: (name, value) => { |
| 36 | pendingName = name; | 41 | debouncedSetItem.maybeExecute(name, value); |
| 37 | pendingValue = value; | | |
| 38 | if (timer !== null) { | | |
| 39 | clearTimeout(timer); | | |
| 40 | } | | |
| 41 | timer = setTimeout(() => { | | |
| 42 | timer = null; | | |
| 43 | pendingName = null; | | |
| 44 | pendingValue = null; | | |
| 45 | baseStorage.setItem(name, value); | | |
| 46 | }, COMPOSER_PERSIST_DEBOUNCE_MS); | | |
| 47 | }, | 42 | }, |
| 48 | removeItem: (name) => { | 43 | removeItem: (name) => { |
| 49 | if (timer !== null) { | 44 | debouncedSetItem.cancel(); |
| 50 | clearTimeout(timer); | | |
| 51 | timer = null; | | |
| 52 | pendingName = null; | | |
| 53 | pendingValue = null; | | |
| 54 | } | | |
| 55 | baseStorage.removeItem(name); | 45 | baseStorage.removeItem(name); |
| 56 | }, | 46 | }, |
| 57 | flush: () => { | 47 | flush: () => { |
| 58 | if (timer !== null && pendingName !== null && pendingValue !== null) { | 48 | debouncedSetItem.flush(); |
| 59 | clearTimeout(timer); | | |
| 60 | timer = null; | | |
| 61 | baseStorage.setItem(pendingName, pendingValue); | | |
| 62 | pendingName = null; | | |
| 63 | pendingValue = null; | | |
| 64 | } | | |
| 65 | }, | 49 | }, |
| 66 | }; | 50 | }; |
| 67 | } | 51 | } |