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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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(() => {