- Replace manual timeout/pending-value debounce logic with `@tanstack/react-pacer` `Debouncer` - Keep `removeItem`/`flush` behavior while simplifying and standardizing persistence timing
Unified · Split
| @@ -14,6 +14,7 @@ import { · 13 unmodified lines | |||
| 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 { · 7 unmodified lines | |||
| 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 | } |