@100mslive/roomkit-react 0.1.14 → 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 (81) hide show
  1. package/dist/{HLSView-662T7R7H.js → HLSView-EMUOLCTM.js} +128 -39
  2. package/dist/HLSView-EMUOLCTM.js.map +7 -0
  3. package/dist/Prebuilt/common/PeersSorter.d.ts +1 -0
  4. package/dist/Prebuilt/common/constants.d.ts +9 -5
  5. package/dist/Prebuilt/common/hooks.d.ts +1 -0
  6. package/dist/Prebuilt/components/Footer/ParticipantList.d.ts +17 -0
  7. package/dist/Prebuilt/components/Footer/RoleAccordion.d.ts +3 -2
  8. package/dist/Prebuilt/components/Footer/WhiteboardToggle.d.ts +2 -0
  9. package/dist/Prebuilt/components/HMSVideo/HLSCaptionSelector.d.ts +5 -0
  10. package/dist/Prebuilt/components/Notifications/HandRaisedNotifications.d.ts +1 -0
  11. package/dist/Prebuilt/components/Polls/Voting/Leaderboard.d.ts +4 -0
  12. package/dist/Prebuilt/components/Polls/Voting/LeaderboardEntry.d.ts +9 -0
  13. package/dist/Prebuilt/components/Polls/Voting/PeerParticipationSummary.d.ts +5 -0
  14. package/dist/Prebuilt/components/PreviousRoleInMetadata.d.ts +1 -0
  15. package/dist/Prebuilt/components/RemoveParticipant.d.ts +5 -0
  16. package/dist/Prebuilt/components/hooks/useCloseScreenshareWhiteboard.d.ts +4 -0
  17. package/dist/Prebuilt/layouts/WhiteboardView.d.ts +2 -0
  18. package/dist/{chunk-2B7YYNHQ.js → chunk-ZYR4B4KQ.js} +2240 -1767
  19. package/dist/chunk-ZYR4B4KQ.js.map +7 -0
  20. package/dist/index.cjs.js +2805 -2172
  21. package/dist/index.cjs.js.map +4 -4
  22. package/dist/index.js +1 -1
  23. package/dist/meta.cjs.json +739 -177
  24. package/dist/meta.esbuild.json +749 -186
  25. package/package.json +7 -7
  26. package/src/Prebuilt/AppStateContext.tsx +1 -1
  27. package/src/Prebuilt/common/PeersSorter.ts +24 -8
  28. package/src/Prebuilt/common/constants.ts +6 -6
  29. package/src/Prebuilt/common/hooks.ts +16 -0
  30. package/src/Prebuilt/common/utils.js +33 -0
  31. package/src/Prebuilt/components/AppData/AppData.tsx +1 -16
  32. package/src/Prebuilt/components/Chat/Chat.jsx +10 -34
  33. package/src/Prebuilt/components/Chat/ChatBody.jsx +107 -66
  34. package/src/Prebuilt/components/Chat/ChatFooter.tsx +21 -12
  35. package/src/Prebuilt/components/Chat/ChatSelector.tsx +25 -25
  36. package/src/Prebuilt/components/Chat/ChatSelectorContainer.tsx +15 -16
  37. package/src/Prebuilt/components/Chat/PinnedMessage.tsx +7 -2
  38. package/src/Prebuilt/components/ConferenceScreen.tsx +2 -0
  39. package/src/Prebuilt/components/Footer/ChatToggle.tsx +30 -7
  40. package/src/Prebuilt/components/Footer/Footer.tsx +2 -1
  41. package/src/Prebuilt/components/Footer/PaginatedParticipants.tsx +0 -1
  42. package/src/Prebuilt/components/Footer/{ParticipantList.jsx → ParticipantList.tsx} +169 -127
  43. package/src/Prebuilt/components/Footer/RoleAccordion.tsx +23 -13
  44. package/src/Prebuilt/components/Footer/WhiteboardToggle.tsx +34 -0
  45. package/src/Prebuilt/components/HMSVideo/HLSCaptionSelector.tsx +13 -0
  46. package/src/Prebuilt/components/HMSVideo/HMSVideo.jsx +34 -2
  47. package/src/Prebuilt/components/Notifications/HandRaisedNotifications.tsx +35 -0
  48. package/src/Prebuilt/components/Notifications/Notifications.tsx +47 -14
  49. package/src/Prebuilt/components/Notifications/PeerNotifications.tsx +7 -2
  50. package/src/Prebuilt/components/Polls/CreatePollQuiz/PollsQuizMenu.jsx +3 -9
  51. package/src/Prebuilt/components/Polls/CreateQuestions/CreateQuestions.jsx +21 -1
  52. package/src/Prebuilt/components/Polls/CreateQuestions/QuestionForm.jsx +34 -7
  53. package/src/Prebuilt/components/Polls/CreateQuestions/SavedQuestion.jsx +2 -2
  54. package/src/Prebuilt/components/Polls/Polls.tsx +3 -0
  55. package/src/Prebuilt/components/Polls/Voting/Leaderboard.tsx +115 -0
  56. package/src/Prebuilt/components/Polls/Voting/LeaderboardEntry.tsx +63 -0
  57. package/src/Prebuilt/components/Polls/Voting/PeerParticipationSummary.tsx +38 -0
  58. package/src/Prebuilt/components/Polls/Voting/QuestionCard.jsx +33 -8
  59. package/src/Prebuilt/components/Polls/Voting/StandardVoting.jsx +7 -1
  60. package/src/Prebuilt/components/Polls/Voting/Voting.jsx +31 -13
  61. package/src/Prebuilt/components/Polls/common/MultipleChoiceOptions.jsx +33 -21
  62. package/src/Prebuilt/components/Polls/common/SingleChoiceOptions.jsx +47 -35
  63. package/src/Prebuilt/components/Polls/common/StatusIndicator.jsx +2 -22
  64. package/src/Prebuilt/components/Polls/common/VoteCount.jsx +1 -15
  65. package/src/Prebuilt/components/PreviousRoleInMetadata.tsx +21 -0
  66. package/src/Prebuilt/components/RemoveParticipant.tsx +35 -0
  67. package/src/Prebuilt/components/RoleChangeModal.jsx +1 -1
  68. package/src/Prebuilt/components/SidePaneTabs.tsx +0 -1
  69. package/src/Prebuilt/components/TileMenu/TileMenuContent.tsx +1 -1
  70. package/src/Prebuilt/components/Toast/ToastConfig.jsx +15 -3
  71. package/src/Prebuilt/components/VideoLayouts/EqualProminence.tsx +6 -5
  72. package/src/Prebuilt/components/VideoLayouts/GridLayout.tsx +27 -5
  73. package/src/Prebuilt/components/VideoLayouts/ScreenshareLayout.tsx +0 -1
  74. package/src/Prebuilt/components/hooks/useCloseScreenshareWhiteboard.tsx +24 -0
  75. package/src/Prebuilt/layouts/HLSView.jsx +51 -3
  76. package/src/Prebuilt/layouts/VideoStreamingSection.tsx +20 -3
  77. package/src/Prebuilt/layouts/WhiteboardView.tsx +66 -0
  78. package/dist/HLSView-662T7R7H.js.map +0 -7
  79. package/dist/chunk-2B7YYNHQ.js.map +0 -7
  80. package/src/Prebuilt/components/AppData/useAppLayout.js +0 -6
  81. package/src/Prebuilt/components/init/initUtils.js +0 -67
