@100mslive/roomkit-react 0.1.12-alpha.0 → 0.1.13-alpha.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. package/dist/{HLSView-J2MIS3H2.js → HLSView-AIPLCDXY.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/Preview/PreviewJoin.d.ts +2 -1
  11. package/dist/Prebuilt/components/VirtualBackground/VBCollection.d.ts +2 -1
  12. package/dist/Prebuilt/components/VirtualBackground/VBPicker.d.ts +3 -0
  13. package/dist/Prebuilt/components/VirtualBackground/constants.d.ts +0 -6
  14. package/dist/Prebuilt/components/hooks/useChatBlacklist.d.ts +4 -0
  15. package/dist/Prebuilt/components/hooks/useSetPinnedMessages.d.ts +16 -0
  16. package/dist/Prebuilt/provider/roomLayoutProvider/hooks/useRoomLayoutScreen.d.ts +3 -2
  17. package/dist/Theme/stitches.config.d.ts +226 -226
  18. package/dist/{chunk-OYSYEA6R.js → chunk-5DCII2TP.js} +2248 -1269
  19. package/dist/chunk-5DCII2TP.js.map +7 -0
  20. package/dist/index.cjs.js +2943 -1902
  21. package/dist/index.cjs.js.map +4 -4
  22. package/dist/index.js +1 -1
  23. package/dist/meta.cjs.json +819 -313
  24. package/dist/meta.esbuild.json +826 -320
  25. package/package.json +8 -8
  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 +4 -4
  32. package/src/Prebuilt/components/Chat/Chat.jsx +51 -73
  33. package/src/Prebuilt/components/Chat/ChatBody.jsx +219 -48
  34. package/src/Prebuilt/components/Chat/ChatFooter.tsx +50 -6
  35. package/src/Prebuilt/components/Chat/ChatStates.jsx +66 -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 +118 -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/Preview/PreviewJoin.tsx +6 -8
  42. package/src/Prebuilt/components/RoleChangeRequest/RoleChangeRequestModal.tsx +4 -1
  43. package/src/Prebuilt/components/Settings/DeviceSettings.jsx +1 -1
  44. package/src/Prebuilt/components/SidePaneTabs.tsx +3 -2
  45. package/src/Prebuilt/components/Toast/ToastConfig.jsx +20 -0
  46. package/src/Prebuilt/components/VirtualBackground/VBCollection.tsx +4 -4
  47. package/src/Prebuilt/components/VirtualBackground/{VBPicker.jsx → VBPicker.tsx} +27 -18
  48. package/src/Prebuilt/components/VirtualBackground/constants.ts +0 -8
  49. package/src/Prebuilt/components/hooks/useAutoStartStreaming.tsx +10 -1
  50. package/src/Prebuilt/components/hooks/useChatBlacklist.ts +21 -0
  51. package/src/Prebuilt/components/hooks/useSetPinnedMessages.ts +76 -0
  52. package/src/Prebuilt/layouts/SidePane.tsx +1 -1
  53. package/src/Prebuilt/layouts/VideoStreamingSection.tsx +30 -2
  54. package/src/Prebuilt/plugins/whiteboard/PusherCommunicationProvider.js +1 -1
  55. package/src/Prebuilt/plugins/whiteboard/README.md +1 -1
  56. package/src/Prebuilt/primitives/DialogContent.jsx +1 -1
  57. package/src/Prebuilt/provider/roomLayoutProvider/constants/index.ts +12 -1
  58. package/src/Prebuilt/provider/roomLayoutProvider/hooks/useRoomLayoutScreen.ts +5 -1
  59. package/src/Theme/stitches.config.ts +1 -2
  60. package/dist/chunk-OYSYEA6R.js.map +0 -7
  61. package/src/Prebuilt/components/hooks/useSetPinnedMessage.js +0 -38
  62. package/src/Prebuilt/services/tokenService.js +0 -49
  63. /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: 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;
@@ -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}
@@ -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}
@@ -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: 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,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(VB_EFFECT.NONE);
48
- setBackgroundType(VB_EFFECT.NONE);
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(VB_EFFECT.NONE, VB_EFFECT.NONE);
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, VB_EFFECT.MEDIA);
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, VB_EFFECT.MEDIA);
90
+ await vbPlugin.setBackground(img, HMSVirtualBackgroundTypes.IMAGE);
83
91
  }
84
92
  }
85
93
  } else if (blurPower) {
86
- await vbPlugin.setBackground(VB_EFFECT.BLUR, VB_EFFECT.BLUR);
94
+ await vbPlugin.setBackground(HMSVirtualBackgroundTypes.BLUR, HMSVirtualBackgroundTypes.BLUR);
87
95
  }
88
- setBackground(mediaURL || VB_EFFECT.BLUR);
89
- setBackgroundType(mediaURL ? VB_EFFECT.MEDIA : VB_EFFECT.BLUR);
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: VB_EFFECT.NONE,
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: VB_EFFECT.BLUR,
153
+ type: HMSVirtualBackgroundTypes.BLUR,
146
154
  onClick: async () => await addPlugin({ blurPower: 0.5 }),
147
155
  },
148
156
  ]}
149
- activeBackgroundType={backgroundType || VB_EFFECT.NONE}
150
- activeBackground={vbPlugin.background?.src || vbPlugin.background || VB_EFFECT.NONE}
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={defaultMedia.map(mediaURL => ({
156
- type: VB_EFFECT.MEDIA,
164
+ options={mediaList.map(mediaURL => ({
165
+ type: HMSVirtualBackgroundTypes.IMAGE,
157
166
  mediaURL,
158
167
  onClick: async () => await addPlugin({ mediaURL }),
159
168
  }))}
160
- activeBackgroundType={backgroundType || VB_EFFECT.NONE}
161
- activeBackground={background?.src || background || VB_EFFECT.NONE}
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
- }, [hmsActions, isHLSRunning, isHLSStarted, setHLSStarted, showStreamingUI, isRTMPRunning]);
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(() => {