@100mslive/roomkit-react 0.1.15 → 0.1.16

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 (57) hide show
  1. package/dist/{HLSView-MXBOUQBG.js → HLSView-EMUOLCTM.js} +2 -2
  2. package/dist/Prebuilt/common/PeersSorter.d.ts +1 -0
  3. package/dist/Prebuilt/common/constants.d.ts +7 -4
  4. package/dist/Prebuilt/common/hooks.d.ts +1 -0
  5. package/dist/Prebuilt/components/Footer/ParticipantList.d.ts +17 -0
  6. package/dist/Prebuilt/components/Footer/RoleAccordion.d.ts +3 -2
  7. package/dist/Prebuilt/components/Footer/WhiteboardToggle.d.ts +2 -0
  8. package/dist/Prebuilt/components/Notifications/HandRaisedNotifications.d.ts +1 -0
  9. package/dist/Prebuilt/components/PreviousRoleInMetadata.d.ts +1 -0
  10. package/dist/Prebuilt/components/RemoveParticipant.d.ts +5 -0
  11. package/dist/Prebuilt/components/hooks/useCloseScreenshareWhiteboard.d.ts +4 -0
  12. package/dist/Prebuilt/layouts/WhiteboardView.d.ts +2 -0
  13. package/dist/{chunk-HEOH5H43.js → chunk-ZYR4B4KQ.js} +1886 -7116
  14. package/dist/chunk-ZYR4B4KQ.js.map +7 -0
  15. package/dist/index.cjs.js +2477 -7662
  16. package/dist/index.cjs.js.map +4 -4
  17. package/dist/index.js +1 -1
  18. package/dist/meta.cjs.json +438 -161
  19. package/dist/meta.esbuild.json +443 -166
  20. package/package.json +7 -7
  21. package/src/Prebuilt/AppStateContext.tsx +1 -1
  22. package/src/Prebuilt/common/PeersSorter.ts +12 -5
  23. package/src/Prebuilt/common/constants.ts +5 -6
  24. package/src/Prebuilt/common/hooks.ts +16 -0
  25. package/src/Prebuilt/common/utils.js +5 -6
  26. package/src/Prebuilt/components/AppData/AppData.tsx +1 -16
  27. package/src/Prebuilt/components/Chat/Chat.jsx +7 -30
  28. package/src/Prebuilt/components/Chat/ChatBody.jsx +107 -66
  29. package/src/Prebuilt/components/Chat/ChatFooter.tsx +21 -12
  30. package/src/Prebuilt/components/Chat/ChatSelector.tsx +25 -25
  31. package/src/Prebuilt/components/Chat/ChatSelectorContainer.tsx +15 -16
  32. package/src/Prebuilt/components/Chat/PinnedMessage.tsx +7 -2
  33. package/src/Prebuilt/components/ConferenceScreen.tsx +2 -0
  34. package/src/Prebuilt/components/Footer/ChatToggle.tsx +30 -7
  35. package/src/Prebuilt/components/Footer/Footer.tsx +2 -1
  36. package/src/Prebuilt/components/Footer/PaginatedParticipants.tsx +0 -1
  37. package/src/Prebuilt/components/Footer/{ParticipantList.jsx → ParticipantList.tsx} +169 -127
  38. package/src/Prebuilt/components/Footer/RoleAccordion.tsx +23 -13
  39. package/src/Prebuilt/components/Footer/WhiteboardToggle.tsx +34 -0
  40. package/src/Prebuilt/components/Notifications/HandRaisedNotifications.tsx +35 -0
  41. package/src/Prebuilt/components/Notifications/Notifications.tsx +14 -12
  42. package/src/Prebuilt/components/Notifications/PeerNotifications.tsx +7 -2
  43. package/src/Prebuilt/components/Polls/Voting/QuestionCard.jsx +10 -2
  44. package/src/Prebuilt/components/PreviousRoleInMetadata.tsx +21 -0
  45. package/src/Prebuilt/components/RemoveParticipant.tsx +35 -0
  46. package/src/Prebuilt/components/RoleChangeModal.jsx +1 -1
  47. package/src/Prebuilt/components/SidePaneTabs.tsx +0 -1
  48. package/src/Prebuilt/components/TileMenu/TileMenuContent.tsx +1 -1
  49. package/src/Prebuilt/components/Toast/ToastConfig.jsx +15 -3
  50. package/src/Prebuilt/components/VideoLayouts/GridLayout.tsx +5 -2
  51. package/src/Prebuilt/components/hooks/useCloseScreenshareWhiteboard.tsx +24 -0
  52. package/src/Prebuilt/layouts/VideoStreamingSection.tsx +20 -3
  53. package/src/Prebuilt/layouts/WhiteboardView.tsx +66 -0
  54. package/dist/chunk-HEOH5H43.js.map +0 -7
  55. package/src/Prebuilt/components/AppData/useAppLayout.js +0 -6
  56. package/src/Prebuilt/components/init/initUtils.js +0 -67
  57. /package/dist/{HLSView-MXBOUQBG.js.map → HLSView-EMUOLCTM.js.map} +0 -0
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "prebuilt",
11
11
  "roomkit"
