@100mslive/roomkit-react 0.3.16-alpha.1 → 0.3.16-alpha.10

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 (30) hide show
  1. package/dist/{HLSView-T2PXKR6I.css → HLSView-D62ZFGUE.css} +3 -3
  2. package/dist/{HLSView-T2PXKR6I.css.map → HLSView-D62ZFGUE.css.map} +1 -1
  3. package/dist/{HLSView-ERPYXVOY.js → HLSView-QYHOAJX7.js} +2 -2
  4. package/dist/{chunk-NKC36NL4.js → chunk-NYBDQX5B.js} +1049 -964
  5. package/dist/chunk-NYBDQX5B.js.map +7 -0
  6. package/dist/index.cjs.css +2 -2
  7. package/dist/index.cjs.css.map +1 -1
  8. package/dist/index.cjs.js +1463 -1380
  9. package/dist/index.cjs.js.map +4 -4
  10. package/dist/index.css +2 -2
  11. package/dist/index.css.map +1 -1
  12. package/dist/index.js +1 -1
  13. package/dist/meta.cjs.json +106 -76
  14. package/dist/meta.esbuild.json +119 -89
  15. package/package.json +7 -7
  16. package/src/Prebuilt/App.tsx +21 -18
  17. package/src/Prebuilt/components/AudioVideoToggle.tsx +2 -2
  18. package/src/Prebuilt/components/ConferenceScreen.tsx +13 -1
  19. package/src/Prebuilt/components/Footer/ParticipantList.tsx +1 -4
  20. package/src/Prebuilt/components/MoreSettings/MoreSettings.tsx +1 -4
  21. package/src/Prebuilt/components/MoreSettings/SplitComponents/DesktopOptions.tsx +12 -2
  22. package/src/Prebuilt/components/Notifications/HandRaisedNotifications.tsx +33 -0
  23. package/src/Prebuilt/components/Notifications/Notifications.tsx +3 -1
  24. package/src/Prebuilt/components/PIP/PIPChat.tsx +62 -14
  25. package/src/Prebuilt/components/PIP/PIPWindow.tsx +1 -1
  26. package/src/Prebuilt/components/PIP/usePIPChat.tsx +2 -0
  27. package/src/Prebuilt/components/Preview/PreviewJoin.tsx +3 -1
  28. package/src/Prebuilt/components/Toast/ToastConfig.jsx +2 -2
  29. package/dist/chunk-NKC36NL4.js.map +0 -7
  30. /package/dist/{HLSView-ERPYXVOY.js.map → HLSView-QYHOAJX7.js.map} +0 -0
@@ -44,7 +44,7 @@ import { useRoomLayoutConferencingScreen } from '../provider/roomLayoutProvider/
44
44
  // @ts-ignore: No implicit Any
45
45
  import { useIsNoiseCancellationEnabled, useSetNoiseCancellation } from './AppData/useUISettings';
46
46
  import { useAudioOutputTest } from './hooks/useAudioOutputTest';
47
- import { isMacOS, TEST_AUDIO_URL } from '../common/constants';
47
+ import { isAndroid, isIOS, isMacOS, TEST_AUDIO_URL } from '../common/constants';
48
48
 
49
49
  const krispPlugin = new HMSKrispPlugin();
50
50
  // const optionsCSS = { fontWeight: '$semiBold', color: '$on_surface_high', w: '100%' };
@@ -372,7 +372,7 @@ export const AudioVideoToggle = ({ hideOptions = false }: { hideOptions?: boolea
372
372
  </IconButtonWithOptions>
373
373
  ) : null}
374
374
 
