@100mslive/roomkit-react 0.2.8-alpha.5 → 0.2.8-alpha.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. package/dist/{HLSView-CVNJNDUQ.js → HLSView-MYKM5AXS.js} +239 -144
  2. package/dist/HLSView-MYKM5AXS.js.map +7 -0
  3. package/dist/Prebuilt/components/Chat/MwebChatOption.d.ts +1 -1
  4. package/dist/Prebuilt/components/HMSVideo/PlayPauseButton.d.ts +6 -0
  5. package/dist/Prebuilt/components/HMSVideo/SeekControls.d.ts +7 -0
  6. package/dist/Prebuilt/components/HMSVideo/VideoProgress.d.ts +3 -1
  7. package/dist/Prebuilt/components/HMSVideo/index.d.ts +10 -2
  8. package/dist/Prebuilt/layouts/SidePane.d.ts +1 -1
  9. package/dist/{chunk-25HZFDG5.js → chunk-DRBTAFKN.js} +356 -222
  10. package/dist/chunk-DRBTAFKN.js.map +7 -0
  11. package/dist/index.cjs.js +949 -712
  12. package/dist/index.cjs.js.map +4 -4
  13. package/dist/index.js +1 -1
  14. package/dist/meta.cjs.json +217 -78
  15. package/dist/meta.esbuild.json +227 -87
  16. package/package.json +7 -6
  17. package/src/Prebuilt/components/AppData/useSidepane.js +34 -7
  18. package/src/Prebuilt/components/AuthToken.jsx +1 -1
  19. package/src/Prebuilt/components/Chat/ChatFooter.tsx +1 -1
  20. package/src/Prebuilt/components/Chat/MwebChatOption.tsx +1 -1
  21. package/src/Prebuilt/components/ConferenceScreen.tsx +11 -14
  22. package/src/Prebuilt/components/Footer/RoleOptions.tsx +32 -15
  23. package/src/Prebuilt/components/HMSVideo/HLSAutoplayBlockedPrompt.tsx +31 -1
  24. package/src/Prebuilt/components/HMSVideo/HMSVideo.jsx +7 -1
  25. package/src/Prebuilt/components/HMSVideo/PlayPauseButton.tsx +27 -0
  26. package/src/Prebuilt/components/HMSVideo/SeekControls.tsx +22 -0
  27. package/src/Prebuilt/components/HMSVideo/VideoProgress.tsx +4 -3
  28. package/src/Prebuilt/components/HMSVideo/VolumeControl.tsx +1 -1
  29. package/src/Prebuilt/components/HMSVideo/index.ts +4 -2
  30. package/src/Prebuilt/components/Header/StreamActions.tsx +1 -1
  31. package/src/Prebuilt/components/MoreSettings/SplitComponents/DesktopOptions.tsx +37 -31
  32. package/src/Prebuilt/components/MoreSettings/SplitComponents/MwebOptions.tsx +6 -5
  33. package/src/Prebuilt/components/Notifications/HandRaisedNotifications.tsx +2 -2
  34. package/src/Prebuilt/components/Notifications/PeerNotifications.tsx +1 -1
  35. package/src/Prebuilt/components/Polls/Voting/QuestionCard.jsx +19 -8
  36. package/src/Prebuilt/components/SidePaneTabs.tsx +27 -35
  37. package/src/Prebuilt/components/StatsForNerds.jsx +14 -6
  38. package/src/Prebuilt/components/Streaming/Common.jsx +1 -1
  39. package/src/Prebuilt/components/TileMenu/TileMenuContent.tsx +2 -2
  40. package/src/Prebuilt/components/Toast/ToastBatcher.js +8 -1
  41. package/src/Prebuilt/components/Toast/ToastConfig.jsx +17 -0
  42. package/src/Prebuilt/layouts/HLSView.jsx +109 -69
  43. package/src/Prebuilt/layouts/SidePane.tsx +125 -67
  44. package/src/Prebuilt/layouts/VideoStreamingSection.tsx +2 -1
  45. package/src/Prebuilt/provider/roomLayoutProvider/index.tsx +1 -1
  46. package/src/Sheet/Sheet.tsx +4 -0
  47. package/dist/HLSView-CVNJNDUQ.js.map +0 -7
  48. package/dist/chunk-25HZFDG5.js.map +0 -7
  49. package/src/Prebuilt/components/HMSVideo/PlayButton.jsx +0 -13
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "prebuilt",
11
11
  "roomkit"
12
12
  ],
13
- "version": "0.2.8-alpha.5",
13
+ "version": "0.2.8-alpha.7",
14
14
  "author": "100ms",
