@100mslive/roomkit-react 0.1.4-alpha.1 → 0.1.5

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 (152) hide show
  1. package/dist/{HLSView-F2K5VSTS.js → HLSView-P57IRMAR.js} +7 -11
  2. package/dist/{HLSView-F2K5VSTS.js.map → HLSView-P57IRMAR.js.map} +1 -1
  3. package/dist/PinnedTrackView-4FYJEBTB.js +102 -0
  4. package/dist/PinnedTrackView-4FYJEBTB.js.map +7 -0
  5. package/dist/Popover/index.d.ts +1 -0
  6. package/dist/Prebuilt/App.d.ts +25 -0
  7. package/dist/Prebuilt/index.d.ts +1 -0
  8. package/dist/Prebuilt/provider/roomLayoutProvider/index.d.ts +1 -1
  9. package/dist/Sheet/Sheet.d.ts +3093 -0
  10. package/dist/Sheet/index.d.ts +1 -0
  11. package/dist/Theme/ThemeProvider.d.ts +4 -286
  12. package/dist/Theme/stitches.config.d.ts +1 -1
  13. package/dist/{VirtualBackground-S3XEPZ2T.js → VirtualBackground-GGCQJ5JM.js} +31 -7
  14. package/dist/VirtualBackground-GGCQJ5JM.js.map +7 -0
  15. package/dist/chunk-IVTWKQI3.js +827 -0
  16. package/dist/chunk-IVTWKQI3.js.map +7 -0
  17. package/dist/{chunk-42SWPN2C.js → chunk-OSM4QEQG.js} +3020 -2189
  18. package/dist/chunk-OSM4QEQG.js.map +7 -0
  19. package/dist/chunk-P5X32KOD.js +67 -0
  20. package/dist/chunk-P5X32KOD.js.map +7 -0
  21. package/dist/chunk-RVCZPPTL.js +1100 -0
  22. package/dist/chunk-RVCZPPTL.js.map +7 -0
  23. package/dist/{chunk-ESUJK7AT.js → conference-P6I6ESVF.js} +3136 -653
  24. package/dist/conference-P6I6ESVF.js.map +7 -0
  25. package/dist/index.cjs.js +15733 -15498
  26. package/dist/index.cjs.js.map +4 -4
  27. package/dist/index.js +4 -8
  28. package/dist/meta.cjs.json +3355 -3017
  29. package/dist/meta.esbuild.json +3534 -3329
  30. package/dist/utils/animations.d.ts +16 -0
  31. package/package.json +8 -10
  32. package/src/Button/Button.tsx +4 -4
  33. package/src/Dropdown/Dropdown.tsx +2 -2
  34. package/src/IconButton/IconButton.tsx +4 -2
  35. package/src/Pagination/StyledPagination.tsx +1 -0
  36. package/src/Popover/index.tsx +2 -1
  37. package/src/Prebuilt/{App.jsx → App.tsx} +95 -48
  38. package/src/Prebuilt/Prebuilt.stories.tsx +22 -8
  39. package/src/Prebuilt/common/constants.js +1 -2
  40. package/src/Prebuilt/common/hooks.js +8 -0
  41. package/src/Prebuilt/common/utils.js +15 -0
  42. package/src/Prebuilt/components/AppData/AppData.jsx +1 -2
  43. package/src/Prebuilt/components/AppData/useUISettings.js +0 -5
  44. package/src/Prebuilt/components/AudioVideoToggle.jsx +69 -26
  45. package/src/Prebuilt/components/AuthToken.jsx +3 -2
  46. package/src/Prebuilt/components/Chat/ChatSelector.jsx +1 -1
  47. package/src/Prebuilt/components/Connection/TileConnection.jsx +0 -1
  48. package/src/Prebuilt/components/EmojiReaction.jsx +23 -73
  49. package/src/Prebuilt/components/EndSessionContent.jsx +57 -0
  50. package/src/Prebuilt/components/EqualProminence.jsx +180 -0
  51. package/src/Prebuilt/components/ErrorBoundary.jsx +4 -10
  52. package/src/Prebuilt/components/Footer/EmojiCard.jsx +34 -0
  53. package/src/Prebuilt/components/Footer/Footer.jsx +73 -0
  54. package/src/Prebuilt/components/{Header → Footer}/ParticipantList.jsx +5 -5
  55. package/src/Prebuilt/components/Header/ConferencingHeader.jsx +27 -7
  56. package/src/Prebuilt/components/Header/HeaderComponents.jsx +16 -14
  57. package/src/Prebuilt/components/Header/StreamActions.jsx +101 -36
  58. package/src/Prebuilt/components/Header/StreamingHeader.jsx +1 -1
  59. package/src/Prebuilt/components/Header/common.jsx +164 -0
  60. package/src/Prebuilt/components/IconButtonWithOptions/IconButtonWithOptions.jsx +1 -2
  61. package/src/Prebuilt/components/LeaveCard.jsx +19 -0
  62. package/src/Prebuilt/components/LeaveRoom.jsx +35 -143
  63. package/src/Prebuilt/components/LeaveSessionContent.jsx +45 -0
  64. package/src/Prebuilt/components/MoreSettings/ActionTile.jsx +55 -0
  65. package/src/Prebuilt/components/MoreSettings/ChangeNameContent.jsx +96 -0
  66. package/src/Prebuilt/components/MoreSettings/ChangeNameModal.jsx +31 -54
  67. package/src/Prebuilt/components/MoreSettings/EmbedUrl.jsx +48 -73
  68. package/src/Prebuilt/components/MoreSettings/MoreSettings.jsx +5 -221
  69. package/src/Prebuilt/components/MoreSettings/MuteAllContent.jsx +61 -0
  70. package/src/Prebuilt/components/MoreSettings/MuteAllModal.jsx +32 -49
  71. package/src/Prebuilt/components/MoreSettings/SplitComponents/DesktopLeaveRoom.jsx +129 -0
  72. package/src/Prebuilt/components/MoreSettings/SplitComponents/DesktopOptions.jsx +219 -0
  73. package/src/Prebuilt/components/MoreSettings/SplitComponents/MwebLeaveRoom.jsx +100 -0
  74. package/src/Prebuilt/components/MoreSettings/SplitComponents/MwebOptions.jsx +259 -0
  75. package/src/Prebuilt/components/Notifications/Notifications.jsx +0 -2
  76. package/src/Prebuilt/components/Notifications/ReconnectNotifications.jsx +0 -4
  77. package/src/Prebuilt/components/PIP/PIPComponent.jsx +30 -26
  78. package/src/Prebuilt/components/PIP/PIPManager.js +13 -0
  79. package/src/Prebuilt/components/PIP/index.jsx +2 -7
  80. package/src/Prebuilt/components/Pagination.jsx +4 -4
  81. package/src/Prebuilt/components/Preview/PreviewContainer.jsx +5 -13
  82. package/src/Prebuilt/components/Preview/PreviewForm.jsx +9 -5
  83. package/src/Prebuilt/components/Preview/PreviewJoin.jsx +20 -27
  84. package/src/Prebuilt/components/RaiseHand.jsx +27 -0
  85. package/src/Prebuilt/components/ScreenShare.jsx +1 -1
  86. package/src/Prebuilt/components/ScreenshareDisplay.jsx +2 -2
  87. package/src/Prebuilt/components/ScreenshareTile.jsx +2 -2
  88. package/src/Prebuilt/components/Settings/DeviceSettings.jsx +2 -1
  89. package/src/Prebuilt/components/Settings/LayoutSettings.jsx +1 -24
  90. package/src/Prebuilt/components/Settings/SettingsModal.jsx +152 -17
  91. package/src/Prebuilt/components/ShareMenuIcon.jsx +1 -0
  92. package/src/Prebuilt/components/TileMenu/TileMenu.jsx +133 -0
  93. package/src/Prebuilt/components/TileMenu/TileMenuContent.jsx +313 -0
  94. package/src/Prebuilt/components/VideoList.jsx +5 -33
  95. package/src/Prebuilt/components/VideoTile.jsx +30 -8
  96. package/src/Prebuilt/components/conference.jsx +14 -1
  97. package/src/Prebuilt/components/init/Init.jsx +0 -27
  98. package/src/Prebuilt/components/init/initUtils.js +0 -23
  99. package/src/Prebuilt/components/pdfAnnotator/pdfFileOptions.jsx +2 -1
  100. package/src/Prebuilt/components/pdfAnnotator/pdfInfo.jsx +1 -1
  101. package/src/Prebuilt/components/pdfAnnotator/shareScreenOptions.jsx +19 -8
  102. package/src/Prebuilt/components/pdfAnnotator/uploadedFile.jsx +1 -0
  103. package/src/Prebuilt/images/pdf-share.png +0 -0
  104. package/src/Prebuilt/images/screen-share.png +0 -0
  105. package/src/Prebuilt/index.ts +1 -0
  106. package/src/Prebuilt/layouts/EmbedView.jsx +0 -1
  107. package/src/Prebuilt/layouts/InsetView.jsx +65 -24
  108. package/src/Prebuilt/layouts/PDFView.jsx +0 -1
  109. package/src/Prebuilt/layouts/SidePane.jsx +8 -7
  110. package/src/Prebuilt/layouts/mainView.jsx +22 -31
  111. package/src/Prebuilt/layouts/screenShareView.jsx +0 -2
  112. package/src/Prebuilt/plugins/VirtualBackground/VirtualBackground.jsx +25 -1
  113. package/src/Prebuilt/primitives/DialogContent.jsx +1 -1
  114. package/src/Prebuilt/provider/roomLayoutProvider/index.tsx +1 -1
  115. package/src/Sheet/Sheet.mdx +19 -0
  116. package/src/Sheet/Sheet.stories.tsx +103 -0
  117. package/src/Sheet/Sheet.tsx +118 -0
  118. package/src/Sheet/index.ts +1 -0
  119. package/src/Theme/ThemeProvider.tsx +10 -13
  120. package/src/Theme/base.config.ts +1 -1
  121. package/src/Theme/stitches.config.ts +1 -1
  122. package/src/TileMenu/StyledMenuTile.tsx +2 -2
  123. package/src/TileMenu/TileMenu.tsx +2 -0
  124. package/src/VideoTile/StyledVideoTile.tsx +5 -0
  125. package/src/utils/animations.ts +18 -0
  126. package/dist/ActiveSpeakerView-V6O4K3BV.js +0 -39
  127. package/dist/ActiveSpeakerView-V6O4K3BV.js.map +0 -7
  128. package/dist/PinnedTrackView-7YQG4QKC.js +0 -70
  129. package/dist/PinnedTrackView-7YQG4QKC.js.map +0 -7
  130. package/dist/VirtualBackground-S3XEPZ2T.js.map +0 -7
  131. package/dist/chunk-42SWPN2C.js.map +0 -7
  132. package/dist/chunk-4NEZLVVH.js +0 -811
  133. package/dist/chunk-4NEZLVVH.js.map +0 -7
  134. package/dist/chunk-4ZBEFSRC.js +0 -58
  135. package/dist/chunk-4ZBEFSRC.js.map +0 -7
  136. package/dist/chunk-ESUJK7AT.js.map +0 -7
  137. package/dist/chunk-R6PDR5WZ.js +0 -243
  138. package/dist/chunk-R6PDR5WZ.js.map +0 -7
  139. package/dist/conference-7QKOMJPP.js +0 -3697
  140. package/dist/conference-7QKOMJPP.js.map +0 -7
  141. package/dist/transcription-RJA4V6PC.js +0 -356
  142. package/dist/transcription-RJA4V6PC.js.map +0 -7
  143. package/src/Prebuilt/common/useSortedPeers.js +0 -28
  144. package/src/Prebuilt/components/BottomActionSheet/BottomActionSheet.jsx +0 -96
  145. package/src/Prebuilt/components/BottomActionSheet/BottomActionSheet.stories.tsx +0 -46
  146. package/src/Prebuilt/components/Footer/ConferencingFooter.jsx +0 -101
  147. package/src/Prebuilt/components/Footer/StreamingFooter.jsx +0 -71
  148. package/src/Prebuilt/components/Footer.jsx +0 -8
  149. package/src/Prebuilt/components/MoreSettings/ChangeSelfRole.jsx +0 -67
  150. package/src/Prebuilt/components/TileMenu.jsx +0 -268
  151. package/src/Prebuilt/index.d.ts +0 -20
  152. package/src/Prebuilt/index.js +0 -2
