@100mslive/roomkit-react 0.2.8-alpha.1 → 0.2.8-alpha.11
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/HLSView-EDROW5VZ.js +1411 -0
- package/dist/HLSView-EDROW5VZ.js.map +7 -0
- package/dist/Prebuilt/common/hooks.d.ts +3 -0
- package/dist/Prebuilt/components/Chat/MwebChatOption.d.ts +1 -1
- package/dist/Prebuilt/components/HMSVideo/FullscreenButton.d.ts +5 -0
- package/dist/Prebuilt/components/HMSVideo/HLSAutoplayBlockedPrompt.d.ts +5 -0
- package/dist/Prebuilt/components/HMSVideo/HLSCaptionSelector.d.ts +1 -2
- package/dist/Prebuilt/components/HMSVideo/HLSQualitySelector.d.ts +13 -0
- package/dist/Prebuilt/components/HMSVideo/MwebHLSViewTitle.d.ts +2 -0
- package/dist/Prebuilt/components/HMSVideo/PlayButton.d.ts +6 -0
- package/dist/Prebuilt/components/HMSVideo/PlayPauseButton.d.ts +6 -0
- package/dist/Prebuilt/components/HMSVideo/PlayerContext.d.ts +8 -0
- package/dist/Prebuilt/components/HMSVideo/SeekControls.d.ts +7 -0
- package/dist/Prebuilt/components/HMSVideo/VideoProgress.d.ts +5 -0
- package/dist/Prebuilt/components/HMSVideo/VideoTime.d.ts +2 -0
- package/dist/Prebuilt/components/HMSVideo/VolumeControl.d.ts +2 -0
- package/dist/Prebuilt/components/HMSVideo/index.d.ts +26 -0
- package/dist/Prebuilt/components/HMSVideo/utils.d.ts +8 -0
- package/dist/Prebuilt/components/Leave/DesktopLeaveRoom.d.ts +2 -1
- package/dist/Prebuilt/components/Leave/LeaveRoom.d.ts +2 -1
- package/dist/Prebuilt/components/Leave/MwebLeaveRoom.d.ts +2 -3
- package/dist/Prebuilt/components/MwebLandscapePrompt.d.ts +1 -1
- package/dist/Prebuilt/components/RaiseHand.d.ts +5 -0
- package/dist/Prebuilt/components/SidePaneTabs.d.ts +1 -1
- package/dist/Sheet/Sheet.d.ts +1 -0
- package/dist/{chunk-ERIM35YN.js → chunk-YFJQ4B6X.js} +1544 -1174
- package/dist/chunk-YFJQ4B6X.js.map +7 -0
- package/dist/index.cjs.js +2727 -1899
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.js +1 -1
- package/dist/meta.cjs.json +777 -290
- package/dist/meta.esbuild.json +798 -299
- package/package.json +7 -6
- package/src/Button/Button.tsx +4 -4
- package/src/Fieldset/Fieldset.tsx +1 -1
- package/src/Input/PasswordInput.stories.tsx +1 -1
- package/src/Pagination/StyledPagination.stories.tsx +2 -2
- package/src/Prebuilt/IconButton.tsx +1 -1
- package/src/Prebuilt/common/hooks.ts +21 -0
- package/src/Prebuilt/components/AppData/useSidepane.js +34 -7
- package/src/Prebuilt/components/AuthToken.jsx +1 -1
- package/src/Prebuilt/components/Chat/Chat.tsx +41 -1
- package/src/Prebuilt/components/Chat/ChatFooter.tsx +33 -13
- package/src/Prebuilt/components/Chat/MwebChatOption.tsx +1 -1
- package/src/Prebuilt/components/ConferenceScreen.tsx +48 -7
- package/src/Prebuilt/components/EmojiReaction.jsx +33 -23
- package/src/Prebuilt/components/Footer/Footer.tsx +0 -1
- package/src/Prebuilt/components/Footer/RoleOptions.tsx +141 -125
- package/src/Prebuilt/components/HMSVideo/Controls.jsx +1 -1
- package/src/Prebuilt/components/HMSVideo/FullscreenButton.tsx +13 -0
- package/src/Prebuilt/components/HMSVideo/HLSAutoplayBlockedPrompt.tsx +72 -0
- package/src/Prebuilt/components/HMSVideo/HLSCaptionSelector.tsx +4 -2
- package/src/Prebuilt/components/HMSVideo/HLSQualitySelector.tsx +248 -0
- package/src/Prebuilt/components/HMSVideo/HMSVideo.jsx +18 -7
- package/src/Prebuilt/components/HMSVideo/MwebHLSViewTitle.tsx +84 -0
- package/src/Prebuilt/components/HMSVideo/PlayButton.tsx +27 -0
- package/src/Prebuilt/components/HMSVideo/PlayPauseButton.tsx +27 -0
- package/src/Prebuilt/components/HMSVideo/PlayerContext.tsx +15 -0
- package/src/Prebuilt/components/HMSVideo/SeekControls.tsx +22 -0
- package/src/Prebuilt/components/HMSVideo/VideoProgress.tsx +95 -0
- package/src/Prebuilt/components/HMSVideo/VideoTime.tsx +43 -0
- package/src/Prebuilt/components/HMSVideo/{VolumeControl.jsx → VolumeControl.tsx} +6 -4
- package/src/Prebuilt/components/HMSVideo/{index.js → index.ts} +6 -2
- package/src/Prebuilt/components/HMSVideo/{HMSVIdeoUtils.js → utils.ts} +5 -5
- package/src/Prebuilt/components/Header/StreamActions.tsx +1 -1
- package/src/Prebuilt/components/IconButtonWithOptions/IconButtonWithOptions.tsx +1 -1
- package/src/Prebuilt/components/Leave/DesktopLeaveRoom.tsx +50 -46
- package/src/Prebuilt/components/Leave/LeaveRoom.tsx +15 -4
- package/src/Prebuilt/components/Leave/MwebLeaveRoom.tsx +46 -27
- package/src/Prebuilt/components/MoreSettings/MoreSettings.tsx +3 -1
- package/src/Prebuilt/components/MoreSettings/SplitComponents/DesktopOptions.tsx +37 -31
- package/src/Prebuilt/components/MoreSettings/SplitComponents/MwebOptions.tsx +12 -8
- package/src/Prebuilt/components/MwebLandscapePrompt.tsx +14 -3
- package/src/Prebuilt/components/Notifications/HandRaisedNotifications.tsx +5 -2
- package/src/Prebuilt/components/Notifications/PeerNotifications.tsx +1 -1
- package/src/Prebuilt/components/Polls/Voting/QuestionCard.jsx +19 -8
- package/src/Prebuilt/components/Polls/Voting/Voting.tsx +3 -2
- package/src/Prebuilt/components/Polls/common/OptionInputWithDelete.tsx +1 -1
- package/src/Prebuilt/components/Polls/common/utils.ts +2 -2
- package/src/Prebuilt/components/RaiseHand.tsx +24 -0
- package/src/Prebuilt/components/RoomDetails/RoomDetailsPane.tsx +41 -14
- package/src/Prebuilt/components/SidePaneTabs.tsx +56 -48
- package/src/Prebuilt/components/StatsForNerds.jsx +14 -6
- package/src/Prebuilt/components/Streaming/Common.jsx +1 -1
- package/src/Prebuilt/components/TileMenu/TileMenuContent.tsx +2 -2
- package/src/Prebuilt/components/Toast/ToastBatcher.js +8 -1
- package/src/Prebuilt/components/Toast/ToastConfig.jsx +17 -0
- package/src/Prebuilt/components/pdfAnnotator/shareScreenOptions.jsx +2 -2
- package/src/Prebuilt/components/pdfAnnotator/uploadedFile.jsx +1 -1
- package/src/Prebuilt/layouts/HLSView.jsx +379 -179
- package/src/Prebuilt/layouts/SidePane.tsx +145 -59
- package/src/Prebuilt/layouts/VideoStreamingSection.tsx +22 -2
- package/src/Prebuilt/primitives/DialogContent.jsx +1 -1
- package/src/Prebuilt/provider/roomLayoutProvider/index.tsx +1 -1
- package/src/Sheet/Sheet.tsx +7 -3
- package/dist/HLSView-SJCF34GE.js +0 -987
- package/dist/HLSView-SJCF34GE.js.map +0 -7
- package/dist/chunk-ERIM35YN.js.map +0 -7
- package/src/Prebuilt/components/HMSVideo/FullscreenButton.jsx +0 -18
- package/src/Prebuilt/components/HMSVideo/HLSAutoplayBlockedPrompt.jsx +0 -35
- package/src/Prebuilt/components/HMSVideo/HLSQualitySelector.jsx +0 -127
- package/src/Prebuilt/components/HMSVideo/PlayButton.jsx +0 -13
- package/src/Prebuilt/components/HMSVideo/VideoProgress.jsx +0 -76
- package/src/Prebuilt/components/HMSVideo/VideoTime.jsx +0 -33
- package/src/Prebuilt/components/RaiseHand.jsx +0 -17
@@ -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,
|
@@ -25,26 +26,78 @@ import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvid
|
|
25
26
|
import { getMetadata } from '../../common/utils';
|
26
27
|
|
27
28
|
const dropdownItemCSS = { backgroundColor: '$surface_default', gap: '$4', p: '$8' };
|
28
|
-
const optionTextCSS = {
|
29
|
+
const optionTextCSS = {
|
30
|
+
fontWeight: '$semiBold',
|
31
|
+
color: '$on_surface_high',
|
32
|
+
textTransform: 'none',
|
33
|
+
whiteSpace: 'nowrap',
|
34
|
+
};
|
29
35
|
|
30
|
-
const
|
31
|
-
const
|
32
|
-
|
33
|
-
|
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
|
+
return (
|
42
|
+
<Dropdown.Root open={openOptions} onOpenChange={setOpenOptions}>
|
43
|
+
<Dropdown.Trigger
|
44
|
+
data-testid="role_group_options"
|
45
|
+
onClick={e => e.stopPropagation()}
|
46
|
+
className="role_actions"
|
47
|
+
asChild
|
48
|
+
css={{
|
49
|
+
p: '$1',
|
50
|
+
r: '$0',
|
51
|
+
c: '$on_surface_high',
|
52
|
+
visibility: openOptions ? 'visible' : 'hidden',
|
53
|
+
'&:hover': {
|
54
|
+
c: '$on_surface_medium',
|
55
|
+
},
|
56
|
+
'@md': {
|
57
|
+
visibility: 'visible',
|
58
|
+
},
|
59
|
+
}}
|
60
|
+
>
|
61
|
+
<Flex>
|
62
|
+
<VerticalMenuIcon />
|
63
|
+
</Flex>
|
64
|
+
</Dropdown.Trigger>
|
65
|
+
<Dropdown.Content
|
66
|
+
onClick={e => e.stopPropagation()}
|
67
|
+
css={{ w: 'max-content', bg: '$surface_default', py: 0 }}
|
68
|
+
align="end"
|
69
|
+
>
|
70
|
+
{children}
|
71
|
+
</Dropdown.Content>
|
72
|
+
</Dropdown.Root>
|
73
|
+
);
|
74
|
+
};
|
75
|
+
|
76
|
+
export const RoleOptions = ({ roleName, peerList }: { roleName: string; peerList: HMSPeer[] }) => {
|
34
77
|
const permissions = useHMSStore(selectPermissions);
|
78
|
+
const hmsActions = useHMSActions();
|
79
|
+
const { elements } = useRoomLayoutConferencingScreen();
|
80
|
+
const { on_stage_role, off_stage_roles = [] } = (elements as DefaultConferencingScreen_Elements)?.on_stage_exp || {};
|
81
|
+
const canRemoveRoleFromStage = permissions?.changeRole && roleName === on_stage_role;
|
35
82
|
const role = useHMSStore(selectRoleByRoleName(roleName));
|
83
|
+
const tracks = useHMSStore(selectTracksMap);
|
84
|
+
if (!role) {
|
85
|
+
return null;
|
86
|
+
}
|
87
|
+
const canPublishAudio = role.publishParams.allowed.includes('audio');
|
88
|
+
const canPublishVideo = role.publishParams.allowed.includes('video');
|
36
89
|
|
37
|
-
let
|
38
|
-
let
|
90
|
+
let isVideoOnForSomePeers = false;
|
91
|
+
let isAudioOnForSomePeers = false;
|
39
92
|
|
40
93
|
peerList.forEach(peer => {
|
41
94
|
if (peer.isLocal) {
|
42
95
|
return;
|
43
96
|
}
|
44
|
-
const isAudioOn = !!peer.audioTrack &&
|
45
|
-
const isVideoOn = !!peer.videoTrack &&
|
46
|
-
|
47
|
-
|
97
|
+
const isAudioOn = !!peer.audioTrack && tracks[peer.audioTrack]?.enabled;
|
98
|
+
const isVideoOn = !!peer.videoTrack && tracks[peer.videoTrack]?.enabled;
|
99
|
+
isAudioOnForSomePeers = isAudioOnForSomePeers || isAudioOn;
|
100
|
+
isVideoOnForSomePeers = isVideoOnForSomePeers || isVideoOn;
|
48
101
|
});
|
49
102
|
|
50
103
|
const setTrackEnabled = async (type: 'audio' | 'video', enabled = false) => {
|
@@ -55,68 +108,16 @@ const MuteUnmuteOption = ({ roleName, peerList }: { peerList: HMSPeer[]; roleNam
|
|
55
108
|
}
|
56
109
|
};
|
57
110
|
|
58
|
-
return (
|
59
|
-
<>
|
60
|
-
{role.publishParams.allowed?.includes('audio') && (
|
61
|
-
<>
|
62
|
-
{allPeersHaveAudioOn && permissions?.mute ? (
|
63
|
-
<Dropdown.Item css={dropdownItemCSS} onClick={() => setTrackEnabled('audio', false)}>
|
64
|
-
<MicOffIcon />
|
65
|
-
<Text variant="sm" css={optionTextCSS}>
|
66
|
-
Mute Audio for All
|
67
|
-
</Text>
|
68
|
-
</Dropdown.Item>
|
69
|
-
) : null}
|
70
|
-
|
71
|
-
{!allPeersHaveAudioOn && permissions?.unmute ? (
|
72
|
-
<Dropdown.Item css={dropdownItemCSS} onClick={() => setTrackEnabled('audio', true)}>
|
73
|
-
<MicOnIcon />
|
74
|
-
<Text variant="sm" css={optionTextCSS}>
|
75
|
-
Unmute Audio for All
|
76
|
-
</Text>
|
77
|
-
</Dropdown.Item>
|
78
|
-
) : null}
|
79
|
-
</>
|
80
|
-
)}
|
81
|
-
|
82
|
-
{role.publishParams.allowed?.includes('audio') && (
|
83
|
-
<>
|
84
|
-
{allPeersHaveVideoOn && permissions?.mute ? (
|
85
|
-
<Dropdown.Item css={dropdownItemCSS} onClick={() => setTrackEnabled('video', false)}>
|
86
|
-
<VideoOffIcon />
|
87
|
-
<Text variant="sm" css={optionTextCSS}>
|
88
|
-
Mute Video for All
|
89
|
-
</Text>
|
90
|
-
</Dropdown.Item>
|
91
|
-
) : null}
|
92
|
-
|
93
|
-
{!allPeersHaveVideoOn && permissions?.unmute ? (
|
94
|
-
<Dropdown.Item css={dropdownItemCSS} onClick={() => setTrackEnabled('video', true)}>
|
95
|
-
<VideoOnIcon />
|
96
|
-
<Text variant="sm" css={optionTextCSS}>
|
97
|
-
Unmute Video for All
|
98
|
-
</Text>
|
99
|
-
</Dropdown.Item>
|
100
|
-
) : null}
|
101
|
-
</>
|
102
|
-
)}
|
103
|
-
</>
|
104
|
-
);
|
105
|
-
};
|
106
|
-
|
107
|
-
export const RoleOptions = ({ roleName, peerList }: { roleName: string; peerList: HMSPeer[] }) => {
|
108
|
-
const [openOptions, setOpenOptions] = useState(false);
|
109
|
-
const permissions = useHMSStore(selectPermissions);
|
110
|
-
const hmsActions = useHMSActions();
|
111
|
-
const { elements } = useRoomLayoutConferencingScreen();
|
112
|
-
const { on_stage_role, off_stage_roles = [] } = (elements as DefaultConferencingScreen_Elements)?.on_stage_exp || {};
|
113
|
-
const canMuteOrUnmute = permissions?.mute || permissions?.unmute;
|
114
|
-
const canRemoveRoleFromStage = permissions?.changeRole && roleName === on_stage_role;
|
115
111
|
// on stage and off stage roles
|
116
112
|
const canRemoveRoleFromRoom =
|
117
113
|
permissions?.removeOthers && (on_stage_role === roleName || off_stage_roles?.includes(roleName));
|
118
114
|
|
119
|
-
if (
|
115
|
+
if (
|
116
|
+
peerList.length === 0 ||
|
117
|
+
// if only local peer is present no need to show any options
|
118
|
+
(peerList.length === 1 && peerList[0].isLocal) ||
|
119
|
+
!role
|
120
|
+
) {
|
120
121
|
return null;
|
121
122
|
}
|
122
123
|
|
@@ -140,60 +141,75 @@ export const RoleOptions = ({ roleName, peerList }: { roleName: string; peerList
|
|
140
141
|
};
|
141
142
|
|
142
143
|
return (
|
143
|
-
<
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
<
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
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>
|
198
214
|
);
|
199
215
|
};
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { ExpandIcon, ShrinkIcon } from '@100mslive/react-icons';
|
3
|
+
import { Flex, IconButton, Tooltip } from '../../..';
|
4
|
+
|
5
|
+
export const FullScreenButton = ({ isFullScreen, onToggle }: { isFullScreen: boolean; onToggle: () => void }) => {
|
6
|
+
return (
|
7
|
+
<Tooltip title={`${isFullScreen ? 'Exit' : 'Go'} fullscreen`} side="top">
|
8
|
+
<IconButton css={{ margin: '0px' }} onClick={onToggle} key="fullscreen_btn" data-testid="fullscreen_btn">
|
9
|
+
<Flex>{isFullScreen ? <ShrinkIcon /> : <ExpandIcon />}</Flex>
|
10
|
+
</IconButton>
|
11
|
+
</Tooltip>
|
12
|
+
);
|
13
|
+
};
|
@@ -0,0 +1,72 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { useMedia } from 'react-use';
|
3
|
+
import { VolumeTwoIcon } from '@100mslive/react-icons';
|
4
|
+
import { Button, config, Dialog, IconButton, Text } from '../../..';
|
5
|
+
// @ts-ignore
|
6
|
+
import { DialogContent, DialogRow } from '../../primitives/DialogContent';
|
7
|
+
import { useIsLandscape } from '../../common/hooks';
|
8
|
+
|
9
|
+
export function HLSAutoplayBlockedPrompt({
|
10
|
+
open,
|
11
|
+
unblockAutoPlay,
|
12
|
+
}: {
|
13
|
+
open: boolean;
|
14
|
+
unblockAutoPlay: () => Promise<void>;
|
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
|
+
}
|
43
|
+
return (
|
44
|
+
<Dialog.Root
|
45
|
+
open={open}
|
46
|
+
onOpenChange={async value => {
|
47
|
+
if (!value) {
|
48
|
+
await unblockAutoPlay();
|
49
|
+
}
|
50
|
+
}}
|
51
|
+
>
|
52
|
+
<DialogContent title="Attention" closeable={false}>
|
53
|
+
<DialogRow>
|
54
|
+
<Text variant="md">
|
55
|
+
The browser wants us to get a confirmation for playing the HLS Stream. Please click "play stream" to
|
56
|
+
proceed.
|
57
|
+
</Text>
|
58
|
+
</DialogRow>
|
59
|
+
<DialogRow justify="end">
|
60
|
+
<Button
|
61
|
+
variant="primary"
|
62
|
+
onClick={async () => {
|
63
|
+
await unblockAutoPlay();
|
64
|
+
}}
|
65
|
+
>
|
66
|
+
Play stream
|
67
|
+
</Button>
|
68
|
+
</DialogRow>
|
69
|
+
</DialogContent>
|
70
|
+
</Dialog.Root>
|
71
|
+
);
|
72
|
+
}
|
@@ -1,11 +1,13 @@
|
|
1
1
|
import React from 'react';
|
2
2
|
import { ClosedCaptionIcon, OpenCaptionIcon } from '@100mslive/react-icons';
|
3
3
|
import { IconButton, Tooltip } from '../../../';
|
4
|
+
import { useHMSPlayerContext } from './PlayerContext';
|
4
5
|
|
5
|
-
export function HLSCaptionSelector({ isEnabled
|
6
|
+
export function HLSCaptionSelector({ isEnabled }: { isEnabled: boolean }) {
|
7
|
+
const { hlsPlayer } = useHMSPlayerContext();
|
6
8
|
return (
|
7
9
|
<Tooltip title="Subtitles/closed captions" side="top">
|
8
|
-
<IconButton css={{ p: '$2' }} onClick={() =>
|
10
|
+
<IconButton css={{ p: '$2' }} onClick={() => hlsPlayer?.toggleCaption()}>
|
9
11
|
{isEnabled ? <ClosedCaptionIcon width="20" height="20px" /> : <OpenCaptionIcon width="20" height="20px" />}
|
10
12
|
</IconButton>
|
11
13
|
</Tooltip>
|
@@ -0,0 +1,248 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { useMedia } from 'react-use';
|
3
|
+
import { HMSHLSLayer } from '@100mslive/hls-player';
|
4
|
+
import { CheckIcon, CrossIcon, SettingsIcon } from '@100mslive/react-icons';
|
5
|
+
import { Box, Dropdown, Flex, Text, Tooltip } from '../../..';
|
6
|
+
import { Sheet } from '../../../Sheet';
|
7
|
+
import { config } from '../../../Theme';
|
8
|
+
import { useIsLandscape } from '../../common/hooks';
|
9
|
+
|
10
|
+
export function HLSQualitySelector({
|
11
|
+
open,
|
12
|
+
onOpenChange,
|
13
|
+
layers,
|
14
|
+
onQualityChange,
|
15
|
+
selection,
|
16
|
+
isAuto,
|
17
|
+
containerRef,
|
18
|
+
}: {
|
19
|
+
open: boolean;
|
20
|
+
onOpenChange: (value: boolean) => void;
|
21
|
+
layers: HMSHLSLayer[];
|
22
|
+
onQualityChange: (quality: { [key: string]: string | number } | HMSHLSLayer) => void;
|
23
|
+
selection: HMSHLSLayer;
|
24
|
+
isAuto: boolean;
|
25
|
+
containerRef?: HTMLDivElement;
|
26
|
+
}) {
|
27
|
+
const isMobile = useMedia(config.media.md);
|
28
|
+
const isLandscape = useIsLandscape();
|
29
|
+
|
30
|
+
if (layers.length === 0) {
|
31
|
+
return null;
|
32
|
+
}
|
33
|
+
if (isMobile || isLandscape) {
|
34
|
+
return (
|
35
|
+
<Sheet.Root open={open} onOpenChange={onOpenChange}>
|
36
|
+
<Sheet.Trigger asChild data-testid="quality_selector">
|
37
|
+
<Flex
|
38
|
+
css={{
|
39
|
+
color: '$on_primary_high',
|
40
|
+
r: '$1',
|
41
|
+
cursor: 'pointer',
|
42
|
+
p: '$2',
|
43
|
+
}}
|
44
|
+
>
|
45
|
+
<SettingsIcon />
|
46
|
+
</Flex>
|
47
|
+
</Sheet.Trigger>
|
48
|
+
<Sheet.Content
|
49
|
+
container={containerRef}
|
50
|
+
css={{ bg: '$surface_default', pb: '$1' }}
|
51
|
+
onClick={() => onOpenChange(false)}
|
52
|
+
>
|
53
|
+
<Sheet.Title
|
54
|
+
css={{
|
55
|
+
display: 'flex',
|
56
|
+
color: '$on_surface_high',
|
57
|
+
w: '100%',
|
58
|
+
justifyContent: 'space-between',
|
59
|
+
mt: '$8',
|
60
|
+
fontSize: '$md',
|
61
|
+
px: '$10',
|
62
|
+
pb: '$8',
|
63
|
+
borderBottom: '1px solid $border_bright',
|
64
|
+
alignItems: 'center',
|
65
|
+
}}
|
66
|
+
>
|
67
|
+
Quality
|
68
|
+
<Sheet.Close css={{ color: '$on_surface_high' }} onClick={() => onOpenChange(false)}>
|
69
|
+
<CrossIcon />
|
70
|
+
</Sheet.Close>
|
71
|
+
</Sheet.Title>
|
72
|
+
{layers.map(layer => {
|
73
|
+
return (
|
74
|
+
<Flex
|
75
|
+
align="center"
|
76
|
+
css={{
|
77
|
+
w: '100%',
|
78
|
+
bg: '$surface_default',
|
79
|
+
'&:hover': {
|
80
|
+
bg: '$surface_brighter',
|
81
|
+
},
|
82
|
+
cursor: 'pointer',
|
83
|
+
gap: '$4',
|
84
|
+
py: '$8',
|
85
|
+
px: '$10',
|
86
|
+
}}
|
87
|
+
key={layer.width}
|
88
|
+
onClick={() => onQualityChange(layer)}
|
89
|
+
>
|
90
|
+
<Text variant="caption" css={{ fontWeight: '$semiBold' }}>
|
91
|
+
{getQualityText(layer)}
|
92
|
+
</Text>
|
93
|
+
<Text variant="caption" css={{ flex: '1 1 0', c: '$on_surface_low', pl: '$2' }}>
|
94
|
+
{getBitrateText(layer)}
|
95
|
+
</Text>
|
96
|
+
{!isAuto && layer.width === selection?.width && layer.height === selection?.height && (
|
97
|
+
<CheckIcon width="16px" height="16px" />
|
98
|
+
)}
|
99
|
+
</Flex>
|
100
|
+
);
|
101
|
+
})}
|
102
|
+
<Flex
|
103
|
+
align="center"
|
104
|
+
css={{
|
105
|
+
w: '100%',
|
106
|
+
bg: '$surface_default',
|
107
|
+
'&:hover': {
|
108
|
+
bg: '$surface_brighter',
|
109
|
+
},
|
110
|
+
cursor: 'pointer',
|
111
|
+
gap: '$4',
|
112
|
+
py: '$8',
|
113
|
+
px: '$10',
|
114
|
+
}}
|
115
|
+
key="auto"
|
116
|
+
onClick={() => onQualityChange({ height: 'auto' })}
|
117
|
+
>
|
118
|
+
<Text variant="caption" css={{ fontWeight: '$semiBold', flex: '1 1 0' }}>
|
119
|
+
Auto
|
120
|
+
</Text>
|
121
|
+
{isAuto && <CheckIcon width="16px" height="16px" />}
|
122
|
+
</Flex>
|
123
|
+
</Sheet.Content>
|
124
|
+
</Sheet.Root>
|
125
|
+
);
|
126
|
+
}
|
127
|
+
return (
|
128
|
+
<Dropdown.Root open={open} onOpenChange={value => onOpenChange(value)} modal={false}>
|
129
|
+
<Dropdown.Trigger asChild data-testid="quality_selector">
|
130
|
+
<Flex
|
131
|
+
css={{
|
132
|
+
color: '$on_primary_high',
|
133
|
+
r: '$1',
|
134
|
+
cursor: 'pointer',
|
135
|
+
p: '$2',
|
136
|
+
}}
|
137
|
+
>
|
138
|
+
<Tooltip title="Select Quality" side="top">
|
139
|
+
<Flex align="center">
|
140
|
+
<Box
|
141
|
+
css={{
|
142
|
+
w: '$9',
|
143
|
+
h: '$9',
|
144
|
+
display: 'inline-flex',
|
145
|
+
alignItems: 'center',
|
146
|
+
c: '$on_surface_high',
|
147
|
+
}}
|
148
|
+
>
|
149
|
+
<SettingsIcon />
|
150
|
+
</Box>
|
151
|
+
<Text
|
152
|
+
variant={{
|
153
|
+
'@md': 'sm',
|
154
|
+
'@sm': 'xs',
|
155
|
+
'@xs': 'tiny',
|
156
|
+
}}
|
157
|
+
css={{ display: 'flex', alignItems: 'center', ml: '$2', c: '$on_surface_medium' }}
|
158
|
+
>
|
159
|
+
{isAuto && (
|
160
|
+
<>
|
161
|
+
Auto
|
162
|
+
<Box
|
163
|
+
css={{
|
164
|
+
mx: '$2',
|
165
|
+
w: '$2',
|
166
|
+
h: '$2',
|
167
|
+
background: '$on_surface_medium',
|
168
|
+
r: '$1',
|
169
|
+
}}
|
170
|
+
/>
|
171
|
+
</>
|
172
|
+
)}
|
173
|
+
{selection && Math.min(selection.width || 0, selection.height || 0)}p
|
174
|
+
</Text>
|
175
|
+
</Flex>
|
176
|
+
</Tooltip>
|
177
|
+
</Flex>
|
178
|
+
</Dropdown.Trigger>
|
179
|
+
<Dropdown.Portal container={containerRef}>
|
180
|
+
<Dropdown.Content
|
181
|
+
sideOffset={5}
|
182
|
+
align="end"
|
183
|
+
css={{
|
184
|
+
height: 'auto',
|
185
|
+
maxHeight: '$52',
|
186
|
+
w: '$40',
|
187
|
+
bg: '$surface_bright',
|
188
|
+
py: '$4',
|
189
|
+
gap: '$4',
|
190
|
+
display: 'grid',
|
191
|
+
}}
|
192
|
+
>
|
193
|
+
{layers.map(layer => {
|
194
|
+
return (
|
195
|
+
<Dropdown.Item
|
196
|
+
onClick={() => onQualityChange(layer)}
|
197
|
+
key={layer.width}
|
198
|
+
css={{
|
199
|
+
bg:
|
200
|
+
!isAuto && layer.width === selection?.width && layer.height === selection?.height
|
201
|
+
? '$surface_default'
|
202
|
+
: '$surface_bright',
|
203
|
+
'&:hover': {
|
204
|
+
bg: '$surface_brighter',
|
205
|
+
},
|
206
|
+
p: '$2 $4 $2 $8',
|
207
|
+
h: '$12',
|
208
|
+
gap: '$2',
|
209
|
+
}}
|
210
|
+
>
|
211
|
+
<Text variant="caption" css={{ fontWeight: '$semiBold' }}>
|
212
|
+
{getQualityText(layer)}
|
213
|
+
</Text>
|
214
|
+
<Text variant="caption" css={{ flex: '1 1 0', c: '$on_surface_low', pl: '$2' }}>
|
215
|
+
{getBitrateText(layer)}
|
216
|
+
</Text>
|
217
|
+
{!isAuto && layer.width === selection?.width && layer.height === selection?.height && (
|
218
|
+
<CheckIcon width="16px" height="16px" />
|
219
|
+
)}
|
220
|
+
</Dropdown.Item>
|
221
|
+
);
|
222
|
+
})}
|
223
|
+
<Dropdown.Item
|
224
|
+
onClick={() => onQualityChange({ height: 'auto' })}
|
225
|
+
key="auto"
|
226
|
+
css={{
|
227
|
+
bg: !isAuto ? '$surface_bright' : '$surface_default',
|
228
|
+
'&:hover': {
|
229
|
+
bg: '$surface_brighter',
|
230
|
+
},
|
231
|
+
p: '$2 $4 $2 $8',
|
232
|
+
h: '$12',
|
233
|
+
gap: '$2',
|
234
|
+
}}
|
235
|
+
>
|
236
|
+
<Text variant="caption" css={{ fontWeight: '$semiBold', flex: '1 1 0' }}>
|
237
|
+
Auto
|
238
|
+
</Text>
|
239
|
+
{isAuto && <CheckIcon width="16px" height="16px" />}
|
240
|
+
</Dropdown.Item>
|
241
|
+
</Dropdown.Content>
|
242
|
+
</Dropdown.Portal>
|
243
|
+
</Dropdown.Root>
|
244
|
+
);
|
245
|
+
}
|
246
|
+
|
247
|
+
const getQualityText = (layer: HMSHLSLayer) => `${Math.min(layer.height || 0, layer.width || 0)}p `;
|
248
|
+
const getBitrateText = (layer: HMSHLSLayer) => `(${(Number(layer.bitrate / 1000) / 1000).toFixed(2)} Mbps)`;
|