@100mslive/roomkit-react 0.3.10-alpha.0 → 0.3.10-alpha.2

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 (36) hide show
  1. package/dist/{HLSView-5GXP76IN.js → HLSView-KRJMDNAF.js} +2 -2
  2. package/dist/Prebuilt/common/constants.d.ts +1 -0
  3. package/dist/Prebuilt/components/AppData/useSidepaneResetOnLayoutUpdate.d.ts +3 -0
  4. package/dist/Prebuilt/components/Chat/Chat.d.ts +1 -1
  5. package/dist/Prebuilt/components/RoleChangeModal.d.ts +5 -0
  6. package/dist/Prebuilt/components/TileMenu/TileMenuContent.d.ts +2 -1
  7. package/dist/Prebuilt/components/VideoLayouts/WhiteboardLayout.d.ts +0 -1
  8. package/dist/{chunk-WSDBUVSZ.js → chunk-AFSI3LBT.js} +618 -564
  9. package/dist/chunk-AFSI3LBT.js.map +7 -0
  10. package/dist/index.cjs.js +1953 -1896
  11. package/dist/index.cjs.js.map +4 -4
  12. package/dist/index.js +1 -1
  13. package/dist/meta.cjs.json +125 -204
  14. package/dist/meta.esbuild.json +131 -280
  15. package/package.json +7 -9
  16. package/src/Modal/DialogContent.tsx +1 -1
  17. package/src/Prebuilt/common/constants.ts +2 -0
  18. package/src/Prebuilt/components/AppData/useSidepaneResetOnLayoutUpdate.tsx +22 -0
  19. package/src/Prebuilt/components/AudioVideoToggle.tsx +1 -1
  20. package/src/Prebuilt/components/Chat/Chat.tsx +3 -4
  21. package/src/Prebuilt/components/Footer/ParticipantList.tsx +56 -37
  22. package/src/Prebuilt/components/RoleChangeModal.tsx +187 -0
  23. package/src/Prebuilt/components/TileMenu/TileMenu.tsx +5 -0
  24. package/src/Prebuilt/components/TileMenu/TileMenuContent.tsx +23 -1
  25. package/src/Prebuilt/components/VideoLayouts/WhiteboardLayout.tsx +13 -7
  26. package/src/Prebuilt/components/VirtualBackground/VBPicker.tsx +3 -0
  27. package/src/Prebuilt/layouts/SidePane.tsx +9 -9
  28. package/dist/HLSView-FBGVUTA5.css +0 -2767
  29. package/dist/HLSView-FBGVUTA5.css.map +0 -7
  30. package/dist/chunk-WSDBUVSZ.js.map +0 -7
  31. package/dist/index.cjs.css +0 -2767
  32. package/dist/index.cjs.css.map +0 -7
  33. package/dist/index.css +0 -2767
  34. package/dist/index.css.map +0 -7
  35. package/src/Prebuilt/components/RoleChangeModal.jsx +0 -185
  36. /package/dist/{HLSView-5GXP76IN.js.map → HLSView-KRJMDNAF.js.map} +0 -0
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "prebuilt",
11
11
  "roomkit"
12
12
  ],
13
- "version": "0.3.10-alpha.0",
13
+ "version": "0.3.10-alpha.2",
14
14
  "author": "100ms",
15
15
  "license": "MIT",
16
16
  "repository": {
@@ -27,8 +27,7 @@
27
27
  "require": "./dist/index.cjs.js",
28
28
  "import": "./dist/index.js",
29
29
  "default": "./dist/index.js"
30
- },
31
- "./index.css": "./dist/index.css"
30
+ }
32
31
  },
33
32
  "sideEffects": false,
34
33
  "scripts": {
@@ -74,12 +73,11 @@
74
73
  "react": ">=17.0.2 <19.0.0"
75
74
  },