@@ -2,11 +2,18 @@ import React, { useCallback, useEffect, useState } from 'react';
2
2
  import { useMedia } from 'react-use';
3
3
  import { selectLocalPeerRoleName, useHMSStore } from '@100mslive/react-sdk';
4
4
  import { ChevronLeftIcon, CrossIcon } from '@100mslive/react-icons';
5
- import { Box, config as cssConfig, Dialog, Flex, IconButton, Tabs, Text } from '../../../';
5
+ import { HorizontalDivider } from '../../../Divider';
6
+ import { IconButton } from '../../../IconButton';
7
+ import { Box, Flex } from '../../../Layout';
8
+ import { Dialog } from '../../../Modal';
9
+ import { Sheet } from '../../../Sheet';
10
+ import { Tabs } from '../../../Tabs';
11
+ import { Text } from '../../../Text';
12
+ import { config as cssConfig } from '../../../Theme';
6
13
  import { useHLSViewerRole } from '../AppData/useUISettings';
7
14
  import { settingContent, settingsList } from './common.js';
8
15
 
9
- const SettingsModal = ({ open, onOpenChange, children }) => {
16
+ const SettingsModal = ({ open, onOpenChange, children = <></> }) => {
10
17
  const mediaQueryLg = cssConfig.media.md;
11
18
  const isMobile = useMedia(mediaQueryLg);
12
19
 
@@ -43,6 +50,145 @@ const SettingsModal = ({ open, onOpenChange, children }) => {
43
50
  }
44
51
  }, [isMobile, showSetting]);
