From e98504ebadd5b41d2b6fbeb2be03db5b30af568f Mon Sep 17 00:00:00 2001 From: Nuwan Date: Tue, 27 Jan 2026 08:04:51 +0530 Subject: [PATCH] feat(07-01): implement sessionChatSlice with initial state structure GREEN phase of TDD: - Create sessionChatSlice.js with complete initial state - All 12 state fields match CHAT_REDUX_DESIGN.md specification - Multi-channel architecture: messagesByChannel keyed by channel ID - Unread tracking: unreadCounts, lastReadAt per channel - State machines: fetchStatus/error per channel, sendStatus/error global - Pagination support: nextCursors per channel - UI state: isWindowOpen, windowPosition - Register slice in store.js as sessionChat reducer All 13 tests pass. Ready for reducers in Task 2. Co-Authored-By: Claude Sonnet 4.5 --- jam-ui/src/store/features/sessionChatSlice.js | 43 +++++++++++++++++++ jam-ui/src/store/store.js | 2 + 2 files changed, 45 insertions(+) create mode 100644 jam-ui/src/store/features/sessionChatSlice.js diff --git a/jam-ui/src/store/features/sessionChatSlice.js b/jam-ui/src/store/features/sessionChatSlice.js new file mode 100644 index 000000000..9e35303b3 --- /dev/null +++ b/jam-ui/src/store/features/sessionChatSlice.js @@ -0,0 +1,43 @@ +import { createSlice } from '@reduxjs/toolkit'; + +/** + * Initial state for session chat + * @type {Object} + * @property {Object.} messagesByChannel - Messages organized by channel ID + * @property {string|null} activeChannel - Currently active channel ID + * @property {string|null} channelType - Type of active channel ('global', 'session', 'lesson') + * @property {Object.} unreadCounts - Unread message count per channel + * @property {Object.} lastReadAt - ISO timestamp of last read per channel + * @property {Object.} fetchStatus - Fetch status per channel ('idle', 'loading', 'succeeded', 'failed') + * @property {Object.} fetchError - Fetch error message per channel + * @property {string} sendStatus - Send status ('idle', 'loading', 'succeeded', 'failed') + * @property {string|null} sendError - Send error message + * @property {Object.} nextCursors - Pagination cursors per channel + * @property {boolean} isWindowOpen - Whether chat window is open + * @property {Object|null} windowPosition - Window position {x, y} + */ +const initialState = { + messagesByChannel: {}, + activeChannel: null, + channelType: null, + unreadCounts: {}, + lastReadAt: {}, + fetchStatus: {}, + fetchError: {}, + sendStatus: 'idle', + sendError: null, + nextCursors: {}, + isWindowOpen: false, + windowPosition: null +}; + +const sessionChatSlice = createSlice({ + name: 'sessionChat', + initialState, + reducers: { + // Reducers will be added in Tasks 2-3 + } +}); + +export const {} = sessionChatSlice.actions; +export default sessionChatSlice.reducer; diff --git a/jam-ui/src/store/store.js b/jam-ui/src/store/store.js index 665d91eba..cf08603ad 100644 --- a/jam-ui/src/store/store.js +++ b/jam-ui/src/store/store.js @@ -15,6 +15,7 @@ import activeSessionReducer from "./features/activeSessionSlice" import sessionUIReducer from "./features/sessionUISlice" import mixersReducer from "./features/mixersSlice" import mediaReducer from "./features/mediaSlice" +import sessionChatReducer from "./features/sessionChatSlice" export default configureStore({ reducer: { @@ -25,6 +26,7 @@ export default configureStore({ session: sessionReducer, // this is the slice that holds the session lists activeSession: activeSessionReducer, // this is the slice that holds the currently active session sessionUI: sessionUIReducer, // this is the slice that holds the session UI state (modals, panels) + sessionChat: sessionChatReducer, // Phase 7: session chat state (messages, channels, unread tracking) mixers: mixersReducer, // Phase 5: mixer state (chat, broadcast, track mixers, metronome, media summary) media: mediaReducer, // Phase 5: media data (backing tracks, jam tracks, recorded tracks) latency: latencyReducer,