@100mslive/roomkit-react 0.1.12-alpha.0 → 0.1.13-alpha.0
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.
- package/dist/{HLSView-J2MIS3H2.js → HLSView-AIPLCDXY.js} +2 -2
- package/dist/Prebuilt/App.d.ts +0 -2
- package/dist/Prebuilt/AppContext.d.ts +0 -2
- package/dist/Prebuilt/common/constants.d.ts +110 -0
- package/dist/Prebuilt/components/AppData/AppData.d.ts +2 -0
- package/dist/Prebuilt/components/Chat/MwebChatOption.d.ts +7 -0
- package/dist/Prebuilt/components/Chat/Navigation.d.ts +8 -0
- package/dist/Prebuilt/components/Chat/PinnedMessage.d.ts +4 -0
- package/dist/Prebuilt/components/Footer/Footer.d.ts +3 -2
- package/dist/Prebuilt/components/Preview/PreviewJoin.d.ts +2 -1
- package/dist/Prebuilt/components/VirtualBackground/VBCollection.d.ts +2 -1
- package/dist/Prebuilt/components/VirtualBackground/VBPicker.d.ts +3 -0
- package/dist/Prebuilt/components/VirtualBackground/constants.d.ts +0 -6
- package/dist/Prebuilt/components/hooks/useChatBlacklist.d.ts +4 -0
- package/dist/Prebuilt/components/hooks/useSetPinnedMessages.d.ts +16 -0
- package/dist/Prebuilt/provider/roomLayoutProvider/hooks/useRoomLayoutScreen.d.ts +3 -2
- package/dist/Theme/stitches.config.d.ts +226 -226
- package/dist/{chunk-OYSYEA6R.js → chunk-5DCII2TP.js} +2248 -1269
- package/dist/chunk-5DCII2TP.js.map +7 -0
- package/dist/index.cjs.js +2943 -1902
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.js +1 -1
- package/dist/meta.cjs.json +819 -313
- package/dist/meta.esbuild.json +826 -320
- package/package.json +8 -8
- package/src/Prebuilt/App.tsx +3 -14
- package/src/Prebuilt/AppContext.tsx +0 -2
- package/src/Prebuilt/common/{constants.js → constants.ts} +22 -20
- package/src/Prebuilt/components/AppData/{AppData.jsx → AppData.tsx} +8 -22
- package/src/Prebuilt/components/AppData/useUISettings.js +0 -4
- package/src/Prebuilt/components/AuthToken.jsx +4 -4
- package/src/Prebuilt/components/Chat/Chat.jsx +51 -73
- package/src/Prebuilt/components/Chat/ChatBody.jsx +219 -48
- package/src/Prebuilt/components/Chat/ChatFooter.tsx +50 -6
- package/src/Prebuilt/components/Chat/ChatStates.jsx +66 -0
- package/src/Prebuilt/components/Chat/MwebChatOption.tsx +24 -0
- package/src/Prebuilt/components/Chat/Navigation.tsx +60 -0
- package/src/Prebuilt/components/Chat/PinnedMessage.tsx +118 -0
- package/src/Prebuilt/components/Footer/Footer.tsx +4 -7
- package/src/Prebuilt/components/Header/common.jsx +1 -1
- package/src/Prebuilt/components/Preview/PreviewJoin.tsx +6 -8
- package/src/Prebuilt/components/RoleChangeRequest/RoleChangeRequestModal.tsx +4 -1
- package/src/Prebuilt/components/Settings/DeviceSettings.jsx +1 -1
- package/src/Prebuilt/components/SidePaneTabs.tsx +3 -2
- package/src/Prebuilt/components/Toast/ToastConfig.jsx +20 -0
- package/src/Prebuilt/components/VirtualBackground/VBCollection.tsx +4 -4
- package/src/Prebuilt/components/VirtualBackground/{VBPicker.jsx → VBPicker.tsx} +27 -18
- package/src/Prebuilt/components/VirtualBackground/constants.ts +0 -8
- package/src/Prebuilt/components/hooks/useAutoStartStreaming.tsx +10 -1
- package/src/Prebuilt/components/hooks/useChatBlacklist.ts +21 -0
- package/src/Prebuilt/components/hooks/useSetPinnedMessages.ts +76 -0
- package/src/Prebuilt/layouts/SidePane.tsx +1 -1
- package/src/Prebuilt/layouts/VideoStreamingSection.tsx +30 -2
- package/src/Prebuilt/plugins/whiteboard/PusherCommunicationProvider.js +1 -1
- package/src/Prebuilt/plugins/whiteboard/README.md +1 -1
- package/src/Prebuilt/primitives/DialogContent.jsx +1 -1
- package/src/Prebuilt/provider/roomLayoutProvider/constants/index.ts +12 -1
- package/src/Prebuilt/provider/roomLayoutProvider/hooks/useRoomLayoutScreen.ts +5 -1
- package/src/Theme/stitches.config.ts +1 -2
- package/dist/chunk-OYSYEA6R.js.map +0 -7
- package/src/Prebuilt/components/hooks/useSetPinnedMessage.js +0 -38
- package/src/Prebuilt/services/tokenService.js +0 -49
- /package/dist/{HLSView-J2MIS3H2.js.map → HLSView-AIPLCDXY.js.map} +0 -0
| @@ -0,0 +1,118 @@ | |
| 1 | 
            +
            import React, { useEffect, useRef, useState } from 'react';
         | 
| 2 | 
            +
            import { useSwipeable } from 'react-swipeable';
         | 
| 3 | 
            +
            import { useMedia } from 'react-use';
         | 
| 4 | 
            +
            import { selectPermissions, selectSessionStore, useHMSStore } from '@100mslive/react-sdk';
         | 
| 5 | 
            +
            import { CrossIcon, PinIcon } from '@100mslive/react-icons';
         | 
| 6 | 
            +
            import { Box, Flex } from '../../../Layout';
         | 
| 7 | 
            +
            import { Text } from '../../../Text';
         | 
| 8 | 
            +
            import { config as cssConfig } from '../../../Theme';
         | 
| 9 | 
            +
            // @ts-ignore
         | 
| 10 | 
            +
            import { AnnotisedMessage } from './ChatBody';
         | 
| 11 | 
            +
            // @ts-ignore
         | 
| 12 | 
            +
            import { Navigation } from './Navigation';
         | 
| 13 | 
            +
            // @ts-ignore
         | 