375
- {localVideoTrack?.facingMode && roomState === HMSRoomState.Preview ? (
375
+ {localVideoTrack?.facingMode && roomState === HMSRoomState.Preview && (isIOS || isAndroid) ? (
376
376
  <Tooltip title="Switch Camera" key="switchCamera">
377
377
  <IconButton
378
378
  onClick={async () => {
@@ -6,6 +6,7 @@ import {
6
6
  selectAppData,
7
7
  selectIsConnectedToRoom,
8
8
  selectRoomState,
9
+ useAwayNotifications,
9
10
  useHMSActions,
10
11
  useHMSStore,
11
12
  } from '@100mslive/react-sdk';
@@ -50,6 +51,7 @@ export const ConferenceScreen = () => {
50
51
  const isMobileDevice = isAndroid || isIOS || isIPadOS;
51
52
  const dropdownListRef = useRef<string[]>();
52
53
  const [isHLSStarted] = useSetAppDataByKey(APP_DATA.hlsStarted);
54
+ const { requestPermission } = useAwayNotifications();
53
55
 
54
56
  // using it in hls stream to show action button when chat is disabled
55
57
  const showChat = !!screenProps.elements?.chat;
@@ -99,10 +101,20 @@ export const ConferenceScreen = () => {
99
101
  speakerAutoSelectionBlacklist: ['Yeti Stereo Microphone'],
100
102
  },
101
103
  })
104
+ .then(() => requestPermission())
102
105
  .catch(console.error);
103
106
  autoRoomJoined.current = true;
104
107
  }
105
- }, [authTokenInAppData, endpoints?.init, hmsActions, isConnectedToRoom, isPreviewScreenEnabled, roomState, userName]);
108
+ }, [
109
+ authTokenInAppData,
110
+ endpoints?.init,
111
+ hmsActions,
112
+ isConnectedToRoom,
113
+ isPreviewScreenEnabled,
114
+ roomState,
115
+ userName,
116
+ requestPermission,
117
+ ]);
106
118
 
