@100mslive/roomkit-react 0.1.14-alpha.1 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/{HLSView-JTO7E2KW.js → HLSView-662T7R7H.js} +2 -2
  2. package/dist/Prebuilt/common/constants.d.ts +1 -0
  3. package/dist/Prebuilt/common/hooks.d.ts +24 -0
  4. package/dist/Prebuilt/components/Chat/{Navigation.d.ts → ArrowNavigation.d.ts} +1 -2
  5. package/dist/Prebuilt/components/Chat/ChatFooter.d.ts +2 -4
  6. package/dist/Prebuilt/components/Chat/ChatSelector.d.ts +5 -0
  7. package/dist/Prebuilt/components/Chat/ChatSelectorContainer.d.ts +2 -0
  8. package/dist/Prebuilt/components/Chat/ChatStates.d.ts +3 -0
  9. package/dist/Prebuilt/components/Chat/StickIndicator.d.ts +5 -0
  10. package/dist/Prebuilt/components/Chat/useUnreadCount.d.ts +4 -0
  11. package/dist/Prebuilt/components/ChatSettings.d.ts +2 -0
  12. package/dist/Prebuilt/components/Preview/PreviewForm.d.ts +2 -1
  13. package/dist/Prebuilt/components/SidePaneTabs.d.ts +0 -2
  14. package/dist/Prebuilt/components/TileMenu/TileMenuContent.d.ts +19 -0
  15. package/dist/Prebuilt/components/VirtualBackground/VBOption.d.ts +2 -1
  16. package/dist/Prebuilt/components/hooks/useChatBlacklist.d.ts +2 -1
  17. package/dist/Prebuilt/components/hooks/useSetPinnedMessages.d.ts +1 -1
  18. package/dist/Prebuilt/layouts/SidePane.d.ts +1 -3
  19. package/dist/{chunk-TOKLXTAS.js → chunk-2B7YYNHQ.js} +1651 -1229
  20. package/dist/chunk-2B7YYNHQ.js.map +7 -0
  21. package/dist/index.cjs.js +2074 -1609
  22. package/dist/index.cjs.js.map +4 -4
  23. package/dist/index.js +1 -1
  24. package/dist/meta.cjs.json +451 -115
  25. package/dist/meta.esbuild.json +457 -121
  26. package/package.json +6 -6
  27. package/src/Prebuilt/common/constants.ts +1 -0
  28. package/src/Prebuilt/common/{hooks.js → hooks.ts} +4 -5
  29. package/src/Prebuilt/components/AppData/AppData.tsx +1 -0
  30. package/src/Prebuilt/components/AppData/useUISettings.js +2 -1
  31. package/src/Prebuilt/components/AuthToken.jsx +16 -8
  32. package/src/Prebuilt/components/Chat/{Navigation.tsx → ArrowNavigation.tsx} +3 -19
  33. package/src/Prebuilt/components/Chat/Chat.jsx +15 -44
  34. package/src/Prebuilt/components/Chat/ChatBody.jsx +114 -69
  35. package/src/Prebuilt/components/Chat/ChatFooter.tsx +128 -130
  36. package/src/Prebuilt/components/Chat/ChatSelector.tsx +225 -0
  37. package/src/Prebuilt/components/Chat/ChatSelectorContainer.tsx +158 -0
  38. package/src/Prebuilt/components/Chat/{ChatStates.jsx → ChatStates.tsx} +4 -4
  39. package/src/Prebuilt/components/Chat/PinnedMessage.tsx +59 -41
  40. package/src/Prebuilt/components/Chat/StickIndicator.tsx +24 -0
  41. package/src/Prebuilt/components/Chat/useUnreadCount.ts +19 -0
  42. package/src/Prebuilt/components/ChatSettings.tsx +68 -0
  43. package/src/Prebuilt/components/Footer/ParticipantList.jsx +2 -1
  44. package/src/Prebuilt/components/Header/ParticipantFilter.jsx +2 -1
  45. package/src/Prebuilt/components/Notifications/ChatNotifications.tsx +1 -1
  46. package/src/Prebuilt/components/Preview/PreviewForm.tsx +3 -0
  47. package/src/Prebuilt/components/Preview/PreviewJoin.tsx +2 -1
  48. package/src/Prebuilt/components/SidePaneTabs.tsx +48 -50
  49. package/src/Prebuilt/components/TileMenu/{TileMenuContent.jsx → TileMenuContent.tsx} +72 -41
  50. package/src/Prebuilt/components/VirtualBackground/VBCollection.tsx +2 -1
  51. package/src/Prebuilt/components/VirtualBackground/VBOption.tsx +3 -0
  52. package/src/Prebuilt/components/VirtualBackground/VBToggle.jsx +1 -1
  53. package/src/Prebuilt/components/hooks/useChatBlacklist.ts +8 -6
  54. package/src/Prebuilt/components/hooks/useSetPinnedMessages.ts +2 -7
  55. package/src/Prebuilt/layouts/SidePane.tsx +1 -5
  56. package/src/Prebuilt/provider/roomLayoutProvider/constants/index.ts +1 -0
  57. package/dist/chunk-TOKLXTAS.js.map +0 -7
  58. package/src/Prebuilt/components/Chat/ChatSelector.jsx +0 -161
  59. package/src/Prebuilt/components/Chat/ChatSelectorContainer.jsx +0 -81
  60. package/src/Prebuilt/components/Chat/useUnreadCount.js +0 -17
  61. /package/dist/{HLSView-JTO7E2KW.js.map → HLSView-662T7R7H.js.map} +0 -0
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "prebuilt",
11
11
  "roomkit"
