@100mslive/roomkit-react 0.1.4-alpha.0 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (166) hide show
  1. package/dist/{HLSView-T267ZHOS.js → HLSView-CTAJQUU4.js} +7 -11
  2. package/dist/{HLSView-T267ZHOS.js.map → HLSView-CTAJQUU4.js.map} +1 -1
  3. package/dist/PinnedTrackView-CQKONH4O.js +102 -0
  4. package/dist/PinnedTrackView-CQKONH4O.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-BCKXNDTD.js → VirtualBackground-GGGBJYVY.js} +31 -7
  14. package/dist/VirtualBackground-GGGBJYVY.js.map +7 -0
  15. package/dist/chunk-I2FJWE74.js +827 -0
  16. package/dist/chunk-I2FJWE74.js.map +7 -0
  17. package/dist/{chunk-NLZVUXR3.js → chunk-L2SX7GBO.js} +3020 -2189
  18. package/dist/chunk-L2SX7GBO.js.map +7 -0
  19. package/dist/chunk-NOKIGB6Y.js +1100 -0
  20. package/dist/chunk-NOKIGB6Y.js.map +7 -0
  21. package/dist/chunk-TJNDX446.js +67 -0
  22. package/dist/chunk-TJNDX446.js.map +7 -0
  23. package/dist/{chunk-3HMUOAD6.js → conference-OEO7VOJD.js} +3136 -653
  24. package/dist/conference-OEO7VOJD.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 +3248 -3039
  29. package/dist/meta.esbuild.json +3398 -3386
  30. package/dist/utils/animations.d.ts +16 -0
  31. package/package.json +8 -11
  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/WhiteboardView.jsx +2 -4
  111. package/src/Prebuilt/layouts/mainView.jsx +22 -31
  112. package/src/Prebuilt/layouts/screenShareView.jsx +0 -2
  113. package/src/Prebuilt/plugins/VirtualBackground/VirtualBackground.jsx +25 -1
  114. package/src/Prebuilt/plugins/whiteboard/index.js +1 -1
  115. package/src/Prebuilt/primitives/DialogContent.jsx +1 -1
  116. package/src/Prebuilt/provider/roomLayoutProvider/index.tsx +1 -1
  117. package/src/Sheet/Sheet.mdx +19 -0
  118. package/src/Sheet/Sheet.stories.tsx +103 -0
  119. package/src/Sheet/Sheet.tsx +118 -0
  120. package/src/Sheet/index.ts +1 -0
  121. package/src/Theme/ThemeProvider.tsx +10 -13
  122. package/src/Theme/base.config.ts +1 -1
  123. package/src/Theme/stitches.config.ts +1 -1
  124. package/src/TileMenu/StyledMenuTile.tsx +2 -2
  125. package/src/TileMenu/TileMenu.tsx +2 -0
  126. package/src/VideoTile/StyledVideoTile.tsx +5 -0
  127. package/src/utils/animations.ts +18 -0
  128. package/dist/ActiveSpeakerView-AGL73O6U.css +0 -11
  129. package/dist/ActiveSpeakerView-AGL73O6U.css.map +0 -7
  130. package/dist/ActiveSpeakerView-UPFD5RXA.js +0 -39
  131. package/dist/ActiveSpeakerView-UPFD5RXA.js.map +0 -7
  132. package/dist/HLSView-64OG755F.css +0 -11
  133. package/dist/HLSView-64OG755F.css.map +0 -7
  134. package/dist/PinnedTrackView-O4FHHHOV.js +0 -70
  135. package/dist/PinnedTrackView-O4FHHHOV.js.map +0 -7
  136. package/dist/PinnedTrackView-YWAZ2342.css +0 -11
  137. package/dist/PinnedTrackView-YWAZ2342.css.map +0 -7
  138. package/dist/VirtualBackground-BCKXNDTD.js.map +0 -7
  139. package/dist/chunk-3HMUOAD6.js.map +0 -7
  140. package/dist/chunk-6GXDYWD5.js +0 -243
  141. package/dist/chunk-6GXDYWD5.js.map +0 -7
  142. package/dist/chunk-NLZVUXR3.js.map +0 -7
  143. package/dist/chunk-ORPC2GYB.js +0 -58
  144. package/dist/chunk-ORPC2GYB.js.map +0 -7
  145. package/dist/chunk-YE4RPJYG.js +0 -811
  146. package/dist/chunk-YE4RPJYG.js.map +0 -7
  147. package/dist/conference-7KHWJZLG.css +0 -11
  148. package/dist/conference-7KHWJZLG.css.map +0 -7
  149. package/dist/conference-ETISNCLN.js +0 -3697
  150. package/dist/conference-ETISNCLN.js.map +0 -7
  151. package/dist/index.cjs.css +0 -11
  152. package/dist/index.cjs.css.map +0 -7
  153. package/dist/index.css +0 -11
  154. package/dist/index.css.map +0 -7
  155. package/dist/transcription-JJQ4UAIK.js +0 -356
  156. package/dist/transcription-JJQ4UAIK.js.map +0 -7
  157. package/src/Prebuilt/common/useSortedPeers.js +0 -28
  158. package/src/Prebuilt/components/BottomActionSheet/BottomActionSheet.jsx +0 -96
  159. package/src/Prebuilt/components/BottomActionSheet/BottomActionSheet.stories.tsx +0 -46
  160. package/src/Prebuilt/components/Footer/ConferencingFooter.jsx +0 -101
  161. package/src/Prebuilt/components/Footer/StreamingFooter.jsx +0 -71
  162. package/src/Prebuilt/components/Footer.jsx +0 -8
  163. package/src/Prebuilt/components/MoreSettings/ChangeSelfRole.jsx +0 -67
  164. package/src/Prebuilt/components/TileMenu.jsx +0 -268
  165. package/src/Prebuilt/index.d.ts +0 -20
  166. 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
+ };