@100mslive/roomkit-react 0.1.12 → 0.1.13

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 (68) hide show
  1. package/dist/{HLSView-PWVM4OIP.js → HLSView-IENE4HRP.js} +2 -2
  2. package/dist/Prebuilt/App.d.ts +0 -2
  3. package/dist/Prebuilt/AppContext.d.ts +0 -2
  4. package/dist/Prebuilt/common/constants.d.ts +110 -0
  5. package/dist/Prebuilt/components/AppData/AppData.d.ts +2 -0
  6. package/dist/Prebuilt/components/Chat/MwebChatOption.d.ts +7 -0
  7. package/dist/Prebuilt/components/Chat/Navigation.d.ts +8 -0
  8. package/dist/Prebuilt/components/Chat/PinnedMessage.d.ts +4 -0
  9. package/dist/Prebuilt/components/Footer/Footer.d.ts +3 -2
  10. package/dist/Prebuilt/components/Notifications/ChatNotifications.d.ts +2 -0
  11. package/dist/Prebuilt/components/Preview/PreviewJoin.d.ts +2 -1
  12. package/dist/Prebuilt/components/VirtualBackground/VBCollection.d.ts +2 -1
  13. package/dist/Prebuilt/components/VirtualBackground/VBPicker.d.ts +3 -0
  14. package/dist/Prebuilt/components/VirtualBackground/constants.d.ts +0 -6
  15. package/dist/Prebuilt/components/hooks/useChatBlacklist.d.ts +4 -0
  16. package/dist/Prebuilt/components/hooks/useSetPinnedMessages.d.ts +16 -0
  17. package/dist/Prebuilt/provider/roomLayoutProvider/hooks/useRoomLayoutScreen.d.ts +3 -2
  18. package/dist/{chunk-WREKGNP6.js → chunk-U4AB6X6M.js} +2178 -1148
  19. package/dist/chunk-U4AB6X6M.js.map +7 -0
  20. package/dist/index.cjs.js +2746 -1638
  21. package/dist/index.cjs.js.map +4 -4
  22. package/dist/index.js +1 -1
  23. package/dist/meta.cjs.json +803 -208
  24. package/dist/meta.esbuild.json +812 -217
  25. package/package.json +8 -7
  26. package/src/Prebuilt/App.tsx +3 -14
  27. package/src/Prebuilt/AppContext.tsx +0 -2
  28. package/src/Prebuilt/common/{constants.js → constants.ts} +22 -20
  29. package/src/Prebuilt/components/AppData/{AppData.jsx → AppData.tsx} +8 -22
  30. package/src/Prebuilt/components/AppData/useUISettings.js +0 -4
  31. package/src/Prebuilt/components/AuthToken.jsx +15 -5
  32. package/src/Prebuilt/components/Chat/Chat.jsx +49 -73
  33. package/src/Prebuilt/components/Chat/ChatBody.jsx +241 -51
  34. package/src/Prebuilt/components/Chat/ChatFooter.tsx +56 -6
  35. package/src/Prebuilt/components/Chat/ChatStates.jsx +68 -0
  36. package/src/Prebuilt/components/Chat/MwebChatOption.tsx +24 -0
  37. package/src/Prebuilt/components/Chat/Navigation.tsx +60 -0
  38. package/src/Prebuilt/components/Chat/PinnedMessage.tsx +116 -0
  39. package/src/Prebuilt/components/Footer/Footer.tsx +4 -7
  40. package/src/Prebuilt/components/Header/common.jsx +1 -1
  41. package/src/Prebuilt/components/Notifications/ChatNotifications.tsx +34 -0
  42. package/src/Prebuilt/components/Notifications/Notifications.tsx +2 -0
  43. package/src/Prebuilt/components/Polls/Voting/QuestionCard.jsx +6 -4
  44. package/src/Prebuilt/components/Polls/Voting/TimedVoting.jsx +3 -2
  45. package/src/Prebuilt/components/Polls/common/VoteCount.jsx +6 -4
  46. package/src/Prebuilt/components/Preview/PreviewJoin.tsx +7 -9
  47. package/src/Prebuilt/components/RoleChangeRequest/RoleChangeRequestModal.tsx +4 -1
  48. package/src/Prebuilt/components/Settings/DeviceSettings.jsx +1 -1
  49. package/src/Prebuilt/components/SidePaneTabs.tsx +3 -2
  50. package/src/Prebuilt/components/Toast/ToastConfig.jsx +1 -0
  51. package/src/Prebuilt/components/VirtualBackground/VBCollection.tsx +4 -4
  52. package/src/Prebuilt/components/VirtualBackground/{VBPicker.jsx → VBPicker.tsx} +64 -44
  53. package/src/Prebuilt/components/VirtualBackground/constants.ts +0 -8
  54. package/src/Prebuilt/components/hooks/useAutoStartStreaming.tsx +10 -1
  55. package/src/Prebuilt/components/hooks/useChatBlacklist.ts +21 -0
  56. package/src/Prebuilt/components/hooks/useSetPinnedMessages.ts +75 -0
  57. package/src/Prebuilt/components/hooks/useUserPreferences.jsx +1 -0
  58. package/src/Prebuilt/layouts/SidePane.tsx +1 -1
  59. package/src/Prebuilt/layouts/VideoStreamingSection.tsx +16 -8
  60. package/src/Prebuilt/plugins/whiteboard/PusherCommunicationProvider.js +1 -1
  61. package/src/Prebuilt/plugins/whiteboard/README.md +1 -1
  62. package/src/Prebuilt/primitives/DialogContent.jsx +1 -1
  63. package/src/Prebuilt/provider/roomLayoutProvider/constants/index.ts +12 -1
  64. package/src/Prebuilt/provider/roomLayoutProvider/hooks/useRoomLayoutScreen.ts +5 -1
  65. package/dist/chunk-WREKGNP6.js.map +0 -7
  66. package/src/Prebuilt/components/hooks/useSetPinnedMessage.js +0 -38
  67. package/src/Prebuilt/services/tokenService.js +0 -49
  68. /package/dist/{HLSView-PWVM4OIP.js.map → HLSView-IENE4HRP.js.map} +0 -0