15
15
  "license": "MIT",
16
16
  "repository": {
@@ -82,11 +82,11 @@
82
82
  "react": ">=17.0.2 <19.0.0"
83
83
  },
84
84
  "dependencies": {
85
- "@100mslive/hls-player": "0.2.8-alpha.5",
85
+ "@100mslive/hls-player": "0.2.8-alpha.7",
86
86
  "@100mslive/hms-noise-cancellation": "0.0.0-alpha.1",
87
- "@100mslive/hms-virtual-background": "1.12.8-alpha.5",
88
- "@100mslive/react-icons": "0.9.8-alpha.5",
89
- "@100mslive/react-sdk": "0.9.8-alpha.5",
87
+ "@100mslive/hms-virtual-background": "1.12.8-alpha.7",
88
+ "@100mslive/react-icons": "0.9.8-alpha.7",
89
+ "@100mslive/react-sdk": "0.9.8-alpha.7",
90
90
  "@100mslive/types-prebuilt": "0.12.7",
91
91
  "@emoji-mart/data": "^1.0.6",
92
92
  "@emoji-mart/react": "^1.0.1",
@@ -118,8 +118,9 @@
118
118
  "react-window": "^1.8.7",
119
119
  "recordrtc": "^5.6.2",
120
120
  "screenfull": "^5.1.0",
121
+ "ts-pattern": "4.3.0",
121
122
  "uuid": "^8.3.2",
122
123
  "worker-timers": "^7.0.40"
123
124
  },
124
- "gitHead": "6ba28232c0bb10f4ba990777369c5c5b132203b1"
125
+ "gitHead": "48bc1b57113a4245b79055a80f4f1f5d3b8795aa"
125
126
  }
@@ -1,4 +1,5 @@
1
1
  import { useCallback } from 'react';
2
+ import { match, P } from 'ts-pattern';
2
3
  import { selectAppData, useHMSActions, useHMSStore, useHMSVanillaStore } from '@100mslive/react-sdk';
3
4
  import { usePollViewState } from './useUISettings';
4
5
  import { APP_DATA, POLL_STATE, POLL_VIEWS, SIDE_PANE_OPTIONS } from '../../common/constants';
@@ -45,13 +46,39 @@ export const usePollViewToggle = () => {
45
46
 
46
47
  const togglePollView = useCallback(
47
48
  id => {
48
- id = typeof id === 'string' ? id : undefined;
49
- const newView = id ? POLL_VIEWS.VOTE : isOpen && view ? null : POLL_VIEWS.CREATE_POLL_QUIZ;
50
- setPollState({
51
- [POLL_STATE.pollInView]: id,
52
- [POLL_STATE.view]: newView,
53
- });
54
- hmsActions.setAppData(APP_DATA.sidePane, newView ? SIDE_PANE_OPTIONS.POLLS : '');
49
+ match({ id, isOpen, view })
50
+ .with(
51
+ {
52
+ id: P.string,
53
+ },
54
+ () => {
55
+ setPollState({
56
+ [POLL_STATE.pollInView]: id,
57
+ [POLL_STATE.view]: POLL_VIEWS.VOTE,
58
+ });
59
+ hmsActions.setAppData(APP_DATA.sidePane, SIDE_PANE_OPTIONS.POLLS);
60
+ },
61
+ )
62
+ .with(
63
+ {
64
+ isOpen: true,
65
+ view: P.when(view => !!view),
66
+ },
67
+ () => {
68
+ setPollState({
69
+ [POLL_STATE.pollInView]: undefined,
70
+ [POLL_STATE.view]: null,
71
+ });
72
+ hmsActions.setAppData(APP_DATA.sidePane, '');
73
+ },
74
+ )
75
+ .otherwise(() => {
76
+ setPollState({
77
+ [POLL_STATE.pollInView]: undefined,
78
+ [POLL_STATE.view]: POLL_VIEWS.CREATE_POLL_QUIZ,
79
+ });
80
+ hmsActions.setAppData(APP_DATA.sidePane, SIDE_PANE_OPTIONS.POLLS);
81
+ });
55
82
  },
56
83
  [hmsActions, view, setPollState, isOpen],
57
84
  );
@@ -70,7 +70,7 @@ const AuthToken = React.memo(({ authTokenByRoomCodeEndpoint, defaultAuthToken })
70
70
  alignItems: 'center',
71
71
  }}
72
72
  >
73
- <img src={errorImage} height={80} width={80} />
73
+ <img src={errorImage} height={80} width={80} alt="Token Error" />
74
74
  <Text variant="h4" css={{ textAlign: 'center', mb: '$4', mt: '$10' }}>
