@100mslive/roomkit-react 0.1.9 → 0.1.10

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 (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;