@100mslive/roomkit-react 0.1.15 → 0.1.16

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -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>
@@ -67,7 +67,6 @@ export const PinnedMessage = ({ clearPinnedMessage }: { clearPinnedMessage: (ind
67
67
  />
68
68
  ) : null}
69
69
  <Flex
70
- title={pinnedMessages[pinnedMessageIndex]?.text}
71
70
  css={{
72
71
  p: '$4',
73
72
  color: '$on_surface_medium',
@@ -95,7 +94,12 @@ export const PinnedMessage = ({ clearPinnedMessage }: { clearPinnedMessage: (ind
95
94
  },
96
95
  }}
97
96
  >
98
- <Text variant="sm" css={{ color: '$on_surface_medium' }} {...swipeHandlers}>
97
+ <Text
98
+ variant="sm"
99
+ css={{ color: '$on_surface_medium' }}
100
+ {...swipeHandlers}
101
+ title={pinnedMessages[pinnedMessageIndex]?.text}
102
+ >
99
103
  <AnnotisedMessage
100
104
  message={`${currentPinnedMessage.slice(
101
105
  0,
@@ -123,6 +127,7 @@ export const PinnedMessage = ({ clearPinnedMessage }: { clearPinnedMessage: (ind
123
127
  '&:hover .hide-on-hover': { display: 'none !important' },
124
128
  '&:hover .show-on-hover': { display: 'block !important' },
125
129
  }}
130
+ title="Unpin Message"
126
131
  >
127
132
  <UnpinIcon className="show-on-hover" style={{ display: 'none' }} height={20} width={20} />
128
133
  <PinIcon className="hide-on-hover" style={{ display: 'block' }} height={20} width={20} />
@@ -21,6 +21,7 @@ import { VideoStreamingSection } from '../layouts/VideoStreamingSection';
21
21
  // @ts-ignore: No implicit Any
22
22
  import FullPageProgress from './FullPageProgress';
23
23
  import { Header } from './Header';
24
+ import { PreviousRoleInMetadata } from './PreviousRoleInMetadata';
24
25
  import {
25
26
  useRoomLayoutConferencingScreen,
26
27
  useRoomLayoutPreviewScreen,
@@ -175,6 +176,7 @@ export const ConferenceScreen = () => {
175
176
  <RoleChangeRequestModal />
176
177
  <HLSFailureModal />
177
178
  <ActivatedPIP />
179
+ <PreviousRoleInMetadata />
178
180
  </Flex>
179
181
  </>
180
182
  );
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { selectUnreadHMSMessagesCount, useHMSStore } from '@100mslive/react-sdk';
3
- import { ChatIcon, ChatUnreadIcon } from '@100mslive/react-icons';
4
- import { Tooltip } from '../../..';
3
+ import { ChatIcon } from '@100mslive/react-icons';
4
+ import { Box, Flex, Text, Tooltip } from '../../..';
5
5
  // @ts-ignore: No implicit Any
6
6
  import IconButton from '../../IconButton';
7
7
  // @ts-ignore: No implicit Any
@@ -15,10 +15,33 @@ export const ChatToggle = () => {
15
15
  const toggleChat = useSidepaneToggle(SIDE_PANE_OPTIONS.CHAT);
16
16
 
17
17
  return (
18
- <Tooltip key="chat" title={`${isChatOpen ? 'Close' : 'Open'} chat`}>
19
- <IconButton onClick={toggleChat} active={!isChatOpen} data-testid="chat_btn">
20
- {countUnreadMessages === 0 ? <ChatIcon /> : <ChatUnreadIcon data-testid="chat_unread_btn" />}
21
- </IconButton>
22
- </Tooltip>
18
+ <Box
19
+ css={{
20
+ position: 'relative',
21
+ }}
22
+ >
23
+ <Tooltip key="chat" title={`${isChatOpen ? 'Close' : 'Open'} chat`}>
24
+ <IconButton onClick={toggleChat} active={!isChatOpen} data-testid="chat_btn">
25
+ <ChatIcon />
26
+ </IconButton>
27
+ </Tooltip>
28
+ {countUnreadMessages > 0 && (
29
+ <Flex
30
+ css={{
31
+ height: '$8',
32
+ p: '$4 4.5px',
33
+ justifyContent: 'center',
34
+ alignItems: 'center',
35
+ position: 'absolute',
36
+ top: '-$4',
37
+ right: '-$4',
38
+ borderRadius: '$space$14',
39
+ background: '$primary_default',
40
+ }}
41
+ >
42
+ <Text variant="overline">{countUnreadMessages > 99 ? '99+' : countUnreadMessages}</Text>
43
+ </Flex>
44
+ )}
45
+ </Box>
23
46
  );
24
47
  };
@@ -20,9 +20,9 @@ import { ScreenshareToggle } from '../ScreenShareToggle';
20
20
  import { VBToggle } from '../VirtualBackground/VBToggle';
21
21
  // @ts-ignore: No implicit Any
22
22
  import { ChatToggle } from './ChatToggle';
23
- // @ts-ignore: No implicit Any
24
23
  import { ParticipantCount } from './ParticipantList';
25
24
  import { PollsToggle } from './PollsToggle';
25
+ import { WhiteboardToggle } from './WhiteboardToggle';
26
26
  import { ConferencingScreenElements } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
27
27
  // @ts-ignore: No implicit Any
28
28
  import { useIsSidepaneTypeOpen, useSidepaneToggle } from '../AppData/useSidepane';
@@ -105,6 +105,7 @@ export const Footer = ({
105
105
  )}
106
106
  </AppFooter.Center>
107
107
  <AppFooter.Right>
108
+ <WhiteboardToggle />
108
109
  {showPolls && <PollsToggle />}
109
110
  {!isMobile && elements?.chat && <ChatToggle />}
110
111
  {elements?.participant_list && <ParticipantCount />}
@@ -8,7 +8,6 @@ import { IconButton } from '../../../IconButton';
8
8
  import { Box, Flex } from '../../../Layout';
9
9
  import { Loading } from '../../../Loading';
10
10
  import { Text } from '../../../Text';
11
- // @ts-ignore: No implicit Any
12
11
  import { Participant, ParticipantSearch } from './ParticipantList';
13
12
  import { ItemData, itemKey, ROW_HEIGHT } from './RoleAccordion';
14
13
  // @ts-ignore: No implicit Any