@100mslive/roomkit-react 0.3.20 → 0.3.21-alpha.1

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.
Files changed (41) hide show
  1. package/README.md +6 -6
  2. package/dist/{HLSView-E242LM6X.css → HLSView-3CVLSXRH.css} +3 -3
  3. package/dist/{HLSView-E242LM6X.css.map → HLSView-3CVLSXRH.css.map} +1 -1
  4. package/dist/{HLSView-6QAHWMQF.js → HLSView-3JERKVTE.js} +63 -47
  5. package/dist/HLSView-3JERKVTE.js.map +7 -0
  6. package/dist/Prebuilt/AppContext.d.ts +1 -1
  7. package/dist/Prebuilt/components/AuthToken.d.ts +1 -1
  8. package/dist/Prebuilt/components/VirtualBackground/VBHandler.d.ts +1 -0
  9. package/dist/Prebuilt/components/peerTileUtils.d.ts +7 -0
  10. package/dist/{chunk-ACEDLM3B.js → chunk-J5FLGI2T.js} +541 -510
  11. package/dist/chunk-J5FLGI2T.js.map +7 -0
  12. package/dist/index.cjs.css +2 -2
  13. package/dist/index.cjs.css.map +1 -1
  14. package/dist/index.cjs.js +608 -561
  15. package/dist/index.cjs.js.map +4 -4
  16. package/dist/index.css +2 -2
  17. package/dist/index.css.map +1 -1
  18. package/dist/index.js +1 -1
  19. package/dist/meta.cjs.json +89 -69
  20. package/dist/meta.esbuild.json +101 -81
  21. package/package.json +7 -7
  22. package/src/Prebuilt/App.tsx +5 -5
  23. package/src/Prebuilt/AppContext.tsx +1 -1
  24. package/src/Prebuilt/components/AuthToken.tsx +1 -1
  25. package/src/Prebuilt/components/HMSVideo/HMSVideo.jsx +24 -4
  26. package/src/Prebuilt/components/Header/common.jsx +5 -3
  27. package/src/Prebuilt/components/MoreSettings/ChangeNameContent.tsx +13 -3
  28. package/src/Prebuilt/components/MoreSettings/ChangeNameModal.tsx +1 -1
  29. package/src/Prebuilt/components/Notifications/TrackUnmuteModal.tsx +1 -1
  30. package/src/Prebuilt/components/ScreenshareTile.tsx +2 -2
  31. package/src/Prebuilt/components/Settings/SettingsModal.jsx +1 -2
  32. package/src/Prebuilt/components/TileMenu/TileMenu.tsx +7 -6
  33. package/src/Prebuilt/components/TileMenu/TileMenuContent.tsx +3 -3
  34. package/src/Prebuilt/components/VideoLayouts/EqualProminence.tsx +4 -2
  35. package/src/Prebuilt/components/VideoTile.tsx +2 -2
  36. package/src/Prebuilt/components/VirtualBackground/VBHandler.tsx +13 -1
  37. package/src/Prebuilt/components/VirtualBackground/VBPicker.tsx +3 -2
  38. package/src/Prebuilt/components/{peerTileUtils.jsx → peerTileUtils.tsx} +21 -6
  39. package/src/Prebuilt/layouts/HLSView.jsx +2 -5
  40. package/dist/HLSView-6QAHWMQF.js.map +0 -7
  41. package/dist/chunk-ACEDLM3B.js.map +0 -7
@@ -1,7 +1,28 @@
1
- import React, { forwardRef } from 'react';
1
+ import React, { forwardRef, useEffect, useState } from 'react';
2
2
  import { Flex } from '../../../Layout';
3
3
 