| 14 | 
            +
            import { SESSION_STORE_KEY } from '../../common/constants';
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            const PINNED_MESSAGE_LENGTH = 75;
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            export const PinnedMessage = ({ clearPinnedMessage }: { clearPinnedMessage: (index: number) => void }) => {
         | 
| 19 | 
            +
              const permissions = useHMSStore(selectPermissions);
         | 
| 20 | 
            +
              const pinnedMessages = useHMSStore(selectSessionStore(SESSION_STORE_KEY.PINNED_MESSAGES)) || [];
         | 
| 21 | 
            +
              const [pinnedMessageIndex, setPinnedMessageIndex] = useState(0);
         | 
| 22 | 
            +
              const isMobile = useMedia(cssConfig.media.md);
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              const [hideOverflow, setHideOverflow] = useState(false);
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              const formattedPinnedMessage = hideOverflow
         | 
| 27 | 
            +
                ? `${pinnedMessages?.[pinnedMessageIndex]?.text.slice(0, PINNED_MESSAGE_LENGTH)}... `
         | 
| 28 | 
            +
                : pinnedMessages?.[pinnedMessageIndex]?.text;
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              const pinnedMessageRef = useRef(null);
         | 
| 31 | 
            +
              const showPreviousPinnedMessage = () => {
         | 
| 32 | 
            +
                const previousIndex = Math.max(pinnedMessageIndex - 1, 0);
         | 
| 33 | 
            +
                setHideOverflow(pinnedMessages[previousIndex].text.length > PINNED_MESSAGE_LENGTH);
         | 
| 34 | 
            +
                setPinnedMessageIndex(previousIndex);
         | 
| 35 | 
            +
              };
         | 
| 36 | 
            +
             | 
| 37 | 
            +
              const showNextPinnedMessage = () => {
         | 
| 38 | 
            +
                const nextIndex = Math.min(pinnedMessageIndex + 1, pinnedMessages.length - 1);
         | 
| 39 | 
            +
                setHideOverflow(pinnedMessages[nextIndex].text.length > PINNED_MESSAGE_LENGTH);
         | 
| 40 | 
            +
                setPinnedMessageIndex(nextIndex);
         | 
| 41 | 
            +
              };
         | 
| 42 | 
            +
             | 
| 43 | 
            +
              const swipeHandlers = useSwipeable({
         | 
| 44 | 
            +
                onSwipedUp: () => showNextPinnedMessage(),
         | 
| 45 | 
            +
                onSwipedDown: () => showPreviousPinnedMessage(),
         | 
| 46 | 
            +
              });
         | 
| 47 | 
            +
             | 
| 48 | 
            +
              useEffect(() => {
         | 
| 49 | 
            +
                setHideOverflow(
         | 
| 50 | 
            +
                  !!(
         | 
| 51 | 
            +
                    pinnedMessages?.[pinnedMessageIndex]?.text?.length &&
         | 
| 52 | 
            +
                    pinnedMessages?.[pinnedMessageIndex]?.text.length > PINNED_MESSAGE_LENGTH
         | 
| 53 | 
            +
                  ),
         | 
| 54 | 
            +
                );
         | 
| 55 | 
            +
              }, [pinnedMessageIndex, pinnedMessages]);
         | 
| 56 | 
            +
             | 
| 57 | 
            +
              return pinnedMessages?.[pinnedMessageIndex]?.text ? (
         | 
| 58 | 
            +
                <Flex ref={pinnedMessageRef} align="center" css={{ w: '100%', gap: '$4' }}>
         | 
| 59 | 
            +
                  <Flex
         | 
| 60 | 
            +
                    title={pinnedMessages[pinnedMessageIndex].text}
         | 
| 61 | 
            +
                    css={{
         | 
| 62 | 
            +
                      p: '$4',
         | 
| 63 | 
            +
                      color: '$on_surface_medium',
         | 
| 64 | 
            +
                      bg: isMobile ? 'rgba(0, 0, 0, 0.64)' : '$surface_default',
         | 
| 65 | 
            +
                      r: '$1',
         | 
| 66 | 
            +
                      gap: '$4',
         | 
| 67 | 
            +
                      mb: '$8',
         | 
| 68 | 
            +
                      mt: '$8',
         | 
| 69 | 
            +
                      flexGrow: 1,
         | 
| 70 | 
            +
                    }}
         | 
| 71 | 
            +
                    align="center"
         | 
| 72 | 
            +
                    justify="between"
         | 
| 73 | 
            +
                  >
         | 
| 74 | 
            +
                    <Navigation
         | 
| 75 | 
            +
                      index={pinnedMessageIndex}
         | 
| 76 | 
            +
                      total={pinnedMessages.length}
         | 
| 77 | 
            +
                      showPrevious={showPreviousPinnedMessage}
         | 
| 78 | 
            +
                      showNext={showNextPinnedMessage}
         | 
| 79 | 
            +
                      isMobile={isMobile}
         | 
| 80 | 
            +
                    />
         | 
| 81 | 
            +
                    <PinIcon />
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                    <Box
         | 
| 84 | 
            +
                      css={{
         | 
| 85 | 
            +
                        w: '100%',
         | 
| 86 | 
            +
                        maxHeight: '$18',
         | 
| 87 | 
            +
                        overflowY: 'auto',
         | 
| 88 | 
            +
                        overflowX: 'hidden',
         | 
| 89 | 
            +
                        wordBreak: 'break-word',
         | 
| 90 | 
            +
                        '& p span': {
         | 
| 91 | 
            +
                          color: '$primary_default',
         | 
| 92 | 
            +
                        },
         | 
| 93 | 
            +
                      }}
         | 
| 94 | 
            +
                    >
         | 
| 95 | 
            +
                      <Text variant="sm" css={{ color: '$on_surface_medium' }} {...swipeHandlers}>
         | 
| 96 | 
            +
                        <AnnotisedMessage message={formattedPinnedMessage} />
         | 
| 97 | 
            +
                        {hideOverflow ? (
         | 
| 98 | 
            +
                          <span style={{ cursor: 'pointer' }} onClick={() => setHideOverflow(false)}>
         | 
| 99 | 
            +
                            See more
         | 
| 100 | 
            +
                          </span>
         | 
| 101 | 
            +
                        ) : null}
         | 
| 102 | 
            +
                      </Text>
         | 
| 103 | 
            +
                    </Box>
         | 
| 104 | 
            +
                    {permissions?.removeOthers && (
         | 
| 105 | 
            +
                      <Flex
         | 
| 106 | 
            +
                        onClick={() => {
         | 
| 107 | 
            +
                          clearPinnedMessage(pinnedMessageIndex);
         | 
| 108 | 
            +
                          setPinnedMessageIndex(Math.max(0, pinnedMessageIndex - 1));
         | 
| 109 | 
            +
                        }}
         | 
| 110 | 
            +
                        css={{ cursor: 'pointer', color: '$on_surface_medium', '&:hover': { color: '$on_surface_high' } }}
         | 
| 111 | 
            +
                      >
         | 
| 112 | 
            +
                        <CrossIcon />
         | 
| 113 | 
            +
                      </Flex>
         | 
| 114 | 
            +
                    )}
         | 
| 115 | 
            +
                  </Flex>
         | 
| 116 | 
            +
                </Flex>
         | 
| 117 | 
            +
              ) : null;
         | 
| 118 | 
            +
            };
         | 
