@100mslive/roomkit-react 0.1.6 → 0.1.7

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 (51) hide show
  1. package/dist/{HLSView-4NSE37G7.js → HLSView-3S74KF3A.js} +23 -4
  2. package/dist/HLSView-3S74KF3A.js.map +7 -0
  3. package/dist/Prebuilt/components/RoleChangeRequest/RequestPrompt.d.ts +9 -0
  4. package/dist/VideoTile/StyledVideoTile.d.ts +445 -3
  5. package/dist/{VirtualBackground-A5UM363O.js → VirtualBackground-3TI5NA4V.js} +3 -3
  6. package/dist/{chunk-BUWIMYLW.js → chunk-36X4ZCLC.js} +2 -2
  7. package/dist/{chunk-NMOZ33TX.js → chunk-5DQ3WTED.js} +3 -2
  8. package/dist/{chunk-NMOZ33TX.js.map → chunk-5DQ3WTED.js.map} +2 -2
  9. package/dist/{chunk-Q6U22HIE.js → chunk-Z7P5WITU.js} +223 -200
  10. package/dist/chunk-Z7P5WITU.js.map +7 -0
  11. package/dist/{conference-S7R3O4OC.js → conference-JNABIZBG.js} +534 -504
  12. package/dist/conference-JNABIZBG.js.map +7 -0
  13. package/dist/index.cjs.js +1011 -924
  14. package/dist/index.cjs.js.map +4 -4
  15. package/dist/index.js +2 -2
  16. package/dist/meta.cjs.json +241 -167
  17. package/dist/meta.esbuild.json +276 -203
  18. package/package.json +6 -6
  19. package/src/Input/Input.tsx +1 -1
  20. package/src/Prebuilt/common/hooks.js +1 -2
  21. package/src/Prebuilt/common/utils.js +7 -2
  22. package/src/Prebuilt/components/Chat/ChatBody.jsx +125 -106
  23. package/src/Prebuilt/components/Chat/ChatFooter.tsx +1 -0
  24. package/src/Prebuilt/components/Footer/Footer.tsx +4 -1
  25. package/src/Prebuilt/components/Footer/ParticipantList.jsx +8 -9
  26. package/src/Prebuilt/components/Header/ParticipantFilter.jsx +9 -12
  27. package/src/Prebuilt/components/Leave/DesktopLeaveRoom.tsx +36 -44
  28. package/src/Prebuilt/components/Leave/MwebLeaveRoom.tsx +23 -35
  29. package/src/Prebuilt/components/MoreSettings/SplitComponents/MwebOptions.tsx +4 -2
  30. package/src/Prebuilt/components/Notifications/Notifications.jsx +14 -1
  31. package/src/Prebuilt/components/Notifications/PeerNotifications.jsx +4 -15
  32. package/src/Prebuilt/components/Preview/PreviewJoin.tsx +12 -5
  33. package/src/Prebuilt/components/RaiseHand.jsx +3 -6
  34. package/src/Prebuilt/components/RoleChangeRequest/RequestPrompt.tsx +66 -0
  35. package/src/Prebuilt/components/RoleChangeRequest/RoleChangeRequestModal.tsx +89 -0
  36. package/src/Prebuilt/components/SidePaneTabs.tsx +21 -2
  37. package/src/Prebuilt/components/VideoTile.jsx +24 -15
  38. package/src/Prebuilt/components/conference.jsx +1 -1
  39. package/src/Prebuilt/components/hooks/useMetadata.jsx +15 -4
  40. package/src/Prebuilt/layouts/HLSView.jsx +20 -1
  41. package/src/Prebuilt/layouts/SidePane.tsx +0 -1
  42. package/src/Tooltip/Tooltip.tsx +1 -1
  43. package/src/VideoTile/StyledVideoTile.tsx +10 -14
  44. package/src/fixtures/peers.ts +5 -3
  45. package/dist/HLSView-4NSE37G7.js.map +0 -7
  46. package/dist/chunk-Q6U22HIE.js.map +0 -7
  47. package/dist/conference-S7R3O4OC.js.map +0 -7
  48. package/src/Prebuilt/components/RoleChangeRequestModal.tsx +0 -120
  49. /package/dist/Prebuilt/components/{RoleChangeRequestModal.d.ts → RoleChangeRequest/RoleChangeRequestModal.d.ts} +0 -0
  50. /package/dist/{VirtualBackground-A5UM363O.js.map → VirtualBackground-3TI5NA4V.js.map} +0 -0
  51. /package/dist/{chunk-BUWIMYLW.js.map → chunk-36X4ZCLC.js.map} +0 -0
