From 7110e7cdf9569ea6a67efb7665cc62409984a74d Mon Sep 17 00:00:00 2001 From: Nuwan Date: Sat, 7 Feb 2026 02:07:01 +0530 Subject: [PATCH] fix(16): persist unread count across page reloads Problem: After page reload, the unread badge on chat button disappeared even though there were unread messages. Root cause: 1. unreadCounts was reset to {} on page reload 2. fetchChatHistory was only called when chat window opened 3. By that time, openChatWindow already reset the count to 0 Fix: 1. Fetch chat history when session joins (not just when chat opens) 2. In fetchChatHistory.fulfilled, calculate unread count based on lastReadAt timestamp from localStorage 3. Only calculate unread if chat window is NOT open for that channel This ensures the badge shows correct unread count after page reload. Co-Authored-By: Claude Opus 4.5 --- .../src/components/client/JKSessionScreen.js | 15 ++++++++++++++- jam-ui/src/store/features/sessionChatSlice.js | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/jam-ui/src/components/client/JKSessionScreen.js b/jam-ui/src/components/client/JKSessionScreen.js index 4879bd746..c9248a173 100644 --- a/jam-ui/src/components/client/JKSessionScreen.js +++ b/jam-ui/src/components/client/JKSessionScreen.js @@ -55,7 +55,7 @@ import { selectBackingTrackData, selectJamTrackData } from '../../store/features/activeSessionSlice'; -import { addMessageFromWebSocket, uploadAttachment, selectIsUploading, selectUploadError, selectUploadFileName, selectUploadStatus, clearUploadError } from '../../store/features/sessionChatSlice'; +import { addMessageFromWebSocket, uploadAttachment, selectIsUploading, selectUploadError, selectUploadFileName, selectUploadStatus, clearUploadError, fetchChatHistory } from '../../store/features/sessionChatSlice'; import { validateFile } from '../../services/attachmentValidation'; import { CLIENT_ROLE, RECORD_TYPE_AUDIO, RECORD_TYPE_BOTH } from '../../helpers/globals'; @@ -471,6 +471,19 @@ const JKSessionScreen = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [hasJoined, sessionId, mixersReady, dispatch]) + // Fetch chat history when session joins to populate unread badge + // This ensures unread count persists across page reloads + useEffect(() => { + if (!hasJoined || !sessionId) { + return; + } + + dispatch(fetchChatHistory({ + channel: sessionId, + sessionId: sessionId + })); + }, [hasJoined, sessionId, dispatch]); + const joinSession = async () => { await jamClient.SetVURefreshRate(150); diff --git a/jam-ui/src/store/features/sessionChatSlice.js b/jam-ui/src/store/features/sessionChatSlice.js index a71142e11..6090d7ab9 100644 --- a/jam-ui/src/store/features/sessionChatSlice.js +++ b/jam-ui/src/store/features/sessionChatSlice.js @@ -372,6 +372,24 @@ const sessionChatSlice = createSlice({ state.fetchStatus[channel] = 'succeeded'; state.nextCursors[channel] = next; + + // Calculate unread count based on lastReadAt (for page reload persistence) + // Only set unread count if chat window is NOT open for this channel + // (if window is open, user is viewing messages so they're not "unread") + if (!state.isWindowOpen || state.activeChannel !== channel) { + const lastRead = state.lastReadAt[channel]; + if (lastRead) { + // Count messages newer than lastReadAt + const lastReadDate = new Date(lastRead); + const unreadCount = state.messagesByChannel[channel].filter( + m => new Date(m.createdAt) > lastReadDate + ).length; + state.unreadCounts[channel] = unreadCount; + } else { + // No lastReadAt means user never opened chat - all messages are unread + state.unreadCounts[channel] = state.messagesByChannel[channel].length; + } + } }) // fetchChatHistory rejected .addCase(fetchChatHistory.rejected, (state, action) => {