@100mslive/roomkit-react 0.2.8-alpha.8 → 0.2.8-alpha.9

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.
@@ -1,12 +1,13 @@
1
1
  import React, { useState } from 'react';
2
2
  import { DefaultConferencingScreen_Elements } from '@100mslive/types-prebuilt';
3
+ import { match } from 'ts-pattern';
3
4
  import {
4
5
  HMSPeer,
5
6
  selectPermissions,
6
7
  selectRoleByRoleName,
8
+ selectTracksMap,
7
9
  useHMSActions,
8
10
  useHMSStore,
9
- useHMSVanillaStore,
10
11
  } from '@100mslive/react-sdk';
11
12
  import {
12
13
  MicOffIcon,
@@ -32,12 +33,59 @@ const optionTextCSS = {
32
33
  whiteSpace: 'nowrap',
33
34
  };
34
35
 
35
- const MuteUnmuteOption = ({ roleName, peerList }: { peerList: HMSPeer[]; roleName: string }) => {
36
- const vanillaStore = useHMSVanillaStore();
37
- const store = vanillaStore.getState();
38
- const hmsActions = useHMSActions();
36
+ const DropdownWrapper = ({ children }: { children: React.ReactNode }) => {
37
+ const [openOptions, setOpenOptions] = useState(false);
38
+ if (React.Children.toArray(children).length === 0) {
39
+ return null;
40
+ }
41
+ React.Children.map(children, child => {
42
+ console.log({ child });
43
+ });
44
+ return (
45
+ <Dropdown.Root open={openOptions} onOpenChange={setOpenOptions}>
46
+ <Dropdown.Trigger
47
+ data-testid="role_group_options"
48
+ onClick={e => e.stopPropagation()}
49
+ className="role_actions"
50
+ asChild
51
+ css={{
52
+ p: '$1',
53
+ r: '$0',
54
+ c: '$on_surface_high',
55
+ visibility: openOptions ? 'visible' : 'hidden',
56
+ '&:hover': {
57
+ c: '$on_surface_medium',
58
+ },
59
+ '@md': {
60
+ visibility: 'visible',
61
+ },
62
+ }}
63
+ >
64
+ <Flex>
65
+ <VerticalMenuIcon />
66
+ </Flex>
67
+ </Dropdown.Trigger>
68
+ <Dropdown.Content
69
+ onClick={e => e.stopPropagation()}
70
+ css={{ w: 'max-content', bg: '$surface_default', py: 0 }}
71
+ align="end"
72
+ >
73
+ {children}
74
+ </Dropdown.Content>
75
+ </Dropdown.Root>
76
+ );
77
+ };
78
+
79
+ export const RoleOptions = ({ roleName, peerList }: { roleName: string; peerList: HMSPeer[] }) => {
39
80
  const permissions = useHMSStore(selectPermissions);
81
+ const hmsActions = useHMSActions();
82
+ const { elements } = useRoomLayoutConferencingScreen();
83
+ const { on_stage_role, off_stage_roles = [] } = (elements as DefaultConferencingScreen_Elements)?.on_stage_exp || {};
84
+ const canRemoveRoleFromStage = permissions?.changeRole && roleName === on_stage_role;
40
85
  const role = useHMSStore(selectRoleByRoleName(roleName));
86
+ const canPublishAudio = role.publishParams.allowed.includes('audio');
87
+ const canPublishVideo = role.publishParams.allowed.includes('video');
88
+ const tracks = useHMSStore(selectTracksMap);
41
89
 
42
90
  let isVideoOnForSomePeers = false;
43
91
  let isAudioOnForSomePeers = false;
@@ -46,8 +94,8 @@ const MuteUnmuteOption = ({ roleName, peerList }: { peerList: HMSPeer[]; roleNam
46
94
  if (peer.isLocal) {
47
95
  return;
48
96
  }
49
- const isAudioOn = !!peer.audioTrack && store.tracks[peer.audioTrack]?.enabled;
50
- const isVideoOn = !!peer.videoTrack && store.tracks[peer.videoTrack]?.enabled;
97
+ const isAudioOn = !!peer.audioTrack && tracks[peer.audioTrack]?.enabled;
98
+ const isVideoOn = !!peer.videoTrack && tracks[peer.videoTrack]?.enabled;
51
99
  isAudioOnForSomePeers = isAudioOnForSomePeers || isAudioOn;
52
100
  isVideoOnForSomePeers = isVideoOnForSomePeers || isVideoOn;
53
101
  });
@@ -60,75 +108,11 @@ const MuteUnmuteOption = ({ roleName, peerList }: { peerList: HMSPeer[]; roleNam
60
108
  }
61
109
  };
62
110
 
63
- if (!role) {
64
- return null;
65
- }
66
-
67
- return (
68
- <>
69
- {role.publishParams.allowed.includes('audio') && (
70
- <>
71
- {isAudioOnForSomePeers && permissions?.mute ? (
72
- <Dropdown.Item css={dropdownItemCSS} onClick={() => setTrackEnabled('audio', false)}>
73
- <MicOffIcon />
74
- <Text variant="sm" css={optionTextCSS}>
75
- Mute Audio for All
76
- </Text>
77
- </Dropdown.Item>
78
- ) : null}
79
-
80
- {!isAudioOnForSomePeers && permissions?.unmute ? (
81
- <Dropdown.Item css={dropdownItemCSS} onClick={() => setTrackEnabled('audio', true)}>
82
- <MicOnIcon />
83
- <Text variant="sm" css={optionTextCSS}>
84
- Request to Unmute Audio for All
85
- </Text>
86
- </Dropdown.Item>
87
- ) : null}
88
- </>
89
- )}
90
-
91
- {role.publishParams.allowed.includes('video') && (
92
- <>
93
- {isVideoOnForSomePeers && permissions?.mute ? (
94
- <Dropdown.Item css={dropdownItemCSS} onClick={() => setTrackEnabled('video', false)}>
95
- <VideoOffIcon />
96
- <Text variant="sm" css={optionTextCSS}>
97
- Mute Video for All
98
- </Text>
99
- </Dropdown.Item>
100
- ) : null}
101
-
102
- {!isVideoOnForSomePeers && permissions?.unmute ? (
103
- <Dropdown.Item css={dropdownItemCSS} onClick={() => setTrackEnabled('video', true)}>
104
- <VideoOnIcon />
105
- <Text variant="sm" css={optionTextCSS}>
106
- Request to Unmute Video for All
107
- </Text>
108
- </Dropdown.Item>
109
- ) : null}
110
- </>
111
- )}
112
- </>
113
- );
114
- };
115
-
116
- export const RoleOptions = ({ roleName, peerList }: { roleName: string; peerList: HMSPeer[] }) => {
117
- const [openOptions, setOpenOptions] = useState(false);
118
- const permissions = useHMSStore(selectPermissions);
119
- const hmsActions = useHMSActions();
120
- const { elements } = useRoomLayoutConferencingScreen();
121
- const { on_stage_role, off_stage_roles = [] } = (elements as DefaultConferencingScreen_Elements)?.on_stage_exp || {};
122
- const canMuteOrUnmute = permissions?.mute || permissions?.unmute;
123
- const canRemoveRoleFromStage = permissions?.changeRole && roleName === on_stage_role;
124
- const role = useHMSStore(selectRoleByRoleName(roleName));
125
-
126
111
  // on stage and off stage roles
127
112
  const canRemoveRoleFromRoom =
128
113
  permissions?.removeOthers && (on_stage_role === roleName || off_stage_roles?.includes(roleName));
129
114
 
130
115
  if (
131
- !(canMuteOrUnmute || canRemoveRoleFromStage || canRemoveRoleFromRoom) ||
132
116
  peerList.length === 0 ||
133
117
  // if only local peer is present no need to show any options
134
118
  (peerList.length === 1 && peerList[0].isLocal) ||
@@ -157,60 +141,75 @@ export const RoleOptions = ({ roleName, peerList }: { roleName: string; peerList
157
141
  };
158
142
 
159
143
  return (
160
- <Dropdown.Root open={openOptions} onOpenChange={setOpenOptions}>
161
- <Dropdown.Trigger
162
- data-testid="role_group_options"
163
- onClick={e => e.stopPropagation()}
164
- className="role_actions"
165
- asChild
166
- css={{
167
- p: '$1',
168
- r: '$0',
169
- c: '$on_surface_high',
170
- visibility: openOptions ? 'visible' : 'hidden',
171
- '&:hover': {
172
- c: '$on_surface_medium',
173
- },
174
- '@md': {
175
- visibility: 'visible',
176
- },
177
- }}
178
- >
179
- <Flex>
180
- <VerticalMenuIcon />
181
- </Flex>
182
- </Dropdown.Trigger>
183
- <Dropdown.Content
184
- onClick={e => e.stopPropagation()}
185
- css={{ w: 'max-content', bg: '$surface_default', py: 0 }}
186
- align="end"
187
- >
188
- {canRemoveRoleFromStage && (
189
- <Dropdown.Item
190
- css={{ ...dropdownItemCSS, borderBottom: '1px solid $border_bright' }}
191
- onClick={removeAllFromStage}
192
- >
193
- <PersonRectangleIcon />
194
- <Text variant="sm" css={optionTextCSS}>
195
- Remove all from Stage
196
- </Text>
197
- </Dropdown.Item>
198
- )}
199
-
200
- {canMuteOrUnmute && <MuteUnmuteOption peerList={peerList} roleName={roleName} />}
201
-
202
- {canRemoveRoleFromRoom && (
203
- <Dropdown.Item
204
- css={{ ...dropdownItemCSS, borderTop: '1px solid $border_bright', color: '$alert_error_default' }}
205
- onClick={removePeersFromRoom}
206
- >
207
- <RemoveUserIcon />
208
- <Text variant="sm" css={{ ...optionTextCSS, color: 'inherit' }}>
209
- Remove all from Room
210
- </Text>
211
- </Dropdown.Item>
212
- )}
213
- </Dropdown.Content>
214
- </Dropdown.Root>
144
+ <DropdownWrapper>
145
+ {canRemoveRoleFromStage ? (
146
+ <Dropdown.Item
147
+ css={{ ...dropdownItemCSS, borderBottom: '1px solid $border_bright' }}
148
+ onClick={removeAllFromStage}
149
+ >
150
+ <PersonRectangleIcon />
151
+ <Text variant="sm" css={optionTextCSS}>
152
+ Remove all from Stage
153
+ </Text>
154
+ </Dropdown.Item>
155
+ ) : null}
156
+
157
+ {match({ canPublishAudio, isAudioOnForSomePeers, canMute: permissions?.mute, canUnmute: permissions?.unmute })
158
+ .with({ canPublishAudio: true, isAudioOnForSomePeers: true, canMute: true }, () => {
159
+ return (
160
+ <Dropdown.Item css={dropdownItemCSS} onClick={() => setTrackEnabled('audio', false)}>
161
+ <MicOffIcon />
162
+ <Text variant="sm" css={optionTextCSS}>
163
+ Mute Audio for All
164
+ </Text>
165
+ </Dropdown.Item>
166
+ );
167
+ })
168
+ .with({ canPublishAudio: true, isAudioOnForSomePeers: false, canUnmute: true }, () => {
169
+ return (
170
+ <Dropdown.Item css={dropdownItemCSS} onClick={() => setTrackEnabled('audio', true)}>
171
+ <MicOnIcon />
172
+ <Text variant="sm" css={optionTextCSS}>
173
+ Request to Unmute Audio for All
174
+ </Text>
175
+ </Dropdown.Item>
176
+ );
177
+ })
178
+ .otherwise(() => null)}
179
+ {match({ canPublishVideo, isVideoOnForSomePeers, canMute: permissions?.mute, canUnmute: permissions?.unmute })
180
+ .with({ canPublishVideo: true, isVideoOnForSomePeers: true, canMute: true }, () => {
181
+ return (
182
+ <Dropdown.Item css={dropdownItemCSS} onClick={() => setTrackEnabled('video', false)}>
183
+ <VideoOffIcon />
184
+ <Text variant="sm" css={optionTextCSS}>
185
+ Mute Video for All
186
+ </Text>
187
+ </Dropdown.Item>
188
+ );
189
+ })
190
+ .with({ canPublishVideo: true, isVideoOnForSomePeers: false, canUnmute: true }, () => {
191
+ return (
192
+ <Dropdown.Item css={dropdownItemCSS} onClick={() => setTrackEnabled('video', true)}>
193
+ <VideoOnIcon />
194
+ <Text variant="sm" css={optionTextCSS}>
195
+ Request to Unmute Video for All
196
+ </Text>
197
+ </Dropdown.Item>
198
+ );
199
+ })
200
+ .otherwise(() => null)}
201
+
202
+ {canRemoveRoleFromRoom ? (
203
+ <Dropdown.Item
204
+ css={{ ...dropdownItemCSS, borderTop: '1px solid $border_bright', color: '$alert_error_default' }}
205
+ onClick={removePeersFromRoom}
206
+ >
207
+ <RemoveUserIcon />
208
+ <Text variant="sm" css={{ ...optionTextCSS, color: 'inherit' }}>
209
+ Remove all from Room
210
+ </Text>
211
+ </Dropdown.Item>
212
+ ) : null}
213
+ </DropdownWrapper>
215
214
  );
216
215
  };
@@ -3,7 +3,7 @@ import { Box, Flex, Slider } from '../../..';
3
3
  import { useHMSPlayerContext } from './PlayerContext';
4
4
  import { getPercentage } from './utils';
5
5
 
6
- export const VideoProgress = ({ isDvr = true }: { isDvr: boolean }) => {
6
+ export const VideoProgress = () => {
7
7
  const { hlsPlayer } = useHMSPlayerContext();
8
8
  const [videoProgress, setVideoProgress] = useState<number>(0);
9
9
  const [bufferProgress, setBufferProgress] = useState(0);
@@ -20,10 +20,11 @@ export const VideoProgress = ({ isDvr = true }: { isDvr: boolean }) => {
20
20
  if (!videoEl) {
21
21
  return;
22
22
  }
23
- const videoProgress = Math.floor(getPercentage(videoEl.currentTime, videoEl.duration));
23
+ const duration = isFinite(videoEl.duration) ? videoEl.duration : videoEl.seekable?.end(0) || 0;
24
+ const videoProgress = Math.floor(getPercentage(videoEl.currentTime, duration));
24
25
  let bufferProgress = 0;
25
26
  if (videoEl.buffered.length > 0) {
26
- bufferProgress = Math.floor(getPercentage(videoEl.buffered?.end(0), videoEl.duration));
27
+ bufferProgress = Math.floor(getPercentage(videoEl.buffered?.end(0), duration));
27
28
  }
28
29
 
29
30
  setVideoProgress(isNaN(videoProgress) ? 0 : videoProgress);
@@ -48,7 +49,7 @@ export const VideoProgress = ({ isDvr = true }: { isDvr: boolean }) => {
48
49
  return null;
49
50
  }
50
51
  return (
51
- <Flex align="center" css={{ cursor: 'pointer', h: '$2', alignSelf: 'stretch', pointerEvents: isDvr ? '' : 'none' }}>
52
+ <Flex align="center" css={{ cursor: 'pointer', h: '$2', alignSelf: 'stretch' }}>
52
53
  <Slider
53
54
  id="video-actual-rest"
54
55
  css={{
@@ -56,7 +57,6 @@ export const VideoProgress = ({ isDvr = true }: { isDvr: boolean }) => {
56
57
  h: '$2',
57
58
  zIndex: 1,
58
59
  transition: `all .2s ease .5s`,
59
- pointerEvents: isDvr ? '' : 'none',
60
60
  }}
61
61
  min={0}
62
62
  max={100}
@@ -64,7 +64,7 @@ export const VideoProgress = ({ isDvr = true }: { isDvr: boolean }) => {
64
64
  value={[videoProgress]}
65
65
  showTooltip={false}
66
66
  onValueChange={onProgress}
67
- thumbStyles={{ w: '$6', h: '$6', display: isDvr ? '' : 'none' }}
67
+ thumbStyles={{ w: '$6', h: '$6' }}
68
68
  />
69
69
  <Box
70
70
  id="video-buffer"
@@ -16,7 +16,7 @@ export const getFormattedTime = (milliseconds: number | undefined, precise = tru
16
16
  if (!precise && (hours || minutes)) {
17
17
  return formattedTime;
18
18
  }
19
- formattedTime += `${minutes >= 1 ? Math.floor(seconds) : seconds.toFixed(3)}s`;
19
+ formattedTime += `${precise ? seconds.toFixed(3) : Math.floor(seconds)}s`;
20
20
 
21
21
  return formattedTime;
22
22
  };
@@ -299,7 +299,7 @@ const HLSView = () => {
299
299
  }, []);
300
300
  const onDoubleClickHandler = useCallback(
301
301
  event => {
302
- if (!(isMobile || isLandscape)) {
302
+ if (!(isMobile || isLandscape) || hlsState?.variants[0]?.playlist_type !== HLSPlaylistType.DVR) {
303
303
  return;
304
304
  }
305
305
  const sidePercentage = (event.screenX * 100) / event.target.clientWidth;
@@ -314,7 +314,7 @@ const HLSView = () => {
314
314
  setIsSeekEnabled(false);
315
315
  }, 200);
316
316
  },
317
- [isLandscape, isMobile, onSeekTo],
317
+ [hlsState?.variants, isLandscape, isMobile, onSeekTo],
318
318
  );
319
319
  const onClickHandler = useCallback(() => {
320
320
  if (!(isMobile || isLandscape)) {
@@ -401,7 +401,7 @@ const HLSView = () => {
401
401
  <>
402
402
  {isMobile || isLandscape ? (
403
403
  <>
404
- {!showLoader && (
404
+ {!showLoader && hlsState?.variants[0]?.playlist_type === HLSPlaylistType.DVR && (
405
405
  <Flex
406
406
  align="center"
407
407
  justify="center"
@@ -511,7 +511,7 @@ const HLSView = () => {
511
511
  opacity: controlsVisible ? `1` : '0',
512
512
  }}
513
513
  >
514
- <HMSVideoPlayer.Progress isDvr={hlsState?.variants[0]?.playlist_type === HLSPlaylistType.DVR} />
514
+ {hlsState?.variants[0]?.playlist_type === HLSPlaylistType.DVR ? <HMSVideoPlayer.Progress /> : null}
515
515
  <HMSVideoPlayer.Controls.Root
516
516
  css={{
517
517
  p: '$4 $8',
@@ -520,24 +520,28 @@ const HLSView = () => {
520
520
  <HMSVideoPlayer.Controls.Left>
521
521
  {!(isMobile || isLandscape) && (
522
522
  <>
523
- <HMSVideoPlayer.Seeker
524
- onClick={() => {
525
- onSeekTo(-10);
526
- }}
527
- title="backward"
528
- >
529
- <BackwardArrowIcon width={20} height={20} />
530
- </HMSVideoPlayer.Seeker>
531
- <HMSVideoPlayer.PlayPauseButton isPaused={isPaused} />
532
- <HMSVideoPlayer.Seeker
533
- onClick={() => {
534
- onSeekTo(10);
535
- }}
536
- title="forward"
537
- >
538
- <ForwardArrowIcon width={20} height={20} />
539
- </HMSVideoPlayer.Seeker>
540
- {!isVideoLive ? <HMSVideoPlayer.Duration /> : null}
523
+ {hlsState?.variants[0]?.playlist_type === HLSPlaylistType.DVR ? (
524
+ <>
525
+ <HMSVideoPlayer.Seeker
526
+ onClick={() => {
527
+ onSeekTo(-10);
528
+ }}
529
+ title="backward"
530
+ >
531
+ <BackwardArrowIcon width={20} height={20} />
532
+ </HMSVideoPlayer.Seeker>
533
+ <HMSVideoPlayer.PlayPauseButton isPaused={isPaused} />
534
+ <HMSVideoPlayer.Seeker
535
+ onClick={() => {
536
+ onSeekTo(10);
537
+ }}
538
+ title="forward"
539
+ >
540
+ <ForwardArrowIcon width={20} height={20} />
541
+ </HMSVideoPlayer.Seeker>
542
+ {!isVideoLive ? <HMSVideoPlayer.Duration /> : null}
543
+ </>
544
+ ) : null}
541
545
  <HMSVideoPlayer.Volume />
542
546
  </>
543
547
  )}
@@ -572,7 +576,11 @@ const HLSView = () => {
572
576
  </Flex>
573
577
  </Tooltip>
574
578
  </IconButton>
575
- {(isMobile || isLandscape) && !isVideoLive ? <HMSVideoPlayer.Duration /> : null}
579
+ {(isMobile || isLandscape) &&
580
+ !isVideoLive &&
581
+ hlsState?.variants[0]?.playlist_type === HLSPlaylistType.DVR ? (
582
+ <HMSVideoPlayer.Duration />
583
+ ) : null}
576
584
  </HMSVideoPlayer.Controls.Left>
577
585
 
578
586
  <HMSVideoPlayer.Controls.Right>