76
75
  "dependencies": {
77
- "@100mslive/hls-player": "0.3.10-alpha.0",
76
+ "@100mslive/hls-player": "0.3.10-alpha.2",
78
77
  "@100mslive/hms-noise-cancellation": "0.0.1",
79
- "@100mslive/hms-virtual-background": "1.13.10-alpha.0",
80
- "@100mslive/hms-whiteboard": "0.0.0-alpha.1",
81
- "@100mslive/react-icons": "0.10.10-alpha.0",
82
- "@100mslive/react-sdk": "0.10.10-alpha.0",
78
+ "@100mslive/hms-virtual-background": "1.13.10-alpha.2",
79
+ "@100mslive/react-icons": "0.10.10-alpha.2",
80
+ "@100mslive/react-sdk": "0.10.10-alpha.2",
83
81
  "@100mslive/types-prebuilt": "0.12.8",
84
82
  "@emoji-mart/data": "^1.0.6",
85
83
  "@emoji-mart/react": "^1.0.1",
@@ -115,5 +113,5 @@
115
113
  "uuid": "^8.3.2",
116
114
  "worker-timers": "^7.0.40"
117
115
  },
118
- "gitHead": "c705b5b8ce729747f28c7aff242057ac8178d338"
116
+ "gitHead": "303a4fc9e47b81f2171e8dfb89ceaa98f52e2c25"
119
117
  }
@@ -27,7 +27,7 @@ export const StyledDialogPortal = styled(DialogPrimitive.Portal, {});
27
27
 