45
52
 
53
+ return isMobile ? (
54
+ <MobileSettingModal
55
+ open={open}
56
+ onOpenChange={onOpenChange}
57
+ selection={selection}
58
+ setSelection={setSelection}
59
+ showSetting={showSetting}
60
+ hideSettingByTabName={hideSettingByTabName}
61
+ resetSelection={resetSelection}
62
+ children={children}
63
+ />
64
+ ) : (
65
+ <DesktopSettingModal
66
+ open={open}
67
+ onOpenChange={onOpenChange}
68
+ selection={selection}
69
+ setSelection={setSelection}
70
+ showSetting={showSetting}
71
+ hideSettingByTabName={hideSettingByTabName}
72
+ resetSelection={resetSelection}
73
+ children={children}
74
+ />
75
+ );
76
+ };
77
+
78
+ const MobileSettingModal = ({
79
+ open,
80
+ onOpenChange,
81
+ selection,
82
+ setSelection,
83
+ showSetting,
84
+ hideSettingByTabName,
85
+ resetSelection,
86
+ children = <></>,
87
+ }) => {
88
+ return (
89
+ <Sheet.Root open={open} onOpenChange={onOpenChange}>
90
+ <Sheet.Trigger asChild>{children}</Sheet.Trigger>
91
+ <Sheet.Content
92
+ css={{
93
+ bg: '$surface_dim',
94
+ overflowY: 'auto',
95
+ }}
96
+ >
97
+ <Sheet.Title css={{ py: '$10', px: '$8', alignItems: 'center' }}>
98
+ <Flex direction="row" justify="between" css={{ w: '100%' }}>
99
+ {!selection ? (
100
+ <Text variant="h6" css={{ display: 'flex' }}>
101
+ Settings
102
+ </Text>
103
+ ) : (
104
+ <Text variant="h6" css={{ display: 'flex' }}>
105
+ <Box as="span" css={{ r: '$round', mr: '$2' }} onClick={resetSelection}>
106
+ <ChevronLeftIcon />
107
+ </Box>
108
+ {selection?.charAt(0).toUpperCase() + selection.slice(1)}
109
+ </Text>
110
+ )}
111
+ <Sheet.Close>
112
+ <IconButton as="div" data-testid="dialog_cross_icon">
113
+ <CrossIcon />
114
+ </IconButton>
115
+ </Sheet.Close>
116
+ </Flex>
117
+ </Sheet.Title>
118
+ <HorizontalDivider />
119
+ {!selection ? (
120
+ <Flex
121
+ direction="column"
122
+ css={{
123
+ px: '$8',
124
+ pb: '$8',
125
+ overflowY: 'auto',
126
+ }}
127
+ >
128
+ {settingsList
129
+ .filter(({ tabName }) => showSetting[tabName])
130
+ .map(({ icon: Icon, tabName, title }) => {
131
+ return (
132
+ <Box
133
+ key={tabName}
134
+ value={tabName}
135
+ onClick={() => {
136
+ setSelection(tabName);
137
+ }}
138
+ as="div"
139
+ css={{
140
+ all: 'unset',
141
+ fontFamily: '$sans',
142
+ py: '$10',
143
+ display: 'flex',
144
+ alignItems: 'center',
145
+ fontSize: '$sm',
146
+ lineHeight: '$sm',
147
+ color: '$on_surface_high',
148
+ userSelect: 'none',
149
+ gap: '$8',
150
+ cursor: 'pointer',
151
+ '&:hover': {
152
+ bg: '$surface_brighter',
153
+ r: '$1',
154
+ gap: '$8',
155
+ border: 'none',
156
+ },
157
+ borderBottom: '1px solid $border_default',
158
+ }}
159
+ >
160
+ <Icon />
161
+ {title}
162
+ </Box>
163
+ );
164
+ })}
165
+ </Flex>
166
+ ) : (
167
+ <Box
168
+ direction="column"
169
+ css={{ overflowY: 'scroll', px: '$8', py: '$10', maxHeight: '70vh', overflowX: 'hidden' }}
170
+ >
171
+ {settingsList
172
+ .filter(({ tabName }) => showSetting[tabName] && selection === tabName)
173
+ .map(({ content: Content, title, tabName }) => {
174
+ return <Content key={title} setHide={hideSettingByTabName(tabName)} />;
175
+ })}
176
+ </Box>
177
+ )}
178
+ </Sheet.Content>
179
+ </Sheet.Root>
180
+ );
181
+ };
182
+ const DesktopSettingModal = ({
183
+ open,
184
+ onOpenChange,
185
+ selection,
186
+ setSelection,
187
+ showSetting,
188
+ hideSettingByTabName,
189
+ resetSelection,
190
+ children = <></>,
191
+ }) => {
46
192
  return (
47
193
  <Dialog.Root open={open} onOpenChange={onOpenChange}>
48
194
  <Dialog.Trigger asChild>{children}</Dialog.Trigger>
@@ -58,13 +204,13 @@ const SettingsModal = ({ open, onOpenChange, children }) => {
58
204
  >
59
205
  <Tabs.Root
60
206
  value={selection}
61
- activationMode={isMobile ? 'manual' : 'automatic'}
207
+ activationMode="automatic"
62
208
  onValueChange={setSelection}
63
209
  css={{ size: '100%', position: 'relative' }}
64
210
  >
65
211
  <Tabs.List
66
212
  css={{
67
- w: isMobile ? '100%' : '18.625rem',
213
+ w: '18.625rem',
68
214
  flexDirection: 'column',
69
215
  bg: '$background_default',
70
216
  p: '$14 $10',
@@ -73,7 +219,7 @@ const SettingsModal = ({ open, onOpenChange, children }) => {
73
219
  }}
74
220
  >
75
221
  <Text variant="h5">Settings </Text>
76
- <Flex direction="column" css={{ mx: isMobile ? '-$8' : 0, overflowY: 'auto', pt: '$10' }}>
222
+ <Flex direction="column" css={{ mx: 0, overflowY: 'auto', pt: '$10' }}>
77
223
  {settingsList
78
224
  .filter(({ tabName }) => showSetting[tabName])
79
225
  .map(({ icon: Icon, tabName, title }) => {
@@ -93,16 +239,6 @@ const SettingsModal = ({ open, onOpenChange, children }) => {
93
239
  flex: '1 1 0',
94
240
  minWidth: 0,
95
241
  mr: '$4',
96
- ...(isMobile
97
- ? {
98
- position: 'absolute',
99
- left: 0,
100
- right: 0,
101
- bg: '$surface_default',
102
- width: '100%',
103
- height: '100%',
104
- }
105
- : {}),
106
242
  }}
107
243
  >
108
244
  {settingsList
@@ -110,7 +246,7 @@ const SettingsModal = ({ open, onOpenChange, children }) => {
110
246
  .map(({ content: Content, title, tabName }) => {
111
247
  return (
112
248
  <Tabs.Content key={tabName} value={tabName} className={settingContent()}>
113
- <SettingsContentHeader onBack={resetSelection} isMobile={isMobile}>
249
+ <SettingsContentHeader onBack={resetSelection} isMobile={false}>
114
250
  {title}
115
251
  </SettingsContentHeader>
116
252
  <Content setHide={hideSettingByTabName(tabName)} />
@@ -130,7 +266,6 @@ const SettingsModal = ({ open, onOpenChange, children }) => {
130
266
  </Dialog.Root>
131
267
  );
132
268
  };
133
-
134
269
  const SettingsContentHeader = ({ children, isMobile, onBack }) => {
135
270
  return (
136
271
  <Text variant="h6" css={{ mb: '$12', display: 'flex', alignItems: 'center' }}>
@@ -21,6 +21,7 @@ export const ShareMenuIcon = styled(ScreenShareButton, {
21
21
  borderLeftWidth: 0,
22
22
  w: '$4',
23
23
  '@md': {
24
+ w: 'unset',
24
25
  px: '$2',
25
26
  },
26
27
  });
@@ -0,0 +1,133 @@
1
+ import React, { useState } from 'react';
2
+ import { useMedia } from 'react-use';
3
+ import {
4
+ selectLocalPeerID,
5
+ selectPeerByID,
6
+ selectPermissions,
7
+ selectTemplateAppData,
8
+ selectTrackByID,
9
+ selectVideoTrackByPeerID,
10
+ useHMSStore,
11
+ useRemoteAVToggle,
12
+ } from '@100mslive/react-sdk';
13
+ import { CrossIcon, VerticalMenuIcon } from '@100mslive/react-icons';
14
+ import { Box, Flex } from '../../../Layout';
15
+ import { Sheet } from '../../../Sheet';
16
+ import { Text } from '../../../Text';
17
+ import { config as cssConfig, useTheme } from '../../../Theme';
18
+ import { StyledMenuTile } from '../../../TileMenu';
19
+ import { ChangeNameModal } from '../MoreSettings/ChangeNameModal';
20
+ import { TileMenuContent } from './TileMenuContent';
21
+ import { useDropdownList } from '../hooks/useDropdownList';
22
+ import { useIsFeatureEnabled } from '../hooks/useFeatures';
23
+ import { FEATURE_LIST } from '../../common/constants';
24
+
25
+ /**
26
+ * Taking peerID as peer won't necesarilly have tracks
27
+ */
28
+ const TileMenu = ({ audioTrackID, videoTrackID, peerID, isScreenshare = false, canMinimise }) => {
29
+ const [open, setOpen] = useState(false);
30
+ const { theme } = useTheme();
31
+
32
+ const localPeerID = useHMSStore(selectLocalPeerID);
33
+ const isLocal = localPeerID === peerID;
34
+ const { removeOthers, changeRole } = useHMSStore(selectPermissions);
35
+ const { setVolume, toggleAudio, toggleVideo } = useRemoteAVToggle(audioTrackID, videoTrackID);
36
+ const showSpotlight = changeRole;
37
+
38
+ const isPrimaryVideoTrack = useHMSStore(selectVideoTrackByPeerID(peerID))?.id === videoTrackID;
39
+ const uiMode = useHMSStore(selectTemplateAppData).uiMode;
40
+ const isInset = uiMode === 'inset';
41
+
42
+ const isPinEnabled = useIsFeatureEnabled(FEATURE_LIST.PIN_TILE);
43
+ const showPinAction = isPinEnabled && (audioTrackID || (videoTrackID && isPrimaryVideoTrack)) && !isInset;
44
+
45
+ const track = useHMSStore(selectTrackByID(videoTrackID));
46
+ const hideSimulcastLayers = !track?.layerDefinitions?.length || track.degraded || !track.enabled;
47
+ const isMobile = useMedia(cssConfig.media.md);
48
+ const peer = useHMSStore(selectPeerByID(peerID));
49
+ const [showNameChangeModal, setShowNameChangeModal] = useState(false);
50
+ useDropdownList({ open, name: 'TileMenu' });
51
+
52
+ if (!(removeOthers || toggleAudio || toggleVideo || setVolume || showPinAction) && hideSimulcastLayers) {
53
+ return null;
54
+ }
55
+
56
+ if (isInset && isLocal) {
57
+ return null;
58
+ }
59
+ const openNameChangeModal = () => setShowNameChangeModal(true);
60
+
61
+ const props = {
62
+ isLocal,
63
+ isScreenshare,
64
+ audioTrackID,
65
+ videoTrackID,
66
+ peerID,
67
+ isPrimaryVideoTrack,
68
+ showSpotlight,
69
+ showPinAction,
70
+ canMinimise,
71
+ openNameChangeModal,
72
+ };
73
+
74
+ return (
75
+ <>
76
+ <StyledMenuTile.Root open={open} onOpenChange={setOpen}>
77
+ <StyledMenuTile.Trigger
78
+ data-testid="participant_menu_btn"
79
+ css={{ bg: `${theme.colors.background_dim.value}A3` }}
80
+ onClick={e => e.stopPropagation()}
81
+ className={isMobile ? '__cancel-drag-event' : ''}
82
+ >
83
+ <VerticalMenuIcon width={20} height={20} />
84
+ </StyledMenuTile.Trigger>
85
+
86
+ {isMobile ? (
87
+ <Sheet.Root open={open} onOpenChange={setOpen}>
88
+ <Sheet.Content css={{ bg: '$surface_dim', pt: '$8' }}>
89
+ <Flex
90
+ css={{
91
+ color: '$on_surface_high',
92
+ display: 'flex',
93
+ w: '100%',
94
+ justifyContent: 'space-between',
95
+ alignItems: 'center',
96
+ px: '$10',
97
+ pb: '$8',
98
+ borderBottom: '1px solid $border_default',
99
+ }}
100
+ >
101
+ <Box>
102
+ <Text css={{ color: '$on_surface_high', fontWeight: '$semiBold' }}>
103
+ {peer.name}
104
+ {isLocal ? ` (You)` : null}
105
+ </Text>
106
+ {peer?.roleName ? (
107
+ <Text variant="xs" css={{ color: '$on_surface_low', mt: '$2' }}>
108
+ {peer.roleName}
109
+ </Text>
110
+ ) : null}
111
+ </Box>
112
+
113
+ <Sheet.Close css={{ color: 'inherit' }}>
114
+ <CrossIcon />
115
+ </Sheet.Close>
116
+ </Flex>
117
+ <Box css={{ px: '$8', pb: '$10' }}>
118
+ <TileMenuContent {...props} closeSheetOnClick={() => setOpen(false)} />
119
+ </Box>
120
+ </Sheet.Content>
121
+ </Sheet.Root>
122
+ ) : (
123
+ <StyledMenuTile.Content side="top" align="end">
124
+ <TileMenuContent {...props} />
125
+ </StyledMenuTile.Content>
126
+ )}
127
+ </StyledMenuTile.Root>
128
+ {showNameChangeModal && <ChangeNameModal onOpenChange={setShowNameChangeModal} />}
129
+ </>
130
+ );
131
+ };
132
+
133
+ export default TileMenu;
@@ -0,0 +1,313 @@
1
+ import React, { Fragment } from 'react';
2
+ import {
3
+ selectPermissions,
4
+ selectSessionStore,
5
+ selectTrackByID,
6
+ useCustomEvent,
7
+ useHMSActions,
8
+ useHMSStore,
9
+ useRemoteAVToggle,
10
+ } from '@100mslive/react-sdk';
11
+ import {
12
+ MicOffIcon,
13
+ MicOnIcon,
14
+ PencilIcon,
15
+ PinIcon,
16
+ RemoveUserIcon,
17
+ ShareScreenIcon,
18
+ ShrinkIcon,
19
+ SpeakerIcon,
20
+ StarIcon,
21
+ VideoOffIcon,
22
+ VideoOnIcon,
23
+ } from '@100mslive/react-icons';
24
+ import { Box, Flex } from '../../../Layout';
25
+ import { Slider } from '../../../Slider';
26
+ import { Text } from '../../../Text';
27
+ import { StyledMenuTile } from '../../../TileMenu';
28
+ import { ToastManager } from '../Toast/ToastManager';
29
+ import { useSetAppDataByKey } from '../AppData/useUISettings';
30
+ import { useDropdownSelection } from '../hooks/useDropdownSelection';
31
+ import { useIsFeatureEnabled } from '../hooks/useFeatures';
32
+ import { APP_DATA, FEATURE_LIST, REMOTE_STOP_SCREENSHARE_TYPE, SESSION_STORE_KEY } from '../../common/constants';
33
+
34
+ const isSameTile = ({ trackId, videoTrackID, audioTrackID }) =>
35
+ trackId && ((videoTrackID && videoTrackID === trackId) || (audioTrackID && audioTrackID === trackId));
36
+
37
+ const spacingCSS = { '@md': { my: '$8', fontWeight: '$semiBold', fontSize: 'sm' } };
38
+
39
+ const SpotlightActions = ({
40
+ peerId,
41
+ onSpotLightClick = () => {
42
+ return;
43
+ },
44
+ }) => {
45
+ const hmsActions = useHMSActions();
46
+ const spotlightPeerId = useHMSStore(selectSessionStore(SESSION_STORE_KEY.SPOTLIGHT));
47
+ const isTileSpotlighted = spotlightPeerId === peerId;
48
+
49
+ const setSpotlightPeerId = peer =>
50
+ hmsActions.sessionStore
51
+ .set(SESSION_STORE_KEY.SPOTLIGHT, peer)
52
+ .catch(err => ToastManager.addToast({ title: err.description }));
53
+
54
+ return (
55
+ <StyledMenuTile.ItemButton
56
+ css={spacingCSS}
57
+ onClick={() => {
58
+ if (isTileSpotlighted) {
59
+ setSpotlightPeerId();
60
+ } else {
61
+ setSpotlightPeerId(peerId);
62
+ }
63
+ onSpotLightClick();
64
+ }}
65
+ >
66
+ <StarIcon />
67
+ <span>{isTileSpotlighted ? 'Remove from Spotlight' : 'Spotlight Tile for everyone'}</span>
68
+ </StyledMenuTile.ItemButton>
69
+ );
70
+ };
71
+
72
+ const PinActions = ({ audioTrackID, videoTrackID }) => {
73
+ const [pinnedTrackId, setPinnedTrackId] = useSetAppDataByKey(APP_DATA.pinnedTrackId);
74
+
75
+ const isTilePinned = isSameTile({
76
+ trackId: pinnedTrackId,
77
+ videoTrackID,
78
+ audioTrackID,
79
+ });
80
+
81
+ return (
82
+ <>
83
+ <StyledMenuTile.ItemButton
84
+ css={spacingCSS}
85
+ onClick={() => (isTilePinned ? setPinnedTrackId() : setPinnedTrackId(videoTrackID || audioTrackID))}
86
+ >
87
+ <PinIcon />
88
+ <span>{isTilePinned ? 'Unpin' : 'Pin'} Tile for myself</span>
89
+ </StyledMenuTile.ItemButton>
90
+ </>
91
+ );
92
+ };
93
+
94
+ const MinimiseInset = () => {
95
+ const [minimised, setMinimised] = useSetAppDataByKey(APP_DATA.minimiseInset);
96
+
97
+ return (
98
+ <>
99
+ <StyledMenuTile.ItemButton css={spacingCSS} onClick={() => setMinimised(!minimised)}>
100
+ <ShrinkIcon />
101
+ <span>{minimised ? 'Show' : 'Minimise'} your video</span>
102
+ </StyledMenuTile.ItemButton>
103
+ </>
104
+ );
105
+ };
106
+
107
+ const SimulcastLayers = ({ trackId }) => {
108
+ const track = useHMSStore(selectTrackByID(trackId));
109
+ const actions = useHMSActions();
110
+ const bg = useDropdownSelection();
111
+ if (!track?.layerDefinitions?.length || track.degraded || !track.enabled) {
112
+ return null;
113
+ }
114
+ const currentLayer = track.layerDefinitions.find(layer => layer.layer === track.layer);
115
+ return (
116
+ <Fragment>
117
+ <StyledMenuTile.ItemButton css={{ color: '$on_surface_medium', cursor: 'default' }}>
118
+ Select maximum resolution
119
+ </StyledMenuTile.ItemButton>
120
+ {track.layerDefinitions.map(layer => {
121
+ return (
122
+ <StyledMenuTile.ItemButton
123
+ key={layer.layer}
124
+ onClick={async () => {
125
+ await actions.setPreferredLayer(trackId, layer.layer);
126
+ }}
127
+ css={{
128
+ justifyContent: 'space-between',
129
+ bg: track.preferredLayer === layer.layer ? bg : undefined,
130
+ '&:hover': {
131
+ bg: track.preferredLayer === layer.layer ? bg : undefined,
132
+ },
133
+ }}
134
+ >
135
+ <Text
136
+ as="span"
137
+ css={{
138
+ textTransform: 'capitalize',
139
+ mr: '$2',
140
+ fontWeight: track.preferredLayer === layer.layer ? '$semiBold' : '$regular',
141
+ }}
142
+ >
143
+ {layer.layer}
144
+ </Text>
145
+ <Text as="span" variant="xs" css={{ color: '$on_surface_medium' }}>
146
+ {layer.resolution.width}x{layer.resolution.height}
147
+ </Text>
148
+ </StyledMenuTile.ItemButton>
149
+ );
150
+ })}
151
+ <StyledMenuTile.ItemButton>
152
+ <Text as="span" variant="xs" css={{ color: '$on_surface_medium' }}>
153
+ Currently streaming:
154
+ <Text
155
+ as="span"
156
+ variant="xs"
157
+ css={{
158
+ fontWeight: '$semiBold',
159
+ textTransform: 'capitalize',
160
+ color: '$on_surface_medium',
161
+ ml: '$2',
162
+ }}
163
+ >
164
+ {currentLayer ? (
165
+ <>
166
+ {track.layer} ({currentLayer.resolution.width}x{currentLayer.resolution.height})
167
+ </>
168
+ ) : (
169
+ '-'
170
+ )}
171
+ </Text>
172
+ </Text>
173
+ </StyledMenuTile.ItemButton>
174
+ </Fragment>
175
+ );
176
+ };
177
+
178
+ export const TileMenuContent = props => {
179
+ const actions = useHMSActions();
180
+ const { removeOthers } = useHMSStore(selectPermissions);
181
+ const {
182
+ videoTrackID,
183
+ audioTrackID,
184
+ isLocal,
185
+ isScreenshare,
186
+ showSpotlight,
187
+ showPinAction,
188
+ peerID,
189
+ canMinimise,
190
+ closeSheetOnClick = () => {
191
+ return;
192
+ },
193
+ openNameChangeModal = () => {
194
+ return;
195
+ },
196
+ } = props;
197
+
198
+ const { isAudioEnabled, isVideoEnabled, setVolume, toggleAudio, toggleVideo, volume } = useRemoteAVToggle(
199
+ audioTrackID,
200
+ videoTrackID,
201
+ );
202
+
203
+ const { sendEvent } = useCustomEvent({
204
+ type: REMOTE_STOP_SCREENSHARE_TYPE,
205
+ });
206
+
207
+ const isChangeNameEnabled = useIsFeatureEnabled(FEATURE_LIST.CHANGE_NAME);
208
+
209
+ return isLocal ? (
210
+ (showPinAction || canMinimise) && (
211
+ <>
212
+ {showPinAction && <PinActions audioTrackID={audioTrackID} videoTrackID={videoTrackID} />}
213
+ {showSpotlight && <SpotlightActions peerId={peerID} onSpotLightClick={() => closeSheetOnClick()} />}
214
+ {canMinimise && <MinimiseInset />}
215
+ {isChangeNameEnabled ? (
216
+ <StyledMenuTile.ItemButton
217
+ onClick={() => {
218
+ openNameChangeModal();
219
+ closeSheetOnClick();
220
+ }}
221
+ >
222
+ <PencilIcon />
223
+ <Text variant="sm" css={{ fontWeight: '$semiBold' }}>
224
+ Change Name
225
+ </Text>
226
+ </StyledMenuTile.ItemButton>
227
+ ) : null}
228
+ </>
229
+ )
230
+ ) : (
231
+ <>
232
+ {toggleVideo ? (
233
+ <StyledMenuTile.ItemButton
234
+ css={spacingCSS}
235
+ onClick={() => {
236
+ toggleVideo();
237
+ closeSheetOnClick();
238
+ }}
239
+ data-testid={isVideoEnabled ? 'mute_video_participant_btn' : 'unmute_video_participant_btn'}
240
+ >
241
+ {isVideoEnabled ? <VideoOnIcon /> : <VideoOffIcon />}
242
+ <span>{isVideoEnabled ? 'Mute' : 'Request Unmute'}</span>
243
+ </StyledMenuTile.ItemButton>
244
+ ) : null}
245
+
246
+ {toggleAudio ? (
247
+ <StyledMenuTile.ItemButton
248
+ css={spacingCSS}
249
+ onClick={() => {
250
+ toggleAudio();
251
+ closeSheetOnClick();
252
+ }}
253
+ data-testid={isVideoEnabled ? 'mute_audio_participant_btn' : 'unmute_audio_participant_btn'}
254
+ >
255
+ {isAudioEnabled ? <MicOnIcon /> : <MicOffIcon />}
256
+ <span>{isAudioEnabled ? 'Mute' : 'Request Unmute'}</span>
257
+ </StyledMenuTile.ItemButton>
258
+ ) : null}
259
+
260
+ {audioTrackID ? (
261
+ <StyledMenuTile.VolumeItem data-testid="participant_volume_slider" css={{ ...spacingCSS, mb: '$0' }}>
262
+ <Flex align="center" gap={1}>
263
+ <SpeakerIcon />
264
+ <Box as="span" css={{ ml: '$4' }}>
265
+ Volume ({volume})
266
+ </Box>
267
+ </Flex>
268
+ <Slider css={{ my: '0.5rem' }} step={5} value={[volume]} onValueChange={e => setVolume(e[0])} />
269
+ </StyledMenuTile.VolumeItem>
270
+ ) : null}
271
+
272
+ {showPinAction && (
273
+ <>
274
+ <PinActions audioTrackID={audioTrackID} videoTrackID={videoTrackID} />
275
+ {showSpotlight && <SpotlightActions peerId={peerID} onSpotLightClick={() => closeSheetOnClick()} />}
276
+ </>
277
+ )}
278
+
279
+ <SimulcastLayers trackId={videoTrackID} />
280
+
281
+ {removeOthers ? (
282
+ <StyledMenuTile.RemoveItem
283
+ css={{ ...spacingCSS, borderTop: 'none' }}
284
+ onClick={async () => {
285
+ try {
286
+ await actions.removePeer(peerID, '');
287
+ } catch (error) {
288
+ // TODO: Toast here
289
+ }
290
+ closeSheetOnClick();
291
+ }}
292
+ data-testid="remove_participant_btn"
293
+ >
294
+ <RemoveUserIcon />
295
+ <span>Remove Participant</span>
296
+ </StyledMenuTile.RemoveItem>
297
+ ) : null}
298
+
299
+ {removeOthers && isScreenshare ? (
300
+ <StyledMenuTile.RemoveItem
301
+ onClick={() => {
302
+ sendEvent({});
303
+ closeSheetOnClick();
304
+ }}
305
+ css={spacingCSS}
306
+ >
307
+ <ShareScreenIcon />
308
+ <span>Stop Screenshare</span>
309
+ </StyledMenuTile.RemoveItem>
310
+ ) : null}
311
+ </>
312
+ );
313
+ };