4
4
  export const HMSVideo = forwardRef(({ children, ...props }, videoRef) => {
5
+ const [width, setWidth] = useState('auto');
6
+
7
+ useEffect(() => {
8
+ const updatingVideoWidth = () => {
9
+ const videoEl = videoRef.current;
10
+ if (!videoEl) {
11
+ return;
12
+ }
13
+ if (videoEl.videoWidth > videoEl.videoHeight && width !== '100%') {
14
+ setWidth('100%');
15
+ }
16
+ };
17
+ const videoEl = videoRef.current;
18
+ if (!videoEl) {
19
+ return;
20
+ }
21
+ videoEl.addEventListener('loadedmetadata', updatingVideoWidth);
22
+ return () => {
23
+ videoEl.removeEventListener('loadedmetadata', updatingVideoWidth);
24
+ };
25
+ }, []);
5
26
  return (
6
27
  <Flex
7
28
  data-testid="hms-video"
@@ -11,9 +32,8 @@ export const HMSVideo = forwardRef(({ children, ...props }, videoRef) => {
11
32
  justifyContent: 'center',
12
33
  transition: 'all 0.3s ease-in-out',
13
34
  '@md': {
14
- height: 'auto',
15
35
  '& video': {
16
- height: '$60 !important',
36
+ height: props.isFullScreen ? '' : '$60 !important',
17
37
  },
18
38
  },
19
39
  '& video::cue': {
@@ -41,7 +61,7 @@ export const HMSVideo = forwardRef(({ children, ...props }, videoRef) => {
41
61
  style={{
42
62
  margin: '0 auto',
43
63
  objectFit: 'contain',
44
- width: 'auto',
64
+ width: width,
45
65
  height: '100%',
46
66
  maxWidth: '100%',
47
67
  }}
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import {
3
3
  DeviceType,
4
+ getAudioDeviceCategory,
4
5
  selectIsLocalVideoEnabled,
5
6
  selectLocalVideoTrackID,
6
7
  selectVideoTrackByID,
@@ -78,12 +79,13 @@ export const AudioActions = () => {
78
79
  if (!audioFiltered) {
79
80
  return null;
80
81
  }
82
+ const deviceCategory = getAudioDeviceCategory(currentSelection.label);
81
83
  let AudioIcon = <SpeakerIcon />;
82
- if (currentSelection && currentSelection.label.toLowerCase().includes('bluetooth')) {
84
+ if (deviceCategory === 'bluetooth') {
83
85
  AudioIcon = <BluetoothIcon />;
84
- } else if (currentSelection && currentSelection.label.toLowerCase().includes('wired')) {
86
+ } else if (deviceCategory === 'wired') {
85
87
  AudioIcon = <HeadphonesIcon />;
86
- } else if (currentSelection && currentSelection.label.toLowerCase().includes('earpiece')) {
88
+ } else if (deviceCategory === 'earpiece') {
87
89
  AudioIcon = <TelePhoneIcon />;
88
90
  }
89
91
  return (
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useEffect, useRef } from 'react';
2
2
  import { ChevronLeftIcon, CrossIcon } from '@100mslive/react-icons';
3
3
  import { Button } from '../../../Button';
4
4
  import { Input } from '../../../Input';
@@ -22,6 +22,16 @@ export const ChangeNameContent = ({
22
22
  onExit: () => void;
23
23
  onBackClick: () => void;
24
24
  }) => {
25
+ const inputRef = useRef<HTMLInputElement>(null);
26
+
27
+ useEffect(() => {
28
+ if (isMobile) {
29
+ setTimeout(() => {
30
+ inputRef.current?.focus();
31
+ }, 200);
32
+ }
33
+ }, [isMobile]);
34
+
25
35
  return (
26
36
  <form
27
37
  onSubmit={async e => {
@@ -53,10 +63,10 @@ export const ChangeNameContent = ({
53
63
  </Text>
54
64
  <Flex justify="center" align="center" css={{ my: '$8', w: '100%', '@md': { px: '$8' } }}>
55
65
  <Input
66
+ ref={inputRef}
56
67
  css={{ width: '100%', bg: '$surface_default' }}
57
68
  value={currentName}
58
- // Prevents popup from showing up on chrome mweb
59
- type={isMobile ? 'search' : 'text'}
69
+ type="text"
60
70
  onChange={e => {
61
71
  setCurrentName(e.target.value);
62
72
  }}
@@ -58,7 +58,7 @@ export const ChangeNameModal = ({
58
58
  if (isMobile) {
59
59
  return (
60
60
  <Sheet.Root defaultOpen onOpenChange={onOpenChange}>
61
- <Sheet.Content css={{ bg: '$surface_dim', p: '$8 0' }}>
61
+ <Sheet.Content css={{ bg: '$surface_dim', p: '$8 0' }} onOpenAutoFocus={e => e.preventDefault()}>
62
62
  <ChangeNameContent {...props} />
63
63
  </Sheet.Content>
64
64
  </Sheet.Root>
@@ -44,7 +44,7 @@ export const TrackUnmuteModal = () => {
44
44
  <RequestDialog
45
45
  title={`Unmute your ${track.type}?`}
46
46
  onOpenChange={(value: boolean) => !value && setMuteNotification(null)}
47
- body={`${peer?.name}is requesting you to unmute your ${track?.type}.`}
47
+ body={`${peer?.name} is requesting you to unmute your ${track?.type}.`}
48
48
  onAction={() => {
49
49
  hmsActions.setEnabledTrack(track.id, enabled);
50
50
  setMuteNotification(null);
@@ -16,7 +16,6 @@ import { config as cssConfig, useTheme } from '../../Theme';
16
16
  import { Video } from '../../Video';
17
17
  import { StyledVideoTile } from '../../VideoTile';
18
18
  import { LayoutModeSelector } from './LayoutModeSelector';
19
- // @ts-ignore: No implicit Any
20
19
  import { getVideoTileLabel } from './peerTileUtils';
21
20
  import { ScreenshareDisplay } from './ScreenshareDisplay';
22
21
  // @ts-ignore: No implicit Any
@@ -62,7 +61,8 @@ const Tile = ({ peerId, width = '100%', height = '100%' }: { peerId: string; wid
62
61
  const label = getVideoTileLabel({
63
62
  peerName: peer?.name,
64
63
  isLocal: false,
65
- track,
64
+ videoTrack: track,
65
+ audioTrack,
66
66
  });
67
67
 
68
68
  return (
@@ -140,7 +140,6 @@ const MobileSettingModal = ({
140
140
  <Flex
141
141
  direction="column"
142
142
  css={{
143
- px: '$8',
144
143
  pb: '$8',
145
144
  overflowY: 'auto',
146
145
  }}
@@ -159,7 +158,7 @@ const MobileSettingModal = ({
159
158
  css={{
160
159
  all: 'unset',
161
160
  fontFamily: '$sans',
162
- py: '$10',
161
+ p: '$10 $8',
163
162
  display: 'flex',
164
163
  alignItems: 'center',
165
164
  fontSize: '$sm',
@@ -1,6 +1,7 @@
1
1
  import React, { useState } from 'react';
2
2
  import { useMedia } from 'react-use';
3
3
  import {
4
+ HMSAudioTrack,
4
5
  HMSVideoTrack,
5
6
  selectLocalPeerID,
6
7
  selectPeerByID,
@@ -17,6 +18,7 @@ import { Text } from '../../../Text';
17
18
  import { config as cssConfig, useTheme } from '../../../Theme';
18
19
  import { StyledMenuTile } from '../../../TileMenu';
19
20
  import { ChangeNameModal } from '../MoreSettings/ChangeNameModal';
21
+ import { getVideoTileLabel } from '../peerTileUtils';
20
22
  import { RoleChangeModal } from '../RoleChangeModal';
21
23
  import { TileMenuContent } from './TileMenuContent';
22
24
  import { useDropdownList } from '../hooks/useDropdownList';
@@ -52,7 +54,8 @@ const TileMenu = ({
52
54
  const isPrimaryVideoTrack = useHMSStore(selectVideoTrackByPeerID(peerID))?.id === videoTrackID;
53
55
  const showPinAction = !!(audioTrackID || (videoTrackID && isPrimaryVideoTrack));
54
56
 
55
- const track = useHMSStore(selectTrackByID(videoTrackID)) as HMSVideoTrack;
57
+ const track = useHMSStore(selectTrackByID(videoTrackID)) as HMSVideoTrack | null;
58
+ const audioTrack = useHMSStore(selectTrackByID(audioTrackID)) as HMSAudioTrack | null;
56
59
  const hideSimulcastLayers = !track?.layerDefinitions?.length || track.degraded || !track.enabled;
57
60
  const isMobile = useMedia(cssConfig.media.md);
58
61
  const peer = useHMSStore(selectPeerByID(peerID));
@@ -61,20 +64,19 @@ const TileMenu = ({
61
64
  useDropdownList({ open, name: 'TileMenu' });
62
65
  const dragClassName = getDragClassName();
63
66
 
64
- if (!(removeOthers || toggleAudio || toggleVideo || setVolume || showPinAction) && hideSimulcastLayers) {
67
+ if (!peer || (!(removeOthers || toggleAudio || toggleVideo || setVolume || showPinAction) && hideSimulcastLayers)) {
65
68
  return null;
66
69
  }
67
70
 
68
71
  const openNameChangeModal = () => setShowNameChangeModal(true);
69
72
  const openRoleChangeModal = () => setShowRoleChangeModal(true);
70
73
 
71
- const props = {
74
+ const props: React.ComponentPropsWithoutRef<typeof TileMenuContent> = {
72
75
  isLocal,
73
76
  isScreenshare,
74
77
  audioTrackID,
75
78
  videoTrackID,
76
79
  peerID,
77
- isPrimaryVideoTrack,
78
80
  showSpotlight,
79
81
  showPinAction,
80
82
  canMinimise,
@@ -111,8 +113,7 @@ const TileMenu = ({
111
113
  >
112
114
  <Box>
113
115
  <Text css={{ color: '$on_surface_high', fontWeight: '$semiBold' }}>
114
- {peer?.name}
115
- {isLocal ? ` (You)` : null}
116
+ {getVideoTileLabel({ peerName: peer?.name, isLocal, audioTrack, videoTrack: track })}
116
117
  </Text>
117
118
  {peer?.roleName ? (
118
119
  <Text variant="xs" css={{ color: '$on_surface_low', mt: '$2' }}>
@@ -41,7 +41,7 @@ import { useSetAppDataByKey } from '../AppData/useUISettings';
41
41
  // @ts-ignore
42
42
  import { useDropdownSelection } from '../hooks/useDropdownSelection';
43
43
  import { getDragClassName } from './utils';
44
- import { APP_DATA, REMOTE_STOP_SCREENSHARE_TYPE, SESSION_STORE_KEY } from '../../common/constants';
44
+ import { APP_DATA, isIOS, REMOTE_STOP_SCREENSHARE_TYPE, SESSION_STORE_KEY } from '../../common/constants';
45
45
 
46
46
  export const isSameTile = ({
47
47
  trackId,
@@ -317,7 +317,7 @@ export const TileMenuContent = ({
317
317
  </StyledMenuTile.ItemButton>
318
318
  ) : null}
319
319
 
320
- {canChangeRole && roles.length > 1 ? (
320
+ {!isScreenshare && canChangeRole && roles.length > 1 ? (
321
321
  <StyledMenuTile.ItemButton
322
322
  className={dragClassName}
323
323
  css={spacingCSS}
@@ -332,7 +332,7 @@ export const TileMenuContent = ({
332
332
  </StyledMenuTile.ItemButton>
333
333
  ) : null}
334
334
 
335
- {audioTrackID ? (
335
+ {!isIOS && audioTrackID ? (
336
336
  <StyledMenuTile.VolumeItem data-testid="participant_volume_slider" css={{ ...spacingCSS, mb: '$0' }}>
337
337
  <Flex align="center" gap={1}>
338
338
  <SpeakerIcon height={20} width={20} />
@@ -1,5 +1,6 @@
1
1
  import React, { useEffect, useState } from 'react';
2
2
  import { useMedia } from 'react-use';
3
+ import { selectPeerCount, useHMSStore } from '@100mslive/react-sdk';
3
4
  import { PeopleAddIcon } from '@100mslive/react-icons';
4
5
  import { Flex } from '../../../Layout';
5
6
  import { config as cssConfig } from '../../../Theme';
@@ -17,6 +18,7 @@ export function EqualProminence({ isInsetEnabled = false, peers, onPageChange, o
17
18
  const isMobile = useMedia(cssConfig.media.md);
18
19
  let maxTileCount = useUISettings(UI_SETTINGS.maxTileCount);
19
20
  maxTileCount = isMobile ? Math.min(maxTileCount, 6) : maxTileCount;
21
+ const peerCount = useHMSStore(selectPeerCount);
20
22
  const pageList = usePagesWithTiles({
21
23
  peers,
22
24
  maxTileCount,
@@ -51,8 +53,8 @@ export function EqualProminence({ isInsetEnabled = false, peers, onPageChange, o
51
53
  )}
52
54
  {pageList.length === 0 ? (
53
55
  <WaitingView
54
- title="You're the first to join"
55
- subtitle="Sit back and relax till others join"
56
+ title={peerCount <= 1 ? "You're the only one here" : 'Glad to have you here'}
57
+ subtitle={peerCount <= 1 ? 'Sit back and relax till others join' : ''}
56
58
  icon={<PeopleAddIcon width="56px" height="56px" style={{ color: 'white' }} />}
57
59
  />
58
60
  ) : null}
@@ -20,7 +20,6 @@ import { VideoTileStats } from '../../Stats';
20
20
  import { CSS } from '../../Theme';
21
21
  import { Video } from '../../Video';
22
22
  import { StyledVideoTile } from '../../VideoTile';
23
- // @ts-ignore: No implicit Any
24
23
  import { getVideoTileLabel } from './peerTileUtils';
25
24
  // @ts-ignore: No implicit Any
26
25
  import { useSetAppDataByKey, useUISettings } from './AppData/useUISettings';
@@ -101,8 +100,9 @@ const Tile = ({
101
100
  });
102
101
  const label = getVideoTileLabel({
103
102
  peerName,
104
- track,
105
103
  isLocal,
104
+ videoTrack: track,
105
+ audioTrack,
106
106
  });
107
107
  const onHoverHandler = useCallback((event: React.MouseEvent) => {
108
108
  setIsMouseHovered(event.type === 'mouseenter');
@@ -2,7 +2,7 @@
2
2
  // eslint-disable-next-line
3
3
  import { HMSVBPlugin, HMSVirtualBackgroundTypes } from '@100mslive/hms-virtual-background/hmsvbplugin';
4
4
  import { parsedUserAgent } from '@100mslive/react-sdk';
5
- import { isSafari } from '../../common/constants';
5
+ import { isIOS, isSafari } from '../../common/constants';
6
6
  export class VBPlugin {
7
7
  private hmsPlugin?: HMSVBPlugin;
8
8
  private effectsPlugin?: any;
@@ -103,6 +103,18 @@ export class VBPlugin {
103
103
  this.hmsPlugin = undefined;
104
104
  };
105
105
 
106
+ isBlurSupported = () => {
107
+ if ((isSafari || isIOS) && this.hmsPlugin) {
108
+ return false;
109
+ }
110
+
111
+ if (this.effectsPlugin) {
112
+ return true;
113
+ }
114
+
115
+ return false;
116
+ };
117
+
106
118
  isEffectsSupported = () => {
107
119
  if (!isSafari) {
108
120
  return true;
@@ -57,6 +57,7 @@ export const VBPicker = ({ backgroundMedia = [] }: { backgroundMedia: VirtualBac
57
57
  const background = useHMSStore(selectAppData(APP_DATA.background));
58
58
  const mediaList = backgroundMedia.map((media: VirtualBackgroundMedia) => media.url || '');
59
59
  const pluginLoadingRef = useRef(false);
60
+ const isBlurSupported = VBHandler?.isBlurSupported();
60
61
 
61
62
  const inPreview = roomState === HMSRoomState.Preview;
62
63
  // Hidden in preview as the effect will be visible in the preview tile
@@ -190,7 +191,7 @@ export const VBPicker = ({ backgroundMedia = [] }: { backgroundMedia: VirtualBac
190
191
  await VBHandler?.setBlur(blurAmount);
191
192
  hmsActions.setAppData(APP_DATA.background, HMSVirtualBackgroundTypes.BLUR);
192
193
  },
193
- supported: isEffectsSupported && isEffectsEnabled,
194
+ supported: isBlurSupported,
194
195
  },
195
196
  ]}
196
197
  activeBackground={background}
@@ -198,7 +199,7 @@ export const VBPicker = ({ backgroundMedia = [] }: { backgroundMedia: VirtualBac
198
199
 
199
200
  {/* Slider */}
200
201
  <Flex direction="column" css={{ w: '100%', gap: '$8', mt: '$8' }}>
201
- {background === HMSVirtualBackgroundTypes.BLUR && isEffectsEnabled && effectsKey ? (
202
+ {background === HMSVirtualBackgroundTypes.BLUR && isBlurSupported ? (
202
203
  <Box>
203
204
  <Text variant="sm" css={{ color: '$on_surface_high', fontWeight: '$semiBold', mb: '$4' }}>
204
205
  Blur intensity
@@ -1,4 +1,8 @@
1
+ import { HMSAudioTrack, HMSVideoTrack } from '@100mslive/react-sdk';
2
+
1
3
  const PEER_NAME_PLACEHOLDER = 'peerName';
4
+
5
+ // Map [isLocal, videoSource] to the label to be displayed.
2
6
  const labelMap = new Map([
3
7
  [[true, 'screen'].toString(), 'Your Screen'],
4
8
  [[true, 'regular'].toString(), `You (${PEER_NAME_PLACEHOLDER})`],
@@ -8,19 +12,30 @@ const labelMap = new Map([
8
12
  [[false, undefined].toString(), `${PEER_NAME_PLACEHOLDER}`],
9
13
  ]);
10
14
 
11
- export const getVideoTileLabel = ({ peerName, isLocal, track }) => {
15
+ export const getVideoTileLabel = ({
16
+ peerName,
17
+ isLocal,
18
+ videoTrack,
19
+ audioTrack,
20
+ }: {
21
+ isLocal: boolean;
22
+ peerName?: string;
23
+ videoTrack?: HMSVideoTrack | null;
24
+ audioTrack?: HMSAudioTrack | null;
25
+ }) => {
12
26
  const isPeerPresent = peerName !== undefined;
13
- if (!isPeerPresent || !track) {
27
+ if (!isPeerPresent || !videoTrack) {
14
28
  // for peers with only audio track
15
- return isPeerPresent ? labelMap.get([isLocal, undefined].toString()).replace(PEER_NAME_PLACEHOLDER, peerName) : '';
29
+ const label = labelMap.get([isLocal, undefined].toString());
30
+ return isPeerPresent && label ? label.replace(PEER_NAME_PLACEHOLDER, peerName) : '';
16
31
  }
17
- const isLocallyMuted = track.volume === 0;
32
+ const isLocallyMuted = audioTrack?.volume === 0;
18
33
  // Map [isLocal, videoSource] to the label to be displayed.
19
- let label = labelMap.get([isLocal, track.source].toString());
34
+ let label = labelMap.get([isLocal, videoTrack.source].toString());
20
35
  if (label) {
21
36
  label = label.replace(PEER_NAME_PLACEHOLDER, peerName);
22
37
  } else {
23
- label = `${peerName} ${track.source}`;
38
+ label = `${peerName} ${videoTrack.source}`;
24
39
  }
25
40
  // label = `${label}${track.degraded ? '(Degraded)' : ''}`;
26
41
  return `${label}${isLocallyMuted ? ' (Muted for you)' : ''}`;
@@ -485,11 +485,6 @@ const HLSView = () => {
485
485
  css={{
486
486
  flex: isLandscape ? '2 1 0' : '1 1 0',
487
487
  transition: 'all 0.3s ease-in-out',
488
- '&:fullscreen': {
489
- '& video': {
490
- height: 'unset !important',
491
- },
492
- },
493
488
  }}
494
489
  >
495
490
  {hlsViewRef.current && (isMobile || isLandscape) && (
@@ -541,6 +536,7 @@ const HLSView = () => {
541
536
  onMouseMove={onHoverHandler}
542
537
  onMouseLeave={onHoverHandler}
543
538
  onClick={onClickHandler}
539
+ isFullScreen={isFullScreen}
544
540
  onDoubleClick={e => {
545
541
  onDoubleClickHandler(e);
546
542
  }}
@@ -728,6 +724,7 @@ const HLSView = () => {
728
724
  selection={currentSelectedQuality}
729
725
  onQualityChange={handleQuality}
730
726
  isAuto={isUserSelectedAuto}
727
+ containerRef={hlsViewRef.current}
731
728
  />
732
729
  ) : null}
733
730
  {isFullScreenSupported ? (