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

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. package/dist/{HLSView-ERPYXVOY.js → HLSView-CTSCXXFA.js} +2 -2
  2. package/dist/{HLSView-T2PXKR6I.css → HLSView-Y2452RZU.css} +3 -3
  3. package/dist/{HLSView-T2PXKR6I.css.map → HLSView-Y2452RZU.css.map} +1 -1
  4. package/dist/Prebuilt/components/IconButtonWithOptions/IconButtonWithOptions.d.ts +2 -1
  5. package/dist/{chunk-NKC36NL4.js → chunk-I7DPNCT4.js} +1066 -968
  6. package/dist/chunk-I7DPNCT4.js.map +7 -0
  7. package/dist/index.cjs.css +2 -2
  8. package/dist/index.cjs.css.map +1 -1
  9. package/dist/index.cjs.js +1480 -1384
  10. package/dist/index.cjs.js.map +4 -4
  11. package/dist/index.css +2 -2
  12. package/dist/index.css.map +1 -1
  13. package/dist/index.js +1 -1
  14. package/dist/meta.cjs.json +110 -80
  15. package/dist/meta.esbuild.json +123 -93
  16. package/package.json +7 -7
  17. package/src/Prebuilt/App.tsx +21 -18
  18. package/src/Prebuilt/components/AudioVideoToggle.tsx +6 -12
  19. package/src/Prebuilt/components/ConferenceScreen.tsx +13 -1
  20. package/src/Prebuilt/components/Footer/ParticipantList.tsx +1 -4
  21. package/src/Prebuilt/components/IconButtonWithOptions/IconButtonWithOptions.tsx +9 -1
  22. package/src/Prebuilt/components/MoreSettings/MoreSettings.tsx +1 -4
  23. package/src/Prebuilt/components/MoreSettings/SplitComponents/DesktopOptions.tsx +12 -2
  24. package/src/Prebuilt/components/Notifications/HandRaisedNotifications.tsx +33 -0
  25. package/src/Prebuilt/components/Notifications/Notifications.tsx +3 -1
  26. package/src/Prebuilt/components/PIP/PIPChat.tsx +62 -14
  27. package/src/Prebuilt/components/PIP/PIPWindow.tsx +1 -1
  28. package/src/Prebuilt/components/PIP/usePIPChat.tsx +2 -0
  29. package/src/Prebuilt/components/Preview/PreviewJoin.tsx +3 -1
  30. package/src/Prebuilt/components/Toast/ToastConfig.jsx +2 -2
  31. package/src/Prebuilt/components/VirtualBackground/VBOption.tsx +1 -1
  32. package/dist/chunk-NKC36NL4.js.map +0 -7
  33. /package/dist/{HLSView-ERPYXVOY.js.map → HLSView-CTSCXXFA.js.map} +0 -0
@@ -23,6 +23,7 @@ import { KeyboardHandler } from './components/Input/KeyboardInputManager';
23
23
  import { LeaveScreen } from './components/LeaveScreen';
24
24
  import { MwebLandscapePrompt } from './components/MwebLandscapePrompt';
25
25
  import { Notifications } from './components/Notifications';
26
+ import { PIPProvider } from './components/PIP/PIPProvider';
26
27
  import { PreviewScreen } from './components/Preview/PreviewScreen';
27
28
  // @ts-ignore: No implicit Any
28
29
  import { ToastContainer } from './components/Toast/ToastContainer';
@@ -218,24 +219,26 @@ export const HMSPrebuilt = React.forwardRef<HMSPrebuiltRefType, HMSPrebuiltProps
218
219
  },
219
220
  }}
220
221
  >
221
- <Init />
222
- <DialogContainerProvider dialogContainerSelector={containerSelector}>
223
- <Box
224
- className={DEFAULT_PORTAL_CONTAINER.slice(1)} // Skips the '.' in the selector
225
- css={{
226
- bg: '$background_dim',
227
- size: '100%',
228
- lineHeight: '1.5',
229
- '-webkit-text-size-adjust': '100%',
230
- position: 'relative',
231
- }}
232
- >
233
- <AppRoutes
234
- authTokenByRoomCodeEndpoint={tokenByRoomCodeEndpoint}
235
- defaultAuthToken={authToken}
236
- />
237
- </Box>
238
- </DialogContainerProvider>
222
+ <PIPProvider>
223
+ <Init />
224
+ <DialogContainerProvider dialogContainerSelector={containerSelector}>
225
+ <Box
226
+ className={DEFAULT_PORTAL_CONTAINER.slice(1)} // Skips the '.' in the selector
227
+ css={{
228
+ bg: '$background_dim',
229
+ size: '100%',
230
+ lineHeight: '1.5',
231
+ '-webkit-text-size-adjust': '100%',
232
+ position: 'relative',
233
+ }}
234
+ >
235
+ <AppRoutes
236
+ authTokenByRoomCodeEndpoint={tokenByRoomCodeEndpoint}
237
+ defaultAuthToken={authToken}
238
+ />
239
+ </Box>
240
+ </DialogContainerProvider>
241
+ </PIPProvider>
239
242
  </HMSThemeProvider>