12
12
  ],
13
- "version": "0.1.14-alpha.1",
13
+ "version": "0.1.14",
14
14
  "author": "100ms",
15
15
  "license": "MIT",
16
16
  "files": [
@@ -76,10 +76,10 @@
76
76
  "react": ">=17.0.2 <19.0.0"
77
77
  },
78
78
  "dependencies": {
79
- "@100mslive/hls-player": "0.1.23-alpha.1",
80
- "@100mslive/hms-virtual-background": "1.11.23-alpha.1",
81
- "@100mslive/react-icons": "0.8.23-alpha.1",
82
- "@100mslive/react-sdk": "0.8.23-alpha.1",
79
+ "@100mslive/hls-player": "0.1.23",
80
+ "@100mslive/hms-virtual-background": "1.11.23",
81
+ "@100mslive/react-icons": "0.8.23",
82
+ "@100mslive/react-sdk": "0.8.23",
83
83
  "@100mslive/types-prebuilt": "0.12.4",
84
84
  "@emoji-mart/data": "^1.0.6",
85
85
  "@emoji-mart/react": "^1.0.1",
@@ -115,5 +115,5 @@
115
115
  "uuid": "^8.3.2",
116
116
  "worker-timers": "^7.0.40"
117
117
  },
118
- "gitHead": "550917685479247b1855d0579161fb729fbcfd8c"
118
+ "gitHead": "aed9a8922bbebf1015858ab5b4896acff5647c0a"
119
119
  }
@@ -19,6 +19,7 @@ export const EMOJI_REACTION_TYPE = 'EMOJI_REACTION';
19
19
  export const CHAT_SELECTOR = {
20
20
  PEER_ID: 'peer_id',
21
21
  ROLE: 'role',
22
+ EVERYONE: 'Everyone',
22
23
  };
23
24
 
