@100mslive/roomkit-react 0.3.2-alpha.0 → 0.3.2-alpha.1

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.
@@ -9,24 +9,30 @@ import {
9
9
  selectIsPeerAudioEnabled,
10
10
  selectLocalPeerID,
11
11
  selectPeerCount,
12
- selectPeerMetadata,
13
12
  selectPermissions,
14
- useHMSActions,
15
13
  useHMSStore,
16
14
  } from '@100mslive/react-sdk';
17
- import { ChangeRoleIcon, HandIcon, MicOffIcon, PeopleIcon, SearchIcon, VerticalMenuIcon } from '@100mslive/react-icons';
15
+ import {
16
+ AddIcon,
17
+ ChangeRoleIcon,
18
+ CrossIcon,
19
+ HandIcon,
20
+ MicOffIcon,
21
+ PeopleIcon,
22
+ SearchIcon,
23
+ VerticalMenuIcon,
24
+ } from '@100mslive/react-icons';
18
25
  import { Accordion, Box, config as cssConfig, Dropdown, Flex, Input, Text, textEllipsis } from '../../..';
26
+ import { IconButton as BaseIconButton } from '../../../IconButton';
19
27
  // @ts-ignore: No implicit Any
20
28
  import IconButton from '../../IconButton';
21
29
  import { ConnectionIndicator } from '../Connection/ConnectionIndicator';
22
30
  import { RemoveParticipant } from '../RemoveParticipant';
23
31
  import { RoleAccordion } from './RoleAccordion';
24
- import {
25
- ConferencingScreenElements,
26
- useRoomLayoutConferencingScreen,
27
- } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
32
+ import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
28
33
  // @ts-ignore: No implicit Any
29
34
  import { useIsSidepaneTypeOpen, useSidepaneToggle } from '../AppData/useSidepane';
35
+ import { usePeerOnStageActions } from '../hooks/usePeerOnStageActions';
30
36
  import { useParticipants } from '../../common/hooks';
31
37
  // @ts-ignore: No implicit Any
32
38
  import { getFormattedCount } from '../../common/utils';
