@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
@@ -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