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

Sign up to get free protection for your applications and to get access to all the features.
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} />