authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-03-09 13:00:27-07:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-03-09 14:02:29-07:00
log82c0a45ea0334a8ffd5747e080ca693f54921d66
tree5efb5ab19b40c8386ba206604f15f145a2de7db1
parente765051f148bf6006e30118eb47f5d33ea9e32be

Use Debouncer for composer draft persistence writes

- Replace manual timeout/pending-value debounce logic with `@tanstack/react-pacer` `Debouncer` - Keep `removeItem`/`flush` behavior while simplifying and standardizing persistence timing

1 files changed, 11 insertions(+), 27 deletions(-)

apps/web/src/composerDraftStore.ts+11-27
......@@ -14,6 +14,7 @@ import {
1414 DEFAULT_RUNTIME_MODE,
1515 type ChatImageAttachment,
1616} from "./types";
17import { Debouncer } from "@tanstack/react-pacer";
1718import { create } from "zustand";
1819import { createJSONStorage, persist, type StateStorage } from "zustand/middleware";
1920
......@@ -27,41 +28,24 @@ interface DebouncedStorage extends StateStorage {
2728}
2829
2930export function createDebouncedStorage(baseStorage: StateStorage): DebouncedStorage {
30 let timer: ReturnType<typeof setTimeout> | null = null;
31 let pendingName: string | null = null;
32 let pendingValue: string | null = null;
31 const debouncedSetItem = new Debouncer(
32 (name: string, value: string) => {
33 baseStorage.setItem(name, value);
34 },
35 { wait: COMPOSER_PERSIST_DEBOUNCE_MS },
36 );
37
3338 return {
3439 getItem: (name) => baseStorage.getItem(name),
3540 setItem: (name, value) => {
36 pendingName = name;
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);
41 debouncedSetItem.maybeExecute(name, value);
4742 },
4843 removeItem: (name) => {
49 if (timer !== null) {
50 clearTimeout(timer);
51 timer = null;
52 pendingName = null;
53 pendingValue = null;
54 }
44 debouncedSetItem.cancel();
5545 baseStorage.removeItem(name);
5646 },
5747 flush: () => {
58 if (timer !== null && pendingName !== null && pendingValue !== null) {
59 clearTimeout(timer);
60 timer = null;
61 baseStorage.setItem(pendingName, pendingValue);
62 pendingName = null;
63 pendingValue = null;
64 }
48 debouncedSetItem.flush();
6549 },
6650 };
6751}