@100mslive/roomkit-react 0.3.9 → 0.3.10-alpha.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. package/dist/{HLSView-A6GH22QT.js → HLSView-524T6OTY.js} +109 -120
  2. package/dist/{HLSView-A6GH22QT.js.map → HLSView-524T6OTY.js.map} +2 -2
  3. package/dist/HLSView-VL3DXGRO.css +2767 -0
  4. package/dist/HLSView-VL3DXGRO.css.map +7 -0
  5. package/dist/Prebuilt/common/constants.d.ts +1 -0
  6. package/dist/Prebuilt/components/AppData/useSidepaneResetOnLayoutUpdate.d.ts +3 -0
  7. package/dist/Prebuilt/components/Chat/Chat.d.ts +1 -1
  8. package/dist/Prebuilt/components/PIP/PIPComponent.d.ts +14 -0
  9. package/dist/Prebuilt/components/PIP/PIPManager.d.ts +100 -0
  10. package/dist/Prebuilt/components/RoleChangeModal.d.ts +5 -0
  11. package/dist/Prebuilt/components/TileMenu/TileMenuContent.d.ts +2 -1
  12. package/dist/Prebuilt/components/VideoLayouts/WhiteboardLayout.d.ts +1 -0
  13. package/dist/{chunk-5KG27WWA.js → chunk-IOHV3H2B.js} +1861 -2103
  14. package/dist/chunk-IOHV3H2B.js.map +7 -0
  15. package/dist/index.cjs.css +2767 -0
  16. package/dist/index.cjs.css.map +7 -0
  17. package/dist/index.cjs.js +3352 -3573
  18. package/dist/index.cjs.js.map +4 -4
  19. package/dist/index.css +2767 -0
  20. package/dist/index.css.map +7 -0
  21. package/dist/index.js +1 -1
  22. package/dist/meta.cjs.json +470 -808
  23. package/dist/meta.esbuild.json +549 -821
  24. package/package.json +9 -16
  25. package/src/Modal/DialogContent.tsx +1 -1
  26. package/src/Prebuilt/common/constants.ts +2 -0
  27. package/src/Prebuilt/components/AppData/useSidepaneResetOnLayoutUpdate.tsx +22 -0
  28. package/src/Prebuilt/components/AudioVideoToggle.tsx +1 -1
  29. package/src/Prebuilt/components/Chat/Chat.tsx +3 -4
  30. package/src/Prebuilt/components/Footer/ParticipantList.tsx +56 -37
  31. package/src/Prebuilt/components/MoreSettings/SplitComponents/DesktopOptions.tsx +2 -2
  32. package/src/Prebuilt/components/PIP/{PIPComponent.jsx → PIPComponent.tsx} +31 -18
  33. package/src/Prebuilt/components/PIP/{PIPManager.js → PIPManager.ts} +60 -35
  34. package/src/Prebuilt/components/RoleChangeModal.tsx +187 -0
  35. package/src/Prebuilt/components/SecondaryTiles.tsx +7 -0
  36. package/src/Prebuilt/components/TileMenu/TileMenu.tsx +5 -0
  37. package/src/Prebuilt/components/TileMenu/TileMenuContent.tsx +23 -1
  38. package/src/Prebuilt/components/VideoLayouts/ScreenshareLayout.tsx +0 -2
  39. package/src/Prebuilt/components/VideoLayouts/WhiteboardLayout.tsx +7 -13
  40. package/src/Prebuilt/components/VirtualBackground/VBPicker.tsx +3 -0
  41. package/src/Prebuilt/layouts/PDFView.jsx +6 -1
  42. package/src/Prebuilt/layouts/SidePane.tsx +9 -9
  43. package/src/Prebuilt/plugins/FlyingEmoji.jsx +2 -4
  44. package/dist/chunk-5KG27WWA.js.map +0 -7
  45. package/src/Prebuilt/components/RoleChangeModal.jsx +0 -185
@@ -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
+ };
@@ -42,6 +42,13 @@ export const SecondaryTiles = ({ peers, onPageChange, onPageSize, edgeToEdge, ha
42
42
  });
43
43
  const pageSize = pagesWithTiles[0]?.length || 0;
44
44
 
