From 40a4e54e9b5f4a0eda20280d0bb8779b960f2223 Mon Sep 17 00:00:00 2001 From: Aryan-Verma-999 Date: Mon, 15 Jun 2026 23:36:45 +0530 Subject: [PATCH] feat: transparent native matrix federation via joinRoom hook in EmbeddedChatApi --- packages/api/src/EmbeddedChatApi.ts | 19 ++++++++++++--- .../stories/EmbeddedChatWithMatrix.stories.js | 23 +++++++++++++++++++ .../react/src/views/ChatHeader/ChatHeader.js | 3 +-- packages/react/src/views/EmbeddedChat.js | 5 ++-- 4 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 packages/react/src/stories/EmbeddedChatWithMatrix.stories.js diff --git a/packages/api/src/EmbeddedChatApi.ts b/packages/api/src/EmbeddedChatApi.ts index b775016170..6f2408414e 100644 --- a/packages/api/src/EmbeddedChatApi.ts +++ b/packages/api/src/EmbeddedChatApi.ts @@ -238,12 +238,12 @@ export default class EmbeddedChatApi { * All subscriptions are implemented here. * TODO: Add logic to call thread message event listeners. To be done after thread implementation */ - async connect() { + async connect(isChannelPrivate = false) { // Guard against concurrent connect() calls (e.g. React StrictMode double-invoke) if (this._connectPromise) { return this._connectPromise; } - this._connectPromise = this._doConnect().finally(() => { + this._connectPromise = this._doConnect(isChannelPrivate).finally(() => { this._connectPromise = null; }); return this._connectPromise; @@ -261,7 +261,18 @@ export default class EmbeddedChatApi { return message; } - private async _doConnect() { + async joinRoom(isChannelPrivate = false) { + try { + const roomType = isChannelPrivate ? "groups" : "channels"; + await this._restRequest(`/v1/${roomType}.join`, "POST", { + roomId: this.rid, + }); + } catch (err) { + console.debug("[EmbeddedChat] joinRoom skipped:", err); + } + } + + private async _doConnect(isChannelPrivate = false) { try { this.close(); // before connection, all previous subscriptions should be cancelled await this.sdk.connection.connect(); @@ -269,7 +280,9 @@ export default class EmbeddedChatApi { const currentUser = (await this.auth.getCurrentUser()) as any; const token = currentUser?.authToken || currentUser?.data?.authToken; if (token) { + this._applyCredentials(currentUser); await this.sdk.account.loginWithToken(token); + await this.joinRoom(isChannelPrivate); } // Subscribe to room messages diff --git a/packages/react/src/stories/EmbeddedChatWithMatrix.stories.js b/packages/react/src/stories/EmbeddedChatWithMatrix.stories.js new file mode 100644 index 0000000000..cd5ceab5d6 --- /dev/null +++ b/packages/react/src/stories/EmbeddedChatWithMatrix.stories.js @@ -0,0 +1,23 @@ +import { EmbeddedChat } from '..'; + +export default { + title: 'EmbeddedChat/WithMatrix', + component: EmbeddedChat, +}; + +export const WithMatrix = { + args: { + host: process.env.STORYBOOK_RC_HOST || 'http://localhost:3000', + roomId: process.env.RC_ROOM_ID || 'GENERAL', + channelName: 'general', + anonymousMode: false, + toastBarPosition: 'bottom right', + showRoles: true, + enableThreads: true, + hideHeader: false, + auth: { + flow: 'PASSWORD', + }, + dark: false, + }, +}; diff --git a/packages/react/src/views/ChatHeader/ChatHeader.js b/packages/react/src/views/ChatHeader/ChatHeader.js index c28e9ad39e..0fcc7a7a5a 100644 --- a/packages/react/src/views/ChatHeader/ChatHeader.js +++ b/packages/react/src/views/ChatHeader/ChatHeader.js @@ -203,8 +203,7 @@ const ChatHeader = ({ ) { setIsChannelArchived(true); const roomInfo = await RCInstance.getRoomInfo(); - const roomData = roomInfo.result[roomInfo.result.length - 1]; - setChannelInfo(roomData); + setChannelInfo(roomInfo?.room); } else if ('errorType' in res && res.errorType === 'Not Allowed') { dispatchToastMessage({ type: 'error', diff --git a/packages/react/src/views/EmbeddedChat.js b/packages/react/src/views/EmbeddedChat.js index a15cdbccd1..153670ecef 100644 --- a/packages/react/src/views/EmbeddedChat.js +++ b/packages/react/src/views/EmbeddedChat.js @@ -18,7 +18,7 @@ import { import { ChatLayout } from './ChatLayout'; import { ChatHeader } from './ChatHeader'; import { RCInstanceProvider } from '../context/RCInstance'; -import { useUserStore, useLoginStore, useMessageStore } from '../store'; +import { useUserStore, useLoginStore, useMessageStore, useChannelStore } from '../store'; import DefaultTheme from '../theme/DefaultTheme'; import { getTokenStorage } from '../lib/auth'; import { styles } from './EmbeddedChat.styles'; @@ -172,7 +172,8 @@ const EmbeddedChat = (props) => { useEffect(() => { const handleAuthChange = (user) => { if (user) { - RCInstance.connect() + const { isChannelPrivate } = useChannelStore.getState(); + RCInstance.connect(isChannelPrivate) .then(() => { console.log(`Connected to RocketChat ${RCInstance.host}`); const me = user.me || user.data?.me;