@100mslive/roomkit-react 0.1.14 → 0.1.16

Sign up to get free protection for your applications and to get access to all the features.
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>