28
28
  export const CustomDialogContent = styled(DialogPrimitive.Content, {
29
29
  color: '$on_surface_medium',
30
- backgroundColor: '$surface_default',
30
+ backgroundColor: '$surface_dim',
31
31
  borderRadius: '8px',
32
32
  position: 'absolute',
33
33
  top: '50%',
@@ -70,6 +70,8 @@ export const SIDE_PANE_OPTIONS = {
70
70
  ROOM_DETAILS: 'ROOM_DETAILS',
71
71
  };
72
72
 
73
+ export type SidePaneOption = (typeof SIDE_PANE_OPTIONS)[keyof typeof SIDE_PANE_OPTIONS];
74
+
73
75
  export const SHEET_OPTIONS = {
74
76
  ROOM_DETAILS: 'ROOM_DETAILS',
75
77
  };
@@ -0,0 +1,22 @@
1
+ import { useEffect } from 'react';
2
+ import { DefaultConferencingScreen_Elements } from '@100mslive/types-prebuilt';
3
+ import { selectAppData, useHMSStore } from '@100mslive/react-sdk';
4
+ import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
5
+ // @ts-ignore
6
+ import { useSidepaneReset } from './useSidepane';
7
+ import { APP_DATA, SidePaneOption } from '../../common/constants';
8
+
9
+ // Closes the sidepane if an element is removed from the layout via the customiser
10
+ export const useSidepaneResetOnLayoutUpdate = (
11
+ layoutKey: keyof DefaultConferencingScreen_Elements,
12
+ sidepaneOption: SidePaneOption,
13
+ ) => {
14
+ const { elements } = useRoomLayoutConferencingScreen();
15
+ const sidepane = useHMSStore(selectAppData(APP_DATA.sidePane));
16
+ const resetSidePane = useSidepaneReset();
17
+ useEffect(() => {
18
+ if (sidepane === sidepaneOption && !elements?.[layoutKey]) {
19
+ resetSidePane();
20
+ }
21
+ }, [elements, elements?.[layoutKey], resetSidePane, sidepane, layoutKey, sidepaneOption]);
22
+ };
@@ -122,6 +122,7 @@ const NoiseCancellation = () => {
122
122
 
123
123
  return (
124
124
  <>
125
+ <Dropdown.ItemSeparator css={{ mx: 0 }} />
125
126
  <Dropdown.Item
126
127
  css={{
127
128
  p: '$4 $8',
@@ -256,7 +257,6 @@ export const AudioVideoToggle = ({ hideOptions = false }) => {
256
257
  </Dropdown.Group>
257
258
  </>
258
259
  )}
259
- <Dropdown.ItemSeparator css={{ mx: 0 }} />
260
260
  <NoiseCancellation />
261
261
  <AudioSettings onClick={() => setShowSettings(true)} />
262
262
  </IconButtonWithOptions>
@@ -15,8 +15,9 @@ import { ChatFooter } from './ChatFooter';
15
15
  import { ChatBlocked, ChatPaused } from './ChatStates';
16
16
  import { PinnedMessage } from './PinnedMessage';
17
17
  import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
18
+ import { useSidepaneResetOnLayoutUpdate } from '../AppData/useSidepaneResetOnLayoutUpdate';
18
19
  import { useLandscapeHLSStream, useMobileHLSStream } from '../../common/hooks';
19
- import { SESSION_STORE_KEY } from '../../common/constants';
20
+ import { SESSION_STORE_KEY, SIDE_PANE_OPTIONS } from '../../common/constants';
20
21
 
21
22
  export const Chat = () => {
22
23
  const { elements } = useRoomLayoutConferencingScreen();
@@ -27,6 +28,7 @@ export const Chat = () => {
27
28
  const isMobile = useMedia(cssConfig.media.md);
28
29
  const isMobileHLSStream = useMobileHLSStream();
29
30
  const isLandscapeStream = useLandscapeHLSStream();
31
+ useSidepaneResetOnLayoutUpdate('chat', SIDE_PANE_OPTIONS.CHAT);
30
32
 
31
33
  const scrollToBottom = useCallback(
32
34
  (unreadCount = 0) => {
@@ -42,9 +44,6 @@ export const Chat = () => {
42
44
  [hmsActions, vanillaStore],
43
45
  );
44
46
 
45
- if (!elements?.chat) {
46
- return null;
47
- }
48
47
  const streaming = isMobileHLSStream || isLandscapeStream;
49
48
 
50
49
  return (
@@ -21,6 +21,7 @@ import {
21
21
  HandIcon,
22
22
  MicOffIcon,
23
23
  PeopleIcon,
24
+ PersonSettingsIcon,
24
25
  SearchIcon,
25
26
  VerticalMenuIcon,
26
27
  } from '@100mslive/react-icons';
@@ -29,10 +30,12 @@ import { Accordion, Box, Button, config as cssConfig, Dropdown, Flex, Input, Tex
29
30
  import IconButton from '../../IconButton';
30
31
  import { ConnectionIndicator } from '../Connection/ConnectionIndicator';
31
32
  import { RemoveParticipant } from '../RemoveParticipant';
33
+ import { RoleChangeModal } from '../RoleChangeModal';
32
34
  import { RoleAccordion } from './RoleAccordion';
33
35
  import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
34
36
  // @ts-ignore: No implicit Any
35
37
  import { useIsSidepaneTypeOpen, useSidepaneToggle } from '../AppData/useSidepane';
38
+ import { useSidepaneResetOnLayoutUpdate } from '../AppData/useSidepaneResetOnLayoutUpdate';
36
39
  import { usePeerOnStageActions } from '../hooks/usePeerOnStageActions';
37
40
  import { useParticipants } from '../../common/hooks';
38
41
  // @ts-ignore: No implicit Any
@@ -71,6 +74,8 @@ export const ParticipantList = ({
71
74
  });
72
75
  }
73
76
 
77
+ useSidepaneResetOnLayoutUpdate('participant_list', SIDE_PANE_OPTIONS.PARTICIPANTS);
78
+
74
79
  const onSearch = useCallback((value: string) => {
75
80
  setFilter(filterValue => {
76
81
  if (!filterValue) {
@@ -365,45 +370,59 @@ const ParticipantMoreActions = ({ peerId, role }: { peerId: string; role: string
365
370
  isInStage,
366
371
  shouldShowStageRoleChange,
367
372
  } = usePeerOnStageActions({ peerId, role });
373
+ const canChangeRole = !!useHMSStore(selectPermissions)?.changeRole;
374
+ const [openRoleChangeModal, setOpenRoleChangeModal] = useState(false);
375
+
368
376
  return (
369
- <Dropdown.Root open={open} onOpenChange={value => setOpen(value)} modal={false}>
370
- <Dropdown.Trigger
371
- asChild
372
- data-testid="participant_more_actions"
373
- className="participant_item"
374
- css={{
375
- p: '$1',
376
- r: '$0',
377
- c: '$on_surface_high',
378
- display: open ? 'flex' : 'none',
379
- '&:hover': {
380
- bg: '$surface_bright',
381
- },
382
- '@md': {
383
- display: 'flex',
384
- },
385
- }}
386
- tabIndex={0}
387
- >
388
- <Box css={{ my: 'auto' }}>
389
- <VerticalMenuIcon />
390
- </Box>
391
- </Dropdown.Trigger>
392
- <Dropdown.Portal>
393
- <Dropdown.Content align="end" sideOffset={8} css={{ w: '$64', bg: '$surface_default' }}>
394
- {shouldShowStageRoleChange ? (
395
- <Dropdown.Item css={{ bg: '$surface_default' }} onClick={() => handleStageAction()}>
396
- <ChangeRoleIcon />
397
- <Text variant="sm" css={{ ml: '$4', fontWeight: '$semiBold', c: '$on_surface_high' }}>
398
- {isInStage ? remove_from_stage_label : bring_to_stage_label}
399
- </Text>
400
- </Dropdown.Item>
401
- ) : null}
377
+ <>
378
+ <Dropdown.Root open={open} onOpenChange={value => setOpen(value)} modal={false}>
379
+ <Dropdown.Trigger
380
+ asChild
381
+ data-testid="participant_more_actions"
382
+ className="participant_item"
383
+ css={{
384
+ p: '$1',
385
+ r: '$0',
386
+ c: '$on_surface_high',
387
+ display: open ? 'flex' : 'none',
388
+ '&:hover': {
389
+ bg: '$surface_bright',
390
+ },
391
+ '@md': {
392
+ display: 'flex',
393
+ },
394
+ }}
395
+ tabIndex={0}
396
+ >
397
+ <Box css={{ my: 'auto' }}>
398
+ <VerticalMenuIcon />
399
+ </Box>
400
+ </Dropdown.Trigger>
401
+ <Dropdown.Portal>
402
+ <Dropdown.Content align="end" sideOffset={8} css={{ w: '$64', bg: '$surface_default' }}>
403
+ {shouldShowStageRoleChange ? (
404
+ <Dropdown.Item css={{ bg: '$surface_default' }} onClick={() => handleStageAction()}>
405
+ <ChangeRoleIcon />
406
+ <Text variant="sm" css={{ ml: '$4', fontWeight: '$semiBold', c: '$on_surface_high' }}>
407
+ {isInStage ? remove_from_stage_label : bring_to_stage_label}
408
+ </Text>
409
+ </Dropdown.Item>
410
+ ) : null}
402
411
 
403
- <RemoveParticipant peerId={peerId} />
404
- </Dropdown.Content>
405
- </Dropdown.Portal>
406
- </Dropdown.Root>
412
+ {canChangeRole ? (
413
+ <Dropdown.Item css={{ bg: '$surface_default' }} onClick={() => setOpenRoleChangeModal(true)}>
414
+ <PersonSettingsIcon />
415
+ <Text variant="sm" css={{ ml: '$4', fontWeight: '$semiBold', c: '$on_surface_high' }}>
416
+ Switch Role
417
+ </Text>
418
+ </Dropdown.Item>
419
+ ) : null}
420
+ <RemoveParticipant peerId={peerId} />
421
+ </Dropdown.Content>
422
+ </Dropdown.Portal>
423
+ </Dropdown.Root>
424
+ {openRoleChangeModal && <RoleChangeModal peerId={peerId} onOpenChange={setOpenRoleChangeModal} />}
425
+ </>
407
426
  );
408
427
  };
409
428
 
@@ -0,0 +1,187 @@
1
+ import React, { useRef, useState } from 'react';
2
+ import { useMedia } from 'react-use';
3
+ import { HMSPeer, selectAvailableRoleNames, selectPeerByID, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
4
+ import { ChevronDownIcon, ChevronUpIcon, CrossIcon } from '@100mslive/react-icons';
5
+ import { Button } from '../../Button';
6
+ import { Dropdown } from '../../Dropdown';
7
+ import { Box, Flex } from '../../Layout';
8
+ import { Dialog } from '../../Modal';
9
+ import { Sheet } from '../../Sheet';
10
+ import { Text } from '../../Text';
11
+ import { config as cssConfig } from '../../Theme';
12
+ import { Tooltip } from '../../Tooltip';
13
+
14
+ const HighlightTerm = ({ value }: { value: string | undefined }) => {
15
+ return value ? (
16
+ <Tooltip side="top" title={value}>
17
+ <Text
18
+ variant="body2"
19
+ css={{
20
+ color: '$on_surface_medium',
21
+ fontWeight: '$semiBold',
22
+ }}
23
+ >
24
+ '{value.slice(0, 100)}
25
+ {value.length > 100 ? '...' : ''}'
26
+ </Text>
27
+ </Tooltip>
28
+ ) : (
29
+ <></>
30
+ );
31
+ };
32
+
33
+ const RoleChangeContent = ({
34
+ peer,
35
+ onOpenChange,
36
+ isMobile = false,
37
+ }: {
38
+ peer: HMSPeer;
39
+ onOpenChange: (open: boolean) => void;
40
+ isMobile?: boolean;
41
+ }) => {
42
+ const roles = useHMSStore(selectAvailableRoleNames).filter(role => role !== peer?.roleName);
43
+ const [selectedRole, setRole] = useState(roles.filter(role => role !== peer?.roleName)?.[0] || peer?.roleName);
44
+ const hmsActions = useHMSActions();
45
+ const [open, setOpen] = useState(false);
46
+ const triggerRef = useRef<HTMLButtonElement | undefined>();
47
+
48
+ return (
49
+ <>
50
+ <Flex align="center" justify="between" css={{ w: '100%' }}>
51
+ <Text as="h6" variant="h6">
52
+ Switch Role
53
+ </Text>
54
+ {isMobile && <CrossIcon onClick={() => onOpenChange(false)} />}
55
+ </Flex>
56
+
57
+ <Box>
58
+ <Text
59
+ variant="body2"
60
+ css={{
61
+ mt: '$4',
62
+ mb: '$8',
63
+ c: '$on_surface_medium',
64
+ display: 'flex',
65
+ flexWrap: 'wrap',
66
+ columnGap: '4px',
67
+ }}
68
+ >
69
+ Switch the role of
70
+ <HighlightTerm value={peer.name} />
71
+ from <HighlightTerm value={peer.roleName} />
72
+ </Text>
73
+ </Box>
74
+ <Flex
75
+ align="center"
76
+ css={{
77
+ w: '100%',
78
+ mb: '$10',
79
+ }}
80
+ >
81
+ <Box
82
+ css={{
83
+ position: 'relative',
84
+ flex: '1 1 0',
85
+ minWidth: 0,
86
+ }}
87
+ >
88
+ <Dropdown.Root open={open} onOpenChange={setOpen} css={{ width: '100%' }}>
89
+ <Dropdown.Trigger
90
+ // @ts-ignore
91
+ ref={triggerRef}
92
+ data-testid="open_role_selection_dropdown"
93
+ asChild
94
+ css={{
95
+ border: '1px solid $border_bright',
96
+ bg: '$surface_default',
97
+ r: '$1',
98
+ p: '$6 $9',
99
+ }}
100
+ >
101
+ <Flex align="center" justify="between" css={{ width: '100%' }}>
102
+ <Text>{selectedRole}</Text>
103
+ {open ? <ChevronUpIcon /> : <ChevronDownIcon />}
104
+ </Flex>
105
+ </Dropdown.Trigger>
106
+
107
+ <Dropdown.Content align="start" sideOffset={8} css={{ zIndex: 1000, w: '100%' }}>
108
+ {roles.map(role => (
109
+ <Dropdown.Item
110
+ data-testid={role}
111
+ key={role}
112
+ onSelect={() => setRole(role)}
113
+ css={{ w: `${triggerRef.current?.clientWidth}px` }}
114
+ >
115
+ {role}
116
+ </Dropdown.Item>
117
+ ))}
118
+ </Dropdown.Content>
119
+ </Dropdown.Root>
120
+ </Box>
121
+ </Flex>
122
+ <Flex justify="center" align="center" css={{ width: '100%', gap: '$md' }}>
123
+ {!isMobile && (
124
+ <Button
125
+ variant="standard"
126
+ outlined
127
+ css={{ width: '100%' }}
128
+ onClick={() => onOpenChange(false)}
129
+ data-testid="cancel_button"
130
+ >
131
+ Cancel
132
+ </Button>
133
+ )}
134
+
135
+ <Button
136
+ data-testid="change_button"
137
+ variant="primary"
138
+ css={{ width: '100%' }}
139
+ onClick={async () => {
140
+ if (selectedRole) {
141
+ await hmsActions.changeRoleOfPeer(peer.id, selectedRole, true);
142
+ onOpenChange(false);
143
+ }
144
+ }}
145
+ >
146
+ Switch Role
147
+ </Button>
148
+ </Flex>
149
+ </>
150
+ );
151
+ };
152
+
153
+ export const RoleChangeModal = ({
154
+ peerId,
155
+ onOpenChange,
156
+ }: {
157
+ peerId: string;
158
+ onOpenChange: (open: boolean) => void;
159
+ }) => {
160
+ const peer = useHMSStore(selectPeerByID(peerId));
161
+ const isMobile = useMedia(cssConfig.media.md);
162
+
163
+ if (!peer) {
164
+ return null;
165
+ }
166
+
167
+ if (isMobile) {
168
+ return (
169
+ <Sheet.Root open={true} onOpenChange={onOpenChange}>
170
+ <Sheet.Content css={{ p: '$12 $8', background: '$surface_dim' }}>
171
+ <RoleChangeContent peer={peer} onOpenChange={onOpenChange} isMobile />
172
+ </Sheet.Content>
173
+ </Sheet.Root>
174
+ );
175
+ }
176
+
177
+ return (
178
+ <Dialog.Root defaultOpen onOpenChange={onOpenChange}>
179
+ <Dialog.Portal>
180
+ <Dialog.Overlay />
181
+ <Dialog.Content css={{ width: 'min(400px,80%)', p: '$10' }}>
182
+ <RoleChangeContent peer={peer} onOpenChange={onOpenChange} />
183
+ </Dialog.Content>
184
+ </Dialog.Portal>
185
+ </Dialog.Root>
186
+ );
187
+ };
@@ -17,6 +17,7 @@ import { Text } from '../../../Text';
17
17
  import { config as cssConfig, useTheme } from '../../../Theme';
18
18
  import { StyledMenuTile } from '../../../TileMenu';
19
19
  import { ChangeNameModal } from '../MoreSettings/ChangeNameModal';
20
+ import { RoleChangeModal } from '../RoleChangeModal';
20
21
  import { TileMenuContent } from './TileMenuContent';
21
22
  import { useDropdownList } from '../hooks/useDropdownList';
22
23
  import { getDragClassName } from './utils';
@@ -56,6 +57,7 @@ const TileMenu = ({
56
57
  const isMobile = useMedia(cssConfig.media.md);
57
58
  const peer = useHMSStore(selectPeerByID(peerID));
58
59
  const [showNameChangeModal, setShowNameChangeModal] = useState(false);
60
+ const [showRoleChangeModal, setShowRoleChangeModal] = useState(false);
59
61
  useDropdownList({ open, name: 'TileMenu' });
60
62
  const dragClassName = getDragClassName();
61
63
 
@@ -64,6 +66,7 @@ const TileMenu = ({
64
66
  }
65
67
 
66
68
  const openNameChangeModal = () => setShowNameChangeModal(true);
69
+ const openRoleChangeModal = () => setShowRoleChangeModal(true);
67
70
 
68
71
  const props = {
69
72
  isLocal,
@@ -76,6 +79,7 @@ const TileMenu = ({
76
79
  showPinAction,
77
80
  canMinimise,
78
81
  openNameChangeModal,
82
+ openRoleChangeModal,
79
83
  };
80
84
 
81
85
  return (
@@ -133,6 +137,7 @@ const TileMenu = ({
133
137
  )}
134
138
  </StyledMenuTile.Root>
135
139
  {showNameChangeModal && <ChangeNameModal onOpenChange={setShowNameChangeModal} />}
140
+ {showRoleChangeModal && <RoleChangeModal peerId={peerID} onOpenChange={setShowRoleChangeModal} />}
136
141
  </>
137
142
  );
138
143
  };
@@ -17,6 +17,7 @@ import {
17
17
  MicOffIcon,
18
18
  MicOnIcon,
19
19
  PencilIcon,
20
+ PersonSettingsIcon,
20
21
  PinIcon,
21
22
  RemoveUserIcon,
22
23
  ShareScreenIcon,
@@ -224,6 +225,9 @@ export const TileMenuContent = ({
224
225
  openNameChangeModal = () => {
225
226
  return;
226
227
  },
228
+ openRoleChangeModal = () => {
229
+ return;
230
+ },
227
231
  }: {
228
232
  videoTrackID: string;
229
233
  audioTrackID: string;
@@ -235,10 +239,13 @@ export const TileMenuContent = ({
235
239
  canMinimise?: boolean;
236
240
  closeSheetOnClick?: () => void;
237
241
  openNameChangeModal?: () => void;
242
+ openRoleChangeModal?: () => void;
238
243
  }) => {
239
244
  const actions = useHMSActions();
240
245
  const dragClassName = getDragClassName();
241
- const removeOthers: boolean | undefined = useHMSStore(selectPermissions)?.removeOthers;
246
+ const permissions = useHMSStore(selectPermissions);
247
+ const canChangeRole = !!permissions?.changeRole;
248
+ const removeOthers = !!permissions?.removeOthers;
242
249
  const { userName } = useHMSPrebuiltContext();
243
250
 
244
251
  const { isAudioEnabled, isVideoEnabled, setVolume, toggleAudio, toggleVideo, volume } = useRemoteAVToggle(
@@ -308,6 +315,21 @@ export const TileMenuContent = ({
308
315
  </StyledMenuTile.ItemButton>
309
316
  ) : null}
310
317
 
318
+ {canChangeRole ? (
319
+ <StyledMenuTile.ItemButton
320
+ className={dragClassName}
321
+ css={spacingCSS}
322
+ onClick={() => {
323
+ openRoleChangeModal();
324
+ closeSheetOnClick();
325
+ }}
326
+ data-testid="change_role_btn"
327
+ >
328
+ <PersonSettingsIcon height={20} width={20} />
329
+ <span>Switch Role</span>
330
+ </StyledMenuTile.ItemButton>
331
+ ) : null}
332
+
311
333
  {audioTrackID ? (
312
334
  <StyledMenuTile.VolumeItem data-testid="participant_volume_slider" css={{ ...spacingCSS, mb: '$0' }}>
313
335
  <Flex align="center" gap={1}>
@@ -1,6 +1,5 @@
1
1
  import React, { useEffect, useMemo } from 'react';
2
2
  import { useMedia } from 'react-use';
3
- import { Whiteboard } from '@100mslive/hms-whiteboard';
4
3
  import { selectPeerByCondition, selectWhiteboard, useHMSStore, useWhiteboard } from '@100mslive/react-sdk';
5
4
  import { Box } from '../../../Layout';
6
5
  import { config as cssConfig } from '../../../Theme';
@@ -12,12 +11,10 @@ import { ProminenceLayout } from './ProminenceLayout';
12
11
  // @ts-ignore: No implicit Any
13
12
  import { useSetUiSettings } from '../AppData/useUISettings';
14
13
  import { UI_SETTINGS } from '../../common/constants';
15
- // eslint-disable-next-line import/no-unresolved
16
- import '@100mslive/hms-whiteboard/index.css';
17
14
 
18
15
  const WhiteboardEmbed = () => {
19
16
  const isMobile = useMedia(cssConfig.media.md);
20
- const { token, endpoint, zoomToContent } = useWhiteboard(isMobile);
17
+ const { iframeRef } = useWhiteboard(isMobile);
21
18
 
22
19
  return (
23
20
  <Box
@@ -31,9 +28,18 @@ const WhiteboardEmbed = () => {
31
28
  },
32
29
  }}
33
30
  >
34
- <Box css={{ size: '100%' }}>
35
- <Whiteboard token={token} endpoint={`https://${endpoint}`} zoomToContent={zoomToContent} />
36
- </Box>
31
+ <iframe
32
+ title="Whiteboard View"
33
+ ref={iframeRef}
34
+ style={{
35
+ width: '100%',
36
+ height: '100%',
37
+ border: 0,
38
+ borderRadius: '0.75rem',
39
+ }}
40
+ allow="autoplay; clipboard-write;"
41
+ referrerPolicy="no-referrer"
42
+ />
37
43
  </Box>
38
44
  );
39
45
  };
@@ -26,6 +26,7 @@ import { VBCollection } from './VBCollection';
26
26
  import { VBHandler } from './VBHandler';
27
27
  // @ts-ignore
28
28
  import { useSidepaneToggle } from '../AppData/useSidepane';
29
+ import { useSidepaneResetOnLayoutUpdate } from '../AppData/useSidepaneResetOnLayoutUpdate';
29
30
  // @ts-ignore
30
31
  import { useSetAppDataByKey, useUISettings } from '../AppData/useUISettings';
31
32
  import { APP_DATA, SIDE_PANE_OPTIONS, UI_SETTINGS } from '../../common/constants';
@@ -113,6 +114,8 @@ export const VBPicker = ({ backgroundMedia = [] }: { backgroundMedia: VirtualBac
113
114
  return () => setLoadingEffects(false);
114
115
  }, [isVideoOn, setLoadingEffects, toggleVB]);
115
116
 
117
+ useSidepaneResetOnLayoutUpdate('virtual_background', SIDE_PANE_OPTIONS.VB);
118
+
116
119
  return (
117
120
  <Flex css={{ pr: '$6', size: '100%' }} direction="column">
118
121
  <Flex align="center" justify="between" css={{ w: '100%', background: '$surface_dim', pb: '$4' }}>
@@ -141,14 +141,6 @@ const SidePane = ({
141
141
  ? preview_elements?.virtual_background?.background_media
142
142
  : elements?.virtual_background?.background_media || [];
143
143
 
144
- const resetSidePane = useSidepaneReset();
145
-
146
- useEffect(() => {
147
- return () => {
148
- resetSidePane();
149
- };
150
- }, [resetSidePane]);
151
-
152
144
  const tileLayout = {
153
145
  hideParticipantNameOnTile: tileProps?.hide_participant_name_on_tile,
154
146
  roundedVideoTile: tileProps?.rounded_video_tile,
@@ -199,7 +191,15 @@ const SidePane = ({
199
191
  return null;
200
192
  });
201
193
 
202
- if (!trackId && !SidepaneComponent) {
194
+ const resetSidePane = useSidepaneReset();
195
+
196
+ useEffect(() => {
197
+ return () => {
198
+ resetSidePane();
199
+ };
200
+ }, [resetSidePane]);
201
+
202
+ if (!SidepaneComponent && !trackId) {
203
203
  return null;
204
204
  }
205
205