From c0a7408ef4db0738703706520f39b0c72ba33700 Mon Sep 17 00:00:00 2001 From: Marcel Date: Tue, 19 May 2026 22:59:01 +0200 Subject: [PATCH] refactor(notification): rename markRead/markAllRead to optimistic helpers without fetch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes raw fetch() calls from the store. optimisticMarkRead(id) and optimisticMarkAllRead() now only mutate local $state — the actual API calls move to SvelteKit form actions on /aktivitaeten. Co-Authored-By: Claude Sonnet 4.6 --- .../notification/notifications.svelte.spec.ts | 42 +++++++++++++++++-- .../lib/notification/notifications.svelte.ts | 31 +++++--------- 2 files changed, 49 insertions(+), 24 deletions(-) diff --git a/frontend/src/lib/notification/notifications.svelte.spec.ts b/frontend/src/lib/notification/notifications.svelte.spec.ts index f4bb1ca0..15345286 100644 --- a/frontend/src/lib/notification/notifications.svelte.spec.ts +++ b/frontend/src/lib/notification/notifications.svelte.spec.ts @@ -108,12 +108,46 @@ describe('notificationStore (singleton)', () => { expect(notificationStore.unreadCount).toBe(1); }); - it('markAllRead resets unreadCount', async () => { - mockFetch.mockResolvedValue(new Response(null, { status: 200 })); - await notificationStore.markAllRead(); + it('optimisticMarkRead marks the notification read and decrements unreadCount without fetching', () => { + notificationStore.init(); + const notification = makeNotification({ id: 'sse-1', read: false }); + lastEventSource!.simulate('notification', JSON.stringify(notification)); + mockFetch.mockReset(); // clear the fetchUnreadCount call from init - expect(mockFetch).toHaveBeenCalledWith('/api/notifications/read-all', { method: 'POST' }); + notificationStore.optimisticMarkRead('sse-1'); + + expect(notificationStore.notifications[0].read).toBe(true); expect(notificationStore.unreadCount).toBe(0); + expect(mockFetch).not.toHaveBeenCalled(); + }); + + it('optimisticMarkRead on an already-read notification does not decrement unreadCount below 0', () => { + notificationStore.init(); + const notification = makeNotification({ id: 'sse-1', read: true }); + lastEventSource!.simulate('notification', JSON.stringify(notification)); + + notificationStore.optimisticMarkRead('sse-1'); + + expect(notificationStore.unreadCount).toBe(0); + }); + + it('optimisticMarkAllRead resets unreadCount and marks all notifications read without fetching', () => { + notificationStore.init(); + lastEventSource!.simulate( + 'notification', + JSON.stringify(makeNotification({ id: 'n1', read: false })) + ); + lastEventSource!.simulate( + 'notification', + JSON.stringify(makeNotification({ id: 'n2', read: false })) + ); + mockFetch.mockReset(); + + notificationStore.optimisticMarkAllRead(); + + expect(notificationStore.unreadCount).toBe(0); + expect(notificationStore.notifications.every((n) => n.read)).toBe(true); + expect(mockFetch).not.toHaveBeenCalled(); }); }); diff --git a/frontend/src/lib/notification/notifications.svelte.ts b/frontend/src/lib/notification/notifications.svelte.ts index 2ac49e4e..1e52cfe3 100644 --- a/frontend/src/lib/notification/notifications.svelte.ts +++ b/frontend/src/lib/notification/notifications.svelte.ts @@ -35,28 +35,19 @@ async function fetchUnreadCount(): Promise { } } -async function markRead(notification: NotificationItem): Promise { - if (!notification.read) { - try { - await fetch(`/api/notifications/${notification.id}/read`, { method: 'PATCH' }); - notification.read = true; - unreadCount = Math.max(0, unreadCount - 1); - } catch (e) { - console.error('Failed to mark notification as read', e); - } +function optimisticMarkRead(id: string): void { + const notification = notifications.find((n) => n.id === id); + if (notification && !notification.read) { + notification.read = true; + unreadCount = Math.max(0, unreadCount - 1); } } -async function markAllRead(): Promise { - try { - await fetch('/api/notifications/read-all', { method: 'POST' }); - for (const n of notifications) { - n.read = true; - } - unreadCount = 0; - } catch (e) { - console.error('Failed to mark all notifications as read', e); +function optimisticMarkAllRead(): void { + for (const n of notifications) { + n.read = true; } + unreadCount = 0; } function init(): void { @@ -123,8 +114,8 @@ export const notificationStore = { }, fetchNotifications, fetchUnreadCount, - markRead, - markAllRead, + optimisticMarkRead, + optimisticMarkAllRead, init, destroy };