@@ -146,10 +152,12 @@ export const ParticipantCount = () => {
146
152
  export const Participant = ({
147
153
  peer,
148
154
  isConnected,
155
+ isHandRaisedAccordion,
149
156
  style,
150
157
  }: {
151
158
  peer: HMSPeer;
152
159
  isConnected: boolean;
160
+ isHandRaisedAccordion?: boolean;
153
161
  style: React.CSSProperties;
154
162
  }) => {
155
163
  const localPeerId = useHMSStore(selectLocalPeerID);
@@ -175,7 +183,12 @@ export const Participant = ({
175
183
  {peer.name} {localPeerId === peer.id ? '(You)' : ''}
176
184
  </Text>
177
185
  {isConnected && peer.roleName ? (
178
- <ParticipantActions peerId={peer.id} isLocal={peer.id === localPeerId} role={peer.roleName} />
186
+ <ParticipantActions
187
+ peerId={peer.id}
188
+ isLocal={peer.id === localPeerId}
189
+ role={peer.roleName}
190
+ isHandRaisedAccordion={isHandRaisedAccordion}
191
+ />
179
192
  ) : null}
180
193
  </Flex>
181
194
  );
@@ -247,7 +260,17 @@ const VirtualizedParticipants = ({
247
260
  * shows settings to change for a participant like changing their role
248
261
  */
249
262
  const ParticipantActions = React.memo(
250
- ({ peerId, role, isLocal }: { peerId: string; role: string; isLocal: boolean }) => {
263
+ ({
264
+ peerId,
265
+ role,
266
+ isLocal,
267
+ isHandRaisedAccordion,
268
+ }: {
269
+ peerId: string;
270
+ role: string;
271
+ isLocal: boolean;
272
+ isHandRaisedAccordion?: boolean;
273
+ }) => {
251
274
  const isHandRaised = useHMSStore(selectHasPeerHandRaised(peerId));
252
275
  const canChangeRole = useHMSStore(selectPermissions)?.changeRole;
253
276
  const canRemoveOthers = useHMSStore(selectPermissions)?.removeOthers;
@@ -264,72 +287,73 @@ const ParticipantActions = React.memo(
264
287
  gap: '$8',
265
288
  }}
266
289
  >
267
- <ConnectionIndicator peerId={peerId} />
268
- {isHandRaised && (
269
- <Flex
270
- align="center"
271
- justify="center"
272
- css={{ p: '$1', c: '$on_surface_high', bg: '$surface_bright', borderRadius: '$round' }}
273
- >
274
- <HandIcon height={19} width={19} />
275
- </Flex>
276
- )}
277
- {isAudioMuted ? (
278
- <Flex
279
- align="center"
280
- justify="center"
281
- css={{ p: '$2', c: '$on_surface_high', bg: '$surface_bright', borderRadius: '$round' }}
282
- >
283
- <MicOffIcon height={19} width={19} />
284
- </Flex>
285
- ) : null}
290
+ {isHandRaisedAccordion ? (
291
+ <HandRaisedAccordionParticipantActions peerId={peerId} role={role} />
292
+ ) : (
293
+ <>
294
+ <ConnectionIndicator peerId={peerId} />
295
+ {isHandRaised && (
296
+ <Flex
297
+ align="center"
298
+ justify="center"
299
+ css={{ p: '$1', c: '$on_surface_high', bg: '$surface_bright', borderRadius: '$round' }}
300
+ >
301
+ <HandIcon height={19} width={19} />
302
+ </Flex>
303
+ )}
304
+ {isAudioMuted ? (
305
+ <Flex
306
+ align="center"
307
+ justify="center"
308
+ css={{ p: '$2', c: '$on_surface_high', bg: '$surface_bright', borderRadius: '$round' }}
309
+ >
310
+ <MicOffIcon height={19} width={19} />
311
+ </Flex>
312
+ ) : null}
286
313
 
287
- {shouldShowMoreActions && !isLocal ? (
288
- <ParticipantMoreActions peerId={peerId} role={role} elements={elements} canChangeRole={!!canChangeRole} />
289
- ) : null}
314
+ {shouldShowMoreActions && !isLocal ? <ParticipantMoreActions peerId={peerId} role={role} /> : null}
315
+ </>
316
+ )}
290
317
  </Flex>
291
318
  );
292
319
  },
293
320
  );
294
321
 
295
- const ParticipantMoreActions = ({
296
- peerId,
297
- role,
298
- elements,
299
- canChangeRole,
300
- }: {
301
- peerId: string;
302
- role: string;
303
- canChangeRole: boolean;
304
- elements: ConferencingScreenElements;
305
- }) => {
306
- const hmsActions = useHMSActions();
322
+ const HandRaisedAccordionParticipantActions = ({ peerId, role }: { peerId: string; role: string }) => {
323
+ const { handleStageAction, lowerPeerHand, shouldShowStageRoleChange, isInStage } = usePeerOnStageActions({
324
+ peerId,
325
+ role,
326
+ });
327
+ return (
328
+ <>
329
+ <BaseIconButton
330
+ css={{ p: '$1', c: '$on_surface_high', bg: '$surface_bright', borderRadius: '$round' }}
331
+ onClick={() => lowerPeerHand()}
332
+ >
333
+ <CrossIcon height={19} width={19} />
334
+ </BaseIconButton>
335
+ {shouldShowStageRoleChange && !isInStage && (
336
+ <BaseIconButton
337
+ css={{ p: '$1', c: '$on_surface_high', bg: '$primary_default', borderRadius: '$round' }}
338
+ onClick={() => handleStageAction()}
339
+ >
340
+ <AddIcon height={19} width={19} />
341
+ </BaseIconButton>
342
+ )}
343
+ </>
344
+ );
345
+ };
346
+
347
+ const ParticipantMoreActions = ({ peerId, role }: { peerId: string; role: string }) => {
307
348
  const {
349
+ open,
350
+ setOpen,
308
351
  bring_to_stage_label,
309
352
  remove_from_stage_label,
310
- on_stage_role,
311
- off_stage_roles = [],
312
- skip_preview_for_role_change = false,
313
- } = elements.on_stage_exp || {};
314
- const isInStage = role === on_stage_role;
315
- const shouldShowStageRoleChange =
316
- canChangeRole &&
317
- ((isInStage && remove_from_stage_label) || (off_stage_roles?.includes(role) && bring_to_stage_label));
318
- const prevRole = useHMSStore(selectPeerMetadata(peerId))?.prevRole;
319
- const [open, setOpen] = useState(false);
320
-
321
- const handleStageAction = async () => {
322
- if (isInStage) {
323
- prevRole && hmsActions.changeRoleOfPeer(peerId, prevRole, true);
324
- } else if (on_stage_role) {
325
- await hmsActions.changeRoleOfPeer(peerId, on_stage_role, skip_preview_for_role_change);
326
- if (skip_preview_for_role_change) {
327
- await hmsActions.lowerRemotePeerHand(peerId);
328
- }
329
- }
330
- setOpen(false);
331
- };
332
-
353
+ handleStageAction,
354
+ isInStage,
355
+ shouldShowStageRoleChange,
356
+ } = usePeerOnStageActions({ peerId, role });
333
357
  return (
334
358
  <Dropdown.Root open={open} onOpenChange={value => setOpen(value)} modal={false}>
335
359
  <Dropdown.Trigger
@@ -4,10 +4,13 @@ import { FixedSizeList } from 'react-window';
4
4
  import { HMSPeer, selectIsLargeRoom, useHMSStore, usePaginatedParticipants } from '@100mslive/react-sdk';
5
5
  import { ChevronRightIcon } from '@100mslive/react-icons';
6
6
  import { Accordion } from '../../../Accordion';
7
+ import { Button } from '../../../Button';
8
+ import { HorizontalDivider } from '../../../Divider';
7
9
  import { Flex } from '../../../Layout';
8
10
  import { Text } from '../../../Text';
9
11
  import { Participant } from './ParticipantList';
10
12
  import { RoleOptions } from './RoleOptions';
13
+ import { useGroupOnStageActions } from '../hooks/useGroupOnStageActions';
11
14
  // @ts-ignore: No implicit Any
12
15
  import { getFormattedCount } from '../../common/utils';
13
16
 
@@ -16,6 +19,7 @@ const ITER_TIMER = 5000;
16
19
 
17
20
  export interface ItemData {
18
21
  peerList: HMSPeer[];
22
+ isHandRaisedAccordion?: boolean;
19
23
  isConnected: boolean;
20
24
  }
21
25
 
@@ -29,6 +33,7 @@ export const VirtualizedParticipantItem = React.memo(
29
33
  <Participant
30
34
  key={data.peerList[index].id}
31
35
  peer={data.peerList[index]}
36
+ isHandRaisedAccordion={data.isHandRaisedAccordion}
32
37
  isConnected={data.isConnected}
33
38
  style={style}
34
39
  />
@@ -63,6 +68,9 @@ export const RoleAccordion = ({
63
68
  peersInAccordion = peersInAccordion.filter(peer => peer.name.toLowerCase().includes(filter.search || ''));
64
69
  }
65
70
  }
71
+ const { bringAllToStage, bring_to_stage_label, canBringToStage, lowerAllHands } = useGroupOnStageActions({
72
+ peers: peersInAccordion,
73
+ });
66
74
 
67
75
  useEffect(() => {
68
76
  if (!isOffStageRole || !isLargeRoom) {
@@ -113,7 +121,7 @@ export const RoleAccordion = ({
113
121
  <Accordion.Content contentStyles={{ border: '1px solid $border_default', borderTop: 'none' }}>
114
122
  <FixedSizeList
115
123
  itemSize={ROW_HEIGHT}
116
- itemData={{ peerList: peersInAccordion, isConnected }}
124
+ itemData={{ peerList: peersInAccordion, isConnected, isHandRaisedAccordion }}
117
125
  itemKey={itemKey}
118
126
  itemCount={peersInAccordion.length}
119
127
  width={width}
@@ -140,6 +148,17 @@ export const RoleAccordion = ({
140
148
  <ChevronRightIcon />
141
149
  </Flex>
142
150
  ) : null}
151
+ {isHandRaisedAccordion && (
152
+ <>
153
+ <HorizontalDivider />
154
+ <Flex css={{ w: '100%', p: '$6', gap: '$4' }} justify="center">
155
+ <Button variant="standard" onClick={() => lowerAllHands()}>
156
+ Lower All Hands
157
+ </Button>
158
+ {canBringToStage && <Button onClick={() => bringAllToStage()}>{bring_to_stage_label}</Button>}
159
+ </Flex>
160
+ </>
161
+ )}
143
162
  </Accordion.Content>
144
163
  </Accordion.Item>
145
164
  );
@@ -32,7 +32,7 @@ export const MwebLandscapePrompt = () => {
32
32
  // Angle check needed to diff bw mobile and desktop
33
33
  setShowMwebLandscapePrompt(
34
34
  match({ angle, isLandscapeHLSStream, isLandscape, type })
35
- .with({ isLandscapeHLSStream }, () => false)
35
+ .with({ isLandscapeHLSStream: true }, () => false)
36
36
  .with({ angle: P.when(angle => angle && angle >= 90) }, ({ type }) => type.includes('landscape'))
37
37
  .otherwise(() => isLandscape),
38
38
  );
@@ -36,7 +36,6 @@ const SecondarySection = ({
36
36
  hasSidebar,
37
37
  }: React.PropsWithChildren<{ tiles: TrackWithPeerAndDimensions[]; edgeToEdge?: boolean; hasSidebar?: boolean }>) => {
38
38
  const tileLayoutProps = useVideoTileContext();
39
- console.log('secondary section', { tilesLength: tiles?.length });
40
39
  if (!tiles?.length) {
41
40
  return null;
42
41
  }
@@ -0,0 +1,54 @@
1
+ import { match, P } from 'ts-pattern';
2
+ import { HMSPeer, selectPermissions, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
3
+ import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
4
+
5
+ export const useGroupOnStageActions = ({ peers }: { peers: HMSPeer[] }) => {
6
+ const hmsActions = useHMSActions();
7
+ const { elements } = useRoomLayoutConferencingScreen();
8
+ const {
9
+ bring_to_stage_label,
10
+ remove_from_stage_label,
11
+ on_stage_role,
12
+ off_stage_roles = [],
13
+ skip_preview_for_role_change = false,
14
+ } = elements.on_stage_exp || {};
15
+ const canChangeRole = useHMSStore(selectPermissions)?.changeRole;
16
+
17
+ const offStageRolePeers = peers.filter(peer =>
18
+ match({ on_stage_role, bring_to_stage_label, roleName: peer.roleName })
19
+ .with(
20
+ {
21
+ on_stage_role: P.when(role => !!role),
22
+ bring_to_stage_label: P.when(label => !!label),
23
+ roleName: P.when(role => !!role && off_stage_roles.includes(role)),
24
+ },
25
+ () => true,
26
+ )
27
+ .otherwise(() => false),
28
+ );
29
+
30
+ const lowerAllHands = async () => {
31
+ return Promise.all(peers.map(peer => hmsActions.lowerRemotePeerHand(peer.id)));
32
+ };
33
+
34
+ const bringAllToStage = () => {
35
+ if (!canChangeRole || !on_stage_role) {
36
+ return;
37
+ }
38
+ return Promise.all(
39
+ offStageRolePeers.map(peer => {
40
+ return hmsActions.changeRoleOfPeer(peer.id, on_stage_role, skip_preview_for_role_change).then(() => {
41
+ return skip_preview_for_role_change ? hmsActions.lowerRemotePeerHand(peer.id) : null;
42
+ });
43
+ }),
44
+ );
45
+ };
46
+
47
+ return {
48
+ lowerAllHands,
49
+ bringAllToStage,
50
+ canBringToStage: canChangeRole && offStageRolePeers.length > 0,
51
+ bring_to_stage_label,
52
+ remove_from_stage_label,
53
+ };
54
+ };
@@ -0,0 +1,49 @@
1
+ import { useState } from 'react';
2
+ import { selectPeerMetadata, selectPermissions, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
3
+ import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
4
+
5
+ export const usePeerOnStageActions = ({ peerId, role }: { peerId: string; role: string }) => {
6
+ const hmsActions = useHMSActions();
7
+ const { elements } = useRoomLayoutConferencingScreen();
8
+ const {
9
+ bring_to_stage_label,
10
+ remove_from_stage_label,
11
+ on_stage_role,
12
+ off_stage_roles = [],
13
+ skip_preview_for_role_change = false,
14
+ } = elements.on_stage_exp || {};
15
+ const isInStage = role === on_stage_role;
16
+ const canChangeRole = useHMSStore(selectPermissions)?.changeRole;
17
+ const shouldShowStageRoleChange =
18
+ canChangeRole &&
19
+ ((isInStage && remove_from_stage_label) || (off_stage_roles?.includes(role) && bring_to_stage_label));
20
+ const prevRole = useHMSStore(selectPeerMetadata(peerId))?.prevRole;
21
+ const [open, setOpen] = useState(false);
22
+
23
+ const lowerPeerHand = async () => {
24
+ await hmsActions.lowerRemotePeerHand(peerId);
25
+ };
26
+
27
+ const handleStageAction = async () => {
28
+ if (isInStage) {
29
+ prevRole && hmsActions.changeRoleOfPeer(peerId, prevRole, true);
30
+ } else if (on_stage_role) {
31
+ await hmsActions.changeRoleOfPeer(peerId, on_stage_role, skip_preview_for_role_change);
32
+ if (skip_preview_for_role_change) {
33
+ await lowerPeerHand();
34
+ }
35
+ }
36
+ setOpen(false);
37
+ };
38
+
39
+ return {
40
+ open,
41
+ setOpen,
42
+ lowerPeerHand,
43
+ handleStageAction,
44
+ shouldShowStageRoleChange,
45
+ isInStage,
46
+ bring_to_stage_label,
47
+ remove_from_stage_label,
48
+ };
49
+ };
@@ -193,7 +193,6 @@ const SidePane = ({
193
193
  });
194
194
 
195
195
  if (!trackId && !SidepaneComponent) {
196
- console.log('sidepane not rendered', { trackId, SidepaneComponent });
197
196
  return null;
198
197
  }
199
198