12
12
  ],
13
- "version": "0.1.15",
13
+ "version": "0.1.16",
14
14
  "author": "100ms",
15
15
  "license": "MIT",
16
16
  "files": [
@@ -76,11 +76,11 @@
76
76
  "react": ">=17.0.2 <19.0.0"
77
77
  },
78
78
  "dependencies": {
79
- "@100mslive/hls-player": "0.1.24",
80
- "@100mslive/hms-virtual-background": "1.11.24",
81
- "@100mslive/react-icons": "0.8.24",
82
- "@100mslive/react-sdk": "0.8.24",
83
- "@100mslive/types-prebuilt": "0.12.4",
79
+ "@100mslive/hls-player": "0.1.25",
80
+ "@100mslive/hms-virtual-background": "1.11.25",
81
+ "@100mslive/react-icons": "0.8.25",
82
+ "@100mslive/react-sdk": "0.8.25",
83
+ "@100mslive/types-prebuilt": "0.12.5",
84
84
  "@emoji-mart/data": "^1.0.6",
85
85
  "@emoji-mart/react": "^1.0.1",
86
86
  "@radix-ui/react-accordion": "1.0.0",
@@ -115,5 +115,5 @@
115
115
  "uuid": "^8.3.2",
116
116
  "worker-timers": "^7.0.40"
117
117
  },
118
- "gitHead": "49795c5577e2d578fc0a8f372f3290dd0a52c9b2"
118
+ "gitHead": "096d50e1ffbeb9f2ec895b630952d2ad9694dfee"
119
119
  }
@@ -55,7 +55,7 @@ export const useAppStateManager = () => {
55
55
  setActiveState(PrebuiltStates.MEETING);
56
56
  } else if (
57
57
  prevRoomState &&
58
- [HMSRoomState.Reconnecting, HMSRoomState.Connected].includes(prevRoomState) &&
58
+ [HMSRoomState.Reconnecting, HMSRoomState.Connected, HMSRoomState.Connecting].includes(prevRoomState) &&
59
59
  [HMSRoomState.Disconnecting, HMSRoomState.Disconnected].includes(roomState)
60
60
  ) {
61
61
  const goTo = isPreviewScreenEnabled ? PrebuiltStates.PREVIEW : PrebuiltStates.MEETING;
@@ -53,6 +53,7 @@ class PeersSorter {
53
53
 
54
54
  moveSpeakerToFront = (speaker?: HMSPeer) => {
55
55
  if (!speaker) {
56
+ this.maintainLruSize(this.tilesPerPage);
56
57
  this.updateListeners();
57
58
  return;
58
59
  }
@@ -62,11 +63,9 @@ class PeersSorter {
62
63
  }
63
64
  // delete to insert at beginning
64
65
  this.lruPeers.delete(speaker.id);
65
- const lruPeerArray = Array.from(this.lruPeers);
66
- while (lruPeerArray.length >= this.tilesPerPage && lruPeerArray.length) {
67
- lruPeerArray.pop();
68
- }
69
- this.lruPeers = new Set([speaker.id, ...lruPeerArray]);
66
+ // - 1 as we are going to insert the new speaker
67
+ this.maintainLruSize(this.tilesPerPage - 1);
68
+ this.lruPeers = new Set([speaker.id, ...this.lruPeers]);
70
69
  this.updateListeners();
71
70
  };
72
71
 
@@ -98,6 +97,14 @@ class PeersSorter {
98
97
  });
99
98
  this.listeners.forEach(listener => listener?.(orderedPeers));
100
99
  };
