@100mslive/roomkit-react 0.2.8-alpha.8 → 0.3.0
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.
- package/dist/{HLSView-6KPQ2KD6.js → HLSView-LZOTIUWE.js} +152 -96
- package/dist/HLSView-LZOTIUWE.js.map +7 -0
- package/dist/Prebuilt/components/Chat/Chat.d.ts +1 -1
- package/dist/Prebuilt/components/HMSVideo/VideoProgress.d.ts +3 -2
- package/dist/Prebuilt/components/HMSVideo/index.d.ts +3 -2
- package/dist/Prebuilt/components/SidePaneTabs.d.ts +0 -1
- package/dist/Prebuilt/layouts/SidePane.d.ts +1 -1
- package/dist/{chunk-JQCSGJIR.js → chunk-LCECN6XD.js} +297 -238
- package/dist/chunk-LCECN6XD.js.map +7 -0
- package/dist/index.cjs.js +446 -332
- package/dist/index.cjs.js.map +3 -3
- package/dist/index.js +1 -1
- package/dist/meta.cjs.json +96 -61
- package/dist/meta.esbuild.json +109 -73
- package/package.json +6 -6
- package/src/Prebuilt/components/Chat/Chat.tsx +23 -4
- package/src/Prebuilt/components/Chat/ChatFooter.tsx +2 -2
- package/src/Prebuilt/components/Chat/EmptyChat.tsx +5 -1
- package/src/Prebuilt/components/ConferenceScreen.tsx +13 -1
- package/src/Prebuilt/components/EmojiReaction.jsx +2 -2
- package/src/Prebuilt/components/Footer/RoleOptions.tsx +125 -126
- package/src/Prebuilt/components/HMSVideo/HMSVideo.jsx +13 -10
- package/src/Prebuilt/components/HMSVideo/MwebHLSViewTitle.tsx +6 -4
- package/src/Prebuilt/components/HMSVideo/VideoProgress.tsx +38 -25
- package/src/Prebuilt/components/MoreSettings/SplitComponents/MwebOptions.tsx +1 -1
- package/src/Prebuilt/components/MwebLandscapePrompt.tsx +9 -3
- package/src/Prebuilt/components/Polls/common/utils.ts +1 -1
- package/src/Prebuilt/components/SidePaneTabs.tsx +1 -4
- package/src/Prebuilt/layouts/HLSView.jsx +293 -239
- package/src/Prebuilt/layouts/SidePane.tsx +73 -49
- package/src/Prebuilt/layouts/VideoStreamingSection.tsx +10 -2
- package/dist/HLSView-6KPQ2KD6.js.map +0 -7
- package/dist/chunk-JQCSGJIR.js.map +0 -7
@@ -152,7 +152,7 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo
|
|
152
152
|
<Flex>
|
153
153
|
<ChatSelectorContainer />
|
154
154
|
{canDisableChat && isMobile && isOverlayChat ? (
|
155
|
-
<Flex align="center" justify="end" css={{ mb: '$4' }}>
|
155
|
+
<Flex align="center" justify="end" css={{ mb: '$4' }} onClick={e => e.stopPropagation()}>
|
156
156
|
<Popover.Root>
|
157
157
|
<Popover.Trigger asChild>
|
158
158
|
<IconButton css={{ border: '1px solid $border_bright' }}>
|
@@ -273,7 +273,7 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo
|
|
273
273
|
css={{
|
274
274
|
alignItems: 'center',
|
275
275
|
}}
|
276
|
-
gap="
|
276
|
+
gap="2"
|
277
277
|
>
|
278
278
|
{noAVPermissions ? <RaiseHand css={{ bg: '$surface_default' }} /> : null}
|
279
279
|
<MoreSettings elements={elements} screenType={screenType} />
|
@@ -7,6 +7,7 @@ import { config as cssConfig } from '../../../Theme';
|
|
7
7
|
import emptyChat from '../../images/empty-chat.svg';
|
8
8
|
import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
|
9
9
|
import { useIsPeerBlacklisted } from '../hooks/useChatBlacklist';
|
10
|
+
import { useLandscapeHLSStream, useMobileHLSStream } from '../../common/hooks';
|
10
11
|
|
11
12
|
export const EmptyChat = () => {
|
12
13
|
const { elements } = useRoomLayoutConferencingScreen();
|
@@ -18,8 +19,11 @@ export const EmptyChat = () => {
|
|
18
19
|
elements.chat.private_chat_enabled ||
|
19
20
|
(elements.chat.roles_whitelist && elements.chat.roles_whitelist.length)) &&
|
20
21
|
!isLocalPeerBlacklisted;
|
22
|
+
const isMobileHLSStream = useMobileHLSStream();
|
23
|
+
const isLandscapeStream = useLandscapeHLSStream();
|
24
|
+
const streaming = isMobileHLSStream || isLandscapeStream;
|
21
25
|
|
22
|
-
if (isMobile && elements?.chat?.is_overlay) return <></>;
|
26
|
+
if (isMobile && elements?.chat?.is_overlay && !streaming) return <></>;
|
23
27
|
|
24
28
|
return (
|
25
29
|
<Flex
|
@@ -22,6 +22,8 @@ import { Box, Flex } from '../../Layout';
|
|
22
22
|
import { useHMSPrebuiltContext } from '../AppContext';
|
23
23
|
import { VideoStreamingSection } from '../layouts/VideoStreamingSection';
|
24
24
|
// @ts-ignore: No implicit Any
|
25
|
+
import { EmojiReaction } from './EmojiReaction';
|
26
|
+
// @ts-ignore: No implicit Any
|
25
27
|
import FullPageProgress from './FullPageProgress';
|
26
28
|
import { Header } from './Header';
|
27
29
|
import { PreviousRoleInMetadata } from './PreviousRoleInMetadata';
|
@@ -195,12 +197,22 @@ export const ConferenceScreen = () => {
|
|
195
197
|
alignItems: 'center',
|
196
198
|
pr: '$4',
|
197
199
|
pb: '$4',
|
200
|
+
position: 'relative',
|
198
201
|
}}
|
199
202
|
justify="end"
|
200
|
-
gap="
|
203
|
+
gap="2"
|
201
204
|
>
|
202
205
|
{noAVPermissions ? <RaiseHand /> : null}
|
203
206
|
<MoreSettings elements={screenProps.elements} screenType={screenProps.screenType} />
|
207
|
+
<Box
|
208
|
+
css={{
|
209
|
+
position: 'absolute',
|
210
|
+
bottom: '100%',
|
211
|
+
mb: '$4',
|
212
|
+
}}
|
213
|
+
>
|
214
|
+
<EmojiReaction />
|
215
|
+
</Box>
|
204
216
|
</Flex>
|
205
217
|
)}
|
206
218
|
<RoleChangeRequestModal />
|
@@ -23,7 +23,7 @@ import { EMOJI_REACTION_TYPE } from '../common/constants';
|
|
23
23
|
|
24
24
|
init({ data });
|
25
25
|
|
26
|
-
export const EmojiReaction = () => {
|
26
|
+
export const EmojiReaction = ({ showCard = false }) => {
|
27
27
|
const [open, setOpen] = useState(false);
|
28
28
|
const isConnected = useHMSStore(selectIsConnectedToRoom);
|
29
29
|
useDropdownList({ open: open, name: 'EmojiReaction' });
|
@@ -68,7 +68,7 @@ export const EmojiReaction = () => {
|
|
68
68
|
return null;
|
69
69
|
}
|
70
70
|
|
71
|
-
if (
|
71
|
+
if (showCard) {
|
72
72
|
return <EmojiCard sendReaction={sendReaction} />;
|
73
73
|
}
|
74
74
|
return (
|
@@ -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
|
36
|
-
const
|
37
|
-
|
38
|
-
|
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[] }) => {
|
39
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;
|
40
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');
|
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 &&
|
50
|
-
const isVideoOn = !!peer.videoTrack &&
|
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
|
-
<
|
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
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
<
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
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
|
};
|
@@ -1,17 +1,20 @@
|
|
1
1
|
import React, { forwardRef } from 'react';
|
2
|
-
import {
|
3
|
-
import { config, Flex } from '../../../';
|
4
|
-
import { useIsLandscape } from '../../common/hooks';
|
2
|
+
import { Flex } from '../../../Layout';
|
5
3
|
|
6
4
|
export const HMSVideo = forwardRef(({ children, ...props }, videoRef) => {
|
7
|
-
const isLandscape = useIsLandscape();
|
8
|
-
const isMobile = useMedia(config.media.md);
|
9
5
|
return (
|
10
6
|
<Flex
|
11
7
|
data-testid="hms-video"
|
12
8
|
css={{
|
13
9
|
size: '100%',
|
14
10
|
position: 'relative',
|
11
|
+
justifyContent: 'center',
|
12
|
+
'@md': {
|
13
|
+
height: 'auto',
|
14
|
+
'& video': {
|
15
|
+
height: '$60 !important',
|
16
|
+
},
|
17
|
+
},
|
15
18
|
'& video::cue': {
|
16
19
|
color: 'white',
|
17
20
|
whiteSpace: 'pre-line',
|
@@ -34,16 +37,16 @@ export const HMSVideo = forwardRef(({ children, ...props }, videoRef) => {
|
|
34
37
|
>
|
35
38
|
<video
|
36
39
|
style={{
|
37
|
-
flex: '1 1 0',
|
38
40
|
margin: '0 auto',
|
39
|
-
minHeight: '0',
|
40
41
|
objectFit: 'contain',
|
41
|
-
width: '
|
42
|
-
height:
|
43
|
-
|
42
|
+
width: 'auto',
|
43
|
+
height: 'auto',
|
44
|
+
maxWidth: '100%',
|
45
|
+
maxHeight: '100%',
|
44
46
|
}}
|
45
47
|
ref={videoRef}
|
46
48
|
playsInline
|
49
|
+
disablePictureInPicture
|
47
50
|
/>
|
48
51
|
{children}
|
49
52
|
</Flex>
|
@@ -17,7 +17,7 @@ import { SIDE_PANE_OPTIONS } from '../../common/constants';
|
|
17
17
|
half page will have chat or participant view
|
18
18
|
*/
|
19
19
|
export const HLSViewTitle = () => {
|
20
|
-
const { title, details } = useRoomLayoutHeader();
|
20
|
+
const { title, details, description } = useRoomLayoutHeader();
|
21
21
|
const toggleDetailsPane = useSidepaneToggle(SIDE_PANE_OPTIONS.ROOM_DETAILS);
|
22
22
|
const isDetailSidepaneOpen = useIsSidepaneTypeOpen(SIDE_PANE_OPTIONS.ROOM_DETAILS);
|
23
23
|
|
@@ -74,9 +74,11 @@ export const HLSViewTitle = () => {
|
|
74
74
|
) : null}
|
75
75
|
<Flex>
|
76
76
|
<RoomDetailsRow details={details} />
|
77
|
-
|
78
|
-
|
79
|
-
|
77
|
+
{description ? (
|
78
|
+
<Text variant="caption" css={{ color: '$on_surface_medium' }} onClick={toggleDetailsPane}>
|
79
|
+
...more
|
80
|
+
</Text>
|
81
|
+
) : null}
|
80
82
|
</Flex>
|
81
83
|
</Flex>
|
82
84
|
</Flex>
|
@@ -1,54 +1,66 @@
|
|
1
|
-
import React, { useEffect, useState } from 'react';
|
1
|
+
import React, { useCallback, useEffect, useState } from 'react';
|
2
2
|
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 = ({
|
7
|
+
seekProgress,
|
8
|
+
setSeekProgress,
|
9
|
+
}: {
|
10
|
+
seekProgress: boolean;
|
11
|
+
setSeekProgress: (value: boolean) => void;
|
12
|
+
}) => {
|
7
13
|
const { hlsPlayer } = useHMSPlayerContext();
|
8
14
|
const [videoProgress, setVideoProgress] = useState<number>(0);
|
9
15
|
const [bufferProgress, setBufferProgress] = useState(0);
|
10
16
|
const videoEl = hlsPlayer?.getVideoElement();
|
11
17
|
|
12
|
-
const
|
13
|
-
|
14
|
-
|
18
|
+
const setProgress = useCallback(() => {
|
19
|
+
if (!videoEl) {
|
20
|
+
return;
|
21
|
+
}
|
22
|
+
const duration = isFinite(videoEl.duration) ? videoEl.duration : videoEl.seekable?.end(0) || 0;
|
23
|
+
const videoProgress = Math.floor(getPercentage(videoEl.currentTime, duration));
|
24
|
+
let bufferProgress = 0;
|
25
|
+
if (videoEl.buffered.length > 0) {
|
26
|
+
bufferProgress = Math.floor(getPercentage(videoEl.buffered?.end(0), duration));
|
27
|
+
}
|
28
|
+
setVideoProgress(isNaN(videoProgress) ? 0 : videoProgress);
|
29
|
+
setBufferProgress(isNaN(bufferProgress) ? 0 : bufferProgress);
|
30
|
+
}, [videoEl]);
|
31
|
+
const timeupdateHandler = useCallback(() => {
|
32
|
+
if (!videoEl || seekProgress) {
|
33
|
+
return;
|
34
|
+
}
|
35
|
+
setProgress();
|
36
|
+
}, [seekProgress, setProgress, videoEl]);
|
15
37
|
useEffect(() => {
|
16
38
|
if (!videoEl) {
|
17
39
|
return;
|
18
40
|
}
|
19
|
-
const timeupdateHandler = () => {
|
20
|
-
if (!videoEl) {
|
21
|
-
return;
|
22
|
-
}
|
23
|
-
const videoProgress = Math.floor(getPercentage(videoEl.currentTime, videoEl.duration));
|
24
|
-
let bufferProgress = 0;
|
25
|
-
if (videoEl.buffered.length > 0) {
|
26
|
-
bufferProgress = Math.floor(getPercentage(videoEl.buffered?.end(0), videoEl.duration));
|
27
|
-
}
|
28
|
-
|
29
|
-
setVideoProgress(isNaN(videoProgress) ? 0 : videoProgress);
|
30
|
-
setBufferProgress(isNaN(bufferProgress) ? 0 : bufferProgress);
|
31
|
-
};
|
32
41
|
videoEl.addEventListener('timeupdate', timeupdateHandler);
|
33
42
|
return function cleanup() {
|
34
43
|
videoEl?.removeEventListener('timeupdate', timeupdateHandler);
|
35
44
|
};
|
36
|
-
}, [videoEl]);
|
45
|
+
}, [timeupdateHandler, videoEl]);
|
37
46
|
|
38
47
|
const onProgress = (progress: number[]) => {
|
39
48
|
const progress1 = Math.floor(getPercentage(progress[0], 100));
|
40
49
|
const videoEl = hlsPlayer?.getVideoElement();
|
41
|
-
|
42
|
-
|
43
|
-
onValueChange(currentTime);
|
50
|
+
if (!videoEl) {
|
51
|
+
return;
|
44
52
|
}
|
53
|
+
const duration = isFinite(videoEl.duration) ? videoEl.duration : videoEl.seekable?.end(0) || 0;
|
54
|
+
const currentTime = (progress1 * duration) / 100;
|
55
|
+
hlsPlayer?.seekTo(currentTime);
|
56
|
+
setProgress();
|
45
57
|
};
|
46
58
|
|
47
59
|
if (!videoEl) {
|
48
60
|
return null;
|
49
61
|
}
|
50
62
|
return (
|
51
|
-
<Flex align="center" css={{ cursor: 'pointer', h: '$2', alignSelf: 'stretch'
|
63
|
+
<Flex align="center" css={{ cursor: 'pointer', h: '$2', alignSelf: 'stretch' }}>
|
52
64
|
<Slider
|
53
65
|
id="video-actual-rest"
|
54
66
|
css={{
|
@@ -56,7 +68,6 @@ export const VideoProgress = ({ isDvr = true }: { isDvr: boolean }) => {
|
|
56
68
|
h: '$2',
|
57
69
|
zIndex: 1,
|
58
70
|
transition: `all .2s ease .5s`,
|
59
|
-
pointerEvents: isDvr ? '' : 'none',
|
60
71
|
}}
|
61
72
|
min={0}
|
62
73
|
max={100}
|
@@ -64,7 +75,9 @@ export const VideoProgress = ({ isDvr = true }: { isDvr: boolean }) => {
|
|
64
75
|
value={[videoProgress]}
|
65
76
|
showTooltip={false}
|
66
77
|
onValueChange={onProgress}
|
67
|
-
|
78
|
+
onPointerDown={() => setSeekProgress(true)}
|
79
|
+
onPointerUp={() => setSeekProgress(false)}
|
80
|
+
thumbStyles={{ w: '$6', h: '$6' }}
|
68
81
|
/>
|
69
82
|
<Box
|
70
83
|
id="video-buffer"
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import React, { useEffect, useState } from 'react';
|
2
2
|
import { useMedia } from 'react-use';
|
3
|
+
import { match, P } from 'ts-pattern';
|
3
4
|
import { RefreshIcon } from '@100mslive/react-icons';
|
4
5
|
import { Button } from '../../Button';
|
5
6
|
import { Box, Flex } from '../../Layout';
|
@@ -22,21 +23,26 @@ export const MwebLandscapePrompt = () => {
|
|
22
23
|
}
|
23
24
|
|
24
25
|
if (!window.screen?.orientation) {
|
25
|
-
setShowMwebLandscapePrompt(isLandscape);
|
26
|
+
setShowMwebLandscapePrompt(isLandscape && !isLandscapeHLSStream);
|
26
27
|
return;
|
27
28
|
}
|
28
29
|
const handleRotation = () => {
|
29
30
|
const angle = window.screen.orientation.angle;
|
30
31
|
const type = window.screen.orientation.type || '';
|
31
32
|
// Angle check needed to diff bw mobile and desktop
|
32
|
-
setShowMwebLandscapePrompt(
|
33
|
+
setShowMwebLandscapePrompt(
|
34
|
+
match({ angle, isLandscapeHLSStream, isLandscape, type })
|
35
|
+
.with({ isLandscapeHLSStream }, () => false)
|
36
|
+
.with({ angle: P.when(angle => angle && angle >= 90) }, ({ type }) => type.includes('landscape'))
|
37
|
+
.otherwise(() => isLandscape),
|
38
|
+
);
|
33
39
|
};
|
34
40
|
handleRotation();
|
35
41
|
window.screen.orientation.addEventListener('change', handleRotation);
|
36
42
|
return () => {
|
37
43
|
window.screen.orientation.removeEventListener('change', handleRotation);
|
38
44
|
};
|
39
|
-
}, [isLandscape]);
|
45
|
+
}, [isLandscape, isLandscapeHLSStream]);
|
40
46
|
|
41
47
|
if (isLandscapeHLSStream) {
|
42
48
|
return null;
|
@@ -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 += `${
|
19
|
+
formattedTime += `${precise ? seconds.toFixed(3) : Math.floor(seconds)}s`;
|
20
20
|
|
21
21
|
return formattedTime;
|
22
22
|
};
|
@@ -41,9 +41,8 @@ const ParticipantCount = ({ count }: { count: number }) => {
|
|
41
41
|
|
42
42
|
export const SidePaneTabs = React.memo<{
|
43
43
|
active: 'Participants | Chat';
|
44
|
-
hideControls?: boolean;
|
45
44
|
hideTab?: boolean;
|
46
|
-
}>(({ active = SIDE_PANE_OPTIONS.CHAT,
|
45
|
+
}>(({ active = SIDE_PANE_OPTIONS.CHAT, hideTab = false }) => {
|
47
46
|
const toggleChat = useSidepaneToggle(SIDE_PANE_OPTIONS.CHAT);
|
48
47
|
const toggleParticipants = useSidepaneToggle(SIDE_PANE_OPTIONS.PARTICIPANTS);
|
49
48
|
const resetSidePane = useSidepaneReset();
|
@@ -85,7 +84,6 @@ export const SidePaneTabs = React.memo<{
|
|
85
84
|
css={{
|
86
85
|
color: '$on_primary_high',
|
87
86
|
h: '100%',
|
88
|
-
marginTop: hideControls && isOverlayChat ? '$17' : '0',
|
89
87
|
transition: 'margin 0.3s ease-in-out',
|
90
88
|
position: 'relative',
|
91
89
|
}}
|
@@ -103,7 +101,6 @@ export const SidePaneTabs = React.memo<{
|
|
103
101
|
css={{
|
104
102
|
color: '$on_primary_high',
|
105
103
|
h: '100%',
|
106
|
-
marginTop: hideControls && isOverlayChat ? '$17' : '0',
|
107
104
|
transition: 'margin 0.3s ease-in-out',
|
108
105
|
}}
|
109
106
|
>
|