@100mslive/roomkit-react 0.1.9 → 0.1.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. package/dist/{HLSView-VGJ2XUDT.js → HLSView-7WXNI2WP.js} +14 -10
  2. package/dist/HLSView-7WXNI2WP.js.map +7 -0
  3. package/dist/Modal/Dialog.d.ts +878 -9
  4. package/dist/Modal/DialogContent.d.ts +2 -2
  5. package/dist/Prebuilt/components/Leave/DesktopLeaveRoom.d.ts +4 -4
  6. package/dist/Prebuilt/components/Leave/EndSessionContent.d.ts +3 -3
  7. package/dist/Prebuilt/components/Leave/LeaveSessionContent.d.ts +3 -3
  8. package/dist/Prebuilt/components/Leave/MwebLeaveRoom.d.ts +4 -4
  9. package/dist/Prebuilt/components/VirtualBackground/VBCollection.d.ts +13 -0
  10. package/dist/Prebuilt/components/VirtualBackground/VBOption.d.ts +15 -0
  11. package/dist/Prebuilt/components/VirtualBackground/constants.d.ts +9 -0
  12. package/dist/Prebuilt/layouts/SidePane.d.ts +2 -2
  13. package/dist/{chunk-EDGWHFCM.js → chunk-N5HPVHTK.js} +6091 -4897
  14. package/dist/chunk-N5HPVHTK.js.map +7 -0
  15. package/dist/index.cjs.js +5217 -5060
  16. package/dist/index.cjs.js.map +4 -4
  17. package/dist/index.js +11 -13
  18. package/dist/meta.cjs.json +1567 -1385
  19. package/dist/meta.esbuild.json +1657 -1551
  20. package/package.json +6 -6
  21. package/src/Modal/Dialog.tsx +3 -11
  22. package/src/Modal/DialogContent.tsx +4 -4
  23. package/src/Prebuilt/common/constants.js +3 -0
  24. package/src/Prebuilt/components/AppData/AppData.jsx +4 -11
  25. package/src/Prebuilt/components/AppData/useSidepane.js +1 -1
  26. package/src/Prebuilt/components/Connection/ConnectionIndicator.tsx +2 -2
  27. package/src/Prebuilt/components/Footer/Footer.tsx +4 -8
  28. package/src/Prebuilt/components/Footer/ParticipantList.jsx +1 -6
  29. package/src/Prebuilt/components/Header/Header.tsx +2 -2
  30. package/src/Prebuilt/components/Header/common.jsx +46 -23
  31. package/src/Prebuilt/components/Leave/DesktopLeaveRoom.tsx +5 -5
  32. package/src/Prebuilt/components/Leave/EndSessionContent.tsx +2 -2
  33. package/src/Prebuilt/components/Leave/LeaveRoom.tsx +6 -5
  34. package/src/Prebuilt/components/Leave/LeaveSessionContent.tsx +2 -2
  35. package/src/Prebuilt/components/Leave/MwebLeaveRoom.tsx +3 -3
  36. package/src/Prebuilt/components/Notifications/HLSFailureModal.tsx +1 -4
  37. package/src/Prebuilt/components/Preview/PreviewForm.tsx +7 -2
  38. package/src/Prebuilt/components/Preview/PreviewJoin.tsx +62 -57
  39. package/src/Prebuilt/components/RoleChangeRequest/RequestPrompt.tsx +2 -3
  40. package/src/Prebuilt/components/Settings/DeviceSettings.jsx +11 -0
  41. package/src/Prebuilt/components/VirtualBackground/VBCollection.tsx +50 -0
  42. package/src/Prebuilt/components/VirtualBackground/VBOption.tsx +50 -0
  43. package/src/Prebuilt/components/VirtualBackground/VBPicker.jsx +165 -0
  44. package/src/Prebuilt/components/VirtualBackground/VBToggle.jsx +25 -0
  45. package/src/Prebuilt/components/VirtualBackground/constants.ts +26 -0
  46. package/src/Prebuilt/components/hooks/useAutoStartStreaming.tsx +4 -7
  47. package/src/Prebuilt/layouts/HLSView.jsx +7 -1
  48. package/src/Prebuilt/layouts/SidePane.tsx +21 -5
  49. package/dist/HLSView-VGJ2XUDT.js.map +0 -7
  50. package/dist/VirtualBackground-2VZVBRIC.js +0 -175
  51. package/dist/VirtualBackground-2VZVBRIC.js.map +0 -7
  52. package/dist/chunk-EDGWHFCM.js.map +0 -7
  53. package/dist/chunk-SONHO3VM.js +0 -962
  54. package/dist/chunk-SONHO3VM.js.map +0 -7
