@100mslive/roomkit-react 0.3.13-alpha.2 → 0.3.13-alpha.4

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.
@@ -5,6 +5,7 @@ import {
5
5
  HMSRoomState,
6
6
  selectIsLocalAudioPluginPresent,
7
7
  selectLocalAudioTrackID,
8
+ selectLocalPeer,
8
9
  selectLocalVideoTrackID,
9
10
  selectRoom,
10
11
  selectRoomState,
@@ -27,9 +28,12 @@ import {
27
28
  } from '@100mslive/react-icons';
28
29
  import { IconButtonWithOptions } from './IconButtonWithOptions/IconButtonWithOptions';
29
30
  // @ts-ignore: No implicit Any
31
+ import { ActionTile } from './MoreSettings/ActionTile';
32
+ // @ts-ignore: No implicit Any
30
33
  import SettingsModal from './Settings/SettingsModal';
31
34
  // @ts-ignore: No implicit Any
32
35
  import { ToastManager } from './Toast/ToastManager';
36
+ import { AudioLevel } from '../../AudioLevel';
33
37
  import { Dropdown } from '../../Dropdown';
34
38
  import { Box, Flex } from '../../Layout';
35
39
  import { Switch } from '../../Switch';
@@ -38,10 +42,11 @@ import { Tooltip } from '../../Tooltip';
38
42
  import IconButton from '../IconButton';
39
43
  import { useRoomLayoutConferencingScreen } from '../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
40
44
  // @ts-ignore: No implicit Any
41
- import { useIsNoiseCancellationEnabled, useSetNoiseCancellationEnabled } from './AppData/useUISettings';
45
+ import { useIsNoiseCancellationEnabled, useSetNoiseCancellation } from './AppData/useUISettings';
42
46
  import { useAudioOutputTest } from './hooks/useAudioOutputTest';
43
47
  import { isMacOS, TEST_AUDIO_URL } from '../common/constants';
44
48
 
49
+ const krispPlugin = new HMSKrispPlugin();
45
50
  // const optionsCSS = { fontWeight: '$semiBold', color: '$on_surface_high', w: '100%' };
46
51
 
47
52
  export const Options = ({
@@ -96,32 +101,82 @@ const OptionLabel = ({ children, icon }: { children: React.ReactNode; icon: Reac
96
101
  );
97
102
  };
98
103
 
99
- const plugin = new HMSKrispPlugin();
100
- const NoiseCancellation = () => {
101
- const localPeerAudioTrackID = useHMSStore(selectLocalAudioTrackID);
102
- const [enabled, setEnabled] = useSetNoiseCancellationEnabled();
103
- const isPluginAdded = useHMSStore(selectIsLocalAudioPluginPresent(plugin.getName()));
104
- const [inProgress, setInProgress] = useState(false);
104
+ const useNoiseCancellationWithPlugin = () => {
105
105
  const actions = useHMSActions();
106
- const room = useHMSStore(selectRoom);
106
+ const [inProgress, setInProgress] = useState(false);
107
+ const [, setNoiseCancellationEnabled] = useSetNoiseCancellation();
108
+ const setNoiseCancellationWithPlugin = async (enabled: boolean) => {
109
+ if (inProgress) {
110
+ return;
111
+ }
112
+ setInProgress(true);
113
+ if (enabled) {
114
+ await actions.addPluginToAudioTrack(krispPlugin);
115
+ } else {
116
+ await actions.removePluginFromAudioTrack(krispPlugin);
117
+ }
118
+ setNoiseCancellationEnabled(enabled);
119
+ setInProgress(false);
120
+ };
121
+ return {
122
+ setNoiseCancellationWithPlugin,
123
+ inProgress,
124
+ };
125
+ };
107
126
 
108
- useEffect(() => {
109
- (async () => {
110
- setInProgress(true);
111
- if (enabled && !isPluginAdded) {
112
- await actions.addPluginToAudioTrack(plugin);
113
- }
114
- if (!enabled && isPluginAdded) {
115
- await actions.removePluginFromAudioTrack(plugin);
116
- }
117
- setInProgress(false);
118
- })();
119
- }, [actions, enabled, isPluginAdded]);
127
+ export const NoiseCancellation = ({
128
+ actionTile,
129
+ iconOnly,
130
+ setOpenOptionsSheet,
131
+ }: {
132
+ setOpenOptionsSheet?: (value: boolean) => void;
133
+ iconOnly?: boolean;
134
+ actionTile?: boolean;
135
+ }) => {
136
+ const localPeerAudioTrackID = useHMSStore(selectLocalAudioTrackID);
137
+ const isNoiseCancellationEnabled = useIsNoiseCancellationEnabled();
138
+ const { setNoiseCancellationWithPlugin, inProgress } = useNoiseCancellationWithPlugin();
139
+ const room = useHMSStore(selectRoom);
140
+ const isKrispPluginAdded = useHMSStore(selectIsLocalAudioPluginPresent(krispPlugin.getName()));
120
141
 
121
- if (!plugin.isSupported() || !room.isNoiseCancellationEnabled || !localPeerAudioTrackID) {
142
+ if (!krispPlugin.isSupported() || !room.isNoiseCancellationEnabled || !localPeerAudioTrackID) {
122
143
  return null;
123
144
  }
124
145
 
146
+ if (actionTile) {
147
+ return (
148
+ <ActionTile.Root
149
+ active={isNoiseCancellationEnabled && isKrispPluginAdded}
150
+ disable={inProgress}
151
+ onClick={async () => {
152
+ await setNoiseCancellationWithPlugin(!isNoiseCancellationEnabled);
153
+ setOpenOptionsSheet?.(false);
154
+ }}
155
+ >
156
+ <AudioLevelIcon />
157
+ <ActionTile.Title>{isNoiseCancellationEnabled ? 'Noise Reduced' : 'Reduce Noise'}</ActionTile.Title>
158
+ </ActionTile.Root>
159
+ );
160
+ }
161
+
162
+ if (iconOnly) {
163
+ return (
164
+ <Tooltip title={isNoiseCancellationEnabled ? 'Noise Reduced' : 'Reduce Noise'}>
165
+ <IconButton
166
+ onClick={async () => {
167
+ await setNoiseCancellationWithPlugin(!isNoiseCancellationEnabled);
168
+ }}
169
+ disabled={inProgress}
170
+ css={{
171
+ bg: isNoiseCancellationEnabled && isKrispPluginAdded ? '$surface_brighter' : '$background_dim',
172
+ borderColor: isNoiseCancellationEnabled && isKrispPluginAdded ? '$border_brighter' : '$border_bright',
173
+ }}
174
+ >
175
+ <AudioLevelIcon />
176
+ </IconButton>
177
+ </Tooltip>
178
+ );
179
+ }
125
180
  return (
126
181
  <>
127
182
  <Dropdown.ItemSeparator css={{ mx: 0 }} />
@@ -132,9 +187,9 @@ const NoiseCancellation = () => {
132
187
  fontSize: '$xs',
133
188
  justifyContent: 'space-between',
134
189
  }}
135
- onClick={e => {
190
+ onClick={async e => {
136
191
  e.preventDefault();
137
- setEnabled(!enabled);
192
+ await setNoiseCancellationWithPlugin(!isNoiseCancellationEnabled);
138
193
  }}
139
194
  >
140
195
  <Text css={{ display: 'flex', alignItems: 'center', gap: '$2', fontSize: '$xs', '& svg': { size: '$8' } }}>
@@ -143,11 +198,11 @@ const NoiseCancellation = () => {
143
198
  </Text>
144
199
  <Switch
145
200
  id="noise_cancellation"
146
- checked={enabled && isPluginAdded}
201
+ checked={isNoiseCancellationEnabled && isKrispPluginAdded}
147
202
  disabled={inProgress}
148
203
  onClick={e => e.stopPropagation()}
149
- onCheckedChange={value => {
150
- setEnabled(value);
204
+ onCheckedChange={async value => {
205
+ await setNoiseCancellationWithPlugin(value);
151
206
  }}
152
207
  />
153
208
  </Dropdown.Item>
@@ -204,10 +259,10 @@ const AudioSettings = ({ onClick }: { onClick: () => void }) => {
204
259
  </>
205
260
  );
206
261
  };
207
-
208
- export const AudioVideoToggle = ({ hideOptions = false }) => {
262
+ export const AudioVideoToggle = ({ hideOptions = false }: { hideOptions?: boolean }) => {
209
263
  const { allDevices, selectedDeviceIDs, updateDevice } = useDevices();
210
264
  const { videoInput, audioInput, audioOutput } = allDevices;
265
+ const localPeer = useHMSStore(selectLocalPeer);
211
266
  const { isLocalVideoEnabled, isLocalAudioEnabled, toggleAudio, toggleVideo } = useAVToggle();
212
267
  const actions = useHMSActions();
213
268
  const videoTrackId = useHMSStore(selectLocalVideoTrackID);
@@ -218,15 +273,15 @@ export const AudioVideoToggle = ({ hideOptions = false }) => {
218
273
  const shouldShowAudioOutput = 'setSinkId' in HTMLMediaElement.prototype && Number(audioOutput?.length) > 0;
219
274
  const { screenType } = useRoomLayoutConferencingScreen();
220
275
  const [showSettings, setShowSettings] = useState(false);
221
- const isPluginAdded = useHMSStore(selectIsLocalAudioPluginPresent(plugin.getName()));
276
+ const isKrispPluginAdded = useHMSStore(selectIsLocalAudioPluginPresent(krispPlugin.getName()));
222
277
  const isNoiseCancellationEnabled = useIsNoiseCancellationEnabled();
278
+ const { setNoiseCancellationWithPlugin, inProgress } = useNoiseCancellationWithPlugin();
279
+ const showMuteIcon = !isLocalAudioEnabled || !toggleAudio;
223
280
 
224
281
  useEffect(() => {
225
282
  (async () => {
226
- if (isNoiseCancellationEnabled && !isPluginAdded) {
227
- await actions.addPluginToAudioTrack(plugin);
228
- }
229
- if (isNoiseCancellationEnabled && isPluginAdded) {
283
+ if (isNoiseCancellationEnabled && !isKrispPluginAdded && !inProgress && localPeer?.audioTrack) {
284
+ await setNoiseCancellationWithPlugin(true);
230
285
  ToastManager.addToast({
231
286
  title: `Noise Reduction Enabled`,
232
287
  variant: 'standard',
@@ -235,7 +290,8 @@ export const AudioVideoToggle = ({ hideOptions = false }) => {
235
290
  });
236
291
  }
237
292
  })();
238
- }, [actions, isNoiseCancellationEnabled, isPluginAdded]);
293
+ // eslint-disable-next-line react-hooks/exhaustive-deps
294
+ }, [isNoiseCancellationEnabled, localPeer?.audioTrack, inProgress]);
239
295
 
240
296
  if (!toggleAudio && !toggleVideo) {
241
297
  return null;
@@ -256,7 +312,10 @@ export const AudioVideoToggle = ({ hideOptions = false }) => {
256
312
  key="toggleAudio"
257
313
  >
258
314
  <Dropdown.Group>
259
- <OptionLabel icon={<MicOnIcon />}>{!shouldShowAudioOutput ? 'Audio' : 'Microphone'}</OptionLabel>
315
+ <OptionLabel icon={<MicOnIcon />}>
316
+ <Box css={{ flex: '1 1 0' }}>{!shouldShowAudioOutput ? 'Audio' : 'Microphone'}</Box>
317
+ {!showMuteIcon && <AudioLevel trackId={localPeer?.audioTrack} />}
318
+ </OptionLabel>
260
319
  <Options
261
320
  options={audioInput}
262
321
  selectedDeviceId={selectedDeviceIDs.audioInput}
@@ -18,7 +18,7 @@ export const CaptionIcon = () => {
18
18
  return null;
19
19
  }
20
20
  return (
21
- <Tooltip title={isCaption ? 'Disable caption' : 'Enable caption'}>
21
+ <Tooltip title={isCaption ? 'Hide closed captions' : 'Show closed captions'}>
22
22
  <IconButton data-testid="caption_btn" onClick={onClick}>
23
23
  {isCaption ? <ClosedCaptionIcon width="20" height="20px" /> : <OpenCaptionIcon width="20" height="20px" />}
24
24
  </IconButton>
@@ -111,7 +111,7 @@ export const IconButtonWithOptions = ({
111
111
  const commonProps = { disabled, active };
112
112
  return (
113
113
  <Flex>
114
- <IconSection {...commonProps} onClick={onClick} hideOptions={hideOptions}>
114
+ <IconSection {...commonProps} onClick={onClick} hideOptions={hideOptions} className="__cancel-drag-event">
115
115
  <Tooltip disabled={!tooltipMessage} title={tooltipMessage}>
116
116
  <Icon {...commonProps}>{icon}</Icon>
117
117
  </Tooltip>
@@ -35,6 +35,7 @@ import { Box, Loading, Tooltip } from '../../../..';
35
35
  import { Sheet } from '../../../../Sheet';
36
36
  // @ts-ignore: No implicit any
37
37
  import IconButton from '../../../IconButton';
38
+ import { NoiseCancellation } from '../../AudioVideoToggle';
38
39
  // @ts-ignore: No implicit any
39
40
  import { EmojiReaction } from '../../EmojiReaction';
40
41
  // @ts-ignore: No implicit any
@@ -54,8 +55,11 @@ import { useRoomLayoutHeader } from '../../../provider/roomLayoutProvider/hooks/
54
55
  import { useSheetToggle } from '../../AppData/useSheet';
55
56
  // @ts-ignore: No implicit any
56
57
  import { usePollViewToggle, useSidepaneToggle } from '../../AppData/useSidepane';
57
- // @ts-ignore: No implicit Any
58
- import { useSetIsCaptionEnabled, useShowPolls } from '../../AppData/useUISettings';
58
+ import {
59
+ useSetIsCaptionEnabled,
60
+ useShowPolls,
61
+ // @ts-ignore: No implicit Any
62
+ } from '../../AppData/useUISettings';
59
63
  // @ts-ignore: No implicit any
60
64
  import { useDropdownList } from '../../hooks/useDropdownList';
61
65
  import { useMyMetadata } from '../../hooks/useMetadata';
@@ -197,6 +201,7 @@ export const MwebOptions = ({
197
201
  <ActionTile.Title>{isHandRaised ? 'Lower' : 'Raise'} Hand</ActionTile.Title>
198
202
  </ActionTile.Root>
199
203
  ) : null}
204
+ <NoiseCancellation setOpenOptionsSheet={setOpenOptionsSheet} actionTile />
200
205
  {isTranscriptionAllowed ? (
201
206
  <ActionTile.Root
202
207
  onClick={() => {
@@ -19,7 +19,7 @@ import { AudioLevel } from '../../../AudioLevel';
19
19
  import { useHMSPrebuiltContext } from '../../AppContext';
20
20
  import IconButton from '../../IconButton';
21
21
  import SidePane from '../../layouts/SidePane';
22
- import { AudioVideoToggle } from '../AudioVideoToggle';
22
+ import { AudioVideoToggle, NoiseCancellation } from '../AudioVideoToggle';
23
23
  import Chip from '../Chip';
24
24
  import TileConnection from '../Connection/TileConnection';
25
25
  import FullPageProgress from '../FullPageProgress';
@@ -30,8 +30,11 @@ import SettingsModal from '../Settings/SettingsModal';
30
30
  import { VBToggle } from '../VirtualBackground/VBToggle';
31
31
  import PreviewForm from './PreviewForm';
32
32
  import { useRoomLayoutPreviewScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
33
- // @ts-ignore: No implicit Any
34
- import { useAuthToken, useUISettings } from '../AppData/useUISettings';
33
+ import {
34
+ useAuthToken,
35
+ useUISettings,
36
+ // @ts-ignore: No implicit Any
37
+ } from '../AppData/useUISettings';
35
38
  // @ts-ignore: No implicit Any
36
39
  import { defaultPreviewPreference, UserPreferencesKeys, useUserPreferences } from '../hooks/useUserPreferences';
37
40
  // @ts-ignore: No implicit Any
@@ -251,7 +254,6 @@ export const PreviewTile = ({ name, error }: { name: string; error?: boolean })
251
254
 
252
255
  export const PreviewControls = ({ hideSettings, vbEnabled }: { hideSettings: boolean; vbEnabled: boolean }) => {
253
256
  const isMobile = useMedia(cssConfig.media.md);
254
-
255
257
  return (
256
258
  <Flex
257
259
  justify={hideSettings && isMobile ? 'center' : 'between'}
@@ -264,7 +266,10 @@ export const PreviewControls = ({ hideSettings, vbEnabled }: { hideSettings: boo
264
266
  <AudioVideoToggle />
265
267
  {vbEnabled ? <VBToggle /> : null}
266
268
  </Flex>
267
- {!hideSettings ? <PreviewSettings /> : null}
269
+ <Flex align="center" gap="1">
270
+ {isMobile && <NoiseCancellation iconOnly />}
271
+ {!hideSettings ? <PreviewSettings /> : null}
272
+ </Flex>
268
273
  </Flex>
269
274
  );
270
275
  };
@@ -1,5 +1,6 @@
1
1
  import React, { Suspense, useEffect, useState } from 'react';
2
2
  import { ControlPosition } from 'react-draggable';
3
+ import { useMedia } from 'react-use';
3
4
  import {
4
5
  ConferencingScreen,
5
6
  DefaultConferencingScreen_Elements,
@@ -17,6 +18,7 @@ import { PeopleAddIcon, ShareScreenIcon } from '@100mslive/react-icons';
17
18
  import FullPageProgress from '../components/FullPageProgress';
18
19
  import { GridLayout } from '../components/VideoLayouts/GridLayout';
19
20
  import { Box, Flex } from '../../Layout';
21
+ import { config } from '../../Theme';
20
22
  // @ts-ignore: No implicit Any
21
23
  import { EmbedView } from './EmbedView';
22
24
  // @ts-ignore: No implicit Any
@@ -51,7 +53,8 @@ export const VideoStreamingSection = ({
51
53
  const pdfAnnotatorActive = usePDFConfig();
52
54
  const isMobileHLSStream = useMobileHLSStream();
53
55
  const isLandscapeHLSStream = useLandscapeHLSStream();
54
- const [captionPosition, setCaptionPosition] = useState<ControlPosition>({ x: -200, y: 0 });
56
+ const isMobile = useMedia(config.media.md);
57
+ const [captionPosition, setCaptionPosition] = useState<ControlPosition>({ x: isMobile ? 0 : -200, y: 0 });
55
58
  useCloseScreenshareWhiteboard();
56
59
 
57
60
  const { isNotAllowedToPublish, isScreenOnlyPublishParams, hasSubscribedRolePublishing } = useWaitingRoomInfo();