@@ -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 (
@@ -2,7 +2,7 @@ import React, { ReactNode, useCallback, useEffect, useRef, useState } from 'reac
2
2
  import { useMedia } from 'react-use';
3
3
  import data from '@emoji-mart/data';
4
4
  import Picker from '@emoji-mart/react';
5
- import { selectLocalPeer, selectPeerNameByID, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
5
+ import { HMSException, selectLocalPeer, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
6
6
  import { EmojiIcon, PauseCircleIcon, SendIcon, VerticalMenuIcon } from '@100mslive/react-icons';
7
7
  import { Box, config as cssConfig, Flex, IconButton as BaseIconButton, Popover, styled, Text } from '../../..';
8
8
  import { IconButton } from '../../../IconButton';
@@ -14,9 +14,10 @@ import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvid
14
14
  // @ts-ignore
15
15
  import { useChatDraftMessage } from '../AppData/useChatState';
16
16
  // @ts-ignore
17
- import { useSubscribeChatSelector } from '../AppData/useUISettings';
17
+ import { useSetSubscribedChatSelector, useSubscribeChatSelector } from '../AppData/useUISettings';
18
18
  // @ts-ignore
19
19
  import { useEmojiPickerStyles } from './useEmojiPickerStyles';
20
+ import { useDefaultChatSelection } from '../../common/hooks';
20
21
  import { CHAT_SELECTOR, SESSION_STORE_KEY } from '../../common/constants';
21
22
 
22
23
  const TextArea = styled('textarea', {
@@ -81,11 +82,17 @@ export const ChatFooter = ({ onSend, children }: { onSend: () => void; children:
81
82
  const localPeer = useHMSStore(selectLocalPeer);
82
83
  const isOverlayChat = elements?.chat?.is_overlay;
83
84
  const canDisableChat = !!elements?.chat?.real_time_controls?.can_disable_chat;
84
- const isPublicChatEnabled = !!elements?.chat?.public_chat_enabled;
85
- const selectedPeer = useSubscribeChatSelector(CHAT_SELECTOR.PEER_ID);
86
- const selectedRole = useSubscribeChatSelector(CHAT_SELECTOR.ROLE);
87
- const selectorPeerName = useHMSStore(selectPeerNameByID(selectedPeer));
88
- const selection = selectorPeerName || selectedRole || CHAT_SELECTOR.EVERYONE;
85
+ const selectedPeer = useSubscribeChatSelector(CHAT_SELECTOR.PEER);
86
+ const [selectedRole, setRoleSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.ROLE);
87
+ const defaultSelection = useDefaultChatSelection();
88
+ const selection = selectedPeer.name || selectedRole || defaultSelection;
89
+ useEffect(() => {
90
+ if (!selectedPeer.id && !selectedRole && !['Everyone', ''].includes(defaultSelection)) {
91
+ setRoleSelector(defaultSelection);
92
+ } else {
93
+ inputRef.current?.focus();
94
+ }
95
+ }, [defaultSelection, selectedPeer, selectedRole, setRoleSelector]);
89
96
  const sendMessage = useCallback(async () => {
90
97
  const message = inputRef?.current?.value;
91
98
  if (!message || !message.trim().length) {
@@ -94,8 +101,8 @@ export const ChatFooter = ({ onSend, children }: { onSend: () => void; children:
94
101
  try {
95
102
  if (selectedRole) {
96
103
  await hmsActions.sendGroupMessage(message, [selectedRole]);
97
- } else if (selectedPeer) {
98
- await hmsActions.sendDirectMessage(message, selectedPeer);
104
+ } else if (selectedPeer.id) {
105
+ await hmsActions.sendDirectMessage(message, selectedPeer.id);
99
106
  } else {
100
107
  await hmsActions.sendBroadcastMessage(message);
101
108
  }
@@ -104,8 +111,10 @@ export const ChatFooter = ({ onSend, children }: { onSend: () => void; children:
104
111
  onSend();
105
112
  }, 0);
106
113
  } catch (error) {
107
- const err = error as Error;
108
- ToastManager.addToast({ title: err.message });
114
+ const err = error as HMSException;
115
+ ToastManager.addToast({
116
+ title: err.message.startsWith('Invalid peer') ? `${selectedPeer.name} is not in this room` : err.message,
117
+ });
109
118
  }
110
119
  }, [selectedRole, selectedPeer, hmsActions, onSend]);
111
120
 
@@ -172,7 +181,7 @@ export const ChatFooter = ({ onSend, children }: { onSend: () => void; children:
172
181
  </Flex>
173
182
  ) : null}
174
183
  </Flex>
175
- {!(selection === CHAT_SELECTOR.EVERYONE && !isPublicChatEnabled) && (
184
+ {selection && (
176
185
  <Flex align="center" css={{ gap: '$4', w: '100%' }}>
177
186
  <Flex
178
187
  align="center"
@@ -11,7 +11,6 @@ import {
11
11
  import { CheckIcon, PeopleIcon } from '@100mslive/react-icons';
12
12
  import { Box, CSS, Dropdown, Flex, HorizontalDivider, Text, Tooltip } from '../../..';
13
13
  import { config as cssConfig } from '../../../Theme';
14
- // @ts-ignore
15
14
  import { ParticipantSearch } from '../Footer/ParticipantList';
16
15
  import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
17
16
  // @ts-ignore
@@ -90,7 +89,7 @@ const SelectorHeader = React.memo(
90
89
 
91
90
  const Everyone = React.memo(({ active }: { active: boolean }) => {
92
91
  const unreadCount: number = useHMSStore(selectUnreadHMSMessagesCount);
93
- const [, setPeerSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.PEER_ID);
92
+ const [, setPeerSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.PEER);
94
93
  const [, setRoleSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.ROLE);
95
94
  return (
96
95
  <SelectorItem
@@ -99,7 +98,7 @@ const Everyone = React.memo(({ active }: { active: boolean }) => {
99
98
  active={active}
100
99
  unreadCount={unreadCount}
101
100
  onClick={() => {
102
- setPeerSelector('');
101
+ setPeerSelector({});
103
102
  setRoleSelector('');
104
103
  }}
105
104
  />
@@ -108,7 +107,7 @@ const Everyone = React.memo(({ active }: { active: boolean }) => {
108
107
 
109
108
  const RoleItem = React.memo(({ role, active }: { role: string; active: boolean }) => {
110
109
  const unreadCount: number = useHMSStore(selectMessagesUnreadCountByRole(role));
111
- const [, setPeerSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.PEER_ID);
110
+ const [, setPeerSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.PEER);
112
111
  const [, setRoleSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.ROLE);
113
112
  return (
114
113
  <SelectorItem
@@ -116,7 +115,7 @@ const RoleItem = React.memo(({ role, active }: { role: string; active: boolean }
116
115
  active={active}
117
116
  unreadCount={unreadCount}
118
117
  onClick={() => {
119
- setPeerSelector('');
118
+ setPeerSelector({});
120
119
  setRoleSelector(role);
121
120
  }}
122
121
  />
@@ -125,7 +124,7 @@ const RoleItem = React.memo(({ role, active }: { role: string; active: boolean }
125
124
 
126
125
  const PeerItem = ({ peerId, name, active }: { name: string; peerId: string; active: boolean }) => {
127
126
  const unreadCount: number = useHMSStore(selectMessagesUnreadCountByPeerID(peerId));
128
- const [, setPeerSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.PEER_ID);
127
+ const [, setPeerSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.PEER);
129
128
  const [, setRoleSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.ROLE);
130
129
 
131
130
  return (
@@ -134,7 +133,7 @@ const PeerItem = ({ peerId, name, active }: { name: string; peerId: string; acti
134
133
  active={active}
135
134
  unreadCount={unreadCount}
136
135
  onClick={() => {
137
- setPeerSelector(peerId);
136
+ setPeerSelector({ id: peerId, name });
138
137
  setRoleSelector('');
139
138
  }}
140
139
  />
@@ -146,14 +145,15 @@ const VirtualizedSelectItemList = ({
146
145
  selectedRole,
147
146
  selectedPeerId,
148
147
  searchValue,
148
+ isPublicChatEnabled,
149
149
  }: {
150
150
  peers: HMSPeer[];
151
151
  selectedRole: string;
152
152
  selectedPeerId: string;
153
153
  searchValue: string;
154
+ isPublicChatEnabled: boolean;
154
155
  }) => {
155
156
  const roles = useFilteredRoles();
156
- const isMobile = useMedia(cssConfig.media.md);
157
157
  const filteredPeers = useMemo(
158
158
  () =>
159
159
  peers.filter(
@@ -164,15 +164,22 @@ const VirtualizedSelectItemList = ({
164
164
  );
165
165
 
166
166
  const listItems = useMemo(() => {
167
- const selectItems = !searchValue ? [<Everyone active={!selectedRole && !selectedPeerId} />] : [];
168
-
169
- roles.length > 0 && !searchValue && selectItems.push(<SelectorHeader>Roles</SelectorHeader>);
170
- !searchValue &&
167
+ let selectItems: React.ReactNode[] = [];
168
+ if (isPublicChatEnabled && !searchValue) {
169
+ selectItems = [<Everyone active={!selectedRole && !selectedPeerId} />];
170
+ }
171
+ if (roles.length > 0 && !searchValue) {
172
+ selectItems.push(<SelectorHeader isHorizontalDivider={isPublicChatEnabled}>Roles</SelectorHeader>);
171
173
  roles.forEach(userRole =>
172
174
  selectItems.push(<RoleItem key={userRole} active={selectedRole === userRole} role={userRole} />),
173
175
  );
176
+ }
174
177
 
175
- filteredPeers.length > 0 && selectItems.push(<SelectorHeader>Participants</SelectorHeader>);
178
+ if (filteredPeers.length > 0) {
179
+ selectItems.push(
180
+ <SelectorHeader isHorizontalDivider={isPublicChatEnabled || roles.length > 0}>Participants</SelectorHeader>,
181
+ );
182
+ }
176
183
  filteredPeers.forEach(peer =>
177
184
  selectItems.push(
178
185
  <PeerItem key={peer.id} name={peer.name} peerId={peer.id} active={peer.id === selectedPeerId} />,
@@ -180,23 +187,14 @@ const VirtualizedSelectItemList = ({
180
187
  );
181
188
 
182
189
  return selectItems;
183
- }, [searchValue, selectedRole, selectedPeerId, roles, filteredPeers]);
190
+ }, [isPublicChatEnabled, searchValue, selectedRole, selectedPeerId, roles, filteredPeers]);
184
191
 
185
- if (!isMobile) {
186
- return (
187
- <Dropdown.Group css={{ overflowY: 'auto', maxHeight: '$64', bg: '$surface_default' }}>
188
- {listItems.map((item, index) => (
189
- <Box key={index}>{item}</Box>
190
- ))}
191
- </Dropdown.Group>
192
- );
193
- }
194
192
  return (
195
- <>
193
+ <Dropdown.Group css={{ overflowY: 'auto', maxHeight: '$64', bg: '$surface_default' }}>
196
194
  {listItems.map((item, index) => (
197
195
  <Box key={index}>{item}</Box>
198
196
  ))}
199
- </>
197
+ </Dropdown.Group>
200
198
  );
201
199
  };
202
200
 
@@ -206,6 +204,7 @@ export const ChatSelector = ({ role, peerId }: { role: string; peerId: string })
206
204
  const [search, setSearch] = useState('');
207
205
 
208
206
  const isPrivateChatEnabled = !!elements?.chat?.private_chat_enabled;
207
+ const isPublicChatEnabled = !!elements?.chat?.public_chat_enabled;
209
208
 
210
209
  return (
211
210
  <>
@@ -218,6 +217,7 @@ export const ChatSelector = ({ role, peerId }: { role: string; peerId: string })
218
217
  selectedRole={role}
219
218
  selectedPeerId={peerId}
220
219
  peers={isPrivateChatEnabled ? peers : []}
220
+ isPublicChatEnabled={isPublicChatEnabled}
221
221
  searchValue={search}
222
222
  />
223
223
  </>
@@ -1,7 +1,6 @@
1
1
  import React, { useState } from 'react';
2
2
  import { useMedia } from 'react-use';
3
- import { selectPeerNameByID, useHMSStore } from '@100mslive/react-sdk';
4
- import { ChevronDownIcon, ChevronUpIcon, CrossIcon, PeopleIcon, PersonIcon } from '@100mslive/react-icons';
3
+ import { ChevronDownIcon, ChevronUpIcon, CrossIcon, GroupIcon, PersonIcon } from '@100mslive/react-icons';
5
4
  import { Dropdown } from '../../../Dropdown';
6
5
  import { Box, Flex } from '../../../Layout';
7
6
  import { Sheet } from '../../../Sheet';
@@ -11,7 +10,7 @@ import { ChatSelector } from './ChatSelector';
11
10
  import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
12
11
  // @ts-ignore
13
12
  import { useSubscribeChatSelector } from '../AppData/useUISettings';
14
- import { useFilteredRoles } from '../../common/hooks';
13
+ import { useDefaultChatSelection, useFilteredRoles } from '../../common/hooks';
15
14
  import { CHAT_SELECTOR } from '../../common/constants';
16
15
 
17
16
  export const ChatSelectorContainer = () => {
@@ -21,10 +20,10 @@ export const ChatSelectorContainer = () => {
21
20
  const isPrivateChatEnabled = !!elements?.chat?.private_chat_enabled;
22
21
  const isPublicChatEnabled = !!elements?.chat?.public_chat_enabled;
23
22
  const roles = useFilteredRoles();
24
- const selectedPeer = useSubscribeChatSelector(CHAT_SELECTOR.PEER_ID);
23
+ const selectedPeer = useSubscribeChatSelector(CHAT_SELECTOR.PEER);
25
24
  const selectedRole = useSubscribeChatSelector(CHAT_SELECTOR.ROLE);
26
- const selectorPeerName = useHMSStore(selectPeerNameByID(selectedPeer));
27
- const selection = selectorPeerName || selectedRole || CHAT_SELECTOR.EVERYONE;
25
+ const defaultSelection = useDefaultChatSelection();
26
+ const selection = selectedPeer.name || selectedRole || defaultSelection;
28
27
 
29
28
  if (!(isPrivateChatEnabled || isPublicChatEnabled || roles.length > 0) && !isPrivateChatEnabled && !selection) {
30
29
  return null;
@@ -47,18 +46,18 @@ export const ChatSelectorContainer = () => {
47
46
  }}
48
47
  >
49
48
  <Text
50
- variant="xs"
49
+ variant="caption"
51
50
  css={{
52
51
  c: '$on_surface_high',
53
52
  pr: '$2',
54
53
  display: 'flex',
55
54
  alignItems: 'center',
56
55
  gap: '$1',
57
- textTransform: 'capitalize',
56
+ textTransform: selection !== selectedPeer.name ? 'capitalize' : undefined,
58
57
  }}
59
58
  >
60
59
  {selection === CHAT_SELECTOR.EVERYONE ? (
61
- <PeopleIcon width={16} height={16} />
60
+ <GroupIcon width={16} height={16} />
62
61
  ) : (
63
62
  <PersonIcon width={16} height={16} />
64
63
  )}
@@ -73,7 +72,7 @@ export const ChatSelectorContainer = () => {
73
72
  asChild
74
73
  data-testid="participant_list_filter"
75
74
  css={{
76
- border: '1px solid $border_bright',
75
+ background: '$primary_default',
77
76
  r: '$0',
78
77
  p: '$1 $2',
79
78
  ml: '$6',
@@ -82,22 +81,22 @@ export const ChatSelectorContainer = () => {
82
81
  >
83
82
  <Flex align="center" css={{ c: '$on_surface_medium' }} gap="1">
84
83
  <Text
85
- variant="xs"
84
+ variant="caption"
86
85
  css={{
87
86
  c: '$on_surface_high',
88
87
  pr: '$2',
89
88
  display: 'flex',
90
89
  alignItems: 'center',
91
90
  gap: '$1',
92
- textTransform: 'capitalize',
91
+ textTransform: selection !== selectedPeer.name ? 'capitalize' : undefined,
93
92
  }}
94
93
  >
95
94
  {selection === CHAT_SELECTOR.EVERYONE ? (
96
- <PeopleIcon width={16} height={16} />
95
+ <GroupIcon width={16} height={16} />
97
96
  ) : (
98
97
  <PersonIcon width={16} height={16} />
99
98
  )}
100
- {selection}
99
+ {selection || 'Search'}
101
100
  </Text>
102
101
  {selection && (
103
102
  <ChevronDownIcon
@@ -119,7 +118,7 @@ export const ChatSelectorContainer = () => {
119
118
  align="start"
120
119
  sideOffset={8}
121
120
  >
122
- <ChatSelector role={selectedRole} peerId={selectedPeer} />
121
+ <ChatSelector role={selectedRole} peerId={selectedPeer.id} />
123
122
  </Dropdown.Content>
124
123
  </Dropdown.Root>
125
124
  )}
@@ -148,7 +147,7 @@ export const ChatSelectorContainer = () => {
148
147
  setOpen(false);
149
148
  }}
150
149
  >
151
- <ChatSelector role={selectedRole} peerId={selectedPeer} />
150
+ <ChatSelector role={selectedRole} peerId={selectedPeer.id} />
152
151
  </Box>
153
152
  </Sheet.Content>
154
153
  </Sheet.Root>