| @@ -1,10 +1,6 @@ | |
| 1 1 | 
             
            import React, { useEffect } from 'react';
         | 
| 2 2 | 
             
            import { useMedia } from 'react-use';
         | 
| 3 | 
            -
            import {
         | 
| 4 | 
            -
              ConferencingScreen,
         | 
| 5 | 
            -
              DefaultConferencingScreen_Elements,
         | 
| 6 | 
            -
              HLSLiveStreamingScreen_Elements,
         | 
| 7 | 
            -
            } from '@100mslive/types-prebuilt';
         | 
| 3 | 
            +
            import { ConferencingScreen } from '@100mslive/types-prebuilt';
         | 
| 8 4 | 
             
            import { Chat_ChatState } from '@100mslive/types-prebuilt/elements/chat';
         | 
| 9 5 | 
             
            import { useAVToggle } from '@100mslive/react-sdk';
         | 
| 10 6 | 
             
            import { config as cssConfig, Footer as AppFooter } from '../../..';
         | 
| @@ -27,6 +23,7 @@ import { ChatToggle } from './ChatToggle'; | |
| 27 23 | 
             
            // @ts-ignore: No implicit Any
         | 
| 28 24 | 
             
            import { ParticipantCount } from './ParticipantList';
         | 
| 29 25 | 
             
            import { PollsToggle } from './PollsToggle';
         | 
| 26 | 
            +
            import { ConferencingScreenElements } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
         | 
| 30 27 | 
             
            // @ts-ignore: No implicit Any
         | 
| 31 28 | 
             
            import { useIsSidepaneTypeOpen, useSidepaneToggle } from '../AppData/useSidepane';
         | 
| 32 29 | 
             
            // @ts-ignore: No implicit Any
         | 