240
243
  );
241
244
  }}
@@ -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%' };
@@ -310,10 +310,9 @@ export const AudioVideoToggle = ({ hideOptions = false }: { hideOptions?: boolea
310
310
  disabled={!toggleAudio}
311
311
  hideOptions={hideOptions || !hasAudioDevices}
312
312
  onDisabledClick={toggleAudio}
313
+ testid="audio_toggle_btn"
313
314
  tooltipMessage={`Turn ${isLocalAudioEnabled ? 'off' : 'on'} audio (${isMacOS ? '⌘' : 'ctrl'} + d)`}
314
- icon={
315
- !isLocalAudioEnabled ? <MicOffIcon data-testid="audio_off_btn" /> : <MicOnIcon data-testid="audio_on_btn" />
316
- }
315
+ icon={!isLocalAudioEnabled ? <MicOffIcon /> : <MicOnIcon />}
317
316
  active={isLocalAudioEnabled}
318
317
  onClick={toggleAudio}
319
318
  key="toggleAudio"
@@ -353,13 +352,8 @@ export const AudioVideoToggle = ({ hideOptions = false }: { hideOptions?: boolea
353
352
  hideOptions={hideOptions || !hasVideoDevices}
354
353
  onDisabledClick={toggleVideo}
355
354
  tooltipMessage={`Turn ${isLocalVideoEnabled ? 'off' : 'on'} video (${isMacOS ? '⌘' : 'ctrl'} + e)`}
356
- icon={
357
- !isLocalVideoEnabled ? (
358
- <VideoOffIcon data-testid="video_off_btn" />
359
- ) : (
360
- <VideoOnIcon data-testid="video_on_btn" />
361
- )
362
- }
355
+ testid="video_toggle_btn"
356
+ icon={!isLocalVideoEnabled ? <VideoOffIcon /> : <VideoOnIcon />}
363
357
  key="toggleVideo"
364
358
  active={isLocalVideoEnabled}
365
359
  onClick={toggleVideo}
@@ -372,7 +366,7 @@ export const AudioVideoToggle = ({ hideOptions = false }: { hideOptions?: boolea
372
366
  </IconButtonWithOptions>
373
367
  ) : null}
374
368
 
375
- {localVideoTrack?.facingMode && roomState === HMSRoomState.Preview ? (
369
+ {localVideoTrack?.facingMode && roomState === HMSRoomState.Preview && (isIOS || isAndroid) ? (
376
370
  <Tooltip title="Switch Camera" key="switchCamera">
377
371
  <IconButton
378
372
  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>
@@ -90,6 +90,7 @@ export const IconButtonWithOptions = ({
90
90
  onDisabledClick = () => {
91
91
  return;
92
92
  },
93
+ testid = '',
93
94
  tooltipMessage = '',
94
95
  icon,
95
96
  children,
@@ -103,6 +104,7 @@ export const IconButtonWithOptions = ({
103
104
  onDisabledClick: () => void;
104
105
  icon: React.ReactNode;
105
106
  children: React.ReactNode;
107
+ testid?: string;
106
108
  hideOptions?: boolean;
107
109
  active: boolean;
108
110
  disabled?: boolean;
@@ -111,7 +113,13 @@ export const IconButtonWithOptions = ({
111
113
  const commonProps = { disabled, active };
112
114
  return (
113
115
  <Flex>
114
- <IconSection {...commonProps} onClick={onClick} hideOptions={hideOptions} className="__cancel-drag-event">
116
+ <IconSection
117
+ data-testid={testid}
118
+ {...commonProps}
119
+ onClick={onClick}
120
+ hideOptions={hideOptions}
121
+ className="__cancel-drag-event"
122
+ >
115
123
  <Tooltip disabled={!tooltipMessage} title={tooltipMessage}>
116
124
  <Icon {...commonProps}>{icon}</Icon>
117
125
  </Tooltip>
@@ -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} />,
@@ -26,7 +26,7 @@ const Root = ({
26
26
  border: `4px solid ${isActive ? '$primary_default' : '$surface_dim'}`,
27
27
  cursor: 'pointer',
28
28
  '&:hover': { border: '4px solid $primary_dim' },
29
- ...(mediaURL ? { height: '$20', backgroundImage: `url(${mediaURL})`, backgroundSize: 'cover' } : {}),
29
+ ...(mediaURL ? { height: '$20', backgroundImage: `url("${mediaURL}")`, backgroundSize: 'cover' } : {}),
30
30
  }}
31
31
  onClick={async () => {
32
32
  await onClick?.();