100
+
101
+ private maintainLruSize = (size: number) => {
102
+ const lruPeerArray = Array.from(this.lruPeers);
103
+ while (lruPeerArray.length > size && lruPeerArray.length) {
104
+ lruPeerArray.pop();
105
+ }
106
+ this.lruPeers = new Set(lruPeerArray);
107
+ };
101
108
  }
102
109
 
103
110
  export default PeersSorter;
@@ -17,7 +17,7 @@ export const RTMP_RECORD_DEFAULT_RESOLUTION = {
17
17
  export const EMOJI_REACTION_TYPE = 'EMOJI_REACTION';
18
18
 
19
19
  export const CHAT_SELECTOR = {
20
- PEER_ID: 'peer_id',
20
+ PEER: 'peer',
21
21
  ROLE: 'role',
22
22
  EVERYONE: 'Everyone',
23
23
  };
@@ -33,7 +33,6 @@ export const APP_DATA = {
33
33
  waitingViewerRole: 'waitingViewerRole',
34
34
  subscribedNotifications: 'subscribedNotifications',
35
35
  logo: 'logo',
36
- appLayout: 'appLayout',
37
36
  hlsStarted: 'hlsStarted',
38
37
  rtmpStarted: 'rtmpStarted',
39
38
  recordingStarted: 'recordingStarted',
@@ -122,15 +121,15 @@ export enum INTERACTION_TYPE {
122
121
  export enum QUESTION_TYPE_TITLE {
123
122
  'single-choice' = 'Single Choice',
124
123
  'multiple-choice' = 'Multiple Choice',
125
- // "short-answer": "Short Answer",
126
- // "long-answer": "Long Answer",
124
+ 'short-answer' = 'Short Answer',
125
+ 'long-answer' = 'Long Answer',
127
126
  }
128
127
 
129
128
  export enum QUESTION_TYPE {
130
129
  SINGLE_CHOICE = 'single-choice',
131
130
  MULTIPLE_CHOICE = 'multiple-choice',
132
- // SHORT_ANSWER: "short-answer",
133
- // LONG_ANSWER: "long-answer",
131
+ SHORT_ANSWER = 'short-answer',
132
+ LONG_ANSWER = 'long-answer',
134
133
  }
135
134
 
136
135
  export const ROLE_CHANGE_DECLINED = 'role_change_declined';
@@ -12,6 +12,7 @@ import {
12
12
  } from '@100mslive/react-sdk';
13
13
  import { useRoomLayout } from '../provider/roomLayoutProvider';
14
14
  import { useRoomLayoutConferencingScreen } from '../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
15
+ import { CHAT_SELECTOR } from './constants';
15
16
  /**
16
17
  * Hook to execute a callback when alone in room(after a certain 5d of time)
17
18
  * @param {number} thresholdMs The threshold(in ms) after which the callback is executed,
@@ -55,6 +56,21 @@ export const useFilteredRoles = () => {
55
56
  return elements?.chat?.roles_whitelist || [];
56
57
  };
57
58
 
59
+ export const useDefaultChatSelection = () => {
60
+ const { elements } = useRoomLayoutConferencingScreen();
61
+ const roles = useFilteredRoles();
62
+ // default is everyone for public chat
63
+ if (elements?.chat?.public_chat_enabled) {
64
+ return CHAT_SELECTOR.EVERYONE;
65
+ }
66
+ // sending first role as default
67
+ if (roles.length > 0) {
68
+ return roles[0];
69
+ }
70
+ // sending empty
71
+ return '';
72
+ };
73
+
58
74
  export const useShowStreamingUI = () => {
59
75
  const layout = useRoomLayout();
60
76
  const { join_form } = layout?.screens?.preview?.default?.elements || {};
@@ -1,4 +1,3 @@
1
- import { isEqual } from 'lodash';
2
1
  import { QUESTION_TYPE } from './constants';
3
2
 
4
3
  // eslint-disable-next-line complexity
@@ -159,11 +158,11 @@ export const getPeerParticipationSummary = (poll, localPeerID, localCustomerUser
159
158
  if (!peerResponse?.[0]) {
160
159
  return;
161
160
  }
162
- const submission = [peerResponse[0].option] || peerResponse[0].options;
163
- const answer =
164
- [questions[peerResponse[0].questionIndex - 1].answer?.option] ||
165
- questions[peerResponse[0].questionIndex - 1].answer?.options;
166
- const isCorrect = isEqual(submission, answer);
161
+ const isCorrect = checkCorrectAnswer(
162
+ questions[peerResponse[0].questionIndex - 1].answer,
163
+ peerResponse[0],
164
+ questions[peerResponse[0].questionIndex - 1].type,
165
+ );
167
166
  if (isCorrect) {
168
167
  score += questions[peerResponse[0].questionIndex - 1]?.weight || 0;
169
168
  correctResponses++;
@@ -1,11 +1,8 @@
1
1
  import React, { useEffect } from 'react';
2
2
  import {
3
3
  HMSRoomState,
4
- selectAvailableRoleNames,
5
4
  selectFullAppData,
6
5
  selectHLSState,
7
- selectLocalPeerRoleName,
8
- selectRolesMap,
9
6
  selectRoomState,
10
7
  selectRTMPState,
11
8
  useHMSActions,
@@ -13,8 +10,6 @@ import {
13
10
  useRecordingStreaming,
14
11
  } from '@100mslive/react-sdk';
15
12
  //@ts-ignore
16
- import { normalizeAppPolicyConfig } from '../init/initUtils';
17
- //@ts-ignore
18
13
  import { UserPreferencesKeys, useUserPreferences } from '../hooks/useUserPreferences';
19
14
  // @ts-ignore
20
15
  import { useIsSidepaneTypeOpen, useSidepaneToggle } from './useSidepane';
@@ -49,7 +44,7 @@ const initialAppData = {
49
44
  [APP_DATA.chatOpen]: false,
50
45
  [APP_DATA.chatSelector]: {
51
46
  [CHAT_SELECTOR.ROLE]: '',
52
- [CHAT_SELECTOR.PEER_ID]: '',
47
+ [CHAT_SELECTOR.PEER]: {},
53
48
  },
54
49
  [APP_DATA.chatDraft]: '',
55
50
  [APP_DATA.sidePane]: '',
@@ -73,9 +68,6 @@ const initialAppData = {
73
68
  export const AppData = React.memo(() => {
74
69
  const hmsActions = useHMSActions();
75
70
  const [preferences = {}] = useUserPreferences(UserPreferencesKeys.UI_SETTINGS);
76
- const roleNames = useHMSStore(selectAvailableRoleNames);
77
- const rolesMap = useHMSStore(selectRolesMap);
78
- const localPeerRole = useHMSStore(selectLocalPeerRoleName);
79
71
  const appData = useHMSStore(selectFullAppData);
80
72
 
81
73
  useEffect(() => {
@@ -109,13 +101,6 @@ export const AppData = React.memo(() => {
109
101
  hmsActions.setAppData(APP_DATA.subscribedNotifications, preferences.subscribedNotifications, true);
110
102
  }, [preferences.subscribedNotifications, hmsActions]);
111
103
 
112
- useEffect(() => {
113
- if (localPeerRole) {
114
- const config = normalizeAppPolicyConfig(roleNames, rolesMap);
115
- hmsActions.setAppData(APP_DATA.appLayout, config[localPeerRole]);
116
- }
117
- }, [roleNames, rolesMap, localPeerRole, hmsActions]);
118
-
119
104
  return <ResetStreamingStart />;
120
105
  });
121
106
 
@@ -1,13 +1,7 @@
1
- import React, { useCallback, useEffect, useRef, useState } from 'react';
1
+ import React, { useCallback, useRef, useState } from 'react';
2
2
  import { useMedia } from 'react-use';
3
- import { selectLocalPeer, selectPeerByID, selectSessionStore } from '@100mslive/hms-video-store';
4
- import {
5
- HMSNotificationTypes,
6
- selectHMSMessagesCount,
7
- useHMSActions,
8
- useHMSNotifications,
9
- useHMSStore,
10
- } from '@100mslive/react-sdk';
3
+ import { selectLocalPeer, selectSessionStore, selectUnreadHMSMessagesCount } from '@100mslive/hms-video-store';
4
+ import { selectHMSMessagesCount, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
11
5
  import { ChevronDownIcon } from '@100mslive/react-icons';
12
6
  import { Button } from '../../../Button';
13
7
  import { Flex } from '../../../Layout';
@@ -17,33 +11,18 @@ import { ChatFooter } from './ChatFooter';
17
11
  import { ChatBlocked, ChatPaused } from './ChatStates';
18
12
  import { PinnedMessage } from './PinnedMessage';
19
13
  import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
20
- import { useSetSubscribedChatSelector } from '../AppData/useUISettings';
21
14
  import { useSetPinnedMessages } from '../hooks/useSetPinnedMessages';
22
- import { useUnreadCount } from './useUnreadCount';
23
- import { CHAT_SELECTOR, SESSION_STORE_KEY } from '../../common/constants';
15
+ import { SESSION_STORE_KEY } from '../../common/constants';
24
16
 
25
17
  export const Chat = () => {
26
18
  const { elements, screenType } = useRoomLayoutConferencingScreen();
27
- const notification = useHMSNotifications(HMSNotificationTypes.PEER_LEFT);
28
- const [selectedPeer, setPeerSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.PEER_ID);
29
- const [selectedRole, setRoleSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.ROLE);
30
19
  const localPeer = useHMSStore(selectLocalPeer);
31
20
  const [isSelectorOpen] = useState(false);
32
21
  const listRef = useRef(null);
33
22
  const hmsActions = useHMSActions();
34
23
  const { removePinnedMessage } = useSetPinnedMessages();
35
24
  const pinnedMessages = useHMSStore(selectSessionStore(SESSION_STORE_KEY.PINNED_MESSAGES)) || [];
36
- const isPeerPresent = !!useHMSStore(selectPeerByID(selectedPeer));
37
25
 
38
- useEffect(() => {
39
- if (notification && notification.data && selectedPeer === notification.data.id) {
40
- setPeerSelector('');
41
- setRoleSelector('');
42
- }
43
- if (selectedPeer && !isPeerPresent) {
44
- setPeerSelector('');
45
- }
46
- }, [notification, selectedPeer, setPeerSelector, setRoleSelector, isPeerPresent]);
47
26
  const blacklistedPeerIDs = useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_PEER_BLACKLIST)) || [];
48
27
  const blacklistedPeerIDSet = new Set(blacklistedPeerIDs);
49
28
  const isLocalPeerBlacklisted = blacklistedPeerIDSet.has(localPeer?.customerUserId);
@@ -97,17 +76,15 @@ export const Chat = () => {
97
76
 
98
77
  {isChatEnabled && !isLocalPeerBlacklisted ? (
99
78
  <ChatFooter onSend={() => scrollToBottom(1)} screenType={screenType}>
100
- {!isSelectorOpen && !isScrolledToBottom && (
101
- <NewMessageIndicator role={selectedRole} peerId={selectedPeer} scrollToBottom={scrollToBottom} />
102
- )}
79
+ {!isSelectorOpen && !isScrolledToBottom && <NewMessageIndicator scrollToBottom={scrollToBottom} />}
103
80
  </ChatFooter>
104
81
  ) : null}
105
82
  </Flex>
106
83
  );
107
84
  };
108
85
 
109
- const NewMessageIndicator = ({ role, peerId, scrollToBottom }) => {
110
- const unreadCount = useUnreadCount({ role, peerId });
86
+ const NewMessageIndicator = ({ scrollToBottom }) => {
87
+ const unreadCount = useHMSStore(selectUnreadHMSMessagesCount);
111
88
  if (!unreadCount) {
112
89
  return null;
113
90
  }
@@ -7,10 +7,11 @@ import {
7
7
  selectHMSMessages,
8
8
  selectLocalPeerID,
9
9
  selectLocalPeerName,
10
- selectMessagesByPeerID,
11
- selectMessagesByRole,
10
+ selectLocalPeerRoleName,
11
+ selectPeerNameByID,
12
12
  selectPermissions,
13
13
  selectSessionStore,
14
+ selectUnreadHMSMessagesCount,
14
15
  useHMSActions,
15
16
  useHMSStore,
16
17
  } from '@100mslive/react-sdk';
@@ -19,6 +20,7 @@ import {
19
20
  CrossCircleIcon,
20
21
  CrossIcon,
21
22
  EyeCloseIcon,
23
+ PeopleRemoveIcon,
22
24
  PinIcon,
23
25
  ReplyIcon,
24
26
  VerticalMenuIcon,
@@ -34,10 +36,9 @@ import emptyChat from '../../images/empty-chat.svg';
34
36
  import { ToastManager } from '../Toast/ToastManager';
35
37
  import { MwebChatOption } from './MwebChatOption';
36
38
  import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
37
- import { useSetSubscribedChatSelector, useSubscribeChatSelector } from '../AppData/useUISettings';
39
+ import { useSetSubscribedChatSelector } from '../AppData/useUISettings';
38
40
  import { useChatBlacklist } from '../hooks/useChatBlacklist';
39
41
  import { useSetPinnedMessages } from '../hooks/useSetPinnedMessages';
40
- import { useUnreadCount } from './useUnreadCount';
41
42
  import { CHAT_SELECTOR, SESSION_STORE_KEY } from '../../common/constants';
42
43
 
43
44
  const iconStyle = { height: '1.125rem', width: '1.125rem' };
@@ -70,27 +71,28 @@ const MessageTypeContainer = ({ left, right }) => {
70
71
  <Flex
71
72
  align="center"
72
73
  css={{
73
- position: 'absolute',
74
- right: 0,
75
- zIndex: 1,
74
+ ml: '$2',
76
75
  mr: '$4',
77
- p: '$2',
78
- border: '1px solid $border_bright',
79
- r: '$0',
80
- gap: '$3',
76
+ gap: '$space$2',
81
77
  }}
82
- className="message_type_container"
83
78
  >
84
79
  {left && (
85
- <SenderName variant="caption" as="span" css={{ color: '$on_surface_medium' }}>
80
+ <SenderName
81
+ variant="xs"
82
+ as="span"
83
+ css={{ color: '$on_surface_medium', textTransform: 'capitalize', fontWeight: '$regular' }}
84
+ >
86
85
  {left}
87
86
  </SenderName>
88
87
  )}
89
88
  {right && (
90
89
  <SenderName
91
90
  as="span"
92
- variant="caption"
93
- css={{ color: '$on_surface_high', textTransform: 'capitalize', fontWeight: '$semiBold' }}
91
+ variant="overline"
92
+ css={{
93
+ color: '$on_surface_medium',
94
+ fontWeight: '$regular',
95
+ }}
94
96
  >
95
97
  {right}
96
98
  </SenderName>
@@ -99,13 +101,17 @@ const MessageTypeContainer = ({ left, right }) => {
99
101
  );
100
102
  };
101
103
 
102
- const MessageType = ({ roles, receiver }) => {
104
+ const MessageType = ({ roles, hasCurrentUserSent, receiver }) => {
105
+ const peerName = useHMSStore(selectPeerNameByID(receiver));
106
+ const localPeerRoleName = useHMSStore(selectLocalPeerRoleName);
103
107
  if (receiver) {
104
- return <MessageTypeContainer left="Direct Message" />;
108
+ return (
109
+ <MessageTypeContainer left={hasCurrentUserSent ? `${peerName ? `to ${peerName}` : ''}` : 'to You'} right="(DM)" />
110
+ );
105
111
  }
106
112
 
107
- if (roles && roles.length > 0) {
108
- return <MessageTypeContainer left="To Group" right={roles[0]} />;
113
+ if (roles && roles.length) {
114
+ return <MessageTypeContainer left={`to ${hasCurrentUserSent ? roles[0] : localPeerRoleName}`} right="(Group)" />;
109
115
  }
110
116
  return null;
111
117
  };
@@ -153,8 +159,8 @@ const getMessageType = ({ roles, receiver }) => {
153
159
  const ChatActions = ({
154
160
  onPin,
155
161
  showPinAction,
156
- onReplyPrivately,
157
- showReplyPrivateAction,
162
+ onReply,
163
+ showReply,
158
164
  message,
159
165
  sentByLocalPeer,
160
166
  isMobile,
@@ -167,6 +173,8 @@ const ChatActions = ({
167
173
  can_block_user: false,
168
174
  };
169
175
  const [open, setOpen] = useState(false);
176
+ const actions = useHMSActions();
177
+ const canRemoveOthers = useHMSStore(selectPermissions)?.removeOthers;
170
178
  const { blacklistItem: blacklistPeer } = useChatBlacklist(SESSION_STORE_KEY.CHAT_PEER_BLACKLIST);
171
179
 
172
180
  const { blacklistItem: blacklistMessage, blacklistedIDs: blacklistedMessageIDs = [] } = useChatBlacklist(
@@ -199,11 +207,11 @@ const ChatActions = ({
199
207
 
200
208
  const options = {
201
209
  reply: {
202
- text: 'Reply Privately',
203
- tooltipText: 'Reply privately',
210
+ text: message.recipientRoles?.length ? 'Reply to Group' : 'Reply Privately',
211
+ tooltipText: message.recipientRoles?.length ? 'Reply to Group' : 'Reply Privately',
204
212
  icon: <ReplyIcon style={iconStyle} />,
205
- onClick: onReplyPrivately,
206
- show: showReplyPrivateAction,
213
+ onClick: onReply,
214
+ show: showReply,
207
215
  },
208
216
  pin: {
209
217
  text: 'Pin message',
@@ -220,7 +228,7 @@ const ChatActions = ({
220
228
  show: true,
221
229
  },
222
230
  hide: {
223
- text: 'Hide for everyone',
231
+ text: message.recipientPeer ? 'Hide for both' : 'Hide for everyone',
224
232
  icon: <EyeCloseIcon style={iconStyle} />,
225
233
  onClick: async () => {
226
234
  blacklistMessage(message.id);
@@ -235,6 +243,19 @@ const ChatActions = ({
235
243
  color: '$alert_error_default',
236
244
  show: can_block_user && !sentByLocalPeer,
237
245
  },
246
+ remove: {
247
+ text: 'Remove Partipant',
248
+ icon: <PeopleRemoveIcon style={iconStyle} />,
249
+ color: '$alert_error_default',
250
+ show: canRemoveOthers && !sentByLocalPeer,
251
+ onClick: async () => {
252
+ try {
253
+ await actions.removePeer(message.sender, '');
254
+ } catch (error) {
255
+ ToastManager.addToast({ title: error.message, variant: 'error' });
256
+ }
257
+ },
258
+ },
238
259
  };
239
260
 
240
261
  if (isMobile) {
@@ -316,7 +337,7 @@ const ChatActions = ({
316
337
  </Tooltip>
317
338
  ) : null}
318
339
 
319
- {options.block.show || options.hide.show ? (
340
+ {options.block.show || options.hide.show || options.remove.show ? (
320
341
  <Tooltip boxCss={tooltipBoxCSS} title="More actions">
321
342
  <Dropdown.Trigger asChild>
322
343
  <IconButton>
@@ -353,6 +374,18 @@ const ChatActions = ({
353
374
  </Text>
354
375
  </Dropdown.Item>
355
376
  ) : null}
377
+ {options.remove.show ? (
378
+ <Dropdown.Item
379
+ data-testid="remove_peer_btn"
380
+ onClick={options.remove.onClick}
381
+ css={{ color: options.remove.color }}
382
+ >
383
+ {options.remove.icon}
384
+ <Text variant="sm" css={{ ml: '$4', color: 'inherit', fontWeight: '$semiBold' }}>
385
+ {options.remove.text}
386
+ </Text>
387
+ </Dropdown.Item>
388
+ ) : null}
356
389
  </Dropdown.Content>
357
390
  </Dropdown.Portal>
358
391
  </Dropdown.Root>
@@ -363,7 +396,7 @@ const SenderName = styled(Text, {
363
396
  overflow: 'hidden',
364
397
  textOverflow: 'ellipsis',
365
398
  whiteSpace: 'nowrap',
366
- maxWidth: '16ch',
399
+ maxWidth: '14ch',
367
400
  minWidth: 0,
368
401
  color: '$on_surface_high',
369
402
  fontWeight: '$semiBold',
@@ -381,20 +414,25 @@ const ChatMessage = React.memo(
381
414
  }, [index, setRowHeight]);
382
415
  const isMobile = useMedia(cssConfig.media.md);
383
416
  const isPrivateChatEnabled = !!elements?.chat?.private_chat_enabled;
417
+ const roleWhiteList = elements?.chat?.roles_whitelist || [];
384
418
  const isOverlay = elements?.chat?.is_overlay && isMobile;
385
419
  const hmsActions = useHMSActions();
386
420
  const localPeerId = useHMSStore(selectLocalPeerID);
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);
421
+ const [selectedRole, setRoleSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.ROLE);
422
+ const [selectedPeer, setPeerSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.PEER);
391
423
  const messageType = getMessageType({
392
424
  roles: message.recipientRoles,
393
425
  receiver: message.recipientPeer,
394
426
  });
395
427
  const [openSheet, setOpenSheet] = useState(false);
396
- // show pin action only if peer has remove others permission and the message is of broadcast type
397
- const showPinAction = permissions.removeOthers && !messageType && elements?.chat?.allow_pinning_messages;
428
+ const showPinAction = !!elements?.chat?.allow_pinning_messages;
429
+ let showReply = false;
430
+ if (message.recipientRoles && roleWhiteList.includes(message.recipientRoles[0])) {
431
+ showReply = true;
432
+ } else if (message.sender !== selectedPeer.id && message.sender !== localPeerId && isPrivateChatEnabled) {
433
+ showReply = true;
434
+ }
435
+
398
436
  useEffect(() => {
399
437
  if (message.id && !message.read && inView) {
400
438
  hmsActions.setMessageRead(true, message.id);
@@ -428,7 +466,7 @@ const ChatMessage = React.memo(
428
466
  position: 'relative',
429
467
  // Theme independent color, token should not be used for transparent chat
430
468
  bg:
431
- messageType && !(selectedPeer || selectedRole)
469
+ messageType && !(selectedPeer.id || selectedRole)
432
470
  ? isOverlay
433
471
  ? 'rgba(0, 0, 0, 0.64)'
434
472
  : '$surface_default'
@@ -482,31 +520,45 @@ const ChatMessage = React.memo(
482
520
  </SenderName>
483
521
  </Tooltip>
484
522
  )}
485
- {!isOverlay ? (
486
- <Text
487
- as="span"
488
- variant="caption"
489
- css={{
490
- ml: '$2',
491
- color: '$on_surface_medium',
492
- flexShrink: 0,
493
- }}
494
- >
495
- {formatTime(message.time)}
496
- </Text>
497
- ) : null}
523
+ <MessageType
524
+ hasCurrentUserSent={message.sender === localPeerId}
525
+ receiver={message.recipientPeer}
526
+ roles={message.recipientRoles}
527
+ />
498
528
  </Flex>
499
- {!(selectedPeer || selectedRole) && (
500
- <MessageType receiver={message.recipientPeer} roles={message.recipientRoles} />
501
- )}
502
529
 
530
+ {!isOverlay ? (
531
+ <Text
532
+ as="span"
533
+ variant="caption"
534
+ css={{
535
+ color: '$on_surface_medium',
536
+ flexShrink: 0,
537
+ position: 'absolute',
538
+ right: 0,
539
+ zIndex: 1,
540
+ mr: '$4',
541
+ p: '$2',
542
+ }}
543
+ >
544
+ {formatTime(message.time)}
545
+ </Text>
546
+ ) : null}
503
547
  <ChatActions
504
548
  onPin={onPin}
505
549
  showPinAction={showPinAction}
506
550
  message={message}
507
551
  sentByLocalPeer={message.sender === localPeerId}
508
- onReplyPrivately={() => setPeerSelector(message.sender)}
509
- showReplyPrivateAction={!selectedPeer && message.sender !== localPeerId && isPrivateChatEnabled}
552
+ onReply={() => {
553
+ if (message.recipientRoles?.length) {
554
+ setRoleSelector(message.recipientRoles[0]);
555
+ setPeerSelector({});
556
+ } else {
557
+ setRoleSelector('');
558
+ setPeerSelector({ id: message.sender, name: message.senderName });
559
+ }
560
+ }}
561
+ showReply={showReply}
510
562
  isMobile={isMobile}
511
563
  openSheet={openSheet}
512
564
  setOpenSheet={setOpenSheet}
@@ -622,27 +674,16 @@ const VirtualizedChatMessages = React.forwardRef(({ messages, unreadCount = 0, s
622
674
  });
623
675
 
624
676
  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
- }
635
- let messages = useHMSStore(storeMessageSelector) || [];
677
+ let messages = useHMSStore(selectHMSMessages);
636
678
  const blacklistedMessageIDs = useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_MESSAGE_BLACKLIST)) || [];
637
679
  const getFilteredMessages = () => {
638
680
  const blacklistedMessageIDSet = new Set(blacklistedMessageIDs);
639
-
640
681
  return messages?.filter(message => message.type === 'chat' && !blacklistedMessageIDSet.has(message.id)) || [];
641
682
  };
642
683
 
643
684
  const isMobile = useMedia(cssConfig.media.md);
644
685
  const { elements } = useRoomLayoutConferencingScreen();
645
- const unreadCount = useUnreadCount({ role: selectedRole, peerId: selectedPeer });
686
+ const unreadCount = useHMSStore(selectUnreadHMSMessagesCount);
646
687
 
647
688
  if (messages.length === 0 && !(isMobile && elements?.chat?.is_overlay)) {
648
689
  return (