24
25
  export const APP_DATA = {
@@ -1,4 +1,3 @@
1
- // @ts-check
2
1
  import { useEffect, useRef, useState } from 'react';
3
2
  import { JoinForm_JoinBtnType } from '@100mslive/types-prebuilt/elements/join_form';
4
3
  import {
@@ -12,7 +11,7 @@ import {
12
11
  useHMSVanillaStore,
13
12
  } from '@100mslive/react-sdk';
14
13
  import { useRoomLayout } from '../provider/roomLayoutProvider';
15
-
14
+ import { useRoomLayoutConferencingScreen } from '../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
16
15
  /**
17
16
  * Hook to execute a callback when alone in room(after a certain 5d of time)
18
17
  * @param {number} thresholdMs The threshold(in ms) after which the callback is executed,
@@ -52,8 +51,8 @@ export const useWhenAloneInRoom = (thresholdMs = 5 * 60 * 1000) => {
52
51
  };
53
52
 
54
53
  export const useFilteredRoles = () => {
55
- const roles = useHMSStore(selectAvailableRoleNames);
56
- return roles;
54
+ const { elements } = useRoomLayoutConferencingScreen();
55
+ return elements?.chat?.roles_whitelist || [];
57
56
  };
58
57
 
59
58
  export const useShowStreamingUI = () => {
@@ -63,7 +62,7 @@ export const useShowStreamingUI = () => {
63
62
  };
64
63
 
65
64
  // The search results should not have role name matches
66
- export const useParticipants = params => {
65
+ export const useParticipants = (params?: { metadata?: { isHandRaised?: boolean }; role?: string; search?: string }) => {
67
66
  const isConnected = useHMSStore(selectIsConnectedToRoom);
68
67
  const peerCount = useHMSStore(selectPeerCount);
69
68
  const availableRoles = useHMSStore(selectAvailableRoleNames);
@@ -96,6 +96,7 @@ export const AppData = React.memo(() => {
96
96
  const uiSettings = preferences || {};
97
97
  const updatedSettings = {
98
98
  ...uiSettings,
99
+ [UI_SETTINGS.isAudioOnly]: undefined,
99
100
  [UI_SETTINGS.uiViewMode]: uiSettings.uiViewMode || UI_MODE_GRID,
100
101
  };
101
102
  hmsActions.setAppData(APP_DATA.uiSettings, updatedSettings, true);
@@ -13,7 +13,7 @@ import {
13
13
  useHMSVanillaStore,
14
14
  } from '@100mslive/react-sdk';
15
15
  import { UserPreferencesKeys, useUserPreferences } from '../hooks/useUserPreferences';
16
- import { APP_DATA, POLL_STATE, SESSION_STORE_KEY } from '../../common/constants';
16
+ import { APP_DATA, POLL_STATE, SESSION_STORE_KEY, UI_SETTINGS } from '../../common/constants';
17
17
 
18
18
  /**
19
19
  * fields saved related to UI settings in store's app data can be
@@ -154,6 +154,7 @@ const useSetAppData = ({ key1, key2 }) => {
154
154
  const appData = store.getState(selectAppData());
155
155
  setPreferences({
156
156
  ...appData.uiSettings,
157
+ [UI_SETTINGS.isAudioOnly]: undefined,
157
158
  subscribedNotifications: appData.subscribedNotifications,
158
159
  });
159
160
  },
@@ -1,6 +1,6 @@
1
1
  import React, { useEffect, useState } from 'react';
2
2
  import { useSessionStorage } from 'react-use';
3
- import { v4 } from 'uuid';
3
+ import { v4 as uuid } from 'uuid';
4
4
  import { useHMSActions } from '@100mslive/react-sdk';
5
5
  import { styled } from '../../Theme';
6
6
  import { useHMSPrebuiltContext } from '../AppContext';
@@ -26,12 +26,6 @@ const AuthToken = React.memo(({ authTokenByRoomCodeEndpoint, defaultAuthToken })
26
26
  const [, setAuthTokenInAppData] = useSetAppDataByKey(APP_DATA.authToken);
27
27
  const [savedUserId, setSavedUserId] = useSessionStorage(UserPreferencesKeys.USER_ID);
28
28
 
29
- useEffect(() => {
30
- if (!savedUserId && !userId) {
31
- setSavedUserId(v4());
32
- }
33
- }, [savedUserId, setSavedUserId, userId]);
34
-
35
29
  useEffect(() => {
36
30
  if (authToken) {
37
31
  setAuthTokenInAppData(authToken);
@@ -42,11 +36,25 @@ const AuthToken = React.memo(({ authTokenByRoomCodeEndpoint, defaultAuthToken })
42
36
  return;
43
37
  }
44
38
 
39
+ if (!savedUserId && !userId) {
40
+ setSavedUserId(uuid());
41
+ return;
42
+ }
43
+
45
44
  hmsActions
46
45
  .getAuthTokenByRoomCode({ roomCode, userId: userId || savedUserId }, { endpoint: authTokenByRoomCodeEndpoint })
47
46
  .then(token => setAuthTokenInAppData(token))
48
47
  .catch(error => setError(convertError(error)));
49
- }, [hmsActions, authToken, authTokenByRoomCodeEndpoint, setAuthTokenInAppData, roomCode, userId, savedUserId]);
48
+ }, [
49
+ hmsActions,
50
+ authToken,
51
+ authTokenByRoomCodeEndpoint,
52
+ setAuthTokenInAppData,
53
+ roomCode,
54
+ userId,
55
+ savedUserId,
56
+ setSavedUserId,
57
+ ]);
50
58
 
51
59
  if (error.title) {
52
60
  return <ErrorDialog title={error.title}>{error.body}</ErrorDialog>;
@@ -1,40 +1,24 @@
1
1
  import React from 'react';
2
2
  import { ChevronDownIcon, ChevronUpIcon } from '@100mslive/react-icons';
3
- import { Box, Flex } from '../../../Layout';
3
+ import { Flex } from '../../../Layout';
4
4
 
5
- export const Navigation = ({
5
+ export const ArrowNavigation = ({
6
6
  total,
7
7
  index,
8
8
  showPrevious,
9
9
  showNext,
10
- isMobile,
11
10
  }: {
12
11
  total: number;
13
12
  index: number;
14
13
  showPrevious: () => void;
15
14
  showNext: () => void;
16
- isMobile: boolean;
17
15
  }) => {
18
- const sticksCount = Math.min(3, total);
19
-
20
16
  if (total < 2) {
21
17
  return null;
22
18
  }
23
19
 
24
- return isMobile ? (
20
+ return (
25
21
  <Flex direction="column" css={{ gap: '$1' }}>
26
- {[...Array(sticksCount)].map((_, i) => (
27
- <Box
28
- css={{
29
- borderLeft: '2px solid',
30
- height: '$4',
31
- borderColor: i === index ? '$on_surface_high' : '$on_surface_low',
32
- }}
33
- />
34
- ))}
35
- </Flex>
36
- ) : (
37
- <Flex direction="column" css={{ gap: '$4' }}>
38
22
  <Flex
39
23
  onClick={showPrevious}
40
24
  css={
@@ -1,10 +1,9 @@
1
1
  import React, { useCallback, useEffect, useRef, useState } from 'react';
2
2
  import { useMedia } from 'react-use';
3
- import { selectLocalPeer, selectSessionStore } from '@100mslive/hms-video-store';
3
+ import { selectLocalPeer, selectPeerByID, selectSessionStore } from '@100mslive/hms-video-store';
4
4
  import {
5
5
  HMSNotificationTypes,
6
6
  selectHMSMessagesCount,
7
- selectPeerNameByID,
8
7
  useHMSActions,
9
8
  useHMSNotifications,
10
9
  useHMSStore,
@@ -23,38 +22,32 @@ import { useSetPinnedMessages } from '../hooks/useSetPinnedMessages';
23
22
  import { useUnreadCount } from './useUnreadCount';
24
23
  import { CHAT_SELECTOR, SESSION_STORE_KEY } from '../../common/constants';
25
24
 
26
- export const Chat = ({ screenType }) => {
25
+ export const Chat = () => {
26
+ const { elements, screenType } = useRoomLayoutConferencingScreen();
27
27
  const notification = useHMSNotifications(HMSNotificationTypes.PEER_LEFT);
28
- const [peerSelector, setPeerSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.PEER_ID);
29
- const [roleSelector, setRoleSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.ROLE);
30
- const peerName = useHMSStore(selectPeerNameByID(peerSelector));
28
+ const [selectedPeer, setPeerSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.PEER_ID);
29
+ const [selectedRole, setRoleSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.ROLE);
31
30
  const localPeer = useHMSStore(selectLocalPeer);
32
- const [chatOptions, setChatOptions] = useState({
33
- role: roleSelector || '',
34
- peerId: peerSelector && peerName ? peerSelector : '',
35
- selection: roleSelector ? roleSelector : peerSelector && peerName ? peerName : 'Everyone',
36
- });
37
31
  const [isSelectorOpen] = useState(false);
38
32
  const listRef = useRef(null);
39
33
  const hmsActions = useHMSActions();
40
34
  const { removePinnedMessage } = useSetPinnedMessages();
41
35
  const pinnedMessages = useHMSStore(selectSessionStore(SESSION_STORE_KEY.PINNED_MESSAGES)) || [];
36
+ const isPeerPresent = !!useHMSStore(selectPeerByID(selectedPeer));
42
37
 
43
38
  useEffect(() => {
44
- if (notification && notification.data && peerSelector === notification.data.id) {
39
+ if (notification && notification.data && selectedPeer === notification.data.id) {
45
40
  setPeerSelector('');
46
- setChatOptions({
47
- role: '',
48
- peerId: '',
49
- selection: 'Everyone',
50
- });
41
+ setRoleSelector('');
51
42
  }
52
- }, [notification, peerSelector, setPeerSelector]);
43
+ if (selectedPeer && !isPeerPresent) {
44
+ setPeerSelector('');
45
+ }
46
+ }, [notification, selectedPeer, setPeerSelector, setRoleSelector, isPeerPresent]);
53
47
  const blacklistedPeerIDs = useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_PEER_BLACKLIST)) || [];
54
48
  const blacklistedPeerIDSet = new Set(blacklistedPeerIDs);
55
49
  const isLocalPeerBlacklisted = blacklistedPeerIDSet.has(localPeer?.customerUserId);
56
50
  const storeMessageSelector = selectHMSMessagesCount;
57
- const { elements } = useRoomLayoutConferencingScreen();
58
51
  const { enabled: isChatEnabled = true } = useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_STATE)) || {};
59
52
  const isMobile = useMedia(cssConfig.media.md);
60
53
 
@@ -92,14 +85,7 @@ export const Chat = ({ screenType }) => {
92
85
  </>
93
86
  )}
94
87
 
95
- <ChatBody
96
- role={chatOptions.role}
97
- peerId={chatOptions.peerId}
98
- ref={listRef}
99
- scrollToBottom={scrollToBottom}
100
- screenType={screenType}
101
- blacklistedPeerIDs={blacklistedPeerIDs}
102
- />
88
+ <ChatBody ref={listRef} scrollToBottom={scrollToBottom} screenType={screenType} />
103
89
 
104
90
  <ChatPaused />
105
91
 
@@ -110,24 +96,9 @@ export const Chat = ({ screenType }) => {
110
96
  ) : null}
111
97
 
112
98
  {isChatEnabled && !isLocalPeerBlacklisted ? (
113
- <ChatFooter
114
- role={chatOptions.role}
115
- onSend={() => scrollToBottom(1)}
116
- selection={chatOptions.selection}
117
- screenType={screenType}
118
- onSelect={({ role, peerId, selection }) => {
119
- setChatOptions({
120
- role,
121
- peerId,
122
- selection,
123
- });
124
- setPeerSelector(peerId);
125
- setRoleSelector(role);
126
- }}
127
- peerId={chatOptions.peerId}
128
- >
99
+ <ChatFooter onSend={() => scrollToBottom(1)} screenType={screenType}>
129
100
  {!isSelectorOpen && !isScrolledToBottom && (
130
- <NewMessageIndicator role={chatOptions.role} peerId={chatOptions.peerId} scrollToBottom={scrollToBottom} />
101
+ <NewMessageIndicator role={selectedRole} peerId={selectedPeer} scrollToBottom={scrollToBottom} />
131
102
  )}
132
103
  </ChatFooter>
133
104
  ) : null}
@@ -7,16 +7,22 @@ import {
7
7
  selectHMSMessages,
8
8
  selectLocalPeerID,
9
9
  selectLocalPeerName,
10
- selectLocalPeerRoleName,
11
10
  selectMessagesByPeerID,
12
11
  selectMessagesByRole,
13
- selectPeerNameByID,
14
12
  selectPermissions,
15
13
  selectSessionStore,
16
14
  useHMSActions,
17
15
  useHMSStore,
18
16
  } from '@100mslive/react-sdk';
19
- import { CopyIcon, CrossCircleIcon, CrossIcon, EyeCloseIcon, PinIcon, VerticalMenuIcon } from '@100mslive/react-icons';
17
+ import {
18
+ CopyIcon,
19
+ CrossCircleIcon,
20
+ CrossIcon,
21
+ EyeCloseIcon,
22
+ PinIcon,
23
+ ReplyIcon,
24
+ VerticalMenuIcon,
25
+ } from '@100mslive/react-icons';
20
26
  import { Dropdown } from '../../../Dropdown';
21
27
  import { IconButton } from '../../../IconButton';
22
28
  import { Box, Flex } from '../../../Layout';
@@ -28,10 +34,11 @@ import emptyChat from '../../images/empty-chat.svg';
28
34
  import { ToastManager } from '../Toast/ToastManager';
29
35
  import { MwebChatOption } from './MwebChatOption';
30
36
  import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
37
+ import { useSetSubscribedChatSelector, useSubscribeChatSelector } from '../AppData/useUISettings';
31
38
  import { useChatBlacklist } from '../hooks/useChatBlacklist';
32
39
  import { useSetPinnedMessages } from '../hooks/useSetPinnedMessages';
33
40
  import { useUnreadCount } from './useUnreadCount';
34
- import { SESSION_STORE_KEY } from '../../common/constants';
41
+ import { CHAT_SELECTOR, SESSION_STORE_KEY } from '../../common/constants';
35
42
 
36
43
  const iconStyle = { height: '1.125rem', width: '1.125rem' };
37
44
  const tooltipBoxCSS = {
@@ -63,21 +70,28 @@ const MessageTypeContainer = ({ left, right }) => {
63
70
  <Flex
64
71
  align="center"
65
72
  css={{
66
- ml: 'auto',
73
+ position: 'absolute',
74
+ right: 0,
75
+ zIndex: 1,
67
76
  mr: '$4',
68
- p: '$2 $4',
77
+ p: '$2',
69
78
  border: '1px solid $border_bright',
70
79
  r: '$0',
80
+ gap: '$3',
71
81
  }}
82
+ className="message_type_container"
72
83
  >
73
84
  {left && (
74
- <SenderName variant="tiny" as="span" css={{ color: '$on_surface_medium' }}>
85
+ <SenderName variant="caption" as="span" css={{ color: '$on_surface_medium' }}>
75
86
  {left}
76
87
  </SenderName>
77
88
  )}
78
- {left && right && <Box css={{ borderLeft: '1px solid $border_bright', mx: '$4', h: '$8' }} />}
79
89
  {right && (
80
- <SenderName as="span" variant="tiny" css={{ textTransform: 'uppercase' }}>
90
+ <SenderName
91
+ as="span"
92
+ variant="caption"
93
+ css={{ color: '$on_surface_high', textTransform: 'capitalize', fontWeight: '$semiBold' }}
94
+ >
81
95
  {right}
82
96
  </SenderName>
83
97
  )}
@@ -85,20 +99,13 @@ const MessageTypeContainer = ({ left, right }) => {
85
99
  );
86
100
  };
87
101
 
88
- const MessageType = ({ roles, hasCurrentUserSent, receiver }) => {
89
- const peerName = useHMSStore(selectPeerNameByID(receiver));
90
- const localPeerRoleName = useHMSStore(selectLocalPeerRoleName);
102
+ const MessageType = ({ roles, receiver }) => {
91
103
  if (receiver) {
92
- return (
93
- <MessageTypeContainer
94
- left={hasCurrentUserSent ? `${peerName ? `TO ${peerName}` : ''}` : 'TO YOU'}
95
- right="PRIVATE"
96
- />
97
- );
104
+ return <MessageTypeContainer left="Direct Message" />;
98
105
  }
99
106
 
100
- if (roles && roles.length) {
101
- return <MessageTypeContainer left="TO" right={hasCurrentUserSent ? roles.join(',') : localPeerRoleName} />;
107
+ if (roles && roles.length > 0) {
108
+ return <MessageTypeContainer left="To Group" right={roles[0]} />;
102
109
  }
103
110
  return null;
104
111
  };
@@ -143,30 +150,37 @@ const getMessageType = ({ roles, receiver }) => {
143
150
  }
144
151
  return receiver ? 'private' : '';
145
152
  };
146
- const ChatActions = ({ onPin, showPinAction, message, sentByLocalPeer, isMobile, openSheet, setOpenSheet }) => {
153
+ const ChatActions = ({
154
+ onPin,
155
+ showPinAction,
156
+ onReplyPrivately,
157
+ showReplyPrivateAction,
158
+ message,
159
+ sentByLocalPeer,
160
+ isMobile,
161
+ openSheet,
162
+ setOpenSheet,
163
+ }) => {
147
164
  const { elements } = useRoomLayoutConferencingScreen();
148
165
  const { can_hide_message, can_block_user } = elements?.chat?.real_time_controls || {
149
166
  can_hide_message: false,
150
167
  can_block_user: false,
151
168
  };
152
169
  const [open, setOpen] = useState(false);
153
- const blacklistedPeerIDs = useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_PEER_BLACKLIST)) || [];
154
170
  const { blacklistItem: blacklistPeer } = useChatBlacklist(SESSION_STORE_KEY.CHAT_PEER_BLACKLIST);
155
171
 
156
- const blacklistedMessageIDs = useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_MESSAGE_BLACKLIST)) || [];
157
- const { blacklistItem: blacklistMessage } = useChatBlacklist(SESSION_STORE_KEY.CHAT_MESSAGE_BLACKLIST);
158
-
172
+ const { blacklistItem: blacklistMessage, blacklistedIDs: blacklistedMessageIDs = [] } = useChatBlacklist(
173
+ SESSION_STORE_KEY.CHAT_MESSAGE_BLACKLIST,
174
+ );
159
175
  const { unpinBlacklistedMessages } = useSetPinnedMessages();
160
176
 
161
- const pinnedMessages = useHMSStore(selectSessionStore(SESSION_STORE_KEY.PINNED_MESSAGES)) || [];
162
-
177
+ const pinnedMessages = useHMSStore(selectSessionStore(SESSION_STORE_KEY.PINNED_MESSAGES));
163
178
  const updatePinnedMessages = useCallback(
164
- ({ messageID = '', peerID = '' }) => {
179
+ (messageID = '') => {
165
180
  const blacklistedMessageIDSet = new Set([...blacklistedMessageIDs, messageID]);
166
- const blacklistedPeerIDSet = new Set([...blacklistedPeerIDs, peerID]);
167
- unpinBlacklistedMessages(pinnedMessages, blacklistedPeerIDSet, blacklistedMessageIDSet);
181
+ unpinBlacklistedMessages(pinnedMessages, blacklistedMessageIDSet);
168
182
  },
169
- [pinnedMessages, blacklistedMessageIDs, blacklistedPeerIDs],
183
+ [blacklistedMessageIDs, unpinBlacklistedMessages, pinnedMessages],
170
184
  );
171
185
 
172
186
  const copyMessageContent = useCallback(() => {
@@ -184,6 +198,13 @@ const ChatActions = ({ onPin, showPinAction, message, sentByLocalPeer, isMobile,
184
198
  }, [message]);
185
199
 
186
200
  const options = {
201
+ reply: {
202
+ text: 'Reply Privately',
203
+ tooltipText: 'Reply privately',
204
+ icon: <ReplyIcon style={iconStyle} />,
205
+ onClick: onReplyPrivately,
206
+ show: showReplyPrivateAction,
207
+ },
187
208
  pin: {
188
209
  text: 'Pin message',
189
210
  tooltipText: 'Pin',
@@ -202,18 +223,15 @@ const ChatActions = ({ onPin, showPinAction, message, sentByLocalPeer, isMobile,
202
223
  text: 'Hide for everyone',
203
224
  icon: <EyeCloseIcon style={iconStyle} />,
204
225
  onClick: async () => {
205
- blacklistMessage(blacklistedMessageIDs, message.id);
206
- updatePinnedMessages({ messageID: message.id });
226
+ blacklistMessage(message.id);
227
+ updatePinnedMessages(message.id);
207
228
  },
208
229
  show: can_hide_message,
209
230
  },
210
231
  block: {
211
232
  text: 'Block from chat',
212
233
  icon: <CrossCircleIcon style={iconStyle} />,
213
- onClick: async () => {
214
- blacklistPeer(blacklistedPeerIDs, message?.senderUserId);
215
- updatePinnedMessages({ peerID: message?.senderUserId });
216
- },
234
+ onClick: async () => blacklistPeer(message?.senderUserId),
217
235
  color: '$alert_error_default',
218
236
  show: can_block_user && !sentByLocalPeer,
219
237
  },
@@ -269,9 +287,19 @@ const ChatActions = ({ onPin, showPinAction, message, sentByLocalPeer, isMobile,
269
287
  borderRadius: '$1',
270
288
  p: '$2',
271
289
  opacity: open ? 1 : 0,
290
+ position: 'absolute',
291
+ right: 0,
292
+ zIndex: 1,
272
293
  '@md': { opacity: 1 },
273
294
  }}
274
295
  >
296
+ {options.reply.show ? (
297
+ <Tooltip boxCss={tooltipBoxCSS} title={options.reply.tooltipText}>
298
+ <IconButton data-testid="reply_message_btn" onClick={options.reply.onClick}>
299
+ {options.reply.icon}
300
+ </IconButton>
301
+ </Tooltip>
302
+ ) : null}
275
303
  {options.pin.show ? (
276
304
  <Tooltip boxCss={tooltipBoxCSS} title={options.pin.tooltipText}>
277
305
  <IconButton data-testid="pin_message_btn" onClick={options.pin.onClick}>
@@ -335,7 +363,7 @@ const SenderName = styled(Text, {
335
363
  overflow: 'hidden',
336
364
  textOverflow: 'ellipsis',
337
365
  whiteSpace: 'nowrap',
338
- maxWidth: '24ch',
366
+ maxWidth: '16ch',
339
367
  minWidth: 0,
340
368
  color: '$on_surface_high',
341
369
  fontWeight: '$semiBold',
@@ -344,6 +372,7 @@ const SenderName = styled(Text, {
344
372
  const ChatMessage = React.memo(
345
373
  ({ index, style = {}, message, setRowHeight, isLast = false, unreadCount = 0, scrollToBottom, onPin }) => {
346
374
  const { ref, inView } = useInView({ threshold: 0.5, triggerOnce: true });
375
+ const { elements } = useRoomLayoutConferencingScreen();
347
376
  const rowRef = useRef(null);
348
377
  useEffect(() => {
349
378
  if (rowRef.current) {
@@ -351,11 +380,14 @@ const ChatMessage = React.memo(
351
380
  }
352
381
  }, [index, setRowHeight]);
353
382
  const isMobile = useMedia(cssConfig.media.md);
354
- const { elements } = useRoomLayoutConferencingScreen();
383
+ const isPrivateChatEnabled = !!elements?.chat?.private_chat_enabled;
355
384
  const isOverlay = elements?.chat?.is_overlay && isMobile;
356
385
  const hmsActions = useHMSActions();
357
386
  const localPeerId = useHMSStore(selectLocalPeerID);
358
387
  const permissions = useHMSStore(selectPermissions);
388
+ const selectedPeer = useSubscribeChatSelector(CHAT_SELECTOR.PEER_ID);
389
+ const selectedRole = useSubscribeChatSelector(CHAT_SELECTOR.ROLE);
390
+ const [, setPeerSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.PEER_ID);
359
391
  const messageType = getMessageType({
360
392
  roles: message.recipientRoles,
361
393
  receiver: message.recipientPeer,
@@ -383,7 +415,8 @@ const ChatMessage = React.memo(
383
415
  mb: '$5',
384
416
  pr: '$10',
385
417
  mt: '$4',
386
- '&:hover .chat_actions': { opacity: 1 },
418
+ '&:not(:hover} .chat_actions': { display: 'none' },
419
+ '&:hover .chat_actions': { display: 'flex', opacity: 1 },
387
420
  }}
388
421
  style={style}
389
422
  >
@@ -392,10 +425,16 @@ const ChatMessage = React.memo(
392
425
  align="center"
393
426
  css={{
394
427
  flexWrap: 'wrap',
428
+ position: 'relative',
395
429
  // Theme independent color, token should not be used for transparent chat
396
- bg: messageType ? (isOverlay ? 'rgba(0, 0, 0, 0.64)' : '$surface_default') : undefined,
430
+ bg:
431
+ messageType && !(selectedPeer || selectedRole)
432
+ ? isOverlay
433
+ ? 'rgba(0, 0, 0, 0.64)'
434
+ : '$surface_default'
435
+ : undefined,
397
436
  r: '$1',
398
- p: '$1 $2',
437
+ p: '$4',
399
438
  userSelect: 'none',
400
439
  '@md': {
401
440
  cursor: 'pointer',
@@ -416,21 +455,29 @@ const ChatMessage = React.memo(
416
455
  css={{
417
456
  color: isOverlay ? '#FFF' : '$on_surface_high',
418
457
  fontWeight: '$semiBold',
419
- display: 'inline-flex',
458
+ display: 'flex',
420
459
  alignItems: 'center',
421
- justifyContent: 'space-between',
460
+ alignSelf: 'stretch',
422
461
  width: '100%',
423
462
  }}
424
463
  as="div"
425
464
  >
426
465
  <Flex align="baseline">
427
466
  {message.senderName === 'You' || !message.senderName ? (
428
- <SenderName as="span" variant="sm" css={{ color: isOverlay ? '#FFF' : '$on_surface_high' }}>
467
+ <SenderName
468
+ as="span"
469
+ variant="sub2"
470
+ css={{ color: isOverlay ? '#FFF' : '$on_surface_high', fontWeight: '$semiBold' }}
471
+ >
429
472
  {message.senderName || 'Anonymous'}
430
473
  </SenderName>
431
474
  ) : (
432
475
  <Tooltip title={message.senderName} side="top" align="start">
433
- <SenderName as="span" variant="sm" css={{ color: isOverlay ? '#FFF' : '$on_surface_high' }}>
476
+ <SenderName
477
+ as="span"
478
+ variant="sub2"
479
+ css={{ color: isOverlay ? '#FFF' : '$on_surface_high', fontWeight: '$semiBold' }}
480
+ >
434
481
  {message.senderName}
435
482
  </SenderName>
436
483
  </Tooltip>
@@ -438,9 +485,9 @@ const ChatMessage = React.memo(
438
485
  {!isOverlay ? (
439
486
  <Text
440
487
  as="span"
441
- variant="xs"
488
+ variant="caption"
442
489
  css={{
443
- ml: '$4',
490
+ ml: '$2',
444
491
  color: '$on_surface_medium',
445
492
  flexShrink: 0,
446
493
  }}
@@ -449,17 +496,17 @@ const ChatMessage = React.memo(
449
496
  </Text>
450
497
  ) : null}
451
498
  </Flex>
452
- <MessageType
453
- hasCurrentUserSent={message.sender === localPeerId}
454
- receiver={message.recipientPeer}
455
- roles={message.recipientRoles}
456
- />
499
+ {!(selectedPeer || selectedRole) && (
500
+ <MessageType receiver={message.recipientPeer} roles={message.recipientRoles} />
501
+ )}
457
502
 
458
503
  <ChatActions
459
504
  onPin={onPin}
460
505
  showPinAction={showPinAction}
461
506
  message={message}
462
507
  sentByLocalPeer={message.sender === localPeerId}
508
+ onReplyPrivately={() => setPeerSelector(message.sender)}
509
+ showReplyPrivateAction={!selectedPeer && message.sender !== localPeerId && isPrivateChatEnabled}
463
510
  isMobile={isMobile}
464
511
  openSheet={openSheet}
465
512
  setOpenSheet={setOpenSheet}
@@ -574,30 +621,28 @@ const VirtualizedChatMessages = React.forwardRef(({ messages, unreadCount = 0, s
574
621
  );
575
622
  });
576
623
 
577
- export const ChatBody = React.forwardRef(({ role, peerId, scrollToBottom, blacklistedPeerIDs }, listRef) => {
578
- const storeMessageSelector = role
579
- ? selectMessagesByRole(role)
580
- : peerId
581
- ? selectMessagesByPeerID(peerId)
582
- : selectHMSMessages;
624
+ export const ChatBody = React.forwardRef(({ scrollToBottom }, listRef) => {
625
+ const selectedPeer = useSubscribeChatSelector(CHAT_SELECTOR.PEER_ID);
626
+ const selectedRole = useSubscribeChatSelector(CHAT_SELECTOR.ROLE);
627
+ let storeMessageSelector;
628
+ if (selectedRole) {
629
+ storeMessageSelector = selectMessagesByRole(selectedRole);
630
+ } else if (selectedPeer) {
631
+ storeMessageSelector = selectMessagesByPeerID(selectedPeer);
632
+ } else {
633
+ storeMessageSelector = selectHMSMessages;
634
+ }
583
635
  let messages = useHMSStore(storeMessageSelector) || [];
584
636
  const blacklistedMessageIDs = useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_MESSAGE_BLACKLIST)) || [];
585
637
  const getFilteredMessages = () => {
586
638
  const blacklistedMessageIDSet = new Set(blacklistedMessageIDs);
587
- const blacklistedPeerIDSet = new Set(blacklistedPeerIDs);
588
- return (
589
- messages?.filter(
590
- message =>
591
- message.type === 'chat' &&
592
- !blacklistedMessageIDSet.has(message.id) &&
593
- !blacklistedPeerIDSet.has(message?.senderUserId),
594
- ) || []
595
- );
639
+
640
+ return messages?.filter(message => message.type === 'chat' && !blacklistedMessageIDSet.has(message.id)) || [];
596
641
  };
597
642
 
598
643
  const isMobile = useMedia(cssConfig.media.md);
599
644
  const { elements } = useRoomLayoutConferencingScreen();
600
- const unreadCount = useUnreadCount({ role, peerId });
645
+ const unreadCount = useUnreadCount({ role: selectedRole, peerId: selectedPeer });
601
646
 
602
647
  if (messages.length === 0 && !(isMobile && elements?.chat?.is_overlay)) {
603
648
  return (