107
119
  useEffect(() => {
108
120
  onJoinFunc?.();
@@ -194,7 +194,6 @@ export const Participant = ({
194
194
  <ParticipantActions
195
195
  peerId={peer.id}
196
196
  peerType={peer.type}
197
- isLocal={peer.id === localPeerId}
198
197
  role={peer.roleName}
199
198
  isHandRaisedAccordion={isHandRaisedAccordion}
200
199
  />
@@ -273,12 +272,10 @@ const ParticipantActions = React.memo(
273
272
  peerId,
274
273
  peerType,
275
274
  role,
276
- isLocal,
277
275
  isHandRaisedAccordion,
278
276
  }: {
279
277
  peerId: string;
280
278
  role: string;
281
- isLocal: boolean;
282
279
  isHandRaisedAccordion?: boolean;
283
280
  peerType: HMSPeerType;
284
281
  }) => {
@@ -331,7 +328,7 @@ const ParticipantActions = React.memo(
331
328
  </Flex>
332
329
  ) : null}
333
330
 
334
- {shouldShowMoreActions && !isLocal ? <ParticipantMoreActions peerId={peerId} role={role} /> : null}
331
+ {shouldShowMoreActions ? <ParticipantMoreActions peerId={peerId} role={role} /> : null}
335
332
  </>
336
333
  )}
337
334
  </Flex>
@@ -10,7 +10,6 @@ import { DesktopOptions } from './SplitComponents/DesktopOptions';
10
10
  // @ts-ignore: No implicit Any
11
11
  import { MwebOptions } from './SplitComponents/MwebOptions';
12
12
  import { config as cssConfig } from '../../..';
13
- import { PIPProvider } from '../PIP/PIPProvider';
14
13
  import { useLandscapeHLSStream } from '../../common/hooks';
15
14
 
16
15
  export const MoreSettings = ({
@@ -25,8 +24,6 @@ export const MoreSettings = ({
25
24
  return isMobile || isLandscapeHLSStream ? (
26
25
  <MwebOptions elements={elements} screenType={screenType} />
27
26
  ) : (
28
- <PIPProvider>
29
- <DesktopOptions elements={elements} screenType={screenType} />
30
- </PIPProvider>
27
+ <DesktopOptions elements={elements} screenType={screenType} />
31
28
  );
32
29
  };
@@ -48,12 +48,14 @@ import { CaptionModal } from '../CaptionModal';
48
48
  import { FullScreenItem } from '../FullScreenItem';
49
49
  import { MuteAllModal } from '../MuteAllModal';
50
50
  // @ts-ignore: No implicit any
51
+ import { useIsSidepaneTypeOpen, useSidepaneToggle } from '../../AppData/useSidepane';
52
+ // @ts-ignore: No implicit any
51
53
  import { useDropdownList } from '../../hooks/useDropdownList';
52
54
  import { useMyMetadata } from '../../hooks/useMetadata';
53
55
  // @ts-ignore: No implicit any
54
56
  import { usePIPChat } from '../../PIP/usePIPChat';
55
57
  // @ts-ignore: No implicit any
56
- import { APP_DATA, isMacOS } from '../../../common/constants';
58
+ import { APP_DATA, isMacOS, SIDE_PANE_OPTIONS } from '../../../common/constants';
57
59
 
58
60
  const MODALS = {
59
61
  CHANGE_NAME: 'changeName',
@@ -85,6 +87,8 @@ export const DesktopOptions = ({
85
87
  const isTranscriptionAllowed = useHMSStore(selectIsTranscriptionAllowedByMode(HMSTranscriptionMode.CAPTION));
86
88
  const isTranscriptionEnabled = useHMSStore(selectIsTranscriptionEnabled);
87
89
  const { isSupported, pipWindow, requestPipWindow } = usePIPChat();
90
+ const isChatOpen = useIsSidepaneTypeOpen(SIDE_PANE_OPTIONS.CHAT);
91
+ const toggleChat = useSidepaneToggle(SIDE_PANE_OPTIONS.CHAT);
88
92
  const showPipChatOption = !!elements?.chat && isSupported;
89
93
 
90
94
  useDropdownList({ open: openModals.size > 0, name: 'MoreSettings' });
@@ -180,7 +184,13 @@ export const DesktopOptions = ({
180
184
  </Dropdown.Item>
181
185
  ) : null}
182
186
 
183
- <PIPChatOption showPIPChat={showPipChatOption} openChat={async () => await requestPipWindow(350, 500)} />
187
+ <PIPChatOption
188
+ showPIPChat={showPipChatOption}
189
+ openChat={async () => {
190
+ isChatOpen && toggleChat();
191
+ await requestPipWindow(350, 500);
192
+ }}
193
+ />
184
194
  <FullScreenItem />
185
195
  <Dropdown.ItemSeparator css={{ mx: 0 }} />
186
196
  <Dropdown.Item onClick={() => updateState(MODALS.DEVICE_SETTINGS, true)} data-testid="device_settings_btn">
@@ -1,14 +1,19 @@
1
1
  import { useEffect } from 'react';
2
+ import { useDebounce } from 'react-use';
2
3
  import {
3
4
  HMSNotificationTypes,
4
5
  HMSRoomState,
6
+ selectHandRaisedPeers,
5
7
  selectHasPeerHandRaised,
8
+ selectIsLocalScreenShared,
6
9
  selectPeerByID,
7
10
  selectRoomState,
11
+ useAwayNotifications,
8
12
  useHMSNotifications,
9
13
  useHMSStore,
10
14
  useHMSVanillaStore,
11
15
  } from '@100mslive/react-sdk';
16
+ import { useRoomLayout } from '../../provider/roomLayoutProvider';
12
17
  // @ts-ignore: No implicit Any
13
18
  import { ToastBatcher } from '../Toast/ToastBatcher';
14
19
  import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
@@ -22,6 +27,9 @@ export const HandRaisedNotifications = () => {
22
27
  const vanillaStore = useHMSVanillaStore();
23
28
  const { on_stage_exp } = useRoomLayoutConferencingScreen().elements || {};
24
29
  const isSubscribing = !!useSubscribedNotifications(SUBSCRIBED_NOTIFICATIONS.METADATA_UPDATED);
30
+ const amIScreenSharing = useHMSStore(selectIsLocalScreenShared);
31
+ const { showNotification } = useAwayNotifications();
32
+ const logoURL = useRoomLayout()?.logo?.url;
25
33
 
26
34
  useEffect(() => {
27
35
  if (!notification?.data) {
@@ -32,6 +40,7 @@ export const HandRaisedNotifications = () => {
32
40
  if (roomState !== HMSRoomState.Connected || notification.data.isLocal || !isSubscribing) {
33
41
  return;
34
42
  }
43
+
35
44
  const hasPeerHandRaised = vanillaStore.getState(selectHasPeerHandRaised(notification.data.id));
36
45
  const peer = vanillaStore.getState(selectPeerByID(notification.data.id));
37
46
  if (hasPeerHandRaised) {
@@ -41,5 +50,29 @@ export const HandRaisedNotifications = () => {
41
50
  }
42
51
  }, [isSubscribing, notification, on_stage_exp, roomState, vanillaStore]);
43
52
 
53
+ useDebounce(
54
+ () => {
55
+ if (!notification?.data) {
56
+ return;
57
+ }
58
+
59
+ // Don't show toast message in case of local peer.
60
+ if (roomState !== HMSRoomState.Connected || notification.data.isLocal || !isSubscribing) {
61
+ return;
62
+ }
63
+
64
+ const hasPeerHandRaised = vanillaStore.getState(selectHasPeerHandRaised(notification.data.id));
65
+ const peer = vanillaStore.getState(selectPeerByID(notification.data.id));
66
+ const handRaisedPeers = vanillaStore.getState(selectHandRaisedPeers);
67
+ if (amIScreenSharing && hasPeerHandRaised) {
68
+ const title = `${peer?.name} ${
69
+ handRaisedPeers.length > 1 ? `and ${handRaisedPeers.length - 1} others` : ''
70
+ } raised hand`;
71
+ showNotification(title, { icon: logoURL });
72
+ }
73
+ },
74
+ 1000,
75
+ [isSubscribing, notification, roomState, vanillaStore, amIScreenSharing],
76
+ );
44
77
  return null;
45
78
  };
@@ -35,6 +35,7 @@ import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvid
35
35
  import { usePollViewToggle } from '../AppData/useSidepane';
36
36
  // @ts-ignore: No implicit Any
37
37
  import { useIsNotificationDisabled, useSubscribedNotifications } from '../AppData/useUISettings';
38
+ import { usePIPWindow } from '../PIP/usePIPWindow';
38
39
  import { ROLE_CHANGE_DECLINED } from '../../common/constants';
39
40
 
40
41
  const pollToastKey: Record<string, string> = {};
@@ -52,6 +53,7 @@ export function Notifications() {
52
53
  const { showNotification } = useAwayNotifications();
53
54
  const amIScreenSharing = useHMSStore(selectIsLocalScreenShared);
54
55
  const logoURL = useRoomLayout()?.logo?.url;
56
+ const { pipWindow } = usePIPWindow();
55
57
 
56
58
  const handleRoleChangeDenied = useCallback((request: HMSRoleChangeRequest & { peerName: string }) => {
57
59
  ToastManager.addToast({
@@ -172,7 +174,7 @@ export function Notifications() {
172
174
  }
173
175
  break;
174
176
  case HMSNotificationTypes.NEW_MESSAGE:
175
- if (amIScreenSharing && !notification.data?.ignored) {
177
+ if (amIScreenSharing && !notification.data?.ignored && !pipWindow) {
176
178
  showNotification(`New message from ${notification.data.senderName}`, {
177
179
  body: notification.data.message,
178
180
  icon: logoURL,
@@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
2
  import {
3
3
  selectHMSMessages,
4
4
  selectLocalPeerID,
5
+ selectPeerNameByID,
5
6
  selectSessionStore,
6
7
  selectUnreadHMSMessagesCount,
7
8
  useHMSStore,
@@ -14,6 +15,7 @@ import { Tooltip } from '../../../Tooltip';
14
15
  import IconButton from '../../IconButton';
15
16
  import { AnnotisedMessage } from '../Chat/ChatBody';
16
17
  import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
18
+ import { useIsPeerBlacklisted } from '../hooks/useChatBlacklist';
17
19
  import { CHAT_MESSAGE_LIMIT, formatTime } from '../Chat/utils';
18
20
  import { SESSION_STORE_KEY } from '../../common/constants';
19
21
 
@@ -43,10 +45,29 @@ export const PIPChat = () => {
43
45
  const blacklistedMessageIDSet = new Set(blacklistedMessageIDs || []);
44
46
  return messages?.filter(message => message.type === 'chat' && !blacklistedMessageIDSet.has(message.id)) || [];
45
47
  }, [blacklistedMessageIDs, messages]);
48
+ const { enabled: isChatEnabled = true, updatedBy: chatStateUpdatedBy = '' } =
49
+ useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_STATE)) || {};
50
+ const isLocalPeerBlacklisted = useIsPeerBlacklisted({ local: true });
46
51
  const { elements } = useRoomLayoutConferencingScreen();
47
52
  const message_placeholder = elements?.chat?.message_placeholder || 'Send a message';
48
53
  const canSendChatMessages = !!elements?.chat?.public_chat_enabled || !!elements?.chat?.roles_whitelist?.length;
49
54
 
55
+ const getChatStatus = useCallback(() => {
56
+ if (isLocalPeerBlacklisted) return "You've been blocked from sending messages";
57
+ if (!isChatEnabled)
58
+ return `Chat has been paused by ${
59
+ chatStateUpdatedBy.peerId === localPeerID ? 'you' : chatStateUpdatedBy?.userName
60
+ }`;
61
+ return message_placeholder;
62
+ }, [
63
+ chatStateUpdatedBy.peerId,
64
+ chatStateUpdatedBy?.userName,
65
+ isChatEnabled,
66
+ isLocalPeerBlacklisted,
67
+ localPeerID,
68
+ message_placeholder,
69
+ ]);
70
+
50
71
  return (
51
72
  <div style={{ height: '100%' }}>
52
73
  <Box
@@ -55,7 +76,7 @@ export const PIPChat = () => {
55
76
  bg: '$surface_dim',
56
77
  overflowY: 'auto',
57
78
  // Subtracting height of footer
58
- h: canSendChatMessages ? 'calc(100% - 90px)' : '100%',
79
+ h: canSendChatMessages ? 'calc(100% - 87px)' : '100%',
59
80
  position: 'relative',
60
81
  }}
61
82
  >
@@ -108,16 +129,11 @@ export const PIPChat = () => {
108
129
  </Text>
109
130
  </Tooltip>
110
131
  )}
111
- {message.recipientRoles ? (
112
- <Text as="span" variant="sub2" css={{ color: '$on_surface_high', fontWeight: '$semiBold' }}>
113
- to {message.recipientRoles} (Group)
114
- </Text>
115
- ) : null}
116
- {message.recipientPeer ? (
117
- <Text as="span" variant="sub2" css={{ color: '$on_surface_high', fontWeight: '$semiBold' }}>
118
- (DM)
119
- </Text>
120
- ) : null}
132
+ <MessageTitle
133
+ localPeerID={localPeerID}
134
+ recipientPeer={message.recipientPeer}
135
+ recipientRoles={message.recipientRoles}
136
+ />
121
137
  </Flex>
122
138
 
123
139
  <Text
@@ -193,13 +209,16 @@ export const PIPChat = () => {
193
209
  <TextArea
194
210
  id="chat-input"
195
211
  maxLength={CHAT_MESSAGE_LIMIT}
196
- style={{ border: 'none', resize: 'none' }}
212
+ disabled={!isChatEnabled || isLocalPeerBlacklisted}
213
+ rows={1}
197
214
  css={{
198
215
  w: '100%',
199
216
  c: '$on_surface_high',
200
- padding: '0.25rem !important',
217
+ p: '0.75rem 0.75rem !important',
218
+ border: 'none',
219
+ resize: 'none',
201
220
  }}
202
- placeholder={message_placeholder}
221
+ placeholder={getChatStatus()}
203
222
  required
204
223
  autoComplete="off"
205
224
  aria-autocomplete="none"
@@ -207,6 +226,8 @@ export const PIPChat = () => {
207
226
 
208
227
  <IconButton
209
228
  id="send-btn"
229
+ disabled={!isChatEnabled || isLocalPeerBlacklisted}
230
+ title={getChatStatus()}
210
231
  css={{
211
232
  ml: 'auto',
212
233
  height: 'max-content',
@@ -223,3 +244,30 @@ export const PIPChat = () => {
223
244
  </div>
224
245
  );
225
246
  };
247
+
248
+ const MessageTitle = ({
249
+ recipientPeer,
250
+ recipientRoles,
251
+ localPeerID,
252
+ }: {
253
+ recipientPeer?: string;
254
+ recipientRoles?: string[];
255
+ localPeerID: string;
256
+ }) => {
257
+ const peerName = useHMSStore(selectPeerNameByID(recipientPeer));
258
+
259
+ return (
260
+ <>
261
+ {recipientRoles ? (
262
+ <Text as="span" variant="sub2" css={{ color: '$on_surface_high', fontWeight: '$semiBold' }}>
263
+ to {recipientRoles} (Group)
264
+ </Text>
265
+ ) : null}
266
+ {recipientPeer ? (
267
+ <Text as="span" variant="sub2" css={{ color: '$on_surface_high', fontWeight: '$semiBold' }}>
268
+ to {recipientPeer === localPeerID ? 'You' : peerName} (DM)
269
+ </Text>
270
+ ) : null}
271
+ </>
272
+ );
273
+ };
@@ -8,6 +8,6 @@ type PIPWindowProps = {
8
8
 
9
9
  export const PIPWindow = ({ pipWindow, children }: PIPWindowProps) => {
10
10
  pipWindow.document.body.style.margin = '0';
11
- pipWindow.document.body.style.overflowX = 'hidden';
11
+ pipWindow.document.body.style.overflow = 'clip';
12
12
  return createPortal(children, pipWindow.document.body);
13
13
  };
@@ -25,6 +25,8 @@ export const usePIPChat = () => {
25
25
  const pipChatInput = pipWindow.document.getElementById('chat-input') as HTMLTextAreaElement;
26
26
  const marker = pipWindow.document.getElementById('marker');
27
27
 
28
+ marker?.scrollIntoView({ block: 'end' });
29
+
28
30
  const observer = new IntersectionObserver(
29
31
  entries => {
30
32
  entries.forEach(entry => {
@@ -8,6 +8,7 @@ import {
8
8
  selectRoomState,
9
9
  selectVideoTrackByID,
10
10
  useAVToggle,
11
+ useAwayNotifications,
11
12
  useHMSStore,
12
13
  useParticipants,
13
14
  usePreviewJoin,
@@ -100,6 +101,7 @@ const PreviewJoin = ({
100
101
  },
101
102
  asRole,
102
103
  });
104
+ const { requestPermission } = useAwayNotifications();
103
105
  const roomState = useHMSStore(selectRoomState);
104
106
  const savePreferenceAndJoin = useCallback(() => {
105
107
  setPreviewPreference({
@@ -115,7 +117,7 @@ const PreviewJoin = ({
115
117
  if (skipPreview) {
116
118
  savePreferenceAndJoin();
117
119
  } else {
118
- preview();
120
+ preview().then(() => requestPermission());
119
121
  }
120
122
  }
121
123
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -111,7 +111,7 @@ export const ToastConfig = {
111
111
  const count = new Set(notifications.map(notification => notification.data?.id)).size;
112
112
  return {
113
113
  title: `${notifications[notifications.length - 1].data?.name} ${
114
- count > 1 ? `${count} and others` : ''
114
+ count > 1 ? `and ${count} others` : ''
115
115
  } raised hand`,
116
116
  icon: <HandIcon />,
117
117
  };
@@ -129,7 +129,7 @@ export const ToastConfig = {
129
129
  const count = new Set(notifications.map(notification => notification.data?.id)).size;
130
130
  return {
131
131
  title: `${notifications[notifications.length - 1].data?.name} ${
132
- count > 1 ? `${count} and others` : ''
132
+ count > 1 ? `and ${count} others` : ''
133
133
  } raised hand`,
134
134
  icon: <HandIcon />,
135
135
  action: <HandRaiseAction isSingleHandRaise={false} />,