From 5132c83458b5b2e264bf95209b7a7cf37a76aaa5 Mon Sep 17 00:00:00 2001 From: Nuwan Date: Sun, 1 Feb 2026 22:16:01 +0530 Subject: [PATCH] fix: pass client_id to exclude sender from WebSocket broadcast The backend uses client_id to identify the sender and exclude them from receiving their own message via WebSocket (avoiding duplication with optimistic update). Changes: - rest.js: Add clientId parameter to sendChatMessage - sessionChatSlice.js: Pass clientId to API call - JKChatComposer.js: Get server.clientId from context and pass it This fixes: Messages now broadcast to OTHER users in session, not just the sender. The sender sees optimistic update immediately, other users receive via WebSocket. Co-Authored-By: Claude Sonnet 4.5 --- jam-ui/src/components/client/chat/JKChatComposer.js | 9 +++++---- jam-ui/src/helpers/rest.js | 4 +++- jam-ui/src/store/features/sessionChatSlice.js | 6 ++++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/jam-ui/src/components/client/chat/JKChatComposer.js b/jam-ui/src/components/client/chat/JKChatComposer.js index 0f1550f28..965237c44 100644 --- a/jam-ui/src/components/client/chat/JKChatComposer.js +++ b/jam-ui/src/components/client/chat/JKChatComposer.js @@ -36,8 +36,8 @@ const JKChatComposer = ({ textareaRef }) => { const sendStatus = useSelector(selectSendStatus); const sendError = useSelector(selectSendError); - // WebSocket connection status - const { isConnected } = useJamServerContext(); + // WebSocket connection status and client ID + const { isConnected, server } = useJamServerContext(); // Current user for optimistic updates const { currentUser } = useAuth(); @@ -86,13 +86,14 @@ const JKChatComposer = ({ textareaRef }) => { message: trimmedText, optimisticId: uuid(), userId: currentUser.id, - userName: currentUser.name + userName: currentUser.name, + clientId: server?.clientId }) ); // Clear input on success setInputText(''); - }, [canSend, dispatch, sessionId, trimmedText, currentUser]); + }, [canSend, dispatch, sessionId, trimmedText, currentUser, server?.clientId]); const handleKeyDown = useCallback( e => { diff --git a/jam-ui/src/helpers/rest.js b/jam-ui/src/helpers/rest.js index d042a0498..f30f0ce6c 100644 --- a/jam-ui/src/helpers/rest.js +++ b/jam-ui/src/helpers/rest.js @@ -986,11 +986,13 @@ export const getChatMessages = async ({ channel, sessionId, before }) => { * @param {string} params.channel - Channel type ('session', 'global', 'lesson') * @param {string} [params.sessionId] - Session ID (required for session channel) * @param {string} params.message - Message content + * @param {string} [params.clientId] - Client ID (used by backend to exclude sender from WebSocket broadcast) * @returns {Promise<{message: Object}>} Created message object */ -export const sendChatMessage = async ({ channel, sessionId, message }) => { +export const sendChatMessage = async ({ channel, sessionId, message, clientId }) => { const body = { channel, message }; if (sessionId) body.music_session = sessionId; + if (clientId) body.client_id = clientId; const baseUrl = process.env.REACT_APP_API_BASE_URL; const response = await fetch(`${baseUrl}/chat`, { diff --git a/jam-ui/src/store/features/sessionChatSlice.js b/jam-ui/src/store/features/sessionChatSlice.js index 1e4db7e09..af185943b 100644 --- a/jam-ui/src/store/features/sessionChatSlice.js +++ b/jam-ui/src/store/features/sessionChatSlice.js @@ -30,14 +30,16 @@ export const fetchChatHistory = createAsyncThunk( * @param {string} params.optimisticId - Temporary ID for optimistic update * @param {string} params.userId - Current user ID * @param {string} params.userName - Current user name + * @param {string} [params.clientId] - WebSocket client ID (used to exclude sender from broadcast) */ export const sendMessage = createAsyncThunk( 'sessionChat/sendMessage', - async ({ channel, sessionId, message }) => { + async ({ channel, sessionId, message, clientId }) => { const response = await sendChatMessage({ channel: sessionId ? 'session' : 'global', sessionId, - message + message, + clientId }); return response; }