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

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. package/dist/{HLSView-TOMPA4E4.js → HLSView-VXHATNNQ.js} +141 -86
  2. package/dist/HLSView-VXHATNNQ.js.map +7 -0
  3. package/dist/Prebuilt/components/Chat/Chat.d.ts +1 -1
  4. package/dist/Prebuilt/components/HMSVideo/VideoProgress.d.ts +4 -1
  5. package/dist/Prebuilt/components/HMSVideo/index.d.ts +4 -1
  6. package/dist/Prebuilt/components/SidePaneTabs.d.ts +0 -1
  7. package/dist/Prebuilt/layouts/SidePane.d.ts +1 -1
  8. package/dist/{chunk-FUDX3LDB.js → chunk-RRXRF2KB.js} +207 -151
  9. package/dist/chunk-RRXRF2KB.js.map +7 -0
  10. package/dist/index.cjs.js +365 -255
  11. package/dist/index.cjs.js.map +3 -3
  12. package/dist/index.js +1 -1
  13. package/dist/meta.cjs.json +88 -63
  14. package/dist/meta.esbuild.json +100 -74
  15. package/package.json +6 -6
  16. package/src/Prebuilt/components/Chat/Chat.tsx +23 -4
  17. package/src/Prebuilt/components/Chat/ChatFooter.tsx +2 -2
  18. package/src/Prebuilt/components/Chat/EmptyChat.tsx +5 -1
  19. package/src/Prebuilt/components/Chat/PinnedMessage.tsx +4 -3
  20. package/src/Prebuilt/components/ConferenceScreen.tsx +13 -1
  21. package/src/Prebuilt/components/EmojiReaction.jsx +2 -2
  22. package/src/Prebuilt/components/Footer/RoleOptions.tsx +4 -4
  23. package/src/Prebuilt/components/HMSVideo/HMSVideo.jsx +13 -10
  24. package/src/Prebuilt/components/HMSVideo/MwebHLSViewTitle.tsx +6 -4
  25. package/src/Prebuilt/components/HMSVideo/VideoProgress.tsx +36 -23
  26. package/src/Prebuilt/components/MoreSettings/SplitComponents/MwebOptions.tsx +1 -1
  27. package/src/Prebuilt/components/MwebLandscapePrompt.tsx +9 -3
  28. package/src/Prebuilt/components/Polls/CreatePollQuiz/PollsQuizMenu.tsx +1 -1
  29. package/src/Prebuilt/components/Polls/CreateQuestions/QuestionForm.tsx +1 -1
  30. package/src/Prebuilt/components/Polls/common/OptionInputWithDelete.tsx +1 -1
  31. package/src/Prebuilt/components/SidePaneTabs.tsx +1 -4
  32. package/src/Prebuilt/components/VideoLayouts/ProminenceLayout.tsx +1 -0
  33. package/src/Prebuilt/components/VideoLayouts/ScreenshareLayout.tsx +2 -0
  34. package/src/Prebuilt/layouts/HLSView.jsx +291 -245
  35. package/src/Prebuilt/layouts/SidePane.tsx +74 -49
  36. package/src/Prebuilt/layouts/VideoStreamingSection.tsx +10 -2
  37. package/dist/HLSView-TOMPA4E4.js.map +0 -7
  38. package/dist/chunk-FUDX3LDB.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="1"
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
@@ -71,13 +71,14 @@ export const PinnedMessage = () => {
71
71
  <Flex
72
72
  css={{
73
73
  p: '$4',
74
- color: '$on_surface_medium',
75
- bg: isMobile ? 'rgba(0, 0, 0, 0.64)' : '$surface_default',
74
+ color: '$on_surface_high',
75
+ bg: isMobile && elements?.chat?.is_overlay ? 'rgba(0, 0, 0, 0.64)' : '$surface_brighter',
76
76
  r: '$1',
77
77
  gap: '$4',
78
78
  mb: '$8',
79
79
  mt: '$8',
80
80
  flexGrow: 1,
81
+ border: '1px solid $border_bright',
81
82
  }}
82
83
  align="center"
83
84
  justify="between"
@@ -98,7 +99,7 @@ export const PinnedMessage = () => {
98
99
  >
99
100
  <Text
100
101
  variant="sm"
101
- css={{ color: '$on_surface_medium' }}
102
+ css={{ color: '$on_surface_high' }}
102
103
  {...swipeHandlers}
103
104
  title={pinnedMessages[pinnedMessageIndex]?.text}
104
105
  >
@@ -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="1"
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 ((isMobile || isLandscape) && !(isLandscapeStream || isMobileHLSStream)) {
71
+ if (showCard) {
72
72
  return <EmojiCard sendReaction={sendReaction} />;
73
73
  }
74
74
  return (
@@ -38,9 +38,6 @@ const DropdownWrapper = ({ children }: { children: React.ReactNode }) => {
38
38
  if (React.Children.toArray(children).length === 0) {
39
39
  return null;
40
40
  }
41
- React.Children.map(children, child => {
42
- console.log({ child });
43
- });
44
41
  return (
45
42
  <Dropdown.Root open={openOptions} onOpenChange={setOpenOptions}>
46
43
  <Dropdown.Trigger
@@ -83,9 +80,12 @@ export const RoleOptions = ({ roleName, peerList }: { roleName: string; peerList
83
80
  const { on_stage_role, off_stage_roles = [] } = (elements as DefaultConferencingScreen_Elements)?.on_stage_exp || {};
84
81
  const canRemoveRoleFromStage = permissions?.changeRole && roleName === on_stage_role;
85
82
  const role = useHMSStore(selectRoleByRoleName(roleName));
83
+ const tracks = useHMSStore(selectTracksMap);
84
+ if (!role) {
85
+ return null;
86
+ }
86
87
  const canPublishAudio = role.publishParams.allowed.includes('audio');
87
88
  const canPublishVideo = role.publishParams.allowed.includes('video');
88
- const tracks = useHMSStore(selectTracksMap);
89
89
 
90
90
  let isVideoOnForSomePeers = false;
91
91
  let isAudioOnForSomePeers = false;
@@ -1,17 +1,20 @@
1
1
  import React, { forwardRef } from 'react';
2
- import { useMedia } from 'react-use';
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: 'inherit',
42
- height: isLandscape || isMobile ? '100%' : '',
43
- position: isLandscape || isMobile ? 'absolute' : '',
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
- <Text variant="caption" css={{ color: '$on_surface_medium' }} onClick={toggleDetailsPane}>
78
- &nbsp;...more
79
- </Text>
77
+ {description ? (
78
+ <Text variant="caption" css={{ color: '$on_surface_medium' }} onClick={toggleDetailsPane}>
79
+ &nbsp;...more
80
+ </Text>
81
+ ) : null}
80
82
  </Flex>
81
83
  </Flex>
82
84
  </Flex>
@@ -1,48 +1,59 @@
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 onValueChange = (time: number) => {
13
- hlsPlayer?.seekTo(time);
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 duration = isFinite(videoEl.duration) ? videoEl.duration : videoEl.seekable?.end(0) || 0;
24
- const videoProgress = Math.floor(getPercentage(videoEl.currentTime, duration));
25
- let bufferProgress = 0;
26
- if (videoEl.buffered.length > 0) {
27
- bufferProgress = Math.floor(getPercentage(videoEl.buffered?.end(0), duration));
28
- }
29
-
30
- setVideoProgress(isNaN(videoProgress) ? 0 : videoProgress);
31
- setBufferProgress(isNaN(bufferProgress) ? 0 : bufferProgress);
32
- };
33
41
  videoEl.addEventListener('timeupdate', timeupdateHandler);
34
42
  return function cleanup() {
35
43
  videoEl?.removeEventListener('timeupdate', timeupdateHandler);
36
44
  };
37
- }, [videoEl]);
45
+ }, [timeupdateHandler, videoEl]);
38
46
 
39
47
  const onProgress = (progress: number[]) => {
40
48
  const progress1 = Math.floor(getPercentage(progress[0], 100));
41
49
  const videoEl = hlsPlayer?.getVideoElement();
42
- const currentTime = (progress1 * (videoEl?.duration || 0)) / 100;
43
- if (onValueChange) {
44
- onValueChange(currentTime);
50
+ if (!videoEl) {
51
+ return;
45
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();
46
57
  };
47
58
 
48
59
  if (!videoEl) {
@@ -64,6 +75,8 @@ export const VideoProgress = () => {
64
75
  value={[videoProgress]}
65
76
  showTooltip={false}
66
77
  onValueChange={onProgress}
78
+ onPointerDown={() => setSeekProgress(true)}
79
+ onPointerUp={() => setSeekProgress(false)}
67
80
  thumbStyles={{ w: '$6', h: '$6' }}
68
81
  />
69
82
  <Box
@@ -343,7 +343,7 @@ export const MwebOptions = ({
343
343
  mx: '$4',
344
344
  }}
345
345
  >
346
- <EmojiReaction />
346
+ <EmojiReaction showCard />
347
347
  </Box>
348
348
  )}
349
349
  {showRecordingOn && (
@@ -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(angle ? angle >= 90 && type.includes('landscape') : isLandscape);
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;
@@ -140,7 +140,7 @@ const AddMenu = () => {
140
140
  type="text"
141
141
  placeholder="Enter a name to continue"
142
142
  value={title}
143
- onChange={event => setTitle(event.target.value)}
143
+ onChange={event => setTitle(event.target.value.trimStart())}
144
144
  css={{
145
145
  backgroundColor: '$surface_bright',
146
146
  border: '1px solid $border_default',
@@ -159,7 +159,7 @@ export const QuestionForm = ({
159
159
  maxHeight: '$32',
160
160
  }}
161
161
  value={text}
162
- onChange={event => setText(event.target.value)}
162
+ onChange={event => setText(event.target.value.trimStart())}
163
163
  />
164
164
  <Text variant="xs" css={{ color: '$on_surface_medium', textAlign: 'end', mt: '$4' }}>
165
165
  {text?.length || 0}/1024
@@ -27,7 +27,7 @@ export const OptionInputWithDelete = ({
27
27
  }}
28
28
  value={option?.text || ''}
29
29
  key={index}
30
- onChange={event => handleOptionTextChange(index, event.target.value)}
30
+ onChange={event => handleOptionTextChange(index, event.target.value.trimStart())}
31
31
  />
32
32
  <IconButton onClick={() => removeOption(index)} css={{ bg: 'transparent', border: 'none' }}>
33
33
  <TrashIcon />
@@ -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, hideControls, hideTab = false }) => {
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
  >
@@ -36,6 +36,7 @@ const SecondarySection = ({
36
36
  hasSidebar,
37
37
  }: React.PropsWithChildren<{ tiles: TrackWithPeerAndDimensions[]; edgeToEdge?: boolean; hasSidebar?: boolean }>) => {
38
38
  const tileLayoutProps = useVideoTileContext();
39
+ console.log('secondary section', { tilesLength: tiles?.length });
39
40
  if (!tiles?.length) {
40
41
  return null;
41
42
  }
@@ -55,6 +55,8 @@ export const ScreenshareLayout = ({ peers, onPageChange, onPageSize, edgeToEdge
55
55
  };
56
56
  }, [activeSharePeer?.id, isMobile, setActiveScreenSharePeer]);
57
57
 
58
+ console.log({ activeSharePeer, secondaryPeers });
59
+
58
60
  return (
59
61
  <ProminenceLayout.Root edgeToEdge={edgeToEdge} hasSidebar={hasSidebar}>
60
62
  <ProminenceLayout.ProminentSection>