@@ -0,0 +1,116 @@
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import { useSwipeable } from 'react-swipeable';
3
+ import { useMedia } from 'react-use';
4
+ import { 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 pinnedMessages = useHMSStore(selectSessionStore(SESSION_STORE_KEY.PINNED_MESSAGES)) || [];
20
+ const [pinnedMessageIndex, setPinnedMessageIndex] = useState(0);
21
+ const isMobile = useMedia(cssConfig.media.md);
22
+
23
+ const [hideOverflow, setHideOverflow] = useState(false);
24
+ const canOverflow = pinnedMessages?.[pinnedMessageIndex]?.text?.length > PINNED_MESSAGE_LENGTH || false;
25
+ const formattedPinnedMessage = hideOverflow
26
+ ? `${pinnedMessages?.[pinnedMessageIndex]?.text.slice(0, PINNED_MESSAGE_LENGTH)}... `
27
+ : pinnedMessages?.[pinnedMessageIndex]?.text;
28
+
29
+ const pinnedMessageRef = useRef(null);
30
+ const showPreviousPinnedMessage = () => {
31
+ const previousIndex = Math.max(pinnedMessageIndex - 1, 0);
32
+ setHideOverflow(pinnedMessages[previousIndex].text.length > PINNED_MESSAGE_LENGTH);
33
+ setPinnedMessageIndex(previousIndex);
34
+ };
35
+
36
+ const showNextPinnedMessage = () => {
37
+ const nextIndex = Math.min(pinnedMessageIndex + 1, pinnedMessages.length - 1);
38
+ setHideOverflow(pinnedMessages[nextIndex].text.length > PINNED_MESSAGE_LENGTH);
39
+ setPinnedMessageIndex(nextIndex);
40
+ };
41
+
42
+ const swipeHandlers = useSwipeable({
43
+ onSwipedUp: () => showNextPinnedMessage(),
44
+ onSwipedDown: () => showPreviousPinnedMessage(),
45
+ });
46
+
47
+ useEffect(() => {
48
+ setHideOverflow(
49
+ !!(
50
+ pinnedMessages?.[pinnedMessageIndex]?.text?.length &&
51
+ pinnedMessages?.[pinnedMessageIndex]?.text.length > PINNED_MESSAGE_LENGTH
52
+ ),
53
+ );
54
+ }, [pinnedMessageIndex, pinnedMessages]);
55
+
56
+ return pinnedMessages?.[pinnedMessageIndex]?.text ? (
57
+ <Flex ref={pinnedMessageRef} align="center" css={{ w: '100%', gap: '$4' }}>
58
+ <Flex
59
+ title={pinnedMessages[pinnedMessageIndex].text}
60
+ css={{
61
+ p: '$4',
62
+ color: '$on_surface_medium',
63
+ bg: isMobile ? 'rgba(0, 0, 0, 0.64)' : '$surface_default',
64
+ r: '$1',
65
+ gap: '$4',
66
+ mb: '$8',
67
+ mt: '$8',
68
+ flexGrow: 1,
69
+ }}
70
+ align="center"
71
+ justify="between"
72
+ >
73
+ <Navigation
74
+ index={pinnedMessageIndex}
75
+ total={pinnedMessages.length}
76
+ showPrevious={showPreviousPinnedMessage}
77
+ showNext={showNextPinnedMessage}
78
+ isMobile={isMobile}
79
+ />
80
+ <PinIcon />
81
+
82
+ <Box
83
+ css={{
84
+ w: '100%',
85
+ maxHeight: '$18',
86
+ overflowY: 'auto',
87
+ overflowX: 'hidden',
88
+ wordBreak: 'break-word',
89
+ '& p span': {
90
+ color: '$primary_default',
91
+ },
92
+ }}
93
+ >
94
+ <Text variant="sm" css={{ color: '$on_surface_medium' }} {...swipeHandlers}>
95
+ <AnnotisedMessage message={formattedPinnedMessage} />
96
+ {canOverflow ? (
97
+ <span style={{ cursor: 'pointer' }} onClick={() => setHideOverflow(prev => !prev)}>
98
+ $nbsp;{hideOverflow ? 'See more' : 'Collapse'}
99
+ </span>
100
+ ) : null}
101
+ </Text>
102
+ </Box>
103
+
104
+ <Flex
105
+ onClick={() => {
106
+ clearPinnedMessage(pinnedMessageIndex);
107
+ setPinnedMessageIndex(Math.max(0, pinnedMessageIndex - 1));
108
+ }}
109
+ css={{ cursor: 'pointer', color: '$on_surface_medium', '&:hover': { color: '$on_surface_high' } }}
110
+ >
111
+ <CrossIcon />
112
+ </Flex>
113
+ </Flex>
114
+ </Flex>
115
+ ) : null;
116
+ };
@@ -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: DefaultConferencingScreen_Elements | HLSLiveStreamingScreen_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 ? null : <VBToggle />}
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 e.g.) this changes both mic and speaker keeping them in sync.
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;
@@ -0,0 +1,34 @@
1
+ import React, { useEffect } from 'react';
2
+ import { v4 as uuid } from 'uuid';
3
+ import { selectLocalPeerID, selectSessionStore, useHMSStore } from '@100mslive/react-sdk';
4
+ import { ChatIcon, ChatUnreadIcon } from '@100mslive/react-icons';
5
+ // @ts-ignore
6
+ import { ToastManager } from '../Toast/ToastManager';
7
+ import { SESSION_STORE_KEY } from '../../common/constants';
8
+
9
+ const NOTIFICATION_TIME_DIFFERENCE = 5000;
10
+
11
+ export const ChatNotifications = () => {
12
+ const chatState = useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_STATE));
13
+ const localPeerId = useHMSStore(selectLocalPeerID);
14
+
15
+ useEffect(() => {
16
+ if (!chatState || chatState.updatedBy?.peerId === localPeerId) {
17
+ return;
18
+ }
19
+
20
+ const showToast = Date.now() - chatState.updatedAt < NOTIFICATION_TIME_DIFFERENCE;
21
+
22
+ if (!showToast) {
23
+ return;
24
+ }
25
+
26
+ const notification = {
27
+ id: uuid(),
28
+ icon: chatState.enabled ? <ChatUnreadIcon /> : <ChatIcon />,
29
+ title: `Chat ${chatState.enabled ? 'resumed' : 'paused'} by ${chatState.updatedBy?.userName}`,
30
+ };
31
+ ToastManager.addToast(notification);
32
+ }, [chatState]);
33
+ return <></>;
34
+ };
@@ -20,6 +20,7 @@ import { ToastBatcher } from '../Toast/ToastBatcher';
20
20
  // @ts-ignore: No implicit Any