| @@ -39,7 +36,7 @@ export const Footer = ({ | |
| 39 36 | 
             
              elements,
         | 
| 40 37 | 
             
            }: {
         | 
| 41 38 | 
             
              screenType: keyof ConferencingScreen;
         | 
| 42 | 
            -
              elements:  | 
| 39 | 
            +
              elements: ConferencingScreenElements;
         | 
| 43 40 | 
             
            }) => {
         | 
| 44 41 | 
             
              const isMobile = useMedia(cssConfig.media.md);
         | 
| 45 42 | 
             
              const isOverlayChat = !!elements?.chat?.is_overlay;
         | 
| @@ -82,7 +79,7 @@ export const Footer = ({ | |
| 82 79 | 
             
                  >
         | 
| 83 80 | 
             
                    {isMobile ? <LeaveRoom screenType={screenType} /> : null}
         | 
| 84 81 | 
             
                    <AudioVideoToggle />
         | 
| 85 | 
            -
                    {isMobile  | 
| 82 | 
            +
                    {!isMobile && elements.virtual_background ? <VBToggle /> : null}
         | 
| 86 83 | 
             
                  </AppFooter.Left>
         | 
| 87 84 | 
             
                  <AppFooter.Center
         | 
| 88 85 | 
             
                    css={{
         | 
| @@ -61,7 +61,7 @@ export const AudioActions = () => { | |
| 61 61 | 
             
              const { allDevices, selectedDeviceIDs, updateDevice } = useDevices();
         | 
| 62 62 |  | 
| 63 63 | 
             
              // don't show speaker selector where the API is not supported, and use
         | 
| 64 | 
            -
              // a generic word("Audio") for Mic. In some cases(Chrome Android for  | 
| 64 | 
            +
              // a generic word("Audio") for Mic. In some cases(Chrome Android for example) this changes both mic and speaker keeping them in sync.
         | 
| 65 65 | 
             
              const shouldShowAudioOutput = 'setSinkId' in HTMLMediaElement.prototype;
         | 
| 66 66 | 
             
              const { audioInput, audioOutput } = allDevices;
         | 
| 67 67 | 
             
              let availableAudioDevices = audioInput;
         | 
| @@ -19,7 +19,6 @@ import { useHMSPrebuiltContext } from '../../AppContext'; | |
| 19 19 | 
             
            // @ts-ignore: No implicit Any
         | 
| 20 20 | 
             
            import IconButton from '../../IconButton';
         | 
| 21 21 | 
             
            import SidePane from '../../layouts/SidePane';
         | 
| 22 | 
            -
            import { useRoomLayout } from '../../provider/roomLayoutProvider';
         | 
| 23 22 | 
             
            // @ts-ignore: No implicit Any
         | 
| 24 23 | 
             
            import { AudioVideoToggle } from '../AudioVideoToggle';
         | 
| 25 24 | 
             
            // @ts-ignore: No implicit Any
         | 
| @@ -36,13 +35,13 @@ import SettingsModal from '../Settings/SettingsModal'; | |
| 36 35 | 
             
            import { VBToggle } from '../VirtualBackground/VBToggle';
         | 
| 37 36 | 
             
            // @ts-ignore: No implicit Any
         | 
| 38 37 | 
             
            import PreviewForm from './PreviewForm';
         | 
| 38 | 
            +
            import { useRoomLayoutPreviewScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
         | 
| 39 39 | 
             
            // @ts-ignore: No implicit Any
         | 
| 40 40 | 
             
            import { useAuthToken, useUISettings } from '../AppData/useUISettings';
         | 
| 41 41 | 
             
            // @ts-ignore: No implicit Any
         | 
| 42 42 | 
             
            import { defaultPreviewPreference, UserPreferencesKeys, useUserPreferences } from '../hooks/useUserPreferences';
         | 
| 43 43 | 
             
            // @ts-ignore: No implicit Any
         | 
| 44 44 | 
             
            import { calculateAvatarAndAttribBoxSize, getFormattedCount } from '../../common/utils';
         | 
| 45 | 
            -
            // @ts-ignore: No implicit Any
         | 
| 46 45 | 
             
            import { UI_SETTINGS } from '../../common/constants';
         | 
| 47 46 |  | 
| 48 47 | 
             
            const getParticipantChipContent = (peerCount = 0) => {
         | 
| @@ -111,9 +110,8 @@ const PreviewJoin = ({ | |
| 111 110 | 
             
                });
         | 
| 112 111 | 
             
                join();
         | 
| 113 112 | 
             
              }, [join, name, setPreviewPreference]);
         | 
| 114 | 
            -
              const  | 
| 115 | 
            -
             | 
| 116 | 
            -
              const { preview_header: previewHeader = {} } = roomLayout?.screens?.preview?.default?.elements || {};
         | 
| 113 | 
            +
              const { elements = {} } = useRoomLayoutPreviewScreen();
         | 
| 114 | 
            +
              const { preview_header: previewHeader = {}, virtual_background } = elements || {};
         | 
| 117 115 | 
             
              const aspectRatio = useLocalTileAspectRatio();
         | 
| 118 116 | 
             
              useEffect(() => {
         | 
| 119 117 | 
             
                if (authToken) {
         | 
| @@ -177,7 +175,7 @@ const PreviewJoin = ({ | |
| 177 175 | 
             
                      </Flex>
         | 
| 178 176 | 
             
                    ) : null}
         | 
| 179 177 | 
             
                    <Box css={{ w: '100%', maxWidth: `${Math.max(aspectRatio, 1) * 360}px` }}>
         | 
| 180 | 
            -
                      <PreviewControls hideSettings={!toggleVideo && !toggleAudio} />
         | 
| 178 | 
            +
                      <PreviewControls hideSettings={!toggleVideo && !toggleAudio} vbEnabled={!!virtual_background} />
         | 
| 181 179 | 
             
                      <PreviewForm
         | 
| 182 180 | 
             
                        name={name}
         | 
| 183 181 | 
             
                        onChange={setName}
         | 
| @@ -266,7 +264,7 @@ export const PreviewTile = ({ name, error }: { name: string; error?: boolean }) | |
| 266 264 | 
             
              );
         | 
| 267 265 | 
             
            };
         | 
| 268 266 |  | 
| 269 | 
            -
            export const PreviewControls = ({ hideSettings }: { hideSettings: boolean }) => {
         | 
| 267 | 
            +
            export const PreviewControls = ({ hideSettings, vbEnabled }: { hideSettings: boolean; vbEnabled: boolean }) => {
         | 
| 270 268 | 
             
              const isMobile = useMedia(cssConfig.media.md);
         | 
| 271 269 |  | 
| 272 270 | 
             
              return (
         | 
| @@ -279,7 +277,7 @@ export const PreviewControls = ({ hideSettings }: { hideSettings: boolean }) => | |
| 279 277 | 
             
                >
         | 
| 280 278 | 
             
                  <Flex css={{ gap: '$4' }}>
         | 
| 281 279 | 
             
                    <AudioVideoToggle />
         | 
| 282 | 
            -
                    {!isMobile ? <VBToggle /> : null}
         | 
| 280 | 
            +
                    {!isMobile && vbEnabled ? <VBToggle /> : null}
         | 
| 283 281 | 
             
                  </Flex>
         | 
| 284 282 | 
             
                  {!hideSettings ? <PreviewSettings /> : null}
         | 
| 285 283 | 
             
                </Flex>
         | 
| @@ -11,6 +11,7 @@ import { Flex, Text } from '../../..'; | |
| 11 11 | 
             
            // @ts-ignore: No implicit Any
         | 
| 12 12 | 
             
            import { PreviewControls, PreviewTile } from '../Preview/PreviewJoin';
         | 
| 13 13 | 
             
            import { RequestPrompt } from './RequestPrompt';
         | 
| 14 | 
            +
            import { useRoomLayoutPreviewScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
         | 
| 14 15 | 
             
            // @ts-ignore: No implicit Any
         | 
| 15 16 | 
             
            import { useMyMetadata } from '../hooks/useMetadata';
         | 
| 16 17 | 
             
            // @ts-ignore: No implicit Any
         | 
| @@ -23,6 +24,8 @@ export const RoleChangeRequestModal = () => { | |
| 23 24 | 
             
              const roleChangeRequest = useHMSStore(selectRoleChangeRequest);
         | 
| 24 25 | 
             
              const name = useHMSStore(selectLocalPeerName);
         | 
| 25 26 | 
             
              const { sendEvent } = useCustomEvent({ type: ROLE_CHANGE_DECLINED });
         | 
| 27 | 
            +
              const { elements = {} } = useRoomLayoutPreviewScreen();
         | 
| 28 | 
            +
              const { virtual_background } = elements || {};
         | 
| 26 29 |  | 
| 27 30 | 
             
              useEffect(() => {
         | 
| 28 31 | 
             
                if (!roleChangeRequest?.role) {
         | 
| @@ -61,7 +64,7 @@ export const RoleChangeRequestModal = () => { | |
| 61 64 | 
             
                  >
         | 
| 62 65 | 
             
                    <PreviewTile name={name || ''} />
         | 
| 63 66 |  | 
| 64 | 
            -
                    <PreviewControls hideSettings={true} />
         | 
| 67 | 
            +
                    <PreviewControls hideSettings={true} vbEnabled={!!virtual_background} />
         | 
| 65 68 | 
             
                  </Flex>
         | 
| 66 69 | 
             
                </>
         | 
| 67 70 | 
             
              );
         | 
| @@ -29,7 +29,7 @@ const Settings = ({ setHide }) => { | |
| 29 29 | 
             
              const videoTrackId = useHMSStore(selectLocalVideoTrackID);
         | 
| 30 30 | 
             
              const isVideoOn = useHMSStore(selectIsLocalVideoEnabled);
         | 
| 31 31 | 
             
              // don't show speaker selector where the API is not supported, and use
         | 
| 32 | 
            -
              // a generic word("Audio") for Mic. In some cases(Chrome Android for  | 
| 32 | 
            +
              // a generic word("Audio") for Mic. In some cases(Chrome Android for example) this changes both mic and speaker keeping them in sync.
         | 
| 33 33 | 
             
              const shouldShowAudioOutput = 'setSinkId' in HTMLMediaElement.prototype;
         | 
| 34 34 | 
             
              const mirrorLocalVideo = useUISettings(UI_SETTINGS.mirrorLocalVideo);
         | 
| 35 35 | 
             
              const trackSelector = selectVideoTrackByID(videoTrackId);
         | 
| @@ -50,6 +50,7 @@ export const SidePaneTabs = React.memo<{ | |
| 50 50 | 
             
              const [activeRole, setActiveRole] = useState('');
         | 
| 51 51 | 
             
              const peerCount = useHMSStore(selectPeerCount);
         | 
| 52 52 | 
             
              const { elements } = useRoomLayoutConferencingScreen();
         | 
| 53 | 
            +
              const chat_title = elements?.chat?.chat_title || 'Chat';
         | 
| 53 54 | 
             
              const showChat = !!elements?.chat;
         | 
| 54 55 | 
             
              const showParticipants = !!elements?.participant_list;
         | 
| 55 56 | 
             
              const hideTabs = !(showChat && showParticipants);
         | 
| @@ -109,7 +110,7 @@ export const SidePaneTabs = React.memo<{ | |
| 109 110 | 
             
                        <>
         | 
| 110 111 | 
             
                          <Text variant="sm" css={{ fontWeight: '$semiBold', p: '$4', c: '$on_surface_high', pr: '$12' }}>
         | 
| 111 112 | 
             
                            {showChat ? (
         | 
| 112 | 
            -
                               | 
| 113 | 
            +
                              chat_title
         | 
| 113 114 | 
             
                            ) : (
         | 
| 114 115 | 
             
                              <span>
         | 
| 115 116 | 
             
                                Participants <ParticipantCount count={peerCount} />
         | 
| @@ -141,7 +142,7 @@ export const SidePaneTabs = React.memo<{ | |
| 141 142 | 
             
                                color: activeTab !== SIDE_PANE_OPTIONS.CHAT ? '$on_surface_low' : '$on_surface_high',
         | 
| 142 143 | 
             
                              }}
         | 
| 143 144 | 
             
                            >
         | 
| 144 | 
            -
                               | 
| 145 | 
            +
                              {chat_title}
         | 
| 145 146 | 
             
                            </Tabs.Trigger>
         | 
| 146 147 | 
             
                            <Tabs.Trigger
         | 
| 147 148 | 
             
                              value={SIDE_PANE_OPTIONS.PARTICIPANTS}
         | 
| @@ -1,6 +1,7 @@ | |
| 1 1 | 
             
            import React, { useCallback } from 'react';
         | 
| 2 2 | 
             
            import { selectPeerByID, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
         | 
| 3 3 | 
             
            import {
         | 
| 4 | 
            +
              ChatIcon,
         | 
| 4 5 | 
             
              ChatUnreadIcon,
         | 
| 5 6 | 
             
              ConnectivityIcon,
         | 
| 6 7 | 
             
              HandIcon,
         | 
| @@ -58,6 +59,7 @@ const HandRaiseAction = React.forwardRef(({ id = '', isSingleHandRaise = true }, | |
| 58 59 | 
             
                </Button>
         | 
| 59 60 | 
             
              );
         | 
| 60 61 | 
             
            });
         | 
| 62 | 
            +
             | 
| 61 63 | 
             
            export const ToastConfig = {
         | 
| 62 64 | 
             
              PEER_LIST: {
         | 
| 63 65 | 
             
                single: function (notification) {
         | 
| @@ -144,6 +146,24 @@ export const ToastConfig = { | |
| 144 146 | 
             
                  };
         | 
| 145 147 | 
             
                },
         | 
| 146 148 | 
             
              },
         | 
| 149 | 
            +
              CHAT_PAUSED: {
         | 
| 150 | 
            +
                single: notification => {
         | 
| 151 | 
            +
                  return {
         | 
| 152 | 
            +
                    title: `Chat paused by ${notification.data?.name}`,
         | 
| 153 | 
            +
                    icon: <ChatIcon />,
         | 
| 154 | 
            +
                    action: <ChatAction />,
         | 
| 155 | 
            +
                  };
         | 
| 156 | 
            +
                },
         | 
| 157 | 
            +
              },
         | 
| 158 | 
            +
              CHAT_RESUMED: {
         | 
| 159 | 
            +
                single: notification => {
         | 
| 160 | 
            +
                  return {
         | 
| 161 | 
            +
                    title: `Chat resumed by ${notification.data?.name}`,
         | 
| 162 | 
            +
                    icon: <ChatUnreadIcon />,
         | 
| 163 | 
            +
                    action: <ChatAction />,
         | 
| 164 | 
            +
                  };
         | 
| 165 | 
            +
                },
         | 
| 166 | 
            +
              },
         | 
| 147 167 | 
             
              RECONNECTED: {
         | 
| 148 168 | 
             
                single: () => {
         | 
| 149 169 | 
             
                  return {
         | 
| @@ -1,13 +1,13 @@ | |
| 1 1 | 
             
            import React from 'react';
         | 
| 2 | 
            +
            import { HMSVirtualBackgroundTypes } from '@100mslive/hms-virtual-background';
         | 
| 2 3 | 
             
            import { Box } from '../../../Layout';
         | 
| 3 4 | 
             
            import { Text } from '../../../Text';
         | 
| 4 5 | 
             
            import { VBOption } from './VBOption';
         | 
| 5 | 
            -
            import { VB_EFFECT } from './constants';
         | 
| 6 6 |  | 
| 7 7 | 
             
            export const VBCollection = ({
         | 
| 8 8 | 
             
              options,
         | 
| 9 9 | 
             
              title,
         | 
| 10 | 
            -
              activeBackgroundType =  | 
| 10 | 
            +
              activeBackgroundType = HMSVirtualBackgroundTypes.NONE,
         | 
| 11 11 | 
             
              activeBackground = '',
         | 
| 12 12 | 
             
            }: {
         | 
| 13 13 | 
             
              options: {
         | 
| @@ -19,7 +19,7 @@ export const VBCollection = ({ | |
| 19 19 | 
             
              }[];
         | 
| 20 20 | 
             
              title: string;
         | 
| 21 21 | 
             
              activeBackground: HTMLImageElement | string;
         | 
| 22 | 
            -
              activeBackgroundType:  | 
| 22 | 
            +
              activeBackgroundType: HMSVirtualBackgroundTypes;
         | 
| 23 23 | 
             
            }) => {
         | 
| 24 24 | 
             
              if (options.length === 0) {
         | 
| 25 25 | 
             
                return null;
         | 
| @@ -35,7 +35,7 @@ export const VBCollection = ({ | |
| 35 35 | 
             
                        key={option?.mediaURL || option?.title}
         | 
| 36 36 | 
             
                        {...option}
         | 
| 37 37 | 
             
                        isActive={
         | 
| 38 | 
            -
                          ([ | 
| 38 | 
            +
                          ([HMSVirtualBackgroundTypes.NONE, HMSVirtualBackgroundTypes.BLUR].includes(activeBackgroundType) &&
         | 
| 39 39 | 
             
                            option.type === activeBackgroundType) ||
         | 
| 40 40 | 
             
                          activeBackground === option?.mediaURL
         | 
| 41 41 | 
             
                        }
         | 
| @@ -1,4 +1,6 @@ | |
| 1 1 | 
             
            import React, { useEffect, useRef, useState } from 'react';
         | 
| 2 | 
            +
            import { HMSVirtualBackgroundTypes } from '@100mslive/hms-virtual-background';
         | 
| 3 | 
            +
            import { VirtualBackground, VirtualBackgroundMedia } from '@100mslive/types-prebuilt/elements/virtual_background';
         | 
| 2 4 | 
             
            import {
         | 
| 3 5 | 
             
              HMSRoomState,
         | 
| 4 6 | 
             
              selectIsLargeRoom,
         | 
| @@ -15,22 +17,27 @@ import { BlurPersonHighIcon, CloseIcon, CrossCircleIcon } from '@100mslive/react | |
| 15 17 | 
             
            import { Box, Flex, Video } from '../../../index';
         | 
| 16 18 | 
             
            import { Text } from '../../../Text';
         | 
| 17 19 | 
             
            import { VBCollection } from './VBCollection';
         | 
| 20 | 
            +
            // @ts-ignore
         | 
| 18 21 | 
             
            import { useSidepaneToggle } from '../AppData/useSidepane';
         | 
| 22 | 
            +
            // @ts-ignore
         | 
| 19 23 | 
             
            import { useUISettings } from '../AppData/useUISettings';
         | 
| 24 | 
            +
            // @ts-ignore
         | 
| 20 25 | 
             
            import { SIDE_PANE_OPTIONS, UI_SETTINGS } from '../../common/constants';
         | 
| 21 | 
            -
            import { defaultMedia,  | 
| 26 | 
            +
            import { defaultMedia, vbPlugin } from './constants';
         | 
| 22 27 |  | 
| 23 28 | 
             
            const iconDims = { height: '40px', width: '40px' };
         | 
| 24 29 | 
             
            const MAX_RETRIES = 2;
         | 
| 25 30 |  | 
| 26 | 
            -
            export const VBPicker = () => {
         | 
| 31 | 
            +
            export const VBPicker = ({ background_media = [] }: VirtualBackground = {}) => {
         | 
| 27 32 | 
             
              const toggleVB = useSidepaneToggle(SIDE_PANE_OPTIONS.VB);
         | 
| 28 33 | 
             
              const hmsActions = useHMSActions();
         | 
| 29 34 | 
             
              const role = useHMSStore(selectLocalPeerRole);
         | 
| 30 35 | 
             
              const [isVBSupported, setIsVBSupported] = useState(false);
         | 
| 31 36 | 
             
              const localPeerVideoTrackID = useHMSStore(selectLocalVideoTrackID);
         | 
| 32 37 | 
             
              const localPeer = useHMSStore(selectLocalPeer);
         | 
| 38 | 
            +
              // @ts-ignore
         | 
| 33 39 | 
             
              const [background, setBackground] = useState(vbPlugin.background);
         | 
| 40 | 
            +
              // @ts-ignore
         | 
| 34 41 | 
             
              const [backgroundType, setBackgroundType] = useState(vbPlugin.backgroundType);
         | 
| 35 42 | 
             
              const isVideoOn = useHMSStore(selectIsLocalVideoEnabled);
         | 
| 36 43 | 
             
              const mirrorLocalVideo = useUISettings(UI_SETTINGS.mirrorLocalVideo);
         | 
| @@ -39,13 +46,14 @@ export const VBPicker = () => { | |
| 39 46 | 
             
              const roomState = useHMSStore(selectRoomState);
         | 
| 40 47 | 
             
              const isLargeRoom = useHMSStore(selectIsLargeRoom);
         | 
| 41 48 | 
             
              const addedPluginToVideoTrack = useRef(false);
         | 
| 49 | 
            +
              const mediaList = [...background_media.map((media: VirtualBackgroundMedia) => media?.url), ...defaultMedia];
         | 
| 42 50 |  | 
| 43 51 | 
             
              // 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 52 | 
             
              const showVideoTile = isVideoOn && isLargeRoom && roomState !== HMSRoomState.Preview;
         | 
| 45 53 |  | 
| 46 54 | 
             
              const clearVBState = () => {
         | 
| 47 | 
            -
                setBackground( | 
| 48 | 
            -
                setBackgroundType( | 
| 55 | 
            +
                setBackground(HMSVirtualBackgroundTypes.NONE);
         | 
| 56 | 
            +
                setBackgroundType(HMSVirtualBackgroundTypes.NONE);
         | 
| 49 57 | 
             
              };
         | 
| 50 58 |  | 
| 51 59 | 
             
              useEffect(() => {
         | 
| @@ -62,7 +70,7 @@ export const VBPicker = () => { | |
| 62 70 |  | 
| 63 71 | 
             
              async function disableEffects() {
         | 
| 64 72 | 
             
                if (vbPlugin) {
         | 
| 65 | 
            -
                  vbPlugin.setBackground( | 
| 73 | 
            +
                  vbPlugin.setBackground(HMSVirtualBackgroundTypes.NONE, HMSVirtualBackgroundTypes.NONE);
         | 
| 66 74 | 
             
                  clearVBState();
         | 
| 67 75 | 
             
                }
         | 
| 68 76 | 
             
              }
         | 
| @@ -75,18 +83,18 @@ export const VBPicker = () => { | |
| 75 83 | 
             
                    img.alt = 'VB';
         | 
| 76 84 | 
             
                    img.src = mediaURL;
         | 
| 77 85 | 
             
                    try {
         | 
| 78 | 
            -
                      await vbPlugin.setBackground(img,  | 
| 86 | 
            +
                      await vbPlugin.setBackground(img, HMSVirtualBackgroundTypes.IMAGE);
         | 
| 79 87 | 
             
                    } catch (e) {
         | 
| 80 88 | 
             
                      console.error(e);
         | 
| 81 89 | 
             
                      if (retries++ < MAX_RETRIES) {
         | 
| 82 | 
            -
                        await vbPlugin.setBackground(img,  | 
| 90 | 
            +
                        await vbPlugin.setBackground(img, HMSVirtualBackgroundTypes.IMAGE);
         | 
| 83 91 | 
             
                      }
         | 
| 84 92 | 
             
                    }
         | 
| 85 93 | 
             
                  } else if (blurPower) {
         | 
| 86 | 
            -
                    await vbPlugin.setBackground( | 
| 94 | 
            +
                    await vbPlugin.setBackground(HMSVirtualBackgroundTypes.BLUR, HMSVirtualBackgroundTypes.BLUR);
         | 
| 87 95 | 
             
                  }
         | 
| 88 | 
            -
                  setBackground(mediaURL ||  | 
| 89 | 
            -
                  setBackgroundType(mediaURL ?  | 
| 96 | 
            +
                  setBackground(mediaURL || HMSVirtualBackgroundTypes.BLUR);
         | 
| 97 | 
            +
                  setBackgroundType(mediaURL ? HMSVirtualBackgroundTypes.IMAGE : HMSVirtualBackgroundTypes.BLUR);
         | 
| 90 98 | 
             
                  if (role && !addedPluginToVideoTrack.current) {
         | 
| 91 99 | 
             
                    await hmsActions.addPluginToVideoTrack(vbPlugin, Math.floor(role.publishParams.video.frameRate / 2));
         | 
| 92 100 | 
             
                    addedPluginToVideoTrack.current = true;
         | 
| @@ -136,29 +144,30 @@ export const VBPicker = () => { | |
| 136 144 | 
             
                      {
         | 
| 137 145 | 
             
                        title: 'No effect',
         | 
| 138 146 | 
             
                        icon: <CrossCircleIcon style={iconDims} />,
         | 
| 139 | 
            -
                        type:  | 
| 147 | 
            +
                        type: HMSVirtualBackgroundTypes.NONE,
         | 
| 140 148 | 
             
                        onClick: async () => await disableEffects(),
         | 
| 141 149 | 
             
                      },
         | 
| 142 150 | 
             
                      {
         | 
| 143 151 | 
             
                        title: 'Blur',
         | 
| 144 152 | 
             
                        icon: <BlurPersonHighIcon style={iconDims} />,
         | 
| 145 | 
            -
                        type:  | 
| 153 | 
            +
                        type: HMSVirtualBackgroundTypes.BLUR,
         | 
| 146 154 | 
             
                        onClick: async () => await addPlugin({ blurPower: 0.5 }),
         | 
| 147 155 | 
             
                      },
         | 
| 148 156 | 
             
                    ]}
         | 
| 149 | 
            -
                    activeBackgroundType={backgroundType ||  | 
| 150 | 
            -
                     | 
| 157 | 
            +
                    activeBackgroundType={backgroundType || HMSVirtualBackgroundTypes.NONE}
         | 
| 158 | 
            +
                    // @ts-ignore
         | 
| 159 | 
            +
                    activeBackground={vbPlugin.background?.src || vbPlugin.background || HMSVirtualBackgroundTypes.NONE}
         | 
| 151 160 | 
             
                  />
         | 
| 152 161 |  | 
| 153 162 | 
             
                  <VBCollection
         | 
| 154 163 | 
             
                    title="Backgrounds"
         | 
| 155 | 
            -
                    options={ | 
| 156 | 
            -
                      type:  | 
| 164 | 
            +
                    options={mediaList.map(mediaURL => ({
         | 
| 165 | 
            +
                      type: HMSVirtualBackgroundTypes.IMAGE,
         | 
| 157 166 | 
             
                      mediaURL,
         | 
| 158 167 | 
             
                      onClick: async () => await addPlugin({ mediaURL }),
         | 
| 159 168 | 
             
                    }))}
         | 
| 160 | 
            -
                    activeBackgroundType={backgroundType ||  | 
| 161 | 
            -
                    activeBackground={background?.src || background ||  | 
| 169 | 
            +
                    activeBackgroundType={backgroundType || HMSVirtualBackgroundTypes.NONE}
         | 
| 170 | 
            +
                    activeBackground={background?.src || background || HMSVirtualBackgroundTypes.NONE}
         | 
| 162 171 | 
             
                  />
         | 
| 163 172 | 
             
                </Box>
         | 
| 164 173 | 
             
              );
         | 
| @@ -1,13 +1,5 @@ | |
| 1 1 | 
             
            import { HMSVBPlugin, HMSVirtualBackgroundTypes } from '@100mslive/hms-virtual-background';
         | 
| 2 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 3 | 
             
            export const defaultMedia = [
         | 
| 12 4 | 
             
              'https://assets.100ms.live/webapp/vb-mini/vb-1.jpg',
         | 
| 13 5 | 
             
              'https://assets.100ms.live/webapp/vb-mini/vb-2.jpg',
         | 
| @@ -42,7 +42,16 @@ export const useAutoStartStreaming = () => { | |
| 42 42 | 
             
                  streamStartedRef.current = false;
         | 
| 43 43 | 
             
                  setHLSStarted(false);
         | 
| 44 44 | 
             
                }
         | 
| 45 | 
            -
              }, [ | 
| 45 | 
            +
              }, [
         | 
| 46 | 
            +
                hmsActions,
         | 
| 47 | 
            +
                isHLSRunning,
         | 
| 48 | 
            +
                isHLSStarted,
         | 
| 49 | 
            +
                setHLSStarted,
         | 
| 50 | 
            +
                showStreamingUI,
         | 
| 51 | 
            +
                isRTMPRunning,
         | 
| 52 | 
            +
                isHLSRecordingOn,
         | 
| 53 | 
            +
                isBrowserRecordingOn,
         | 
| 54 | 
            +
              ]);
         | 
| 46 55 |  | 
| 47 56 | 
             
              useEffect(() => {
         | 
| 48 57 | 
             
                if (!isHLSStarted && !isHLSRunning) {
         | 
| @@ -0,0 +1,21 @@ | |
| 1 | 
            +
            import { useCallback } from 'react';
         | 
| 2 | 
            +
            import { useHMSActions } from '@100mslive/react-sdk';
         | 
| 3 | 
            +
            // @ts-ignore
         | 
| 4 | 
            +
            import { ToastManager } from '../Toast/ToastManager';
         | 
| 5 | 
            +
            import { SESSION_STORE_KEY } from '../../common/constants';
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            export const useChatBlacklist = (
         | 
| 8 | 
            +
              sessionStoreKey: SESSION_STORE_KEY.CHAT_MESSAGE_BLACKLIST | SESSION_STORE_KEY.CHAT_PEER_BLACKLIST,
         | 
| 9 | 
            +
            ) => {
         | 
| 10 | 
            +
              const hmsActions = useHMSActions();
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              const blacklistItem = useCallback(
         | 
| 13 | 
            +
                async (blacklistedIDs: string[], blacklistID: string) =>
         | 
| 14 | 
            +
                  await hmsActions.sessionStore
         | 
| 15 | 
            +
                    .set(sessionStoreKey, [...blacklistedIDs, blacklistID])
         | 
| 16 | 
            +
                    .catch(err => ToastManager.addToast({ title: err.description })),
         | 
| 17 | 
            +
                [hmsActions, sessionStoreKey],
         | 
| 18 | 
            +
              );
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              return { blacklistItem };
         | 
| 21 | 
            +
            };
         | 
| @@ -0,0 +1,76 @@ | |
| 1 | 
            +
            import { useCallback } from 'react';
         | 
| 2 | 
            +
            import { HMSMessage, selectPeerNameByID, useHMSActions, useHMSVanillaStore } from '@100mslive/react-sdk';
         | 
| 3 | 
            +
            // @ts-ignore
         | 
| 4 | 
            +
            import { ToastManager } from '../Toast/ToastManager';
         | 
| 5 | 
            +
            // @ts-ignore
         | 
| 6 | 
            +
            import { SESSION_STORE_KEY } from '../../common/constants';
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            type PinnedMessage = {
         | 
| 9 | 
            +
              text: string;
         | 
| 10 | 
            +
              id: string;
         | 
| 11 | 
            +
              authorId: string;
         | 
| 12 | 
            +
              pinnedBy: string;
         | 
| 13 | 
            +
            };
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            /**
         | 
| 16 | 
            +
             * set pinned chat message by updating the session store
         | 
| 17 | 
            +
             */
         | 
| 18 | 
            +
            export const useSetPinnedMessages = () => {
         | 
| 19 | 
            +
              const hmsActions = useHMSActions();
         | 
| 20 | 
            +
              const vanillaStore = useHMSVanillaStore();
         | 
| 21 | 
            +
              // const pinnedMessages: PinnedMessage[] = useHMSStore(selectSessionStore(SESSION_STORE_KEY.PINNED_MESSAGES)) || [];
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              const setPinnedMessages = useCallback(
         | 
| 24 | 
            +
                async (pinnedMessages: PinnedMessage[] = [], message: HMSMessage, pinnedBy: string) => {
         | 
| 25 | 
            +
                  const peerName = vanillaStore.getState(selectPeerNameByID(message?.sender)) || message?.senderName;
         | 
| 26 | 
            +
                  const newPinnedMessage = { text: '', id: message.id, pinnedBy, authorId: message?.sender || '' };
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  if (message && peerName) {
         | 
| 29 | 
            +
                    newPinnedMessage['text'] = `${peerName}: ${message.message}`;
         | 
| 30 | 
            +
                  } else if (message) {
         | 
| 31 | 
            +
                    newPinnedMessage['text'] = message.message;
         | 
| 32 | 
            +
                  }
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  if (newPinnedMessage && !pinnedMessages.find(pinnedMessage => pinnedMessage.id === newPinnedMessage.id)) {
         | 
| 35 | 
            +
                    await hmsActions.sessionStore
         | 
| 36 | 
            +
                      .set(SESSION_STORE_KEY.PINNED_MESSAGES, [...pinnedMessages, newPinnedMessage].slice(-3)) // Limiting to maximum of 3 messages - FIFO
         | 
| 37 | 
            +
                      .catch(err => ToastManager.addToast({ title: err.description }));
         | 
| 38 | 
            +
                  }
         | 
| 39 | 
            +
                },
         | 
| 40 | 
            +
                [hmsActions, vanillaStore],
         | 
| 41 | 
            +
              );
         | 
| 42 | 
            +
             | 
| 43 | 
            +
              const removePinnedMessage = useCallback(
         | 
| 44 | 
            +
                async (pinnedMessages: PinnedMessage[] = [], indexToRemove: number) => {
         | 
| 45 | 
            +
                  if (pinnedMessages[indexToRemove]) {
         | 
| 46 | 
            +
                    await hmsActions.sessionStore
         | 
| 47 | 
            +
                      .set(
         | 
| 48 | 
            +
                        SESSION_STORE_KEY.PINNED_MESSAGES,
         | 
| 49 | 
            +
                        pinnedMessages.filter((_, index: number) => index !== indexToRemove),
         | 
| 50 | 
            +
                      )
         | 
| 51 | 
            +
                      .catch(err => ToastManager.addToast({ title: err.description }));
         | 
| 52 | 
            +
                  }
         | 
| 53 | 
            +
                },
         | 
| 54 | 
            +
                [hmsActions],
         | 
| 55 | 
            +
              );
         | 
| 56 | 
            +
             | 
| 57 | 
            +
              const unpinBlacklistedMessages = useCallback(
         | 
| 58 | 
            +
                async (
         | 
| 59 | 
            +
                  pinnedMessages: PinnedMessage[] = [],
         | 
| 60 | 
            +
                  blacklistedPeerIDSet: Set<string>,
         | 
| 61 | 
            +
                  blacklistedMessageIDSet: Set<string>,
         | 
| 62 | 
            +
                ) => {
         | 
| 63 | 
            +
                  const filteredPinnedMessages = pinnedMessages?.filter(
         | 
| 64 | 
            +
                    pinnedMessage =>
         | 
| 65 | 
            +
                      !blacklistedMessageIDSet?.has(pinnedMessage.id) && !blacklistedPeerIDSet.has(pinnedMessage.authorId),
         | 
| 66 | 
            +
                  );
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  await hmsActions.sessionStore
         | 
| 69 | 
            +
                    .set(SESSION_STORE_KEY.PINNED_MESSAGES, filteredPinnedMessages)
         | 
| 70 | 
            +
                    .catch(err => ToastManager.addToast({ title: err.description }));
         | 
| 71 | 
            +
                },
         | 
| 72 | 
            +
                [hmsActions],
         | 
| 73 | 
            +
              );
         | 
| 74 | 
            +
             | 
| 75 | 
            +
              return { setPinnedMessages, removePinnedMessage, unpinBlacklistedMessages };
         | 
| 76 | 
            +
            };
         | 
| @@ -41,7 +41,7 @@ const SidePane = ({ | |
| 41 41 | 
             
                ViewComponent = <SidePaneTabs screenType={screenType} hideControls={hideControls} active={sidepane} />;
         | 
| 42 42 | 
             
              }
         | 
| 43 43 | 
             
              if (sidepane === SIDE_PANE_OPTIONS.VB) {
         | 
| 44 | 
            -
                ViewComponent = <VBPicker />;
         | 
| 44 | 
            +
                ViewComponent = <VBPicker {...elements.virtual_background} />;
         | 
| 45 45 | 
             
              }
         | 
| 46 46 |  | 
| 47 47 | 
             
              useEffect(() => {
         |