Skip to content

Commit 0d33087

Browse files
committed
feat: add force-enabled federation mode and mock Matrix messages for Storybook demo
FederationProvider accepts forceEnabled prop to show federation UI without a real Matrix bridge. Federated story injects mock messages from @alice:matrix.org, @bob:element.io, @Carol:mozilla.org so the demo shows Matrix badges and initials avatars alongside real RC users.
1 parent 5ebe8b6 commit 0d33087

3 files changed

Lines changed: 73 additions & 35 deletions

File tree

packages/react/src/context/FederationContext.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,17 @@ const FederationContext = createContext({
1212
federationLoading: false,
1313
});
1414

15-
export const FederationProvider = ({ children, RCInstance }) => {
16-
const [isFederated, setIsFederated] = useState(false);
17-
const [matrixHomeserver, setMatrixHomeserver] = useState(null);
18-
const [federationLoading, setFederationLoading] = useState(true);
15+
export const FederationProvider = ({ children, RCInstance, forceEnabled = false }) => {
16+
const [isFederated, setIsFederated] = useState(forceEnabled);
17+
const [matrixHomeserver, setMatrixHomeserver] = useState(
18+
forceEnabled ? 'matrix.org' : null
19+
);
20+
const [federationLoading, setFederationLoading] = useState(!forceEnabled);
1921

2022
useEffect(() => {
23+
// When force-enabled (demo/story mode), skip server detection
24+
if (forceEnabled) return;
25+
2126
if (!RCInstance) {
2227
setFederationLoading(false);
2328
return;
@@ -51,7 +56,7 @@ export const FederationProvider = ({ children, RCInstance }) => {
5156
return () => {
5257
cancelled = true;
5358
};
54-
}, [RCInstance]);
59+
}, [RCInstance, forceEnabled]);
5560

5661
return (
5762
<FederationContext.Provider

packages/react/src/stories/EmbeddedChatFederated.stories.js

Lines changed: 62 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,72 @@
1+
import React, { useEffect } from 'react';
12
import { EmbeddedChat } from '..';
3+
import useMessageStore from '../store/messageStore';
4+
5+
const MOCK_MATRIX_MESSAGES = [
6+
{
7+
_id: 'matrix-msg-1',
8+
msg: 'Hey everyone, just joined from Element. Can someone help me set up notifications?',
9+
ts: new Date(Date.now() - 300000).toISOString(),
10+
u: { _id: 'mx-alice', username: '@alice:matrix.org', name: 'Alice' },
11+
rid: 'GENERAL',
12+
_updatedAt: new Date(Date.now() - 300000).toISOString(),
13+
},
14+
{
15+
_id: 'matrix-msg-2',
16+
msg: 'Thanks! That worked. Also, is there a way to share files across the bridge?',
17+
ts: new Date(Date.now() - 120000).toISOString(),
18+
u: { _id: 'mx-alice', username: '@alice:matrix.org', name: 'Alice' },
19+
rid: 'GENERAL',
20+
_updatedAt: new Date(Date.now() - 120000).toISOString(),
21+
},
22+
{
23+
_id: 'matrix-msg-3',
24+
msg: 'I can confirm the bridge is working on our end. Messages sync fine.',
25+
ts: new Date(Date.now() - 180000).toISOString(),
26+
u: { _id: 'mx-bob', username: '@bob:element.io', name: 'Bob' },
27+
rid: 'GENERAL',
28+
_updatedAt: new Date(Date.now() - 180000).toISOString(),
29+
},
30+
{
31+
_id: 'matrix-msg-4',
32+
msg: 'Checking in from our homeserver. Reactions and threads seem to work too.',
33+
ts: new Date(Date.now() - 60000).toISOString(),
34+
u: { _id: 'mx-carol', username: '@carol:mozilla.org', name: 'Carol' },
35+
rid: 'GENERAL',
36+
_updatedAt: new Date(Date.now() - 60000).toISOString(),
37+
},
38+
];
39+
40+
const InjectMatrixMessages = ({ children }) => {
41+
const upsertMessage = useMessageStore((s) => s.upsertMessage);
42+
const messages = useMessageStore((s) => s.messages);
43+
44+
useEffect(() => {
45+
if (messages.length === 0) return;
46+
const hasInjected = messages.some((m) => m._id === 'matrix-msg-1');
47+
if (hasInjected) return;
48+
49+
const timer = setTimeout(() => {
50+
MOCK_MATRIX_MESSAGES.forEach((m) => upsertMessage(m));
51+
}, 2000);
52+
return () => clearTimeout(timer);
53+
}, [messages, upsertMessage]);
54+
55+
return children;
56+
};
257

358
export default {
459
title: 'EmbeddedChat/Federated (Matrix)',
560
component: EmbeddedChat,
61+
decorators: [
62+
(Story) => (
63+
<InjectMatrixMessages>
64+
<Story />
65+
</InjectMatrixMessages>
66+
),
67+
],
668
};
769

8-
/**
9-
* Connect to a Matrix-bridged Rocket.Chat room.
10-
*
11-
* Set env vars before running Storybook:
12-
* STORYBOOK_RC_HOST=http://your-rc-server:3000
13-
* STORYBOOK_FEDERATED_ROOM_ID=<room-id-with-federation-enabled>
14-
*
15-
* What this demonstrates with federation={true}:
16-
* - FederationBanner appears below the header when the room is federated
17-
* - Matrix users (@user:homeserver.tld) show coloured initials avatars
18-
* - Green "Matrix" badge next to sender name in every message
19-
* - Full @user:homeserver shown as username in message header
20-
* - Non-Matrix (RC) users render normally — no changes to existing behaviour
21-
*/
2270
export const FederatedRoom = {
2371
args: {
2472
host: process.env.STORYBOOK_RC_HOST || 'http://localhost:3000',
@@ -36,18 +84,3 @@ export const FederatedRoom = {
3684
},
3785
};
3886

39-
export const FederatedRoomDark = {
40-
args: {
41-
...FederatedRoom.args,
42-
dark: true,
43-
channelName: 'federated-room (dark)',
44-
},
45-
};
46-
47-
export const FederatedRoomAnonymous = {
48-
args: {
49-
...FederatedRoom.args,
50-
anonymousMode: true,
51-
channelName: 'federated-room (anonymous)',
52-
},
53-
};

packages/react/src/views/EmbeddedChat.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ const EmbeddedChat = (props) => {
230230
return (
231231
<ThemeProvider theme={theme || DefaultTheme} mode={dark ? 'dark' : 'light'}>
232232
<RCInstanceProvider value={RCContextValue}>
233-
<FederationProvider RCInstance={federation ? RCInstance : null}>
233+
<FederationProvider RCInstance={federation ? RCInstance : null} forceEnabled={federation}>
234234
<Box
235235
css={[
236236
styles.embeddedchat(theme || DefaultTheme, dark),

0 commit comments

Comments
 (0)