21
21
  import { ToastManager } from '../Toast/ToastManager';
22
22
  import { AutoplayBlockedModal } from './AutoplayBlockedModal';
23
+ import { ChatNotifications } from './ChatNotifications';
23
24
  import { InitErrorModal } from './InitErrorModal';
24
25
  import { PeerNotifications } from './PeerNotifications';
25
26
  import { PermissionErrorModal } from './PermissionErrorModal';
@@ -186,6 +187,7 @@ export function Notifications() {
186
187
  <AutoplayBlockedModal />
187
188
  <PermissionErrorModal />
188
189
  <InitErrorModal />
190
+ <ChatNotifications />
189
191
  </>
190
192
  );
191
193
  }
@@ -1,6 +1,6 @@
1
1
  // @ts-check
2
2
  import React, { useCallback, useMemo, useState } from 'react';
3
- import { selectLocalPeerID, selectLocalPeerRoleName, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
3
+ import { selectLocalPeer, selectLocalPeerRoleName, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
4
4
  import { ChevronLeftIcon, ChevronRightIcon } from '@100mslive/react-icons';
5
5
  import { Box, Button, Flex, IconButton, Input, styled, Text } from '../../../../';
6
6
  import { checkCorrectAnswer } from '../../../common/utils';
@@ -38,9 +38,11 @@ export const QuestionCard = ({
38
38
  rolesThatCanViewResponses,
39
39
  }) => {
40
40
  const actions = useHMSActions();
41
- const localPeerID = useHMSStore(selectLocalPeerID);
42
- const localPeerResponse = responses?.find(response => response.peer?.peerid === localPeerID);
43
- const isLocalPeerCreator = localPeerID === startedBy;
41
+ const localPeer = useHMSStore(selectLocalPeer);
42
+ const localPeerResponse = responses?.find(
43
+ response => response.peer?.peerid === localPeer?.id || response.peer?.userid === localPeer?.customerUserId,
44
+ );
45
+ const isLocalPeerCreator = localPeer?.id === startedBy;
44
46
  const localPeerRoleName = useHMSStore(selectLocalPeerRoleName);
45
47
  const roleCanViewResponse =
46
48
  !rolesThatCanViewResponses ||
@@ -8,8 +8,9 @@ import { QuestionCard } from './QuestionCard';
8
8
  * @returns
9
9
  */
10
10
  export const TimedView = ({ poll }) => {
11
- const [currentIndex, setCurrentIndex] = useState(0);
12
- const activeQuestion = poll.questions?.[currentIndex];
11
+ // backend question index starts at 1
12
+ const [currentIndex, setCurrentIndex] = useState(1);
13
+ const activeQuestion = poll.questions?.find(question => question.index === currentIndex);
13
14
  if (!activeQuestion) {
14
15
  return null;
15
16
  }
@@ -19,10 +19,12 @@ export const VoteCount = ({ isQuiz, voteCount, isCorrectAnswer }) => {
19
19
  {isCorrectAnswer ? 'Correct' : 'Incorrect'}
20
20
  </Text>
21
21
  )}
22
- <Text variant="sm" css={{ color: '$on_surface_medium' }}>
23
- {voteCount}&nbsp;
24
- {voteCount === 1 ? 'vote' : 'votes'}
25
- </Text>
22
+ {voteCount ? (
23
+ <Text variant="sm" css={{ color: '$on_surface_medium' }}>
24
+ {voteCount}&nbsp;
25
+ {voteCount === 1 ? 'vote' : 'votes'}
26
+ </Text>
27
+ ) : null}
26
28
  </Flex>
27
29
  );
28
30
  };
@@ -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 roomLayout = useRoomLayout();
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}
@@ -188,7 +186,7 @@ const PreviewJoin = ({
188
186
  />
189
187
  </Box>
190
188
  </Container>
191
- <Box css={{ position: 'absolute', right: '0', top: 0, height: '100%' }}>
189
+ <Box css={{ position: 'absolute', right: '0', top: 0, height: '100%', overflow: 'hidden' }}>
192
190
  <SidePane screenType="default" />
193
191
  </Box>
194
192
  </Flex>
@@ -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 e.g.) this changes both mic and speaker keeping them in sync.
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
- 'Chat'
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
- Chat
145
+ {chat_title}
145
146
  </Tabs.Trigger>
146
147
  <Tabs.Trigger
147
148
  value={SIDE_PANE_OPTIONS.PARTICIPANTS}
@@ -58,6 +58,7 @@ const HandRaiseAction = React.forwardRef(({ id = '', isSingleHandRaise = true },
58
58
  </Button>
59
59
  );
60
60
  });
