From 7bafb3c69708625686f91c247a4b3cd8558ff474 Mon Sep 17 00:00:00 2001 From: Nuwan Date: Tue, 27 Jan 2026 08:07:44 +0530 Subject: [PATCH] feat(07-01): implement unread tracking reducers and complete slice GREEN phase of TDD for Task 3: - Implement markAsRead: resets unread count to 0, updates lastReadAt timestamp - Implement incrementUnreadCount: safely handles missing channels - Implement setWindowPosition: stores UI position for WindowPortal - Export all 7 action creators from slice - Complete sessionChatSlice with full reducer suite All 40 tests pass including 3 integration tests: - Complete flow validates message receiving, channel switching, window state - Multi-channel flow validates independent unread counts per channel - Deduplication test validates WebSocket + REST double-message scenario Ready for async thunks in Phase 7 Plan 2. Co-Authored-By: Claude Sonnet 4.5 --- jam-ui/src/store/features/sessionChatSlice.js | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/jam-ui/src/store/features/sessionChatSlice.js b/jam-ui/src/store/features/sessionChatSlice.js index ee7084096..8af8cc757 100644 --- a/jam-ui/src/store/features/sessionChatSlice.js +++ b/jam-ui/src/store/features/sessionChatSlice.js @@ -111,6 +111,33 @@ const sessionChatSlice = createSlice({ */ closeChatWindow: (state) => { state.isWindowOpen = false; + }, + + /** + * Mark channel as read + * Resets unread count and updates lastReadAt timestamp + */ + markAsRead: (state, action) => { + const { channel } = action.payload; + state.unreadCounts[channel] = 0; + state.lastReadAt[channel] = new Date().toISOString(); + }, + + /** + * Increment unread count for a channel + * Initializes count to 1 if channel doesn't exist + */ + incrementUnreadCount: (state, action) => { + const { channel } = action.payload; + state.unreadCounts[channel] = (state.unreadCounts[channel] || 0) + 1; + }, + + /** + * Set window position for persistence + * Used by WindowPortal to save drag position + */ + setWindowPosition: (state, action) => { + state.windowPosition = action.payload; } } }); @@ -119,7 +146,10 @@ export const { addMessageFromWebSocket, setActiveChannel, openChatWindow, - closeChatWindow + closeChatWindow, + markAsRead, + incrementUnreadCount, + setWindowPosition } = sessionChatSlice.actions; export default sessionChatSlice.reducer;