45
+ // Handles final peer leaving from the last page
46
+ useEffect(() => {
47
+ if (peers.length > 0 && !pagesWithTiles[page]?.length) {
48
+ setPage(Math.max(0, page - 1));
49
+ }
50
+ }, [peers, page, pagesWithTiles]);
51
+
45
52
  useEffect(() => {
46
53
  if (pageSize > 0) {
47
54
  onPageSize?.(pageSize);
@@ -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}>
@@ -55,8 +55,6 @@ export const ScreenshareLayout = ({ peers, onPageChange, onPageSize, edgeToEdge
55
55
  };
56
56
  }, [activeSharePeer?.id, isMobile, setActiveScreenSharePeer]);
57
57
 
58
- console.log({ activeSharePeer, secondaryPeers });
59
-
60
58
  return (
61
59
  <ProminenceLayout.Root edgeToEdge={edgeToEdge} hasSidebar={hasSidebar}>
62
60
  <ProminenceLayout.ProminentSection>
@@ -1,5 +1,6 @@
1
1
  import React, { useEffect, useMemo } from 'react';
2
2
  import { useMedia } from 'react-use';
3
+ import { Whiteboard } from '@100mslive/hms-whiteboard';
3
4
  import { selectPeerByCondition, selectWhiteboard, useHMSStore, useWhiteboard } from '@100mslive/react-sdk';
4
5
  import { Box } from '../../../Layout';
5
6
  import { config as cssConfig } from '../../../Theme';
@@ -11,10 +12,12 @@ import { ProminenceLayout } from './ProminenceLayout';
11
12
  // @ts-ignore: No implicit Any
12
13
  import { useSetUiSettings } from '../AppData/useUISettings';
13
14
  import { UI_SETTINGS } from '../../common/constants';
15
+ // eslint-disable-next-line import/no-unresolved
16
+ import '@100mslive/hms-whiteboard/index.css';
14
17
 
15
18
  const WhiteboardEmbed = () => {
16
19
  const isMobile = useMedia(cssConfig.media.md);
17
- const { iframeRef } = useWhiteboard(isMobile);
20
+ const { token, endpoint, zoomToContent } = useWhiteboard(isMobile);
18
21
 
19
22
  return (
20
23
  <Box
@@ -28,18 +31,9 @@ const WhiteboardEmbed = () => {
28
31
  },
29
32
  }}
30
33
  >
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
- />
34
+ <Box css={{ size: '100%' }}>
35
+ <Whiteboard token={token} endpoint={`https://${endpoint}`} zoomToContent={zoomToContent} />
36
+ </Box>
43
37
  </Box>
44
38
  );
45
39
  };
@@ -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' }}>
@@ -11,10 +11,15 @@ import { usePDFConfig, useResetPDFConfig } from '../components/AppData/useUISett
11
11
  export const PDFView = () => {
12
12
  const pdfConfig = usePDFConfig();
13
13
  const resetConfig = useResetPDFConfig();
14
-
15
14
  // need to send resetConfig to clear configuration, if stop screenshare occurs.
16
15
  const { iframeRef, startPDFShare, isPDFShareInProgress } = usePDFShare(resetConfig);
17
16
 
17
+ useEffect(() => {
18
+ // no working in other useEffect, as return is called multiple time on state change
19
+ return () => {
20
+ resetConfig();
21
+ };
22
+ }, []);
18
23
  useEffect(() => {
19
24
  (async () => {
20
25
  try {
@@ -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
 
@@ -119,12 +119,10 @@ export function FlyingEmoji() {
119
119
  emoji.wiggleType === 0 ? wiggleLeftRight() : wiggleRightLeft()
120
120
  } 1s ease-in-out infinite alternate`,
121
121
  }}
122
- onAnimationEnd={() => {
123
- setEmojis(emojis.filter(item => item.id !== emoji.id));
124
- }}
122
+ onAnimationEnd={() => setEmojis(emojis.filter(item => item.id !== emoji.id))}
125
123
  >
126
124
  <Box>
127
- <em-emoji id={emoji.emojiId} size="48px" set="apple"></em-emoji>
125
+ <em-emoji id={emoji.emojiId} size="48px" set="apple" />
128
126
  </Box>
129
127
  {emoji.senderName ? (
130
128
  <Box