61
+
61
62
  export const ToastConfig = {
62
63
  PEER_LIST: {
63
64
  single: function (notification) {
@@ -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: string;
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
- ([VB_EFFECT.NONE, VB_EFFECT.BLUR].includes(activeBackgroundType) &&
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, VB_EFFECT, vbPlugin } from './constants';
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,15 @@ 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
 
51
+ const inPreview = roomState === HMSRoomState.Preview;
43
52
  // 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;
53
+ const showVideoTile = isVideoOn && isLargeRoom && !inPreview;
45
54
 
46
55
  const clearVBState = () => {
47
- setBackground(VB_EFFECT.NONE);
48
- setBackgroundType(VB_EFFECT.NONE);
56
+ setBackground(HMSVirtualBackgroundTypes.NONE);
57
+ setBackgroundType(HMSVirtualBackgroundTypes.NONE);
49
58
  };
50
59
 
51
60
  useEffect(() => {
@@ -62,7 +71,7 @@ export const VBPicker = () => {
62
71
 
63
72
  async function disableEffects() {
64
73
  if (vbPlugin) {
65
- vbPlugin.setBackground(VB_EFFECT.NONE, VB_EFFECT.NONE);
74
+ vbPlugin.setBackground(HMSVirtualBackgroundTypes.NONE, HMSVirtualBackgroundTypes.NONE);
66
75
  clearVBState();
67
76
  }
68
77
  }
@@ -75,18 +84,18 @@ export const VBPicker = () => {
75
84
  img.alt = 'VB';
76
85
  img.src = mediaURL;
77
86
  try {
78
- await vbPlugin.setBackground(img, VB_EFFECT.MEDIA);
87
+ await vbPlugin.setBackground(img, HMSVirtualBackgroundTypes.IMAGE);
79
88
  } catch (e) {
80
89
  console.error(e);
81
90
  if (retries++ < MAX_RETRIES) {
82
- await vbPlugin.setBackground(img, VB_EFFECT.MEDIA);
91
+ await vbPlugin.setBackground(img, HMSVirtualBackgroundTypes.IMAGE);
83
92
  }
84
93
  }
85
94
  } else if (blurPower) {
86
- await vbPlugin.setBackground(VB_EFFECT.BLUR, VB_EFFECT.BLUR);
95
+ await vbPlugin.setBackground(HMSVirtualBackgroundTypes.BLUR, HMSVirtualBackgroundTypes.BLUR);
87
96
  }
88
- setBackground(mediaURL || VB_EFFECT.BLUR);
89
- setBackgroundType(mediaURL ? VB_EFFECT.MEDIA : VB_EFFECT.BLUR);
97
+ setBackground(mediaURL || HMSVirtualBackgroundTypes.BLUR);
98
+ setBackgroundType(mediaURL ? HMSVirtualBackgroundTypes.IMAGE : HMSVirtualBackgroundTypes.BLUR);
90
99
  if (role && !addedPluginToVideoTrack.current) {
91
100
  await hmsActions.addPluginToVideoTrack(vbPlugin, Math.floor(role.publishParams.video.frameRate / 2));
92
101
  addedPluginToVideoTrack.current = true;
@@ -108,8 +117,8 @@ export const VBPicker = () => {
108
117
  }
109
118
 
110
119
  return (
111
- <Box css={{ maxHeight: '100%', overflowY: 'auto', pr: '$6' }}>
112
- <Flex align="center" justify="between" css={{ w: '100%', position: 'sticky', top: 0 }}>
120
+ <Flex css={{ pr: '$6', size: '100%' }} direction="column">
121
+ <Flex align="center" justify="between" css={{ w: '100%', background: '$surface_dim', pb: '$4' }}>
113
122
  <Text variant="h6" css={{ color: '$on_surface_high' }}>
114
123
  Virtual Background
115
124
  </Text>
@@ -126,40 +135,51 @@ export const VBPicker = () => {
126
135
  mirror={track?.facingMode !== 'environment' && mirrorLocalVideo}
127
136
  trackId={localPeer?.videoTrack}
128
137
  data-testid="preview_tile"
129
- css={{ width: '100%', height: '16rem', position: 'sticky', top: '$17' }}
138
+ css={{ width: '100%', height: '16rem' }}
130
139
  />
131
140
  ) : null}
132
141
 
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
- />
142
+ <Box
143
+ css={{
144
+ mt: '$4',
145
+ overflowY: 'auto',
146
+ flex: '1 1 0',
147
+ mr: '-$10',
148
+ pr: '$10',
149
+ }}
150
+ >
151
+ <VBCollection
152
+ title="Effects"
153
+ options={[
154
+ {
155
+ title: 'No effect',
156
+ icon: <CrossCircleIcon style={iconDims} />,
157
+ type: HMSVirtualBackgroundTypes.NONE,
158
+ onClick: async () => await disableEffects(),
159
+ },
160
+ {
161
+ title: 'Blur',
162
+ icon: <BlurPersonHighIcon style={iconDims} />,
163
+ type: HMSVirtualBackgroundTypes.BLUR,
164
+ onClick: async () => await addPlugin({ blurPower: 0.5 }),
165
+ },
166
+ ]}
167
+ activeBackgroundType={backgroundType || HMSVirtualBackgroundTypes.NONE}
168
+ // @ts-ignore
169
+ activeBackground={vbPlugin.background?.src || vbPlugin.background || HMSVirtualBackgroundTypes.NONE}
170
+ />
152
171
 
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>
172
+ <VBCollection
173
+ title="Backgrounds"
174
+ options={mediaList.map(mediaURL => ({
175
+ type: HMSVirtualBackgroundTypes.IMAGE,
176
+ mediaURL,
177
+ onClick: async () => await addPlugin({ mediaURL }),
178
+ }))}
179
+ activeBackgroundType={backgroundType || HMSVirtualBackgroundTypes.NONE}
180
+ activeBackground={background?.src || background || HMSVirtualBackgroundTypes.NONE}
181
+ />
182
+ </Box>
183
+ </Flex>
164
184
  );
165
185
  };
@@ -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',