@100mslive/roomkit-react 0.3.1 → 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.
Files changed (29) hide show
  1. package/dist/{HLSView-7WNDXOED.js → HLSView-7E6TK45Q.js} +13 -3
  2. package/dist/HLSView-7E6TK45Q.js.map +7 -0
  3. package/dist/Prebuilt/common/hooks.d.ts +2 -0
  4. package/dist/Prebuilt/components/Footer/ParticipantList.d.ts +2 -1
  5. package/dist/Prebuilt/components/Footer/RoleAccordion.d.ts +1 -0
  6. package/dist/Prebuilt/components/hooks/useGroupOnStageActions.d.ts +10 -0
  7. package/dist/Prebuilt/components/hooks/usePeerOnStageActions.d.ts +14 -0
  8. package/dist/{chunk-3OCZREZ3.js → chunk-FKNIYFKS.js} +697 -552
  9. package/dist/chunk-FKNIYFKS.js.map +7 -0
  10. package/dist/index.cjs.js +1910 -1745
  11. package/dist/index.cjs.js.map +4 -4
  12. package/dist/index.js +1 -1
  13. package/dist/meta.cjs.json +152 -29
  14. package/dist/meta.esbuild.json +160 -36
  15. package/package.json +6 -6
  16. package/src/Prebuilt/common/hooks.ts +27 -1
  17. package/src/Prebuilt/components/Chat/PinnedMessage.tsx +4 -3
  18. package/src/Prebuilt/components/Footer/ParticipantList.tsx +90 -66
  19. package/src/Prebuilt/components/Footer/RoleAccordion.tsx +20 -1
  20. package/src/Prebuilt/components/MwebLandscapePrompt.tsx +1 -1
  21. package/src/Prebuilt/components/Polls/CreatePollQuiz/PollsQuizMenu.tsx +1 -1
  22. package/src/Prebuilt/components/Polls/CreateQuestions/QuestionForm.tsx +1 -1
  23. package/src/Prebuilt/components/Polls/common/OptionInputWithDelete.tsx +1 -1
  24. package/src/Prebuilt/components/VideoLayouts/ScreenshareLayout.tsx +2 -0
  25. package/src/Prebuilt/components/hooks/useGroupOnStageActions.tsx +54 -0
  26. package/src/Prebuilt/components/hooks/usePeerOnStageActions.tsx +49 -0
  27. package/src/Prebuilt/layouts/HLSView.jsx +10 -1
  28. package/dist/HLSView-7WNDXOED.js.map +0 -7
  29. package/dist/chunk-3OCZREZ3.js.map +0 -7
@@ -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
  );
@@ -140,7 +140,7 @@ const AddMenu = () => {
140
140
  type="text"
141
141
  placeholder="Enter a name to continue"
142
142
  value={title}
143
- onChange={event => setTitle(event.target.value)}
143
+ onChange={event => setTitle(event.target.value.trimStart())}
144
144
  css={{
145
145
  backgroundColor: '$surface_bright',
146
146
  border: '1px solid $border_default',
@@ -159,7 +159,7 @@ export const QuestionForm = ({
159
159
  maxHeight: '$32',
160
160
  }}
161
161
  value={text}
162
- onChange={event => setText(event.target.value)}
162
+ onChange={event => setText(event.target.value.trimStart())}
163
163
  />
164
164
  <Text variant="xs" css={{ color: '$on_surface_medium', textAlign: 'end', mt: '$4' }}>
165
165
  {text?.length || 0}/1024
@@ -27,7 +27,7 @@ export const OptionInputWithDelete = ({
27
27
  }}
28
28
  value={option?.text || ''}
29
29
  key={index}
30
- onChange={event => handleOptionTextChange(index, event.target.value)}
30
+ onChange={event => handleOptionTextChange(index, event.target.value.trimStart())}
31
31
  />
32
32
  <IconButton onClick={() => removeOption(index)} css={{ bg: 'transparent', border: 'none' }}>
33
33
  <TrashIcon />
@@ -55,6 +55,8 @@ export const ScreenshareLayout = ({ peers, onPageChange, onPageSize, edgeToEdge
55
55
  };
56
56
  }, [activeSharePeer?.id, isMobile, setActiveScreenSharePeer]);
57
57
 
58
+ console.log({ activeSharePeer, secondaryPeers });
59
+
58
60
  return (
59
61
  <ProminenceLayout.Root edgeToEdge={edgeToEdge} hasSidebar={hasSidebar}>
60
62
  <ProminenceLayout.ProminentSection>
@@ -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
+ };
@@ -36,7 +36,7 @@ import { config, theme, useTheme } from '../../Theme';
36
36
  import { Tooltip } from '../../Tooltip';
37
37
  import { useSidepaneToggle } from '../components/AppData/useSidepane';
38
38
  import { useRoomLayoutConferencingScreen } from '../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
39
- import { useIsLandscape } from '../common/hooks';
39
+ import { useIsLandscape, useKeyboardHandler } from '../common/hooks';
40
40
  import { APP_DATA, EMOJI_REACTION_TYPE, POLL_STATE, POLL_VIEWS, SIDE_PANE_OPTIONS } from '../common/constants';
41
41
 
42
42
  let hlsPlayer;
@@ -367,6 +367,8 @@ const HLSView = () => {
367
367
  [controlsVisible, isLandscape, isMobile, qualityDropDownOpen, seekProgress],
368
368
  );
369
369
 
370
+ const keyHandler = useKeyboardHandler(isPaused, hlsPlayer);
371
+
370
372
  if (!hlsUrl || streamEnded) {
371
373
  return (
372
374
  <>
@@ -437,7 +439,14 @@ const HLSView = () => {
437
439
  '@md': {
438
440
  height: 'auto',
439
441
  },
442
+ outline: 'none',
443
+ }}
444
+ onKeyDown={async event => {
445
+ if (hlsState?.variants[0]?.playlist_type === HLSPlaylistType.DVR) {
446
+ await keyHandler(event);
447
+ }
440
448
  }}
449
+ tabIndex="0"
441
450
  >
442
451
  {!(isMobile || isLandscape) && (
443
452
  <HLSAutoplayBlockedPrompt open={isHlsAutoplayBlocked} unblockAutoPlay={unblockAutoPlay} />