@@ -1,4 +1,4 @@
1
- import React, { Fragment, Suspense, useCallback, useEffect, useMemo, useState } from 'react';
1
+ import React, { Fragment, useCallback, useEffect, useMemo, useState } from 'react';
2
2
  import { useMeasure, useMedia } from 'react-use';
3
3
  import {
4
4
  HMSRoomState,
@@ -18,6 +18,7 @@ import { AudioLevel } from '../../../AudioLevel';
18
18
  import { useHMSPrebuiltContext } from '../../AppContext';
19
19
  // @ts-ignore: No implicit Any
20
20
  import IconButton from '../../IconButton';
21
+ import SidePane from '../../layouts/SidePane';
21
22
  import { useRoomLayout } from '../../provider/roomLayoutProvider';
22
23
  // @ts-ignore: No implicit Any
23
24
  import { AudioVideoToggle } from '../AudioVideoToggle';
@@ -32,6 +33,8 @@ import { Logo } from '../Header/HeaderComponents';
32
33
  // @ts-ignore: No implicit Any
33
34
  import SettingsModal from '../Settings/SettingsModal';
34
35
  // @ts-ignore: No implicit Any
36
+ import { VBToggle } from '../VirtualBackground/VBToggle';
37
+ // @ts-ignore: No implicit Any
35
38
  import PreviewForm from './PreviewForm';
36
39
  // @ts-ignore: No implicit Any
37
40
  import { useAuthToken, useUISettings } from '../AppData/useUISettings';
@@ -42,9 +45,6 @@ import { calculateAvatarAndAttribBoxSize, getFormattedCount } from '../../common
42
45
  // @ts-ignore: No implicit Any
43
46
  import { UI_SETTINGS } from '../../common/constants';
44
47
 
45
- // @ts-ignore: No implicit Any
46
- const VirtualBackground = React.lazy(() => import('../../plugins/VirtualBackground/VirtualBackground'));
47
-
48
48
  const getParticipantChipContent = (peerCount = 0) => {
49
49
  if (peerCount === 0) {
50
50
  return 'You are the first to join';
@@ -133,60 +133,65 @@ const PreviewJoin = ({
133
133
  }, [initialName]);
134
134
 
135
135
  return roomState === HMSRoomState.Preview ? (
136
- <Container css={{ h: '100%', pt: '$10', '@md': { justifyContent: 'space-between' } }}>
137
- {toggleVideo ? null : <Box />}
138
- <Flex direction="column" justify="center" css={{ w: '100%', maxWidth: '640px' }}>
139
- <Logo />
140
- <Text
141
- variant="h4"
142
- css={{ wordBreak: 'break-word', textAlign: 'center', mt: '$14', mb: '$4', '@md': { mt: '$8', mb: '$2' } }}
143
- >
144
- {previewHeader.title}
145
- </Text>
146
- <Text
147
- css={{ c: '$on_surface_medium', my: '0', textAlign: 'center', maxWidth: '100%', wordWrap: 'break-word' }}
148
- variant="sm"
149
- >
150
- {previewHeader.sub_title}
151
- </Text>
152
- <Flex justify="center" css={{ mt: '$14', '@md': { mt: '$8', mb: '0' }, gap: '$4' }}>
153
- {isStreamingOn ? (
154
- <Chip
155
- content="LIVE"
156
- backgroundColor="$alert_error_default"
157
- textColor="#FFF"
158
- icon={<Box css={{ h: '$sm', w: '$sm', backgroundColor: '$on_primary_high', borderRadius: '$round' }} />}
159
- />
160
- ) : null}
161
- <Chip content={getParticipantChipContent(peerCount)} hideIfNoContent />
136
+ <Flex justify="center" css={{ size: '100%', position: 'relative' }}>
137
+ <Container css={{ h: '100%', pt: '$10', '@md': { justifyContent: 'space-between' } }}>
138
+ {toggleVideo ? null : <Box />}
139
+ <Flex direction="column" justify="center" css={{ w: '100%', maxWidth: '640px' }}>
140
+ <Logo />
141
+ <Text
142
+ variant="h4"
143
+ css={{ wordBreak: 'break-word', textAlign: 'center', mt: '$14', mb: '$4', '@md': { mt: '$8', mb: '$2' } }}
144
+ >
145
+ {previewHeader.title}
146
+ </Text>
147
+ <Text
148
+ css={{ c: '$on_surface_medium', my: '0', textAlign: 'center', maxWidth: '100%', wordWrap: 'break-word' }}
149
+ variant="sm"
150
+ >
151
+ {previewHeader.sub_title}
152
+ </Text>
153
+ <Flex justify="center" css={{ mt: '$14', '@md': { mt: '$8', mb: '0' }, gap: '$4' }}>
154
+ {isStreamingOn ? (
155
+ <Chip
156
+ content="LIVE"
157
+ backgroundColor="$alert_error_default"
158
+ textColor="#FFF"
159
+ icon={<Box css={{ h: '$sm', w: '$sm', backgroundColor: '$on_primary_high', borderRadius: '$round' }} />}
160
+ />
161
+ ) : null}
162
+ <Chip content={getParticipantChipContent(peerCount)} hideIfNoContent />
163
+ </Flex>
162
164
  </Flex>
163
- </Flex>
164
- {toggleVideo ? (
165
- <Flex
166
- align="center"
167
- justify="center"
168
- css={{
169
- mt: '$14',
170
- '@md': { mt: 0 },
171
- '@sm': { width: '100%' },
172
- flexDirection: 'column',
173
- }}
174
- >
175
- <PreviewTile name={name} error={previewError} />
176
- </Flex>
177
- ) : null}
178
- <Box css={{ w: '100%', maxWidth: `${Math.max(aspectRatio, 1) * 360}px` }}>
179
- <PreviewControls hideSettings={!toggleVideo && !toggleAudio} />
180
- <PreviewForm
181
- name={name}
182
- onChange={setName}
183
- enableJoin={enableJoin}
184
- onJoin={savePreferenceAndJoin}
185
- cannotPublishVideo={!toggleVideo}
186
- cannotPublishAudio={!toggleAudio}
187
- />
165
+ {toggleVideo ? (
166
+ <Flex
167
+ align="center"
168
+ justify="center"
169
+ css={{
170
+ mt: '$14',
171
+ '@md': { mt: 0 },
172
+ '@sm': { width: '100%' },
173
+ flexDirection: 'column',
174
+ }}
175
+ >
176
+ <PreviewTile name={name} error={previewError} />
177
+ </Flex>
178
+ ) : null}
179
+ <Box css={{ w: '100%', maxWidth: `${Math.max(aspectRatio, 1) * 360}px` }}>
180
+ <PreviewControls hideSettings={!toggleVideo && !toggleAudio} />
181
+ <PreviewForm
182
+ name={name}
183
+ onChange={setName}
184
+ enableJoin={enableJoin}
185
+ onJoin={savePreferenceAndJoin}
186
+ cannotPublishVideo={!toggleVideo}
187
+ cannotPublishAudio={!toggleAudio}
188
+ />
189
+ </Box>
190
+ </Container>
191
+ <Box css={{ position: 'absolute', right: '0', top: 0, height: '100%' }}>
192
+ <SidePane screenType="default" />
188
193
  </Box>
189
- </Container>
194
+ </Flex>
190
195
  ) : (
191
196
  <FullPageProgress />
192
197
  );
@@ -274,7 +279,7 @@ export const PreviewControls = ({ hideSettings }: { hideSettings: boolean }) =>
274
279
  >
275
280
  <Flex css={{ gap: '$4' }}>
276
281
  <AudioVideoToggle />
277
- <Suspense fallback="">{!isMobile ? <VirtualBackground /> : null}</Suspense>
282
+ {!isMobile ? <VBToggle /> : null}
278
283
  </Flex>
279
284
  {!hideSettings ? <PreviewSettings /> : null}
280
285
  </Flex>
@@ -33,10 +33,9 @@ export const RequestPrompt = ({
33
33
  }
34
34
 
35
35
  return (
36
- <Dialog.Root open={open} onOpenChange={onOpenChange}>
36
+ <Dialog.Root open={open} modal={false} onOpenChange={onOpenChange}>
37
37
  <Dialog.Portal>
38
- <Dialog.Overlay />
39
- <Dialog.Content css={{ p: '$10' }}>
38
+ <Dialog.Content css={{ p: '$10' }} onInteractOutside={e => e.preventDefault()}>
40
39
  <Dialog.Title css={{ p: 0, display: 'flex', flexDirection: 'row', gap: '$md', justifyContent: 'center' }}>
41
40
  <Text variant="h6">{title}</Text>
42
41
  </Dialog.Title>
@@ -1,14 +1,17 @@
1
1
  import React, { Fragment, useEffect, useRef, useState } from 'react';
2
+ import { useMedia } from 'react-use';
2
3
  import {
3
4
  DeviceType,
4
5
  selectIsLocalVideoEnabled,
5
6
  selectLocalVideoTrackID,
6
7
  selectVideoTrackByID,
7
8
  useDevices,
9
+ useHMSActions,
8
10
  useHMSStore,
9
11
  } from '@100mslive/react-sdk';
10
12
  import { MicOnIcon, SpeakerIcon, VideoOnIcon } from '@100mslive/react-icons';
11
13
  import { Box, Button, Dropdown, Flex, StyledVideoTile, Text, Video } from '../../../';
14
+ import { config as cssConfig } from '../../../Theme';
12
15
  import { DialogDropdownTrigger } from '../../primitives/DropdownTrigger';
13
16
  import { useUISettings } from '../AppData/useUISettings';
14
17
  import { useDropdownSelection } from '../hooks/useDropdownSelection';
@@ -30,7 +33,15 @@ const Settings = ({ setHide }) => {
30
33
  const shouldShowAudioOutput = 'setSinkId' in HTMLMediaElement.prototype;
31
34
  const mirrorLocalVideo = useUISettings(UI_SETTINGS.mirrorLocalVideo);
32
35
  const trackSelector = selectVideoTrackByID(videoTrackId);
36
+ const hmsActions = useHMSActions();
33
37
  const track = useHMSStore(trackSelector);
38
+ const isMobile = useMedia(cssConfig.media.md);
39
+
40
+ useEffect(() => {
41
+ if (isMobile) {
42
+ hmsActions.refreshDevices();
43
+ }
44
+ }, [hmsActions, isMobile]);
34
45
 
35
46
  /**
36
47
  * Chromium browsers return an audioOutput with empty label when no permissions are given
@@ -0,0 +1,50 @@
1
+ import React from 'react';
2
+ import { Box } from '../../../Layout';
3
+ import { Text } from '../../../Text';
4
+ import { VBOption } from './VBOption';
5
+ import { VB_EFFECT } from './constants';
6
+
7
+ export const VBCollection = ({
8
+ options,
9
+ title,
10
+ activeBackgroundType = '',
11
+ activeBackground = '',
12
+ }: {
13
+ options: {
14
+ title?: string;
15
+ icon?: React.JSX.Element;
16
+ onClick?: () => Promise<void>;
17
+ mediaURL?: string;
18
+ type: string;
19
+ }[];
20
+ title: string;
21
+ activeBackground: HTMLImageElement | string;
22
+ activeBackgroundType: string;
23
+ }) => {
24
+ if (options.length === 0) {
25
+ return null;
26
+ }
27
+ return (
28
+ <Box css={{ mt: '$10' }}>
29
+ <Text variant="sm" css={{ color: '$on_surface_high', fontWeight: '$semiBold' }}>
30
+ {title}
31
+ </Text>
32
+ <Box css={{ py: '$4', display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: '$8' }}>
33
+ {options.map(option => (
34
+ <VBOption.Root
35
+ key={option?.mediaURL || option?.title}
36
+ {...option}
37
+ isActive={
38
+ ([VB_EFFECT.NONE, VB_EFFECT.BLUR].includes(activeBackgroundType) &&
39
+ option.type === activeBackgroundType) ||
40
+ activeBackground === option?.mediaURL
41
+ }
42
+ >
43
+ <VBOption.Icon>{option?.icon}</VBOption.Icon>
44
+ <VBOption.Title>{option?.title}</VBOption.Title>
45
+ </VBOption.Root>
46
+ ))}
47
+ </Box>
48
+ </Box>
49
+ );
50
+ };
@@ -0,0 +1,50 @@
1
+ import React from 'react';
2
+ import { Box, Flex } from '../../../Layout';
3
+ import { Text } from '../../../Text';
4
+
5
+ const Root = ({
6
+ onClick,
7
+ mediaURL,
8
+ isActive,
9
+ children,
10
+ }: {
11
+ onClick?: () => Promise<void>;
12
+ mediaURL?: string;
13
+ isActive: boolean;
14
+ children?: React.JSX.Element[];
15
+ }) => (
16
+ <Flex
17
+ direction="column"
18
+ align="center"
19
+ css={{
20
+ p: '$5',
21
+ borderRadius: '$1',
22
+ bg: '$surface_bright',
23
+ border: `4px solid ${isActive ? '$primary_default' : '$surface_dim'}`,
24
+ cursor: 'pointer',
25
+ '&:hover': { border: '4px solid $primary_dim' },
26
+ ...(mediaURL ? { height: '$20', backgroundImage: `url(${mediaURL})`, backgroundSize: 'cover' } : {}),
27
+ }}
28
+ onClick={async () => await onClick?.()}
29
+ >
30
+ {children}
31
+ </Flex>
32
+ );
33
+
34
+ const Title = ({ children }: { children?: string }) => {
35
+ return children ? (
36
+ <Text variant="xs" css={{ color: '$on_surface_medium' }}>
37
+ {children}
38
+ </Text>
39
+ ) : null;
40
+ };
41
+
42
+ const Icon = ({ children }: { children?: React.JSX.Element }) => {
43
+ return children ? <Box css={{ color: '$on_surface_high' }}>{children}</Box> : null;
44
+ };
45
+
46
+ export const VBOption = {
47
+ Root,
48
+ Title,
49
+ Icon,
50
+ };
@@ -0,0 +1,165 @@
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import {
3
+ HMSRoomState,
4
+ selectIsLargeRoom,
5
+ selectIsLocalVideoEnabled,
6
+ selectLocalPeer,
7
+ selectLocalPeerRole,
8
+ selectLocalVideoTrackID,
9
+ selectRoomState,
10
+ selectVideoTrackByID,
11
+ useHMSActions,
12
+ useHMSStore,
13
+ } from '@100mslive/react-sdk';
14
+ import { BlurPersonHighIcon, CloseIcon, CrossCircleIcon } from '@100mslive/react-icons';
15
+ import { Box, Flex, Video } from '../../../index';
16
+ import { Text } from '../../../Text';
17
+ import { VBCollection } from './VBCollection';
18
+ import { useSidepaneToggle } from '../AppData/useSidepane';
19
+ import { useUISettings } from '../AppData/useUISettings';
20
+ import { SIDE_PANE_OPTIONS, UI_SETTINGS } from '../../common/constants';
21
+ import { defaultMedia, VB_EFFECT, vbPlugin } from './constants';
22
+
23
+ const iconDims = { height: '40px', width: '40px' };
24
+ const MAX_RETRIES = 2;
25
+
26
+ export const VBPicker = () => {
27
+ const toggleVB = useSidepaneToggle(SIDE_PANE_OPTIONS.VB);
28
+ const hmsActions = useHMSActions();
29
+ const role = useHMSStore(selectLocalPeerRole);
30
+ const [isVBSupported, setIsVBSupported] = useState(false);
31
+ const localPeerVideoTrackID = useHMSStore(selectLocalVideoTrackID);
32
+ const localPeer = useHMSStore(selectLocalPeer);
33
+ const [background, setBackground] = useState(vbPlugin.background);
34
+ const [backgroundType, setBackgroundType] = useState(vbPlugin.backgroundType);
35
+ const isVideoOn = useHMSStore(selectIsLocalVideoEnabled);
36
+ const mirrorLocalVideo = useUISettings(UI_SETTINGS.mirrorLocalVideo);
37
+ const trackSelector = selectVideoTrackByID(localPeer?.videoTrack);
38
+ const track = useHMSStore(trackSelector);
39
+ const roomState = useHMSStore(selectRoomState);
40
+ const isLargeRoom = useHMSStore(selectIsLargeRoom);
41
+ const addedPluginToVideoTrack = useRef(false);
42
+
43
+ // Hidden in preview as the effect will be visible in the preview tile. Needed inside the room because the peer might not be on-screen
44
+ const showVideoTile = isVideoOn && isLargeRoom && roomState !== HMSRoomState.Preview;
45
+
46
+ const clearVBState = () => {
47
+ setBackground(VB_EFFECT.NONE);
48
+ setBackgroundType(VB_EFFECT.NONE);
49
+ };
50
+
51
+ useEffect(() => {
52
+ if (!localPeerVideoTrackID) {
53
+ return;
54
+ }
55
+
56
+ //check support of plugin
57
+ if (vbPlugin) {
58
+ const pluginSupport = hmsActions.validateVideoPluginSupport(vbPlugin);
59
+ setIsVBSupported(pluginSupport.isSupported);
60
+ }
61
+ }, [hmsActions, localPeerVideoTrackID]);
62
+
63
+ async function disableEffects() {
64
+ if (vbPlugin) {
65
+ vbPlugin.setBackground(VB_EFFECT.NONE, VB_EFFECT.NONE);
66
+ clearVBState();
67
+ }
68
+ }
69
+
70
+ async function addPlugin({ mediaURL = '', blurPower = 0 }) {
71
+ let retries = 0;
72
+ try {
73
+ if (mediaURL) {
74
+ const img = document.createElement('img');
75
+ img.alt = 'VB';
76
+ img.src = mediaURL;
77
+ try {
78
+ await vbPlugin.setBackground(img, VB_EFFECT.MEDIA);
79
+ } catch (e) {
80
+ console.error(e);
81
+ if (retries++ < MAX_RETRIES) {
82
+ await vbPlugin.setBackground(img, VB_EFFECT.MEDIA);
83
+ }
84
+ }
85
+ } else if (blurPower) {
86
+ await vbPlugin.setBackground(VB_EFFECT.BLUR, VB_EFFECT.BLUR);
87
+ }
88
+ setBackground(mediaURL || VB_EFFECT.BLUR);
89
+ setBackgroundType(mediaURL ? VB_EFFECT.MEDIA : VB_EFFECT.BLUR);
90
+ if (role && !addedPluginToVideoTrack.current) {
91
+ await hmsActions.addPluginToVideoTrack(vbPlugin, Math.floor(role.publishParams.video.frameRate / 2));
92
+ addedPluginToVideoTrack.current = true;
93
+ }
94
+ } catch (err) {
95
+ console.error('Failed to apply VB', err);
96
+ disableEffects();
97
+ }
98
+ }
99
+
100
+ useEffect(() => {
101
+ if (!isVideoOn) {
102
+ toggleVB();
103
+ }
104
+ }, [isVideoOn, toggleVB]);
105
+
106
+ if (!isVBSupported) {
107
+ return null;
108
+ }
109
+
110
+ return (
111
+ <Box css={{ maxHeight: '100%', overflowY: 'auto', pr: '$6' }}>
112
+ <Flex align="center" justify="between" css={{ w: '100%', position: 'sticky', top: 0 }}>
113
+ <Text variant="h6" css={{ color: '$on_surface_high' }}>
114
+ Virtual Background
115
+ </Text>
116
+ <Box
117
+ css={{ color: '$on_surface_high', '&:hover': { color: '$on_surface_medium' }, cursor: 'pointer' }}
118
+ onClick={toggleVB}
119
+ >
120
+ <CloseIcon />
121
+ </Box>
122
+ </Flex>
123
+
124
+ {showVideoTile ? (
125
+ <Video
126
+ mirror={track?.facingMode !== 'environment' && mirrorLocalVideo}
127
+ trackId={localPeer?.videoTrack}
128
+ data-testid="preview_tile"
129
+ css={{ width: '100%', height: '16rem', position: 'sticky', top: '$17' }}
130
+ />
131
+ ) : null}
132
+
133
+ <VBCollection
134
+ title="Effects"
135
+ options={[
136
+ {
137
+ title: 'No effect',
138
+ icon: <CrossCircleIcon style={iconDims} />,
139
+ type: VB_EFFECT.NONE,
140
+ onClick: async () => await disableEffects(),
141
+ },
142
+ {
143
+ title: 'Blur',
144
+ icon: <BlurPersonHighIcon style={iconDims} />,
145
+ type: VB_EFFECT.BLUR,
146
+ onClick: async () => await addPlugin({ blurPower: 0.5 }),
147
+ },
148
+ ]}
149
+ activeBackgroundType={backgroundType || VB_EFFECT.NONE}
150
+ activeBackground={vbPlugin.background?.src || vbPlugin.background || VB_EFFECT.NONE}
151
+ />
152
+
153
+ <VBCollection
154
+ title="Backgrounds"
155
+ options={defaultMedia.map(mediaURL => ({
156
+ type: VB_EFFECT.MEDIA,
157
+ mediaURL,
158
+ onClick: async () => await addPlugin({ mediaURL }),
159
+ }))}
160
+ activeBackgroundType={backgroundType || VB_EFFECT.NONE}
161
+ activeBackground={background?.src || background || VB_EFFECT.NONE}
162
+ />
163
+ </Box>
164
+ );
165
+ };
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+ import { selectIsLocalVideoEnabled, useHMSStore } from '@100mslive/react-sdk';
3
+ import { VirtualBackgroundIcon } from '@100mslive/react-icons';
4
+ import { Tooltip } from '../../../Tooltip';
5
+ import IconButton from '../../IconButton';
6
+ import { useIsSidepaneTypeOpen, useSidepaneToggle } from '../AppData/useSidepane';
7
+ import { isSafari, SIDE_PANE_OPTIONS } from '../../common/constants';
8
+
9
+ export const VBToggle = () => {
10
+ const toggleVB = useSidepaneToggle(SIDE_PANE_OPTIONS.VB);
11
+ const isVBOpen = useIsSidepaneTypeOpen(SIDE_PANE_OPTIONS.VB);
12
+ const isVideoOn = useHMSStore(selectIsLocalVideoEnabled);
13
+
14
+ if (!isVideoOn || isSafari) {
15
+ return null;
16
+ }
17
+
18
+ return (
19
+ <Tooltip side="top" disabled={isVBOpen} title="Configure Virtual Background">
20
+ <IconButton active={!isVBOpen} onClick={toggleVB}>
21
+ <VirtualBackgroundIcon />
22
+ </IconButton>
23
+ </Tooltip>
24
+ );
25
+ };
@@ -0,0 +1,26 @@
1
+ import { HMSVBPlugin, HMSVirtualBackgroundTypes } from '@100mslive/hms-virtual-background';
2
+
3
+ // Will support all media, setting to image here to test with current plugin interface
4
+ export const VB_EFFECT = {
5
+ BLUR: 'blur',
6
+ BEAUTIFY: 'BEAUTIFY',
7
+ NONE: 'none',
8
+ MEDIA: 'image',
9
+ };
10
+
11
+ export const defaultMedia = [
12
+ 'https://assets.100ms.live/webapp/vb-mini/vb-1.jpg',
13
+ 'https://assets.100ms.live/webapp/vb-mini/vb-2.jpg',
14
+ 'https://assets.100ms.live/webapp/vb-mini/vb-3.png',
15
+ 'https://assets.100ms.live/webapp/vb-mini/vb-4.jpg',
16
+ 'https://assets.100ms.live/webapp/vb-mini/vb-5.jpg',
17
+ 'https://assets.100ms.live/webapp/vb-mini/vb-6.jpg',
18
+ 'https://assets.100ms.live/webapp/vb-mini/vb-7.jpg',
19
+ 'https://assets.100ms.live/webapp/vb-mini/vb-8.jpg',
20
+ 'https://assets.100ms.live/webapp/vb-mini/vb-9.jpg',
21
+ 'https://assets.100ms.live/webapp/vb-mini/vb-10.jpg',
22
+ 'https://assets.100ms.live/webapp/vb-mini/vb-11.jpg',
23
+ 'https://assets.100ms.live/webapp/vb-mini/vb-12.jpg',
24
+ ];
25
+
26
+ export const vbPlugin = new HMSVBPlugin(HMSVirtualBackgroundTypes.NONE, HMSVirtualBackgroundTypes.NONE);
@@ -1,6 +1,5 @@
1
1
  import { useCallback, useEffect, useRef } from 'react';
2
2
  import {
3
- HMSException,
4
3
  selectIsConnectedToRoom,
5
4
  selectPermissions,
6
5
  useHMSActions,
@@ -20,7 +19,7 @@ export const useAutoStartStreaming = () => {
20
19
  const showStreamingUI = useShowStreamingUI();
21
20
  const hmsActions = useHMSActions();
22
21
  const isConnected = useHMSStore(selectIsConnectedToRoom);
23
- const { isHLSRunning, isRTMPRunning } = useRecordingStreaming();
22
+ const { isHLSRunning, isRTMPRunning, isRecordingOn } = useRecordingStreaming();
24
23
  const streamStartedRef = useRef(false);
25
24
 
26
25
  const startHLS = useCallback(async () => {
@@ -32,9 +31,7 @@ export const useAutoStartStreaming = () => {
32
31
  streamStartedRef.current = true;
33
32
  await hmsActions.startHLSStreaming();
34
33
  } catch (error) {
35
- if ((error as HMSException).message?.includes('beam already started')) {
36
- return;
37
- }
34
+ console.error(error);
38
35
  streamStartedRef.current = false;
39
36
  setHLSStarted(false);
40
37
  }
@@ -47,10 +44,10 @@ export const useAutoStartStreaming = () => {
47
44
  }, [isHLSStarted, isHLSRunning]);
48
45
 
49
46
  useEffect(() => {
50
- if (!isConnected || streamStartedRef.current || !permissions?.hlsStreaming) {
47
+ if (!isConnected || streamStartedRef.current || !permissions?.hlsStreaming || isRecordingOn) {
51
48
  return;
52
49
  }
53
- // Is a streaming kit and broadcaster joins
50
+ // Is a streaming kit and peer with streaming permissions joins
54
51
  startHLS();
55
52
  // eslint-disable-next-line react-hooks/exhaustive-deps
56
53
  }, [isConnected]);
@@ -1,5 +1,5 @@
1
1
  import React, { useCallback, useEffect, useRef, useState } from 'react';
2
- import { useFullscreen, useMedia, useToggle } from 'react-use';
2
+ import { useFullscreen, useMedia, usePrevious, useToggle } from 'react-use';
3
3
  import { HLSPlaybackState, HMSHLSPlayer, HMSHLSPlayerEvents } from '@100mslive/hls-player';
4
4
  import screenfull from 'screenfull';
5
5
  import { selectAppData, selectHLSState, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
@@ -42,6 +42,7 @@ const HLSView = () => {
42
42
  const controlsRef = useRef();
43
43
  const controlsTimerRef = useRef();
44
44
  const [qualityDropDownOpen, setQualityDropDownOpen] = useState(false);
45
+ const lastHlsUrl = usePrevious(hlsUrl);
45
46
 
46
47
  const isMobile = useMedia(config.media.md);
47
48
  const isFullScreen = useFullscreen(hlsViewRef, show, {
@@ -64,6 +65,11 @@ const HLSView = () => {
64
65
  videoEl?.removeEventListener('waiting', showLoader);
65
66
  };
66
67
  }, []);
68
+ useEffect(() => {
69
+ if (streamEnded && lastHlsUrl !== hlsUrl) {
70
+ setStreamEnded(false);
71
+ }
72
+ }, [hlsUrl, streamEnded, lastHlsUrl]);
67
73
 
68
74
  useEffect(() => {
69
75
  const videoElem = videoRef.current;