75
75
  {error.title}
76
76
  </Text>
@@ -99,7 +99,7 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo
99
99
  setRoleSelector(defaultSelection);
100
100
  } else {
101
101
  // @ts-ignore
102
- if (!(isMobile || isLandscapeHLSStream) || !elements?.chat?.disable_autofocus) {
102
+ if (!(isMobile || isLandscapeHLSStream) && !elements?.chat?.disable_autofocus) {
103
103
  inputRef.current?.focus();
104
104
  }
105
105
  }
@@ -8,7 +8,7 @@ export const MwebChatOption = ({
8
8
  onClick,
9
9
  color = '$on_surface_high',
10
10
  }: {
11
- icon: any;
11
+ icon: React.ReactNode;
12
12
  text: string;
13
13
  onClick: () => void | Promise<void>;
14
14
  color?: string;
@@ -11,7 +11,6 @@ import {
11
11
  useHMSStore,
12
12
  } from '@100mslive/react-sdk';
13
13
  import { Footer } from './Footer/Footer';
14
- import { LeaveRoom } from './Leave/LeaveRoom';
15
14
  import { MoreSettings } from './MoreSettings/MoreSettings';
16
15
  import { HLSFailureModal } from './Notifications/HLSFailureModal';
17
16
  // @ts-ignore: No implicit Any
@@ -57,16 +56,17 @@ export const ConferenceScreen = () => {
57
56
  const noAVPermissions = !(toggleAudio || toggleVideo);
58
57
  // using it in hls stream to show action button when chat is disabled
59
58
  const showChat = !!screenProps.elements?.chat;
60
- const toggleControls = () => {
61
- if (dropdownListRef.current?.length === 0 && isMobileDevice) {
62
- setHideControls(value => !value);
63
- }
64
- };
65
59
  const autoRoomJoined = useRef(isPreviewScreenEnabled);
66
60
  const isMobileHLSStream = useMobileHLSStream();
67
61
  const isLandscapeHLSStream = useLandscapeHLSStream();
68
62
  const isMwebHLSStream = isMobileHLSStream || isLandscapeHLSStream;
69
63
 
64
+ const toggleControls = () => {
65
+ if (dropdownListRef.current?.length === 0 && isMobileDevice && !isMwebHLSStream) {
66
+ setHideControls(value => !value);
67
+ }
68
+ };
69
+
70
70
  useEffect(() => {
71
71
  let timeout: undefined | ReturnType<typeof setTimeout>;
72
72
  dropdownListRef.current = dropdownList || [];
@@ -118,6 +118,8 @@ export const ConferenceScreen = () => {
118
118
  return <FullPageProgress text={roomState === HMSRoomState.Connecting ? 'Joining...' : ''} />;
119
119
  }
120
120
 
121
+ const hideControlsForStreaming = isMwebHLSStream ? true : hideControls;
122
+
121
123
  return (
122
124
  <>
123
125
  {isHLSStarted ? (
@@ -132,7 +134,7 @@ export const ConferenceScreen = () => {
132
134
  css={{
133
135
  h: '$18',
134
136
  transition: 'margin 0.3s ease-in-out',
135
- marginTop: hideControls ? `-${headerRef.current?.clientHeight}px` : 'none',
137
+ marginTop: hideControlsForStreaming ? `-${headerRef.current?.clientHeight}px` : 'none',
136
138
  '@md': {
137
139
  h: '$17',
138
140
  },
@@ -142,11 +144,6 @@ export const ConferenceScreen = () => {
142
144
  <Header />
143
145
  </Box>
144
146
  )}
145
- {isMwebHLSStream && (
146
- <Flex align="center" gap="2" css={{ position: 'absolute', left: '$4', top: '$4', zIndex: 1 }}>
147
- <LeaveRoom screenType={screenProps.screenType} />
148
- </Flex>
149
- )}
150
147
  <Box
151
148
  css={{
152
149
  w: '100%',
@@ -169,7 +166,7 @@ export const ConferenceScreen = () => {
169
166
  <VideoStreamingSection
170
167
  screenType={screenProps.screenType}
171
168
  elements={screenProps.elements}
172
- hideControls={hideControls}
169
+ hideControls={hideControlsForStreaming}
173
170
  />
174
171
  ) : null}
175
172
  </Box>
@@ -181,7 +178,7 @@ export const ConferenceScreen = () => {
181
178
  maxHeight: '$24',
182
179
  transition: 'margin 0.3s ease-in-out',
183
180
  bg: '$background_dim',
184
- marginBottom: hideControls ? `-${footerRef.current?.clientHeight}px` : undefined,
181
+ marginBottom: hideControlsForStreaming ? `-${footerRef.current?.clientHeight}px` : undefined,
185
182
  '@md': {
186
183
  maxHeight: 'unset',
187
184
  bg: screenProps.screenType === 'hls_live_streaming' ? 'transparent' : '$background_dim',
@@ -25,7 +25,12 @@ import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvid
25
25
  import { getMetadata } from '../../common/utils';
26
26
 
27
27
  const dropdownItemCSS = { backgroundColor: '$surface_default', gap: '$4', p: '$8' };
28
- const optionTextCSS = { fontWeight: '$semiBold', color: '$on_surface_high', textTransform: 'none' };
28
+ const optionTextCSS = {
29
+ fontWeight: '$semiBold',
30
+ color: '$on_surface_high',
31
+ textTransform: 'none',
32
+ whiteSpace: 'nowrap',
33
+ };
29
34
 
30
35
  const MuteUnmuteOption = ({ roleName, peerList }: { peerList: HMSPeer[]; roleName: string }) => {
31
36
  const vanillaStore = useHMSVanillaStore();
@@ -34,8 +39,8 @@ const MuteUnmuteOption = ({ roleName, peerList }: { peerList: HMSPeer[]; roleNam
34
39
  const permissions = useHMSStore(selectPermissions);
35
40
  const role = useHMSStore(selectRoleByRoleName(roleName));
36
41
 
37
- let allPeersHaveVideoOn = true;
38
- let allPeersHaveAudioOn = true;
42
+ let isVideoOnForSomePeers = false;
43
+ let isAudioOnForSomePeers = false;
39
44
 
40
45
  peerList.forEach(peer => {
41
46
  if (peer.isLocal) {
@@ -43,8 +48,8 @@ const MuteUnmuteOption = ({ roleName, peerList }: { peerList: HMSPeer[]; roleNam
43
48
  }
44
49
  const isAudioOn = !!peer.audioTrack && store.tracks[peer.audioTrack]?.enabled;
45
50
  const isVideoOn = !!peer.videoTrack && store.tracks[peer.videoTrack]?.enabled;
46
- allPeersHaveAudioOn = allPeersHaveAudioOn && isAudioOn;
47
- allPeersHaveVideoOn = allPeersHaveVideoOn && isVideoOn;
51
+ isAudioOnForSomePeers = isAudioOnForSomePeers || isAudioOn;
52
+ isVideoOnForSomePeers = isVideoOnForSomePeers || isVideoOn;
48
53
  });
49
54
 
50
55
  const setTrackEnabled = async (type: 'audio' | 'video', enabled = false) => {
@@ -55,11 +60,15 @@ const MuteUnmuteOption = ({ roleName, peerList }: { peerList: HMSPeer[]; roleNam
55
60
  }
56
61
  };
57
62
 
63
+ if (!role) {
64
+ return null;
65
+ }
66
+
58
67
  return (
59
68
  <>
60
- {role.publishParams.allowed?.includes('audio') && (
69
+ {role.publishParams.allowed.includes('audio') && (
61
70
  <>
62
- {allPeersHaveAudioOn && permissions?.mute ? (
71
+ {isAudioOnForSomePeers && permissions?.mute ? (
63
72
  <Dropdown.Item css={dropdownItemCSS} onClick={() => setTrackEnabled('audio', false)}>
64
73
  <MicOffIcon />
65
74
  <Text variant="sm" css={optionTextCSS}>
@@ -68,20 +77,20 @@ const MuteUnmuteOption = ({ roleName, peerList }: { peerList: HMSPeer[]; roleNam
68
77
  </Dropdown.Item>
69
78
  ) : null}
70
79
 
71
- {!allPeersHaveAudioOn && permissions?.unmute ? (
80
+ {!isAudioOnForSomePeers && permissions?.unmute ? (
72
81
  <Dropdown.Item css={dropdownItemCSS} onClick={() => setTrackEnabled('audio', true)}>
73
82
  <MicOnIcon />
74
83
  <Text variant="sm" css={optionTextCSS}>
75
- Unmute Audio for All
84
+ Request to Unmute Audio for All
76
85
  </Text>
77
86
  </Dropdown.Item>
78
87
  ) : null}
79
88
  </>
80
89
  )}
81
90
 
82
- {role.publishParams.allowed?.includes('audio') && (
91
+ {role.publishParams.allowed.includes('video') && (
83
92
  <>
84
- {allPeersHaveVideoOn && permissions?.mute ? (
93
+ {isVideoOnForSomePeers && permissions?.mute ? (
85
94
  <Dropdown.Item css={dropdownItemCSS} onClick={() => setTrackEnabled('video', false)}>
86
95
  <VideoOffIcon />
87
96
  <Text variant="sm" css={optionTextCSS}>
@@ -90,11 +99,11 @@ const MuteUnmuteOption = ({ roleName, peerList }: { peerList: HMSPeer[]; roleNam
90
99
  </Dropdown.Item>
91
100
  ) : null}
92
101
 
93
- {!allPeersHaveVideoOn && permissions?.unmute ? (
102
+ {!isVideoOnForSomePeers && permissions?.unmute ? (
94
103
  <Dropdown.Item css={dropdownItemCSS} onClick={() => setTrackEnabled('video', true)}>
95
104
  <VideoOnIcon />
96
105
  <Text variant="sm" css={optionTextCSS}>
97
- Unmute Video for All
106
+ Request to Unmute Video for All
98
107
  </Text>
99
108
  </Dropdown.Item>
100
109
  ) : null}
@@ -112,11 +121,19 @@ export const RoleOptions = ({ roleName, peerList }: { roleName: string; peerList
112
121
  const { on_stage_role, off_stage_roles = [] } = (elements as DefaultConferencingScreen_Elements)?.on_stage_exp || {};
113
122
  const canMuteOrUnmute = permissions?.mute || permissions?.unmute;
114
123
  const canRemoveRoleFromStage = permissions?.changeRole && roleName === on_stage_role;
124
+ const role = useHMSStore(selectRoleByRoleName(roleName));
125
+
115
126
  // on stage and off stage roles
116
127
  const canRemoveRoleFromRoom =
117
128
  permissions?.removeOthers && (on_stage_role === roleName || off_stage_roles?.includes(roleName));
118
129
 
119
- if (!(canMuteOrUnmute || canRemoveRoleFromStage || canRemoveRoleFromRoom) || peerList.length === 0) {
130
+ if (
131
+ !(canMuteOrUnmute || canRemoveRoleFromStage || canRemoveRoleFromRoom) ||
132
+ peerList.length === 0 ||
133
+ // if only local peer is present no need to show any options
134
+ (peerList.length === 1 && peerList[0].isLocal) ||
135
+ !role
136
+ ) {
120
137
  return null;
121
138
  }
122
139
 
@@ -165,7 +182,7 @@ export const RoleOptions = ({ roleName, peerList }: { roleName: string; peerList
165
182
  </Dropdown.Trigger>
166
183
  <Dropdown.Content
167
184
  onClick={e => e.stopPropagation()}
168
- css={{ w: 'max-content', maxWidth: '$64', bg: '$surface_default', py: 0 }}
185
+ css={{ w: 'max-content', bg: '$surface_default', py: 0 }}
169
186
  align="end"
170
187
  >
171
188
  {canRemoveRoleFromStage && (
@@ -1,7 +1,10 @@
1
1
  import React from 'react';
2
- import { Button, Dialog, Text } from '../../..';
2
+ import { useMedia } from 'react-use';
3
+ import { VolumeTwoIcon } from '@100mslive/react-icons';
4
+ import { Button, config, Dialog, IconButton, Text } from '../../..';
3
5
  // @ts-ignore
4
6
  import { DialogContent, DialogRow } from '../../primitives/DialogContent';
7
+ import { useIsLandscape } from '../../common/hooks';
5
8
 
6
9
  export function HLSAutoplayBlockedPrompt({
7
10
  open,
@@ -10,6 +13,33 @@ export function HLSAutoplayBlockedPrompt({
10
13
  open: boolean;
11
14
  unblockAutoPlay: () => Promise<void>;
12
15
  }) {
16
+ const isLandscape = useIsLandscape();
17
+ const isMobile = useMedia(config.media.md);
18
+ if ((isMobile || isLandscape) && open) {
19
+ return (
20
+ <IconButton
21
+ css={{
22
+ border: '1px solid white',
23
+ bg: 'white',
24
+ color: '#000',
25
+ r: '$2',
26
+ }}
27
+ onClick={async () => await unblockAutoPlay()}
28
+ >
29
+ <VolumeTwoIcon width="32" height="32" />
30
+ <Text
31
+ variant="body1"
32
+ css={{
33
+ fontWeight: '$semiBold',
34
+ px: '$2',
35
+ color: '#000',
36
+ }}
37
+ >
38
+ Tap To Unmute
39
+ </Text>
40
+ </IconButton>
41
+ );
42
+ }
13
43
  return (
14
44
  <Dialog.Root
15
45
  open={open}
@@ -1,7 +1,11 @@
1
1
  import React, { forwardRef } from 'react';
2
- import { Flex } from '../../../';
2
+ import { useMedia } from 'react-use';
3
+ import { config, Flex } from '../../../';
4
+ import { useIsLandscape } from '../../common/hooks';
3
5
 
4
6
  export const HMSVideo = forwardRef(({ children, ...props }, videoRef) => {
7
+ const isLandscape = useIsLandscape();
8
+ const isMobile = useMedia(config.media.md);
5
9
  return (
6
10
  <Flex
7
11
  data-testid="hms-video"
@@ -36,6 +40,8 @@ export const HMSVideo = forwardRef(({ children, ...props }, videoRef) => {
36
40
  minHeight: '0',
37
41
  objectFit: 'contain',
38
42
  width: 'inherit',
43
+ height: isLandscape || isMobile ? '100%' : '',
44
+ position: isLandscape || isMobile ? 'absolute' : '',
39
45
  }}
40
46
  ref={videoRef}
41
47
  playsInline
@@ -0,0 +1,27 @@
1
+ import React, { MouseEvent } from 'react';
2
+ import { PauseIcon, PlayIcon } from '@100mslive/react-icons';
3
+ import { IconButton, Tooltip } from '../../..';
4
+ import { useHMSPlayerContext } from './PlayerContext';
5
+
6
+ export const PlayPauseButton = ({
7
+ isPaused,
8
+ width = 20,
9
+ height = 20,
10
+ }: {
11
+ isPaused: boolean;
12
+ width: number;
13
+ height: number;
14
+ }) => {
15
+ const { hlsPlayer } = useHMSPlayerContext();
16
+ const onClick = async (event: MouseEvent) => {
17
+ event?.stopPropagation();
18
+ isPaused ? await hlsPlayer?.play() : hlsPlayer?.pause();
19
+ };
20
+ return (
21
+ <Tooltip title={isPaused ? 'Play' : 'Pause'} side="top">
22
+ <IconButton onClick={onClick} data-testid="play_pause_btn">
23
+ {isPaused ? <PlayIcon width={width} height={height} /> : <PauseIcon width={width} height={height} />}
24
+ </IconButton>
25
+ </Tooltip>
26
+ );
27
+ };
@@ -0,0 +1,22 @@
1
+ import React, { MouseEventHandler } from 'react';
2
+ import { IconButton, Tooltip } from '../../..';
3
+
4
+ export const SeekControls = ({
5
+ title,
6
+ onClick,
7
+ children,
8
+ css,
9
+ }: {
10
+ title: string;
11
+ onClick?: MouseEventHandler<HTMLButtonElement>;
12
+ css: any;
13
+ children: React.ReactNode;
14
+ }) => {
15
+ return (
16
+ <Tooltip title={title} side="top">
17
+ <IconButton onClick={onClick} data-testid="backward_forward_arrow_btn" css={css}>
18
+ {children}
19
+ </IconButton>
20
+ </Tooltip>
21
+ );
22
+ };
@@ -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 = () => {
6
+ export const VideoProgress = ({ isDvr = true }: { isDvr: boolean }) => {
7
7
  const { hlsPlayer } = useHMSPlayerContext();
8
8
  const [videoProgress, setVideoProgress] = useState<number>(0);
9
9
  const [bufferProgress, setBufferProgress] = useState(0);
@@ -48,7 +48,7 @@ export const VideoProgress = () => {
48
48
  return null;
49
49
  }
50
50
  return (
51
- <Flex align="center" css={{ cursor: 'pointer', h: '$2', alignSelf: 'stretch' }}>
51
+ <Flex align="center" css={{ cursor: 'pointer', h: '$2', alignSelf: 'stretch', pointerEvents: isDvr ? '' : 'none' }}>
52
52
  <Slider
53
53
  id="video-actual-rest"
54
54
  css={{
@@ -56,6 +56,7 @@ export const VideoProgress = () => {
56
56
  h: '$2',
57
57
  zIndex: 1,
58
58
  transition: `all .2s ease .5s`,
59
+ pointerEvents: isDvr ? '' : 'none',
59
60
  }}
60
61
  min={0}
61
62
  max={100}
@@ -63,7 +64,7 @@ export const VideoProgress = () => {
63
64
  value={[videoProgress]}
64
65
  showTooltip={false}
65
66
  onValueChange={onProgress}
66
- thumbStyles={{ w: '$6', h: '$6' }}
67
+ thumbStyles={{ w: '$6', h: '$6', display: isDvr ? '' : 'none' }}
67
68
  />
68
69
  <Box
69
70
  id="video-buffer"
@@ -5,7 +5,7 @@ import { useHMSPlayerContext } from './PlayerContext';
5
5
 
6
6
  export const VolumeControl = () => {
7
7
  const { hlsPlayer } = useHMSPlayerContext();
8
- const [volume, setVolume] = useState(hlsPlayer?.volume || 0);
8
+ const [volume, setVolume] = useState(hlsPlayer?.volume ?? 100);
9
9
  const [showSlider, setShowSlider] = useState(false);
10
10
 
11
11
  return (
@@ -2,14 +2,15 @@
2
2
  import { LeftControls, RightControls, VideoControls } from './Controls';
3
3
  // @ts-ignore
4
4
  import { HMSVideo } from './HMSVideo';
5
- import { PlayButton } from './PlayButton';
5
+ import { PlayPauseButton } from './PlayPauseButton';
6
+ import { SeekControls } from './SeekControls';
6
7
  import { VideoProgress } from './VideoProgress';
7
8
  import { VideoTime } from './VideoTime';
8
9
  import { VolumeControl } from './VolumeControl';
9
10
 
10
11
  export const HMSVideoPlayer = {
11
12
  Root: HMSVideo,
12
- PlayButton: PlayButton,
13
+ PlayPauseButton: PlayPauseButton,
13
14
  Progress: VideoProgress,
14
15
  Duration: VideoTime,
15
16
  Volume: VolumeControl,
@@ -18,4 +19,5 @@ export const HMSVideoPlayer = {
18
19
  Left: LeftControls,
19
20
  Right: RightControls,
20
21
  },
22
+ Seeker: SeekControls,
21
23
  };
@@ -42,7 +42,7 @@ export const LiveStatus = () => {
42
42
  setLiveTime(Date.now() - timeStamp.getTime());
43
43
  }
44
44
  }, 1000);
45
- }, [hlsState?.running, hlsState?.variants]);
45
+ }, [hlsState?.running, hlsState?.variants, screenType]);
46
46
 
47
47
  useEffect(() => {
48
48
  if (hlsState?.running) {
@@ -5,6 +5,7 @@ import {
5
5
  DefaultConferencingScreen_Elements,
6
6
  HLSLiveStreamingScreen_Elements,
7
7
  } from '@100mslive/types-prebuilt';
8
+ import { match } from 'ts-pattern';
8
9
  import { selectAppData, selectLocalPeerID, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
9
10
  import { BrbIcon, CheckIcon, HamburgerMenuIcon, InfoIcon, PipIcon, SettingsIcon } from '@100mslive/react-icons';
10
11
  import { Checkbox, Dropdown, Flex, Text, Tooltip } from '../../../..';
@@ -143,41 +144,46 @@ export const DesktopOptions = ({
143
144
  Settings
144
145
  </Text>
145
146
  </Dropdown.Item>
147
+ {match({ screenType, isSupported: HMSHLSPlayer.isSupported() })
148
+ .with({ screenType: 'hls_live_streaming', isSupported: false }, () => null)
149
+ .with({ screenType: 'hls_live_streaming', isSupported: true }, () => {
150
+ return (
151
+ <Dropdown.Item
152
+ onClick={() => hmsActions.setAppData(APP_DATA.hlsStats, !enablHlsStats)}
153
+ data-testid="hls_stats"
154
+ >
155
+ <Checkbox.Root
156
+ css={{ margin: '$2' }}
157
+ checked={enablHlsStats}
158
+ onCheckedChange={() => hmsActions.setAppData(APP_DATA.hlsStats, !enablHlsStats)}
159
+ >
160
+ <Checkbox.Indicator>
161
+ <CheckIcon width={16} height={16} />
162
+ </Checkbox.Indicator>
163
+ </Checkbox.Root>
164
+ <Flex justify="between" css={{ width: '100%' }}>
165
+ <Text variant="sm" css={{ ml: '$4' }}>
166
+ Show HLS Stats
167
+ </Text>
146
168
 
147
- {screenType === 'hls_live_streaming' ? (
148
- HMSHLSPlayer.isSupported() ? (
169
+ <Text variant="sm" css={{ ml: '$4' }}>
170
+ {`${isMacOS ? '⌘' : 'ctrl'} + ]`}
171
+ </Text>
172
+ </Flex>
173
+ </Dropdown.Item>
174
+ );
175
+ })
176
+ .otherwise(() => (
149
177
  <Dropdown.Item
150
- onClick={() => hmsActions.setAppData(APP_DATA.hlsStats, !enablHlsStats)}
151
- data-testid="hls_stats"
178
+ onClick={() => updateState(MODALS.STATS_FOR_NERDS, true)}
179
+ data-testid="stats_for_nerds_btn"
152
180
  >
153
- <Checkbox.Root
154
- css={{ margin: '$2' }}
155
- checked={enablHlsStats}
156
- onCheckedChange={() => hmsActions.setAppData(APP_DATA.hlsStats, !enablHlsStats)}
157
- >
158
- <Checkbox.Indicator>
159
- <CheckIcon width={16} height={16} />
160
- </Checkbox.Indicator>
161
- </Checkbox.Root>
162
- <Flex justify="between" css={{ width: '100%' }}>
163
- <Text variant="sm" css={{ ml: '$4' }}>
164
- Show HLS Stats
165
- </Text>
166
-
167
- <Text variant="sm" css={{ ml: '$4' }}>
168
- {`${isMacOS ? '⌘' : 'ctrl'} + ]`}
169
- </Text>
170
- </Flex>
181
+ <InfoIcon />
182
+ <Text variant="sm" css={{ ml: '$4' }}>
183
+ Stats for Nerds
184
+ </Text>
171
185
  </Dropdown.Item>
172
- ) : null
173
- ) : (
174
- <Dropdown.Item onClick={() => updateState(MODALS.STATS_FOR_NERDS, true)} data-testid="stats_for_nreds_btn">
175
- <InfoIcon />
176
- <Text variant="sm" css={{ ml: '$4' }}>
177
- Stats for Nerds
178
- </Text>
179
- </Dropdown.Item>
180
- )}
186
+ ))}
181
187
  </Dropdown.Content>
182
188
  </Dropdown.Root>
183
189
  {openModals.has(MODALS.BULK_ROLE_CHANGE) && (
@@ -1,6 +1,7 @@
1
1
  import React, { useRef, useState } from 'react';
2
2
  import { useClickAway } from 'react-use';
3
3
  import { ConferencingScreen, DefaultConferencingScreen_Elements } from '@100mslive/types-prebuilt';
4
+ import { match } from 'ts-pattern';
4
5
  import {
5
6
  selectIsConnectedToRoom,
6
7
  selectPeerCount,
@@ -287,11 +288,11 @@ export const MwebOptions = ({
287
288
  >
288
289
  {isRecordingLoading ? <Loading /> : <RecordIcon />}
289
290
  <ActionTile.Title>
290
- {isBrowserRecordingOn
291
- ? 'Recording On'
292
- : isRecordingLoading
293
- ? 'Starting Recording'
294
- : 'Start Recording'}
291
+ {match({ isBrowserRecordingOn, isRecordingLoading })
292
+ .with({ isBrowserRecordingOn: true, isRecordingLoading: false }, () => 'Recording On')
293
+ .with({ isRecordingLoading: true }, () => 'Starting Recording')
294
+ .with({ isRecordingLoading: false }, () => 'Start Recording')
295
+ .otherwise(() => null)}
295
296
  </ActionTile.Title>
296
297
  </ActionTile.Root>
297
298
  ) : null}
@@ -28,12 +28,12 @@ export const HandRaisedNotifications = () => {
28
28
  }
29
29
 
30
30
  // Don't show toast message in case of local peer.
31
- if (roomState !== HMSRoomState.Connected || notification.data.isLocal || !on_stage_exp || !isSubscribing) {
31
+ if (roomState !== HMSRoomState.Connected || notification.data.isLocal || !isSubscribing) {
32
32
  return;
33
33
  }
34
34
  const hasPeerHandRaised = vanillaStore.getState(selectHasPeerHandRaised(notification.data.id));
35
35
  if (hasPeerHandRaised) {
36
- ToastBatcher.showToast({ notification, type: 'RAISE_HAND' });
36
+ ToastBatcher.showToast({ notification, type: on_stage_exp ? 'RAISE_HAND_HLS' : 'RAISE_HAND' });
37
37
  console.debug('Metadata updated', notification.data);
38
38
  }
39
39
  }, [isSubscribing, notification, on_stage_exp, roomState, vanillaStore]);
@@ -49,7 +49,7 @@ export const PeerNotifications = () => {
49
49
  }
50
50
 
51
51
  ToastBatcher.showToast({ notification });
52
- }, [notification, isPeerJoinSubscribed, isPeerLeftSubscribed]);
52
+ }, [notification, isPeerJoinSubscribed, isPeerLeftSubscribed, selectedPeer.id, setPeerSelector]);
53
53
 
54
54
  return null;
55
55
  };