@@ -30,6 +30,7 @@ export const MwebLeaveRoom = ({
30
30
  const permissions = useHMSStore(selectPermissions);
31
31
  const { isStreamingOn } = useRecordingStreaming();
32
32
  const showStream = screenType !== 'hls_live_streaming' && isStreamingOn;
33
+ const showLeaveOptions = (permissions?.hlsStreaming && isStreamingOn) || permissions?.endRoom;
33
34
 
34
35
  useDropdownList({ open, name: 'LeaveRoom' });
35
36
 
@@ -39,7 +40,7 @@ export const MwebLeaveRoom = ({
39
40
 
40
41
  return (
41
42
  <Fragment>
42
- {screenType !== 'hls_live_streaming' ? (
43
+ {showLeaveOptions ? (
43
44
  <Sheet.Root open={open} onOpenChange={setOpen}>
44
45
  <Sheet.Trigger asChild>
45
46
  <LeaveIconButton
@@ -70,36 +71,24 @@ export const MwebLeaveRoom = ({
70
71
  css={{ pt: 0, mt: '$10', color: '$on_surface_low', '&:hover': { color: '$on_surface_high' } }}
71
72
  />
72
73
 
73
- {permissions?.endRoom || permissions?.hlsStreaming ? (
74
- <LeaveCard
75
- title={showStream ? 'End Stream' : 'End Session'}
76
- subtitle={`The will end the ${
77
- showStream ? 'stream' : 'session'
78
- } for everyone. You can't undo this action.`}
79
- bg="$alert_error_dim"
80
- titleColor="$alert_error_brighter"
81
- css={{ color: '$alert_error_bright', '&:hover': { color: '$alert_error_brighter' } }}
82
- icon={<StopIcon height={24} width={24} />}
83
- onClick={() => {
84
- setOpen(false);
85
- setShowEndStreamAlert(true);
86
- }}
87
- />
88
- ) : null}
74
+ <LeaveCard
75
+ title={showStream ? 'End Stream' : 'End Session'}
76
+ subtitle={`The will end the ${
77
+ showStream ? 'stream' : 'session'
78
+ } for everyone. You can't undo this action.`}
79
+ bg="$alert_error_dim"
80
+ titleColor="$alert_error_brighter"
81
+ css={{ color: '$alert_error_bright', '&:hover': { color: '$alert_error_brighter' } }}
82
+ icon={<StopIcon height={24} width={24} />}
83
+ onClick={() => {
84
+ setOpen(false);
85
+ setShowEndStreamAlert(true);
86
+ }}
87
+ />
89
88
  </Sheet.Content>
90
89
  </Sheet.Root>
91
90
  ) : (
92
- <LeaveIconButton
93
- key="LeaveRoom"
94
- data-testid="leave_room_btn"
95
- onClick={() => {
96
- if (screenType === 'hls_live_streaming') {
97
- setShowLeaveRoomAlert(true);
98
- } else {
99
- leaveRoom({ endstream: false });
100
- }
101
- }}
102
- >
91
+ <LeaveIconButton key="LeaveRoom" data-testid="leave_room_btn" onClick={() => setShowLeaveRoomAlert(true)}>
103
92
  <Tooltip title="Leave Room">
104
93
  <Box>
105
94
  <ExitIcon style={{ transform: 'rotate(180deg)' }} />
@@ -116,13 +105,12 @@ export const MwebLeaveRoom = ({
116
105
  />
117
106
  </Sheet.Content>
118
107
  </Sheet.Root>
119
- {screenType === 'hls_live_streaming' ? (
120
- <Sheet.Root open={showLeaveRoomAlert} onOpenChange={setShowLeaveRoomAlert}>
121
- <Sheet.Content css={{ bg: '$surface_dim', p: '$10', pb: '$12' }}>
122
- <LeaveSessionContent setShowLeaveRoomAlert={setShowLeaveRoomAlert} leaveRoom={leaveRoom} />
123
- </Sheet.Content>
124
- </Sheet.Root>
125
- ) : null}
108
+
109
+ <Sheet.Root open={showLeaveRoomAlert} onOpenChange={setShowLeaveRoomAlert}>
110
+ <Sheet.Content css={{ bg: '$surface_dim', p: '$10', pb: '$12' }}>
111
+ <LeaveSessionContent setShowLeaveRoomAlert={setShowLeaveRoomAlert} leaveRoom={leaveRoom} />
112
+ </Sheet.Content>
113
+ </Sheet.Root>
126
114
  </Fragment>
127
115
  );
128
116
  };
@@ -5,6 +5,7 @@ import {
5
5
  selectIsConnectedToRoom,
6
6
  selectPeerCount,
7
7
  selectPermissions,
8
+ useAVToggle,
8
9
  useHMSActions,
9
10
  useHMSStore,
10
11
  useRecordingStreaming,
@@ -84,6 +85,8 @@ export const MwebOptions = ({
84
85
  const peerCount = useHMSStore(selectPeerCount);
85
86
  const emojiCardRef = useRef(null);
86
87
  const { isBRBOn, toggleBRB, isHandRaised, toggleHandRaise } = useMyMetadata();
88
+ const { toggleAudio, toggleVideo } = useAVToggle();
89
+ const noAVPermissions = !(toggleAudio || toggleVideo);
87
90
  // const isVideoOn = useHMSStore(selectIsLocalVideoEnabled);
88
91
 
89
92
  useDropdownList({ open: openModals.size > 0 || openOptionsSheet || openSettingsSheet, name: 'MoreSettings' });
@@ -158,7 +161,7 @@ export const MwebOptions = ({
158
161
  </ActionTile.Root>
159
162
  )}
160
163
 
161
- {screenType !== 'hls_live_streaming' ? (
164
+ {!noAVPermissions ? (
162
165
  <ActionTile.Root
163
166
  active={isHandRaised}
164
167
  onClick={() => {
@@ -279,7 +282,6 @@ export const MwebOptions = ({
279
282
 
280
283
  {showEmojiCard && (
281
284
  <Box
282
- onClick={() => setShowEmojiCard(false)}
283
285
  ref={emojiCardRef}
284
286
  css={{
285
287
  maxWidth: '100%',
@@ -4,10 +4,12 @@ import { useNavigate, useParams } from 'react-router-dom';
4
4
  import {
5
5
  HMSNotificationTypes,
6
6
  HMSRoomState,
7
+ selectHasPeerHandRaised,
7
8
  selectRoomState,
8
9
  useCustomEvent,
9
10
  useHMSNotifications,
10
11
  useHMSStore,
12
+ useHMSVanillaStore,
11
13
  } from '@100mslive/react-sdk';
12
14
  import { Button } from '../../../';
13
15
  import { useUpdateRoomLayout } from '../../provider/roomLayoutProvider';
@@ -34,6 +36,7 @@ export function Notifications() {
34
36
  const updateRoomLayoutForRole = useUpdateRoomLayout();
35
37
  const isNotificationDisabled = useIsNotificationDisabled();
36
38
  const { redirectToLeave } = useRedirectToLeave();
39
+ const vanillaStore = useHMSVanillaStore();
37
40
 
38
41
  const handleRoleChangeDenied = useCallback(request => {
39
42
  ToastManager.addToast({
@@ -49,6 +52,16 @@ export function Notifications() {
49
52
  return;
50
53
  }
51
54
  switch (notification.type) {
55
+ case HMSNotificationTypes.HAND_RAISE_CHANGED: {
56
+ if (roomState !== HMSRoomState.Connected || notification.data.isLocal) {
57
+ return;
58
+ }
59
+ const hasPeerHandRaised = vanillaStore.getState(selectHasPeerHandRaised(notification.data.id));
60
+ if (hasPeerHandRaised) {
61
+ ToastBatcher.showToast({ notification, type: 'RAISE_HAND' });
62
+ }
63
+ break;
64
+ }
52
65
  case HMSNotificationTypes.METADATA_UPDATED:
53
66
  if (roomState !== HMSRoomState.Connected) {
54
67
  return;
@@ -154,7 +167,7 @@ export function Notifications() {
154
167
  <TrackUnmuteModal />
155
168
  <TrackBulkUnmuteModal />
156
169
  <TrackNotifications />
157
- <PeerNotifications />
170
+ {roomState === HMSRoomState.Connected ? <PeerNotifications /> : null}
158
171
  <ReconnectNotifications />
159
172
  <AutoplayBlockedModal />
160
173
  <PermissionErrorModal />
@@ -1,14 +1,7 @@
1
1
  import { useEffect } from 'react';
2
- import {
3
- HMSNotificationTypes,
4
- HMSRoomState,
5
- selectRoomState,
6
- useHMSNotifications,
7
- useHMSStore,
8
- } from '@100mslive/react-sdk';
2
+ import { HMSNotificationTypes, useHMSNotifications } from '@100mslive/react-sdk';
9
3
  import { ToastBatcher } from '../Toast/ToastBatcher';
10
4
  import { useSubscribedNotifications } from '../AppData/useUISettings';
11
- import { isInternalRole } from '../../common/utils';
12
5
  import { SUBSCRIBED_NOTIFICATIONS } from '../../common/constants';
13
6
 
14
7
  const notificationTypes = [
@@ -21,19 +14,15 @@ export const PeerNotifications = () => {
21
14
  const notification = useHMSNotifications(notificationTypes);
22
15
  const isPeerJoinSubscribed = useSubscribedNotifications(SUBSCRIBED_NOTIFICATIONS.PEER_JOINED);
23
16
  const isPeerLeftSubscribed = useSubscribedNotifications(SUBSCRIBED_NOTIFICATIONS.PEER_LEFT);
24
- const roomState = useHMSStore(selectRoomState);
25
17
  useEffect(() => {
26
- if (roomState !== HMSRoomState.Connected) {
27
- return;
28
- }
29
- if (!notification || (notification?.data?.roleName && isInternalRole(notification.data.roleName))) {
18
+ if (!notification?.data) {
30
19
  return;
31
20
  }
32
21
 
33
22
  console.debug(`[${notification.type}]`, notification);
34
23
  switch (notification.type) {
35
24
  case HMSNotificationTypes.PEER_LIST:
36
- if (!isPeerJoinSubscribed || notification.data?.length === 0) {
25
+ if (!isPeerJoinSubscribed || notification.data.length === 0) {
37
26
  return;
38
27
  }
39
28
  break;
@@ -52,7 +41,7 @@ export const PeerNotifications = () => {
52
41
  }
53
42
 
54
43
  ToastBatcher.showToast({ notification });
55
- }, [notification, isPeerJoinSubscribed, isPeerLeftSubscribed, roomState]);
44
+ }, [notification, isPeerJoinSubscribed, isPeerLeftSubscribed]);
56
45
 
57
46
  return null;
58
47
  };
@@ -105,7 +105,11 @@ const PreviewJoin = ({
105
105
  const roomLayout = useRoomLayout();
106
106
 
107
107
  const { preview_header: previewHeader = {} } = roomLayout?.screens?.preview?.default?.elements || {};
108
-
108
+ const localPeer = useHMSStore(selectLocalPeer);
109
+ const videoTrack = useHMSStore(selectVideoTrackByID(localPeer?.videoTrack));
110
+ const isMobile = useMedia(cssConfig.media.md);
111
+ const aspectRatio =
112
+ videoTrack?.width && videoTrack?.height ? videoTrack.width / videoTrack.height : isMobile ? 9 / 16 : 16 / 9;
109
113
  useEffect(() => {
110
114
  if (authToken) {
111
115
  if (skipPreview) {
@@ -166,7 +170,7 @@ const PreviewJoin = ({
166
170
  <PreviewTile name={name} error={previewError} />
167
171
  </Flex>
168
172
  ) : null}
169
- <Box css={{ w: '100%', maxWidth: '640px' }}>
173
+ <Box css={{ w: '100%', maxWidth: `${Math.max(aspectRatio, 1) * 360}px` }}>
170
174
  <PreviewControls hideSettings={!toggleVideo && !toggleAudio} />
171
175
  <PreviewForm
172
176
  name={name}
@@ -202,12 +206,13 @@ export const PreviewTile = ({ name, error }: { name: string; error?: boolean })
202
206
  const isMobile = useMedia(cssConfig.media.md);
203
207
  const aspectRatio =
204
208
  videoTrack?.width && videoTrack?.height ? videoTrack.width / videoTrack.height : isMobile ? 9 / 16 : 16 / 9;
209
+
205
210
  return (
206
211
  <StyledVideoTile.Container
207
212
  css={{
208
213
  bg: '$surface_default',
209
214
  aspectRatio,
210
- height: 'min(640px, 40vh)',
215
+ height: 'min(360px, 70vh)',
211
216
  maxWidth: '640px',
212
217
  overflow: 'clip',
213
218
  '@md': {
@@ -225,6 +230,7 @@ export const PreviewTile = ({ name, error }: { name: string; error?: boolean })
225
230
  trackId={localPeer.videoTrack}
226
231
  data-testid="preview_tile"
227
232
  />
233
+
228
234
  {!isVideoOn ? (
229
235
  <StyledVideoTile.AvatarContainer>
230
236
  <Avatar name={name} data-testid="preview_avatar_tile" />
@@ -234,8 +240,9 @@ export const PreviewTile = ({ name, error }: { name: string; error?: boolean })
234
240
  ) : !error ? (
235
241
  <FullPageProgress />
236
242
  ) : null}
243
+
237
244
  {showMuteIcon ? (
238
- <StyledVideoTile.AudioIndicator size="medium">
245
+ <StyledVideoTile.AudioIndicator>
239
246
  <MicOffIcon />
240
247
  </StyledVideoTile.AudioIndicator>
241
248
  ) : (
@@ -250,7 +257,7 @@ export const PreviewControls = ({ hideSettings }: { hideSettings: boolean }) =>
250
257
 
251
258
  return (
252
259
  <Flex
253
- justify="between"
260
+ justify={hideSettings && isMobile ? 'center' : 'between'}
254
261
  css={{
255
262
  width: '100%',
256
263
  mt: '$8',
@@ -1,18 +1,15 @@
1
1
  import React from 'react';
2
- import { selectLocalPeer, selectPeerMetadata, useHMSStore } from '@100mslive/react-sdk';
3
2
  import { HandIcon } from '@100mslive/react-icons';
4
3
  import { Tooltip } from '../../Tooltip';
5
4
  import IconButton from '../IconButton';
5
+ // @ts-ignore: No implicit Any
6
6
  import { useMyMetadata } from './hooks/useMetadata';
7
7
 
8
8
  export const RaiseHand = () => {
9
- const { toggleHandRaise } = useMyMetadata();
10
- const localPeer = useHMSStore(selectLocalPeer);
11
- const isHandRaised = useHMSStore(selectPeerMetadata(localPeer.id))?.isHandRaised || false;
12
-
9
+ const { isHandRaised, toggleHandRaise } = useMyMetadata();
13
10
  return (
14
11
  <Tooltip title={isHandRaised ? 'Lower hand' : 'Raise hand'}>
15
- <IconButton active={!isHandRaised} onClick={toggleHandRaise}>
12
+ <IconButton active={!isHandRaised} onClick={async () => await toggleHandRaise()}>
16
13
  <HandIcon />
17
14
  </IconButton>
18
15
  </Tooltip>
@@ -0,0 +1,66 @@
1
+ import React from 'react';
2
+ import { useMedia } from 'react-use';
3
+ import { Box, Button, config as cssConfig, Dialog, Flex, Text } from '../../..';
4
+ import { Sheet } from '../../../Sheet';
5
+
6
+ export const RequestPrompt = ({
7
+ open = true,
8
+ onOpenChange,
9
+ title,
10
+ body,
11
+ actionText = 'Accept',
12
+ onAction,
13
+ }: {
14
+ open?: boolean;
15
+ onOpenChange: (value: boolean) => void;
16
+ title: string;
17
+ body: React.ReactNode;
18
+ actionText?: string;
19
+ onAction: () => void;
20
+ }) => {
21
+ const isMobile = useMedia(cssConfig.media.md);
22
+
23
+ if (isMobile) {
24
+ return (
25
+ <Sheet.Root open={open} onOpenChange={onOpenChange}>
26
+ <Sheet.Content css={{ py: '$8' }}>
27
+ <Text css={{ fontWeight: '$semiBold', c: '$on_surface_high', '@md': { px: '$8' } }}>{title}</Text>
28
+ {body}
29
+ <RequestActions actionText={actionText} onAction={onAction} />
30
+ </Sheet.Content>
31
+ </Sheet.Root>
32
+ );
33
+ }
34
+
35
+ return (
36
+ <Dialog.Root open={open} onOpenChange={onOpenChange}>
37
+ <Dialog.Portal>
38
+ <Dialog.Overlay />
39
+ <Dialog.Content css={{ p: '$10' }}>
40
+ <Dialog.Title css={{ p: 0, display: 'flex', flexDirection: 'row', gap: '$md', justifyContent: 'center' }}>
41
+ <Text variant="h6">{title}</Text>
42
+ </Dialog.Title>
43
+ <Box css={{ mt: '$4', mb: '$10' }}>{body}</Box>
44
+ <RequestActions actionText={actionText} onAction={onAction} />
45
+ </Dialog.Content>
46
+ </Dialog.Portal>
47
+ </Dialog.Root>
48
+ );
49
+ };
50
+
51
+ const RequestActions = ({ onAction, actionText }: { actionText?: string; onAction: () => void }) => (
52
+ <Flex justify="center" align="center" css={{ width: '100%', gap: '$md', '@md': { mt: '$8', px: '$8' } }}>
53
+ <Box css={{ width: '50%' }}>
54
+ <Dialog.Close css={{ width: '100%' }}>
55
+ <Button variant="standard" outlined css={{ width: '100%' }}>
56
+ Decline
57
+ </Button>
58
+ </Dialog.Close>
59
+ </Box>
60
+ <Box css={{ width: '50%' }}>
61
+ <Button variant="primary" css={{ width: '100%' }} onClick={onAction}>
62
+ {actionText}
63
+ </Button>
64
+ </Box>
65
+ </Flex>
66
+ );
@@ -0,0 +1,89 @@
1
+ import React, { useEffect } from 'react';
2
+ import {
3
+ selectLocalPeerName,
4
+ selectLocalPeerRoleName,
5
+ selectRoleChangeRequest,
6
+ useCustomEvent,
7
+ useHMSActions,
8
+ useHMSStore,
9
+ } from '@100mslive/react-sdk';
10
+ import { Flex, Text } from '../../..';
11
+ // @ts-ignore: No implicit Any
12
+ import { PreviewControls, PreviewTile } from '../Preview/PreviewJoin';
13
+ import { RequestPrompt } from './RequestPrompt';
14
+ // @ts-ignore: No implicit Any
15
+ import { useMyMetadata } from '../hooks/useMetadata';
16
+ // @ts-ignore: No implicit Any
17
+ import { ROLE_CHANGE_DECLINED } from '../../common/constants';
18
+
19
+ export const RoleChangeRequestModal = () => {
20
+ const hmsActions = useHMSActions();
21
+ const { updateMetaData } = useMyMetadata();
22
+ const currentRole = useHMSStore(selectLocalPeerRoleName);
23
+ const roleChangeRequest = useHMSStore(selectRoleChangeRequest);
24
+ const name = useHMSStore(selectLocalPeerName);
25
+ const { sendEvent } = useCustomEvent({ type: ROLE_CHANGE_DECLINED });
26
+
27
+ useEffect(() => {
28
+ if (!roleChangeRequest?.role) {
29
+ return;
30
+ }
31
+ (async () => {
32
+ await hmsActions.preview({ asRole: roleChangeRequest.role.name });
33
+ })();
34
+ }, [hmsActions, roleChangeRequest, currentRole, updateMetaData]);
35
+
36
+ if (!roleChangeRequest?.role) {
37
+ return null;
38
+ }
39
+
40
+ const body = (
41
+ <>
42
+ <Text
43
+ variant="xs"
44
+ css={{
45
+ c: '$on_surface_medium',
46
+ textAlign: 'center',
47
+ '@md': { textAlign: 'left', borderBottom: '1px solid $border_bright', pb: '$4', px: '$8' },
48
+ }}
49
+ >
50
+ Setup your audio and video before joining
51
+ </Text>
52
+ <Flex
53
+ align="center"
54
+ justify="center"
55
+ css={{
56
+ '@sm': { width: '100%' },
57
+ flexDirection: 'column',
58
+ mt: '$6',
59
+ '@md': { px: '$8' },
60
+ }}
61
+ >
62
+ <PreviewTile name={name || ''} />
63
+
64
+ <PreviewControls hideSettings={true} />
65
+ </Flex>
66
+ </>
67
+ );
68
+
69
+ return (
70
+ <RequestPrompt
71
+ title="You're invited to join the stage"
72
+ onOpenChange={async value => {
73
+ if (!value) {
74
+ hmsActions.rejectChangeRole(roleChangeRequest);
75
+ sendEvent({ ...roleChangeRequest, peerName: name }, { peerId: roleChangeRequest.requestedBy?.id });
76
+ await hmsActions.cancelMidCallPreview();
77
+ await hmsActions.lowerLocalPeerHand();
78
+ }
79
+ }}
80
+ body={body}
81
+ onAction={async () => {
82
+ await hmsActions.acceptChangeRole(roleChangeRequest);
83
+ await updateMetaData({ prevRole: currentRole });
84
+ await hmsActions.lowerLocalPeerHand();
85
+ }}
86
+ actionText="Accept"
87
+ />
88
+ );
89
+ };
@@ -8,11 +8,14 @@ import { Chat } from './Chat/Chat';
8
8
  // @ts-ignore: No implicit Any
9
9
  import { ParticipantList } from './Footer/ParticipantList';
10
10
  import { config as cssConfig, Flex, IconButton, Tabs, Text } from '../..';
11
+ import { Tooltip } from '../../Tooltip';
11
12
  // @ts-ignore: No implicit Any
12
13
  import { useRoomLayoutConferencingScreen } from '../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
13
14
  // @ts-ignore: No implicit Any
14
15
  import { useIsSidepaneTypeOpen, useSidepaneReset, useSidepaneToggle } from './AppData/useSidepane';
15
16
  // @ts-ignore: No implicit Any
17
+ import { getFormattedCount } from '../common/utils';
18
+ // @ts-ignore: No implicit Any
16
19
  import { SIDE_PANE_OPTIONS } from '../common/constants';
17
20
 
18
21
  const tabTriggerCSS = {
@@ -24,6 +27,16 @@ const tabTriggerCSS = {
24
27
  justifyContent: 'center',
25
28
  };
26
29
 
30
+ const ParticipantCount = ({ count }: { count: number }) => {
31
+ return count < 1000 ? (
32
+ <span>({count})</span>
33
+ ) : (
34
+ <Tooltip title={count}>
35
+ <span>({getFormattedCount(count)})</span>
36
+ </Tooltip>
37
+ );
38
+ };
39
+
27
40
  export const SidePaneTabs = React.memo<{
28
41
  active: 'Participants | Chat';
29
42
  screenType: keyof ConferencingScreen;
@@ -73,7 +86,13 @@ export const SidePaneTabs = React.memo<{
73
86
  {hideTabs ? (
74
87
  <>
75
88
  <Text variant="sm" css={{ fontWeight: '$semiBold', p: '$4', c: '$on_surface_high', pr: '$12' }}>
76
- {showChat ? 'Chat' : `Participants (${peerCount})`}
89
+ {showChat ? (
90
+ 'Chat'
91
+ ) : (
92
+ <span>
93
+ Participants <ParticipantCount count={peerCount} />
94
+ </span>
95
+ )}
77
96
  </Text>
78
97
 
79
98
  {showChat ? <Chat screenType={screenType} /> : <ParticipantList />}
@@ -106,7 +125,7 @@ export const SidePaneTabs = React.memo<{
106
125
  color: activeTab !== SIDE_PANE_OPTIONS.PARTICIPANTS ? '$on_surface_low' : '$on_surface_high',
107
126
  }}
108
127
  >
109
- Participants ({peerCount})
128
+ Participants <ParticipantCount count={peerCount} />
110
129
  </Tabs.Trigger>
111
130
  </Tabs.List>
112
131
  <Tabs.Content value={SIDE_PANE_OPTIONS.PARTICIPANTS} css={{ p: 0 }}>
@@ -1,6 +1,8 @@
1
1
  import React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { useMedia } from 'react-use';
2
3
  import {
3
4
  selectAudioTrackByPeerID,
5
+ selectHasPeerHandRaised,
4
6
  selectIsPeerAudioEnabled,
5
7
  selectLocalPeerID,
6
8
  selectPeerMetadata,
@@ -18,11 +20,12 @@ import TileMenu, { isSameTile } from './TileMenu/TileMenu';
18
20
  import { Avatar } from '../../Avatar';
19
21
  import { Box, Flex } from '../../Layout';
20
22
  import { VideoTileStats } from '../../Stats';
21
- import { keyframes } from '../../Theme';
23
+ import { config as cssConfig, keyframes } from '../../Theme';
22
24
  import { Video } from '../../Video';
23
25
  import { StyledVideoTile } from '../../VideoTile';
24
26
  import { getVideoTileLabel } from './peerTileUtils';
25
27
  import { useSetAppDataByKey, useUISettings } from './AppData/useUISettings';
28
+ import { getAttributeBoxSize } from '../common/utils';
26
29
  import { APP_DATA, SESSION_STORE_KEY, UI_SETTINGS } from '../common/constants';
27
30
 
28
31
  const Tile = ({
@@ -43,6 +46,7 @@ const Tile = ({
43
46
  }) => {
44
47
  const trackSelector = trackId ? selectVideoTrackByID(trackId) : selectVideoTrackByPeerID(peerId);
45
48
  const track = useHMSStore(trackSelector);
49
+ const isMobile = useMedia(cssConfig.media.md);
46
50
  const peerName = useHMSStore(selectPeerNameByID(peerId));
47
51
  const audioTrack = useHMSStore(selectAudioTrackByPeerID(peerId));
48
52
  const localPeerID = useHMSStore(selectLocalPeerID);
@@ -69,21 +73,28 @@ const Tile = ({
69
73
  const onHoverHandler = useCallback(event => {
70
74
  setIsMouseHovered(event.type === 'mouseenter');
71
75
  }, []);
72
- const isTileBigEnoughToShowStats = height >= 180 && width >= 180;
76
+
77
+ const ref = useRef(null);
78
+ const calculatedHeight = ref.current?.clientHeight || '';
79
+ const calculatedWidth = ref.current?.clientWidth || '';
80
+
81
+ const isTileBigEnoughToShowStats = calculatedHeight >= 180 && calculatedWidth >= 180;
82
+
73
83
  const avatarSize = useMemo(() => {
74
- if (!width || !height) {
84
+ if (!calculatedWidth || !calculatedHeight) {
75
85
  return undefined;
76
86
  }
77
- if (width <= 150 || height <= 150) {
87
+ if (calculatedWidth <= 150 || calculatedHeight <= 150) {
78
88
  return 'small';
79
- } else if (width <= 300 || height <= 300) {
89
+ } else if (calculatedWidth <= 300 || calculatedHeight <= 300) {
80
90
  return 'medium';
81
91
  }
82
92
  return 'large';
83
- }, [width, height]);
93
+ }, [calculatedWidth, calculatedHeight]);
84
94
 
85
95
  return (
86
96
  <StyledVideoTile.Root
97
+ ref={ref}
87
98
  css={{
88
99
  width,
89
100
  height,
@@ -130,7 +141,7 @@ const Tile = ({
130
141
  isAudioMuted ? (
131
142
  <StyledVideoTile.AudioIndicator
132
143
  data-testid="participant_audio_mute_icon"
133
- size={width && height && (width < 180 || height < 180) ? 'small' : 'medium'}
144
+ size={getAttributeBoxSize(calculatedWidth, calculatedHeight)}
134
145
  >
135
146
  <MicOffIcon />
136
147
  </StyledVideoTile.AudioIndicator>
@@ -138,7 +149,7 @@ const Tile = ({
138
149
  <AudioLevel trackId={audioTrack?.id} />
139
150
  )
140
151
  ) : null}
141
- {isMouseHovered || isDragabble ? (
152
+ {isMouseHovered || (isDragabble && isMobile) ? (
142
153
  <TileMenu
143
154
  peerID={peerId}
144
155
  audioTrackID={audioTrack?.id}
@@ -147,7 +158,7 @@ const Tile = ({
147
158
  enableSpotlightingPeer={enableSpotlightingPeer}
148
159
  />
149
160
  ) : null}
150
- {!hideMetadataOnTile && <PeerMetadata peerId={peerId} />}
161
+ {!hideMetadataOnTile && <PeerMetadata peerId={peerId} height={calculatedHeight} width={calculatedWidth} />}
151
162
 
152
163
  <TileConnection
153
164
  hideLabel={hideParticipantNameOnTile}
@@ -164,8 +175,6 @@ const Tile = ({
164
175
  );
165
176
  };
166
177
 
167
- const metaStyles = { top: '$4', left: '$4', width: '$14', height: '$14' };
168
-
169
178
  const heightAnimation = value =>
170
179
  keyframes({
171
180
  '50%': {
@@ -215,20 +224,20 @@ export const AudioLevel = ({ trackId }) => {
215
224
  );
216
225
  };
217
226
 
218
- const PeerMetadata = ({ peerId }) => {
227
+ const PeerMetadata = ({ peerId, height, width }) => {
219
228
  const metaData = useHMSStore(selectPeerMetadata(peerId));
220
- const isHandRaised = metaData?.isHandRaised || false;
221
229
  const isBRB = metaData?.isBRBOn || false;
230
+ const isHandRaised = useHMSStore(selectHasPeerHandRaised(peerId));
222
231
 
223
232
  return (
224
233
  <Fragment>
225
234
  {isHandRaised ? (
226
- <StyledVideoTile.AttributeBox css={metaStyles} data-testid="raiseHand_icon_onTile">
235
+ <StyledVideoTile.AttributeBox size={getAttributeBoxSize(width, height)} data-testid="raiseHand_icon_onTile">
227
236
  <HandIcon width={24} height={24} />
228
237
  </StyledVideoTile.AttributeBox>
229
238
  ) : null}
230
239
  {isBRB ? (
231
- <StyledVideoTile.AttributeBox css={metaStyles} data-testid="brb_icon_onTile">
240
+ <StyledVideoTile.AttributeBox size={getAttributeBoxSize(width, height)} data-testid="brb_icon_onTile">
232
241
  <BrbTileIcon width={22} height={22} />
233
242
  </StyledVideoTile.AttributeBox>
234
243
  ) : null}
@@ -13,12 +13,12 @@ import { Footer } from './Footer/Footer';
13
13
  import { HLSFailureModal } from './Notifications/HLSFailureModal';
14
14
  import { ActivatedPIP } from './PIP/PIPComponent';
15
15
  import { PictureInPicture } from './PIP/PIPManager';
16
+ import { RoleChangeRequestModal } from './RoleChangeRequest/RoleChangeRequestModal';
16
17
  import { Box, Flex } from '../../Layout';
17
18
  import { useHMSPrebuiltContext } from '../AppContext';
18
19
  import { VideoStreamingSection } from '../layouts/VideoStreamingSection';
19
20
  import FullPageProgress from './FullPageProgress';
20
21
  import { Header } from './Header';
21
- import { RoleChangeRequestModal } from './RoleChangeRequestModal';
22
22
  import {
23
23
  useRoomLayoutConferencingScreen,
24
24
  useRoomLayoutPreviewScreen,