diff --git a/jam-ui/src/store/features/sessionChatSlice.js b/jam-ui/src/store/features/sessionChatSlice.js index 9e35303b3..ee7084096 100644 --- a/jam-ui/src/store/features/sessionChatSlice.js +++ b/jam-ui/src/store/features/sessionChatSlice.js @@ -31,13 +31,95 @@ const initialState = { windowPosition: null }; +/** + * Helper function to get channel key from message + * Session messages use 'session-{sessionId}', global uses 'global' + * @param {Object} message - Message object + * @returns {string} Channel key + */ +const getChannelKey = (message) => { + if (message.channel === 'session' && message.sessionId) { + return message.sessionId; + } + if (message.channel === 'lesson' && message.lessonSessionId) { + return message.lessonSessionId; + } + return message.channel; // 'global' +}; + const sessionChatSlice = createSlice({ name: 'sessionChat', initialState, reducers: { - // Reducers will be added in Tasks 2-3 + /** + * Add message from WebSocket + * Handles deduplication, sorting, and unread count increment + */ + addMessageFromWebSocket: (state, action) => { + const message = action.payload; + const channel = getChannelKey(message); + + // Initialize channel if not exists + if (!state.messagesByChannel[channel]) { + state.messagesByChannel[channel] = []; + } + + // Deduplicate by msg_id + const exists = state.messagesByChannel[channel].some(m => m.id === message.id); + if (exists) return; + + // Add message + state.messagesByChannel[channel].push(message); + + // Sort by createdAt ASC + state.messagesByChannel[channel].sort((a, b) => + new Date(a.createdAt) - new Date(b.createdAt) + ); + + // Increment unread count if window closed OR viewing different channel + if (!state.isWindowOpen || state.activeChannel !== channel) { + state.unreadCounts[channel] = (state.unreadCounts[channel] || 0) + 1; + } + }, + + /** + * Set active channel for viewing + */ + setActiveChannel: (state, action) => { + const { channel, channelType } = action.payload; + state.activeChannel = channel; + state.channelType = channelType; + }, + + /** + * Open chat window + * Resets unread count for active channel + */ + openChatWindow: (state) => { + state.isWindowOpen = true; + + // Reset unread count for active channel + if (state.activeChannel) { + state.unreadCounts[state.activeChannel] = 0; + state.lastReadAt[state.activeChannel] = new Date().toISOString(); + } + }, + + /** + * Close chat window + * Does not affect unread counts + */ + closeChatWindow: (state) => { + state.isWindowOpen = false; + } } }); -export const {} = sessionChatSlice.actions; +export const { + addMessageFromWebSocket, + setActiveChannel, + openChatWindow, + closeChatWindow +} = sessionChatSlice.actions; + export default sessionChatSlice.reducer;