diff --git a/packages/ui/src/components/sections/openchamber/NotificationSettings.tsx b/packages/ui/src/components/sections/openchamber/NotificationSettings.tsx index e0cb8d44..0914e02a 100644 --- a/packages/ui/src/components/sections/openchamber/NotificationSettings.tsx +++ b/packages/ui/src/components/sections/openchamber/NotificationSettings.tsx @@ -152,13 +152,13 @@ export const NotificationSettings: React.FC = () => { field: 'title' | 'message', value: string, ) => { - setNotificationTemplates({ - ...notificationTemplates, + setNotificationTemplates((current) => ({ + ...current, [event]: { - ...notificationTemplates[event], + ...current[event], [field]: value, }, - }); + })); }; const base64UrlToUint8Array = (base64Url: string): Uint8Array => { diff --git a/packages/ui/src/stores/useUIStore.notificationTemplates.test.ts b/packages/ui/src/stores/useUIStore.notificationTemplates.test.ts new file mode 100644 index 00000000..de4eef7e --- /dev/null +++ b/packages/ui/src/stores/useUIStore.notificationTemplates.test.ts @@ -0,0 +1,29 @@ +import { afterEach, describe, expect, test } from 'bun:test'; +import { useUIStore } from './useUIStore'; + +const initialTemplates = useUIStore.getState().notificationTemplates; + +afterEach(() => { + useUIStore.setState({ notificationTemplates: initialTemplates }); +}); + +describe('useUIStore notification templates', () => { + test('preserves rapid updates to separate template fields', () => { + const { setNotificationTemplates } = useUIStore.getState(); + + setNotificationTemplates((current) => ({ + ...current, + completion: { ...current.completion, title: 'Completed' }, + })); + setNotificationTemplates((current) => ({ + ...current, + error: { ...current.error, message: 'Failed' }, + })); + + expect(useUIStore.getState().notificationTemplates).toEqual({ + ...initialTemplates, + completion: { ...initialTemplates.completion, title: 'Completed' }, + error: { ...initialTemplates.error, message: 'Failed' }, + }); + }); +}); diff --git a/packages/ui/src/stores/useUIStore.ts b/packages/ui/src/stores/useUIStore.ts index d00bb6ff..0849cfdc 100644 --- a/packages/ui/src/stores/useUIStore.ts +++ b/packages/ui/src/stores/useUIStore.ts @@ -842,7 +842,9 @@ interface UIStore { setNotifyOnCompletion: (value: boolean) => void; setNotifyOnError: (value: boolean) => void; setNotifyOnQuestion: (value: boolean) => void; - setNotificationTemplates: (templates: UIStore['notificationTemplates']) => void; + setNotificationTemplates: ( + templates: UIStore['notificationTemplates'] | ((current: UIStore['notificationTemplates']) => UIStore['notificationTemplates']), + ) => void; setSummarizeLastMessage: (value: boolean) => void; setSummaryThreshold: (value: number) => void; setSummaryLength: (value: number) => void; @@ -2135,7 +2137,13 @@ export const useUIStore = create()( setNotifyOnCompletion: (value) => { set({ notifyOnCompletion: value }); }, setNotifyOnError: (value) => { set({ notifyOnError: value }); }, setNotifyOnQuestion: (value) => { set({ notifyOnQuestion: value }); }, - setNotificationTemplates: (templates) => { set({ notificationTemplates: templates }); }, + setNotificationTemplates: (templates) => { + set((state) => ({ + notificationTemplates: typeof templates === 'function' + ? templates(state.notificationTemplates) + : templates, + })); + }, setSummarizeLastMessage: (value) => { set({ summarizeLastMessage: value }); }, setSummaryThreshold: (value) => { set({ summaryThreshold: value }); }, setSummaryLength: (value) => { set({ summaryLength: value }); },