@100mslive/roomkit-react 0.1.12 → 0.1.13

Sign up to get free protection for your applications and to get access to all the features.
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',