@100mslive/roomkit-react 0.1.14-alpha.0 → 0.1.14-alpha.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. package/dist/{HLSView-HCZVI2RM.js → HLSView-3RARRZJO.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-DKWT744J.js → chunk-W76VLHN6.js} +1690 -1268
  20. package/dist/chunk-W76VLHN6.js.map +7 -0
  21. package/dist/index.cjs.js +2104 -1639
  22. package/dist/index.cjs.js.map +4 -4
  23. package/dist/index.js +1 -1
  24. package/dist/meta.cjs.json +452 -116
  25. package/dist/meta.esbuild.json +458 -122
  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-DKWT744J.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-HCZVI2RM.js.map → HLSView-3RARRZJO.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.0",
13
+ "version": "0.1.14-alpha.2",
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.0",
80
- "@100mslive/hms-virtual-background": "1.11.23-alpha.0",
81
- "@100mslive/react-icons": "0.8.23-alpha.0",
82
- "@100mslive/react-sdk": "0.8.23-alpha.0",
79
+ "@100mslive/hls-player": "0.1.23-alpha.2",
80
+ "@100mslive/hms-virtual-background": "1.11.23-alpha.2",
81
+ "@100mslive/react-icons": "0.8.23-alpha.2",
82
+ "@100mslive/react-sdk": "0.8.23-alpha.2",
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": "47c92f0479d2c2066f78d66333d669a33e5e38e6"
118
+ "gitHead": "1b964f1d356fe098de341574a63a6c3b5332e418"
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 (