diff --git a/packages/react-icons/assets/BlurPersonHighIcon.svg b/packages/react-icons/assets/BlurPersonHighIcon.svg
new file mode 100644
index 0000000000..1bb4b581fb
--- /dev/null
+++ b/packages/react-icons/assets/BlurPersonHighIcon.svg
@@ -0,0 +1,18 @@
+
diff --git a/packages/react-icons/assets/BlurPersonLowIcon.svg b/packages/react-icons/assets/BlurPersonLowIcon.svg
new file mode 100644
index 0000000000..15811eea54
--- /dev/null
+++ b/packages/react-icons/assets/BlurPersonLowIcon.svg
@@ -0,0 +1,18 @@
+
diff --git a/packages/react-icons/assets/SparkleIcon.svg b/packages/react-icons/assets/SparkleIcon.svg
new file mode 100644
index 0000000000..9016989109
--- /dev/null
+++ b/packages/react-icons/assets/SparkleIcon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/react-icons/src/BlurPersonHighIcon.tsx b/packages/react-icons/src/BlurPersonHighIcon.tsx
new file mode 100644
index 0000000000..81a1b11838
--- /dev/null
+++ b/packages/react-icons/src/BlurPersonHighIcon.tsx
@@ -0,0 +1,17 @@
+import * as React from 'react';
+import { SVGProps } from 'react';
+const SvgBlurPersonHighIcon = (props: SVGProps) => (
+
+);
+export default SvgBlurPersonHighIcon;
diff --git a/packages/react-icons/src/BlurPersonLowIcon.tsx b/packages/react-icons/src/BlurPersonLowIcon.tsx
new file mode 100644
index 0000000000..cbad4a8a53
--- /dev/null
+++ b/packages/react-icons/src/BlurPersonLowIcon.tsx
@@ -0,0 +1,21 @@
+import * as React from 'react';
+import { SVGProps } from 'react';
+const SvgBlurPersonLowIcon = (props: SVGProps) => (
+
+);
+export default SvgBlurPersonLowIcon;
diff --git a/packages/react-icons/src/SparkleIcon.tsx b/packages/react-icons/src/SparkleIcon.tsx
new file mode 100644
index 0000000000..909338e0a4
--- /dev/null
+++ b/packages/react-icons/src/SparkleIcon.tsx
@@ -0,0 +1,14 @@
+import * as React from 'react';
+import { SVGProps } from 'react';
+const SvgSparkleIcon = (props: SVGProps) => (
+
+);
+export default SvgSparkleIcon;
diff --git a/packages/react-icons/src/index.ts b/packages/react-icons/src/index.ts
index df48d624f9..6642166993 100644
--- a/packages/react-icons/src/index.ts
+++ b/packages/react-icons/src/index.ts
@@ -30,6 +30,8 @@ export { default as BatteryFullIcon } from './BatteryFullIcon';
export { default as BatteryPowerIcon } from './BatteryPowerIcon';
export { default as BillIcon } from './BillIcon';
export { default as BluetoothIcon } from './BluetoothIcon';
+export { default as BlurPersonHighIcon } from './BlurPersonHighIcon';
+export { default as BlurPersonLowIcon } from './BlurPersonLowIcon';
export { default as BoltIcon } from './BoltIcon';
export { default as BookIcon } from './BookIcon';
export { default as BookmarkIcon } from './BookmarkIcon';
@@ -218,6 +220,7 @@ export { default as ShrinkIcon } from './ShrinkIcon';
export { default as ShuffleIcon } from './ShuffleIcon';
export { default as SlackIcon } from './SlackIcon';
export { default as SolidCheckCircleIcon } from './SolidCheckCircleIcon';
+export { default as SparkleIcon } from './SparkleIcon';
export { default as SpeakerIcon } from './SpeakerIcon';
export { default as SpotlightIcon } from './SpotlightIcon';
export { default as SquareMenuIcon } from './SquareMenuIcon';
diff --git a/packages/roomkit-react/src/Modal/Dialog.tsx b/packages/roomkit-react/src/Modal/Dialog.tsx
index fe9c7cb2e0..21cca76a07 100644
--- a/packages/roomkit-react/src/Modal/Dialog.tsx
+++ b/packages/roomkit-react/src/Modal/Dialog.tsx
@@ -1,28 +1,20 @@
import React, { ReactNode, useRef } from 'react';
import { Root } from '@radix-ui/react-dialog';
import { styled } from '@stitches/react';
-import { CSS } from '../Theme';
import {
+ CustomDialogContent,
+ CustomDialogOverlay,
DialogClose,
DialogDefaultCloseIcon,
DialogDescription,
DialogTitle,
- StyledDialogContent,
- StyledDialogOverlay,
StyledDialogPortal,
StyledDialogTrigger,
} from './DialogContent';
import { useDialogContainerSelector } from '../hooks/useDialogContainerSelector';
const StyledDialog = styled(Root, {});
-const CustomDialogContent = ({ children, props = {}, css = {} }: { children: ReactNode; props?: any; css?: CSS }) => (
-
- {children}
-
-);
-const CustomDialogOverlay = ({ css = {} }: { css?: CSS }) => (
-
-);
+
const CustomDialogPortal = ({ children, container }: { children: ReactNode; container?: HTMLElement | null }) => {
const dialogContainerSelector = useDialogContainerSelector();
const containerRef = useRef(null);
diff --git a/packages/roomkit-react/src/Modal/DialogContent.tsx b/packages/roomkit-react/src/Modal/DialogContent.tsx
index 91a7dae300..8631bae8a7 100644
--- a/packages/roomkit-react/src/Modal/DialogContent.tsx
+++ b/packages/roomkit-react/src/Modal/DialogContent.tsx
@@ -17,19 +17,19 @@ export const StyledDialogTrigger = styled(DialogPrimitive.Trigger, {
appearance: 'none !important', // Needed for safari it shows white overlay
});
-export const StyledDialogOverlay = styled(DialogPrimitive.Overlay, {
+export const CustomDialogOverlay = styled(DialogPrimitive.Overlay, {
backgroundColor: 'rgba(0, 0, 0, 0.5);',
- position: 'fixed',
+ position: 'absolute',
inset: 0,
});
export const StyledDialogPortal = styled(DialogPrimitive.Portal, {});
-export const StyledDialogContent = styled(DialogPrimitive.Content, {
+export const CustomDialogContent = styled(DialogPrimitive.Content, {
color: '$on_surface_medium',
backgroundColor: '$surface_default',
borderRadius: '8px',
- position: 'fixed',
+ position: 'absolute',
top: '50%',
left: '50%',
border: '$space$px solid $border_bright',
diff --git a/packages/roomkit-react/src/Prebuilt/common/constants.js b/packages/roomkit-react/src/Prebuilt/common/constants.js
index bb9e4692ba..4aaa2aa6d5 100644
--- a/packages/roomkit-react/src/Prebuilt/common/constants.js
+++ b/packages/roomkit-react/src/Prebuilt/common/constants.js
@@ -46,6 +46,8 @@ export const APP_DATA = {
activeScreensharePeerId: 'activeScreensharePeerId',
disableNotifications: 'disableNotifications',
pollState: 'pollState',
+ background: 'background',
+ backgroundType: 'backgroundType',
};
export const UI_SETTINGS = {
isAudioOnly: 'isAudioOnly',
@@ -61,6 +63,7 @@ export const SIDE_PANE_OPTIONS = {
CHAT: 'Chat',
STREAMING: 'STREAMING',
POLLS: 'POLLS',
+ VB: 'VB',
};
export const POLL_STATE = {
diff --git a/packages/roomkit-react/src/Prebuilt/components/AppData/AppData.jsx b/packages/roomkit-react/src/Prebuilt/components/AppData/AppData.jsx
index 1039ce6225..4e206005b8 100644
--- a/packages/roomkit-react/src/Prebuilt/components/AppData/AppData.jsx
+++ b/packages/roomkit-react/src/Prebuilt/components/AppData/AppData.jsx
@@ -4,7 +4,6 @@ import {
selectAvailableRoleNames,
selectFullAppData,
selectHLSState,
- selectIsConnectedToRoom,
selectLocalPeerRoleName,
selectRolesMap,
selectRoomState,
@@ -15,7 +14,7 @@ import {
} from '@100mslive/react-sdk';
import { normalizeAppPolicyConfig } from '../init/initUtils';
import { UserPreferencesKeys, useUserPreferences } from '../hooks/useUserPreferences';
-import { useIsSidepaneTypeOpen, useSidepaneReset, useSidepaneState, useSidepaneToggle } from './useSidepane';
+import { useIsSidepaneTypeOpen, useSidepaneToggle } from './useSidepane';
import { useSetAppDataByKey } from './useUISettings';
import {
APP_DATA,
@@ -26,6 +25,7 @@ import {
UI_MODE_GRID,
UI_SETTINGS,
} from '../../common/constants';
+import { VB_EFFECT } from '../VirtualBackground/constants';
export const getAppDetails = appDetails => {
try {
@@ -67,6 +67,8 @@ const initialAppData = {
[APP_DATA.minimiseInset]: false,
[APP_DATA.activeScreensharePeerId]: '',
[APP_DATA.disableNotifications]: false,
+ [APP_DATA.background]: VB_EFFECT.NONE,
+ [APP_DATA.backgroundType]: VB_EFFECT.NONE,
[APP_DATA.pollState]: {
[POLL_STATE.pollInView]: '',
[POLL_STATE.view]: '',
@@ -75,21 +77,12 @@ const initialAppData = {
export const AppData = React.memo(({ appDetails, tokenEndpoint }) => {
const hmsActions = useHMSActions();
- const isConnected = useHMSStore(selectIsConnectedToRoom);
- const sidePane = useSidepaneState();
- const resetSidePane = useSidepaneReset();
const [preferences = {}] = useUserPreferences(UserPreferencesKeys.UI_SETTINGS);
const roleNames = useHMSStore(selectAvailableRoleNames);
const rolesMap = useHMSStore(selectRolesMap);
const localPeerRole = useHMSStore(selectLocalPeerRoleName);
const appData = useHMSStore(selectFullAppData);
- useEffect(() => {
- if (!isConnected && sidePane && sidePane !== SIDE_PANE_OPTIONS.PARTICIPANTS) {
- resetSidePane();
- }
- }, [isConnected, sidePane, resetSidePane]);
-
useEffect(() => {
hmsActions.initAppData({
...initialAppData,
diff --git a/packages/roomkit-react/src/Prebuilt/components/AppData/useSidepane.js b/packages/roomkit-react/src/Prebuilt/components/AppData/useSidepane.js
index 05a386a47b..d0e99386a1 100644
--- a/packages/roomkit-react/src/Prebuilt/components/AppData/useSidepane.js
+++ b/packages/roomkit-react/src/Prebuilt/components/AppData/useSidepane.js
@@ -60,7 +60,7 @@ export const usePollViewToggle = () => {
};
/**
- * reset's the sidepane value
+ * resets the sidepane value
*/
export const useSidepaneReset = () => {
const hmsActions = useHMSActions();
diff --git a/packages/roomkit-react/src/Prebuilt/components/Footer/Footer.tsx b/packages/roomkit-react/src/Prebuilt/components/Footer/Footer.tsx
index 76eb8ffc83..9de5b072c7 100644
--- a/packages/roomkit-react/src/Prebuilt/components/Footer/Footer.tsx
+++ b/packages/roomkit-react/src/Prebuilt/components/Footer/Footer.tsx
@@ -1,4 +1,4 @@
-import React, { Suspense, useEffect } from 'react';
+import React, { useEffect } from 'react';
import { useMedia } from 'react-use';
import {
ConferencingScreen,
@@ -21,6 +21,8 @@ import { RaiseHand } from '../RaiseHand';
// @ts-ignore: No implicit Any
import { ScreenshareToggle } from '../ScreenShareToggle';
// @ts-ignore: No implicit Any
+import { VBToggle } from '../VirtualBackground/VBToggle';
+// @ts-ignore: No implicit Any
import { ChatToggle } from './ChatToggle';
// @ts-ignore: No implicit Any
import { ParticipantCount } from './ParticipantList';
@@ -31,8 +33,6 @@ import { useIsSidepaneTypeOpen, useSidepaneToggle } from '../AppData/useSidepane
import { useShowPolls } from '../AppData/useUISettings';
// @ts-ignore: No implicit Any
import { SIDE_PANE_OPTIONS } from '../../common/constants';
-// @ts-ignore: No implicit Any
-const VirtualBackground = React.lazy(() => import('../../plugins/VirtualBackground/VirtualBackground'));
export const Footer = ({
screenType,
@@ -82,11 +82,7 @@ export const Footer = ({
>
{isMobile ? : null}
- {isMobile ? null : (
- >}>
-
-
- )}
+ {isMobile ? null : }
{
const peerCount = useHMSStore(selectPeerCount);
const toggleSidepane = useSidepaneToggle(SIDE_PANE_OPTIONS.PARTICIPANTS);
const isParticipantsOpen = useIsSidepaneTypeOpen(SIDE_PANE_OPTIONS.PARTICIPANTS);
- useEffect(() => {
- if (isParticipantsOpen && peerCount === 0) {
- toggleSidepane();
- }
- }, [isParticipantsOpen, peerCount, toggleSidepane]);
if (peerCount === 0) {
return null;
diff --git a/packages/roomkit-react/src/Prebuilt/components/Preview/PreviewJoin.tsx b/packages/roomkit-react/src/Prebuilt/components/Preview/PreviewJoin.tsx
index dc9a2ec647..918b453e4b 100644
--- a/packages/roomkit-react/src/Prebuilt/components/Preview/PreviewJoin.tsx
+++ b/packages/roomkit-react/src/Prebuilt/components/Preview/PreviewJoin.tsx
@@ -1,4 +1,4 @@
-import React, { Fragment, Suspense, useCallback, useEffect, useMemo, useState } from 'react';
+import React, { Fragment, useCallback, useEffect, useMemo, useState } from 'react';
import { useMeasure, useMedia } from 'react-use';
import {
HMSRoomState,
@@ -18,6 +18,7 @@ import { AudioLevel } from '../../../AudioLevel';
import { useHMSPrebuiltContext } from '../../AppContext';
// @ts-ignore: No implicit Any
import IconButton from '../../IconButton';
+import SidePane from '../../layouts/SidePane';
import { useRoomLayout } from '../../provider/roomLayoutProvider';
// @ts-ignore: No implicit Any
import { AudioVideoToggle } from '../AudioVideoToggle';
@@ -32,6 +33,8 @@ import { Logo } from '../Header/HeaderComponents';
// @ts-ignore: No implicit Any
import SettingsModal from '../Settings/SettingsModal';
// @ts-ignore: No implicit Any
+import { VBToggle } from '../VirtualBackground/VBToggle';
+// @ts-ignore: No implicit Any
import PreviewForm from './PreviewForm';
// @ts-ignore: No implicit Any
import { useAuthToken, useUISettings } from '../AppData/useUISettings';
@@ -42,9 +45,6 @@ import { calculateAvatarAndAttribBoxSize, getFormattedCount } from '../../common
// @ts-ignore: No implicit Any
import { UI_SETTINGS } from '../../common/constants';
-// @ts-ignore: No implicit Any
-const VirtualBackground = React.lazy(() => import('../../plugins/VirtualBackground/VirtualBackground'));
-
const getParticipantChipContent = (peerCount = 0) => {
if (peerCount === 0) {
return 'You are the first to join';
@@ -133,60 +133,65 @@ const PreviewJoin = ({
}, [initialName]);
return roomState === HMSRoomState.Preview ? (
-
- {toggleVideo ? null : }
-
-
-
- {previewHeader.title}
-
-
- {previewHeader.sub_title}
-
-
- {isStreamingOn ? (
- }
- />
- ) : null}
-
+
+
+ {toggleVideo ? null : }
+
+
+
+ {previewHeader.title}
+
+
+ {previewHeader.sub_title}
+
+
+ {isStreamingOn ? (
+ }
+ />
+ ) : null}
+
+
-
- {toggleVideo ? (
-
-
-
- ) : null}
-
-
-
+ {toggleVideo ? (
+
+
+
+ ) : null}
+
+
+
+
+
+
+
-
+
) : (
);
@@ -274,7 +279,7 @@ export const PreviewControls = ({ hideSettings }: { hideSettings: boolean }) =>
>
- {!isMobile ? : null}
+ {!isMobile ? : null}
{!hideSettings ? : null}
diff --git a/packages/roomkit-react/src/Prebuilt/components/RoleChangeRequest/RequestPrompt.tsx b/packages/roomkit-react/src/Prebuilt/components/RoleChangeRequest/RequestPrompt.tsx
index bbbc7f036e..1cbd39ddea 100644
--- a/packages/roomkit-react/src/Prebuilt/components/RoleChangeRequest/RequestPrompt.tsx
+++ b/packages/roomkit-react/src/Prebuilt/components/RoleChangeRequest/RequestPrompt.tsx
@@ -33,10 +33,9 @@ export const RequestPrompt = ({
}
return (
-
+
-
-
+ e.preventDefault()}>
{title}
diff --git a/packages/roomkit-react/src/Prebuilt/components/VirtualBackground/VBCollection.tsx b/packages/roomkit-react/src/Prebuilt/components/VirtualBackground/VBCollection.tsx
new file mode 100644
index 0000000000..f801722816
--- /dev/null
+++ b/packages/roomkit-react/src/Prebuilt/components/VirtualBackground/VBCollection.tsx
@@ -0,0 +1,50 @@
+import React from 'react';
+import { Box } from '../../../Layout';
+import { Text } from '../../../Text';
+import { VBOption } from './VBOption';
+import { VB_EFFECT } from './constants';
+
+export const VBCollection = ({
+ options,
+ title,
+ activeBackgroundType = '',
+ activeBackground = '',
+}: {
+ options: {
+ title?: string;
+ icon?: React.JSX.Element;
+ onClick?: () => Promise;
+ mediaURL?: string;
+ type: string;
+ }[];
+ title: string;
+ activeBackground: HTMLImageElement | string;
+ activeBackgroundType: string;
+}) => {
+ if (options.length === 0) {
+ return null;
+ }
+ return (
+
+
+ {title}
+
+
+ {options.map(option => (
+
+ {option?.icon}
+ {option?.title}
+
+ ))}
+
+
+ );
+};
diff --git a/packages/roomkit-react/src/Prebuilt/components/VirtualBackground/VBOption.tsx b/packages/roomkit-react/src/Prebuilt/components/VirtualBackground/VBOption.tsx
new file mode 100644
index 0000000000..ab0c452ad2
--- /dev/null
+++ b/packages/roomkit-react/src/Prebuilt/components/VirtualBackground/VBOption.tsx
@@ -0,0 +1,50 @@
+import React from 'react';
+import { Box, Flex } from '../../../Layout';
+import { Text } from '../../../Text';
+
+const Root = ({
+ onClick,
+ mediaURL,
+ isActive,
+ children,
+}: {
+ onClick?: () => Promise;
+ mediaURL?: string;
+ isActive: boolean;
+ children?: React.JSX.Element[];
+}) => (
+ await onClick?.()}
+ >
+ {children}
+
+);
+
+const Title = ({ children }: { children?: string }) => {
+ return children ? (
+
+ {children}
+
+ ) : null;
+};
+
+const Icon = ({ children }: { children?: React.JSX.Element }) => {
+ return children ? {children} : null;
+};
+
+export const VBOption = {
+ Root,
+ Title,
+ Icon,
+};
diff --git a/packages/roomkit-react/src/Prebuilt/components/VirtualBackground/VBPicker.jsx b/packages/roomkit-react/src/Prebuilt/components/VirtualBackground/VBPicker.jsx
new file mode 100644
index 0000000000..e25242482c
--- /dev/null
+++ b/packages/roomkit-react/src/Prebuilt/components/VirtualBackground/VBPicker.jsx
@@ -0,0 +1,155 @@
+import React, { useEffect, useRef, useState } from 'react';
+import { HMSVBPlugin } from '@100mslive/hms-virtual-background';
+import {
+ HMSRoomState,
+ selectIsLocalVideoEnabled,
+ selectLocalPeer,
+ selectLocalPeerRole,
+ selectLocalVideoTrackID,
+ selectRoomState,
+ selectVideoTrackByID,
+ useHMSActions,
+ useHMSStore,
+} from '@100mslive/react-sdk';
+import { BlurPersonHighIcon, CloseIcon, CrossCircleIcon } from '@100mslive/react-icons';
+import { Box, Flex, Video } from '../../../index';
+import { Text } from '../../../Text';
+import { VBCollection } from './VBCollection';
+// @ts-ignore
+import { useSidepaneToggle } from '../AppData/useSidepane';
+// @ts-ignore
+import { useSetAppDataByKey, useUISettings } from '../AppData/useUISettings';
+// @ts-ignore
+import { APP_DATA, SIDE_PANE_OPTIONS, UI_SETTINGS } from '../../common/constants';
+import { defaultMedia, VB_EFFECT } from './constants';
+
+const iconDims = { height: '40px', width: '40px' };
+
+export const VBPicker = () => {
+ const toggleVB = useSidepaneToggle(SIDE_PANE_OPTIONS.VB);
+ const pluginRef = useRef(null);
+ const hmsActions = useHMSActions();
+ const role = useHMSStore(selectLocalPeerRole);
+ const [isVBSupported, setIsVBSupported] = useState(false);
+ const localPeerVideoTrackID = useHMSStore(selectLocalVideoTrackID);
+ const [background, setBackground] = useSetAppDataByKey(APP_DATA.background);
+ const [backgroundType, setBackgroundType] = useSetAppDataByKey(APP_DATA.backgroundType);
+ const localPeer = useHMSStore(selectLocalPeer);
+ const isVideoOn = useHMSStore(selectIsLocalVideoEnabled);
+ const mirrorLocalVideo = useUISettings(UI_SETTINGS.mirrorLocalVideo);
+ const trackSelector = selectVideoTrackByID(localPeer?.videoTrack);
+ const track = useHMSStore(trackSelector);
+ const roomState = useHMSStore(selectRoomState);
+
+ async function createPlugin() {
+ if (!pluginRef.current) {
+ pluginRef.current = new HMSVBPlugin(background, backgroundType);
+ }
+ }
+
+ useEffect(() => {
+ if (!localPeerVideoTrackID) {
+ return;
+ }
+ createPlugin().then(() => {
+ //check support of plugin
+ if (pluginRef.current) {
+ const pluginSupport = hmsActions.validateVideoPluginSupport(pluginRef.current);
+ setIsVBSupported(pluginSupport.isSupported);
+ }
+ });
+ }, [hmsActions, localPeerVideoTrackID]);
+
+ async function addPlugin({ mediaURL = '', blurPower = 0 }) {
+ if (!pluginRef.current) {
+ return;
+ }
+ try {
+ await createPlugin();
+ window.HMS.virtualBackground = pluginRef.current;
+ if (mediaURL) {
+ const img = document.createElement('img');
+ img.alt = 'VB';
+ img.src = mediaURL;
+ await pluginRef.current.setBackground(img, VB_EFFECT.MEDIA);
+ } else if (blurPower) {
+ await pluginRef.current.setBackground(VB_EFFECT.BLUR, VB_EFFECT.BLUR);
+ }
+ // Will store blur power here
+ setBackground(mediaURL || VB_EFFECT.BLUR);
+ setBackgroundType(mediaURL ? VB_EFFECT.MEDIA : VB_EFFECT.BLUR);
+ if (role)
+ await hmsActions.addPluginToVideoTrack(pluginRef.current, Math.floor(role.publishParams.video.frameRate / 2));
+ } catch (err) {
+ console.error('add virtual background plugin failed', err);
+ }
+ }
+
+ async function removePlugin() {
+ if (pluginRef.current) {
+ await hmsActions.removePluginFromVideoTrack(pluginRef.current);
+ pluginRef.current = null;
+ }
+ }
+
+ if (!isVBSupported) {
+ return null;
+ }
+
+ return (
+
+
+
+ Virtual Background
+
+
+
+
+
+
+ {/* 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 */}
+ {isVideoOn && roomState !== HMSRoomState.Preview ? (
+
+ ) : null}
+
+ ,
+ type: VB_EFFECT.NONE,
+ onClick: async () => await removePlugin(),
+ },
+ {
+ title: 'Blur',
+ icon: ,
+ type: VB_EFFECT.BLUR,
+ onClick: async () => await addPlugin({ blurPower: 0.5 }),
+ },
+ ]}
+ activeBackgroundType={pluginRef.current?.backgroundType || VB_EFFECT.NONE}
+ activeBackground={pluginRef.current?.background?.src || pluginRef.current?.background || VB_EFFECT.NONE}
+ />
+
+ ({
+ type: VB_EFFECT.MEDIA,
+ mediaURL,
+ onClick: async () => await addPlugin({ mediaURL }),
+ }))}
+ activeBackgroundType={pluginRef.current?.backgroundType || VB_EFFECT.NONE}
+ activeBackground={pluginRef.current?.background?.src || pluginRef.current?.background || VB_EFFECT.NONE}
+ />
+
+ );
+};
diff --git a/packages/roomkit-react/src/Prebuilt/components/VirtualBackground/VBToggle.jsx b/packages/roomkit-react/src/Prebuilt/components/VirtualBackground/VBToggle.jsx
new file mode 100644
index 0000000000..a1d52f2ca3
--- /dev/null
+++ b/packages/roomkit-react/src/Prebuilt/components/VirtualBackground/VBToggle.jsx
@@ -0,0 +1,24 @@
+import React from 'react';
+import { selectIsLocalVideoEnabled, useHMSStore } from '@100mslive/react-sdk';
+import { VirtualBackgroundIcon } from '@100mslive/react-icons';
+import { Tooltip } from '../../../Tooltip';
+import IconButton from '../../IconButton';
+import { useIsSidepaneTypeOpen, useSidepaneToggle } from '../AppData/useSidepane';
+import { SIDE_PANE_OPTIONS } from '../../common/constants';
+
+export const VBToggle = () => {
+ const toggleVB = useSidepaneToggle(SIDE_PANE_OPTIONS.VB);
+ const isVBOpen = useIsSidepaneTypeOpen(SIDE_PANE_OPTIONS.VB);
+ const isVideoOn = useHMSStore(selectIsLocalVideoEnabled);
+ if (!isVideoOn) {
+ return null;
+ }
+
+ return (
+
+
+
+
+
+ );
+};
diff --git a/packages/roomkit-react/src/Prebuilt/components/VirtualBackground/constants.ts b/packages/roomkit-react/src/Prebuilt/components/VirtualBackground/constants.ts
new file mode 100644
index 0000000000..754d4673b2
--- /dev/null
+++ b/packages/roomkit-react/src/Prebuilt/components/VirtualBackground/constants.ts
@@ -0,0 +1,19 @@
+// Will support all media, setting to image here to test with current plugin interface
+export const VB_EFFECT = { BLUR: 'blur', BEAUTIFY: 'BEAUTIFY', NONE: 'none', MEDIA: 'image' };
+
+export const defaultMedia = [
+ 'https://d2qi07yyjujoxr.cloudfront.net/webapp/vb/vb-1.jpg',
+ 'https://d2qi07yyjujoxr.cloudfront.net/webapp/vb/vb-2.jpg',
+ 'https://d2qi07yyjujoxr.cloudfront.net/webapp/vb/vb-3.png',
+ 'https://d2qi07yyjujoxr.cloudfront.net/webapp/vb/vb-4.jpg',
+ 'https://d2qi07yyjujoxr.cloudfront.net/webapp/vb/vb-5.jpg',
+ 'https://d2qi07yyjujoxr.cloudfront.net/webapp/vb/vb-6.jpg',
+ 'https://d2qi07yyjujoxr.cloudfront.net/webapp/vb/vb-7.jpg',
+ 'https://d2qi07yyjujoxr.cloudfront.net/webapp/vb/vb-8.jpg',
+ 'https://d2qi07yyjujoxr.cloudfront.net/webapp/vb/vb-9.jpg',
+ 'https://d2qi07yyjujoxr.cloudfront.net/webapp/vb/vb-10.jpg',
+ 'https://d2qi07yyjujoxr.cloudfront.net/webapp/vb/vb-11.jpg',
+ 'https://d2qi07yyjujoxr.cloudfront.net/webapp/vb/vb-12.jpg',
+ // 'https://d2qi07yyjujoxr.cloudfront.net/webapp/vb/vb-13.jpg',
+ // 'https://d2qi07yyjujoxr.cloudfront.net/webapp/vb/vb-14.jpg',
+];
diff --git a/packages/roomkit-react/src/Prebuilt/layouts/SidePane.tsx b/packages/roomkit-react/src/Prebuilt/layouts/SidePane.tsx
index 0839ffb976..582e111dcd 100644
--- a/packages/roomkit-react/src/Prebuilt/layouts/SidePane.tsx
+++ b/packages/roomkit-react/src/Prebuilt/layouts/SidePane.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useEffect } from 'react';
import { useMedia } from 'react-use';
import { ConferencingScreen } from '@100mslive/types-prebuilt';
import { selectAppData, selectVideoTrackByPeerID, useHMSStore } from '@100mslive/react-sdk';
@@ -7,8 +7,12 @@ import { SidePaneTabs } from '../components/SidePaneTabs';
import { TileCustomisationProps } from '../components/VideoLayouts/GridLayout';
// @ts-ignore: No implicit Any
import VideoTile from '../components/VideoTile';
+// @ts-ignore: No implicit Any
+import { VBPicker } from '../components/VirtualBackground/VBPicker';
import { Box, Flex } from '../../Layout';
import { config as cssConfig } from '../../Theme';
+// @ts-ignore: No implicit Any
+import { useSidepaneReset } from '../components/AppData/useSidepane';
import { useRoomLayoutConferencingScreen } from '../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
import { translateAcross } from '../../utils';
// @ts-ignore: No implicit Any
@@ -20,14 +24,15 @@ const SidePane = ({
hideControls = false,
}: {
screenType: keyof ConferencingScreen;
- tileProps: TileCustomisationProps;
- hideControls: boolean;
+ tileProps?: TileCustomisationProps;
+ hideControls?: boolean;
}) => {
const isMobile = useMedia(cssConfig.media.md);
const sidepane = useHMSStore(selectAppData(APP_DATA.sidePane));
const activeScreensharePeerId = useHMSStore(selectAppData(APP_DATA.activeScreensharePeerId));
const trackId = useHMSStore(selectVideoTrackByPeerID(activeScreensharePeerId))?.id;
const { elements } = useRoomLayoutConferencingScreen();
+ const resetSidePane = useSidepaneReset();
let ViewComponent;
if (sidepane === SIDE_PANE_OPTIONS.POLLS) {
ViewComponent = ;
@@ -35,6 +40,16 @@ const SidePane = ({
if (sidepane === SIDE_PANE_OPTIONS.PARTICIPANTS || sidepane === SIDE_PANE_OPTIONS.CHAT) {
ViewComponent = ;
}
+ if (sidepane === SIDE_PANE_OPTIONS.VB) {
+ ViewComponent = ;
+ }
+
+ useEffect(() => {
+ return () => {
+ resetSidePane();
+ };
+ }, [resetSidePane]);
+
if (!ViewComponent && !trackId) {
return null;
}
@@ -58,6 +73,7 @@ const SidePane = ({
h: '100%',
flexShrink: 0,
gap: '$4',
+ position: 'relative',
'@md': { position: mwebStreamingChat ? 'absolute' : '', zIndex: 12 },
}}
>