@100mslive/roomkit-react 0.2.6 → 0.2.7-alpha.1

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 (28) hide show
  1. package/README.md +30 -33
  2. package/dist/{HLSView-A5DVXKEU.js → HLSView-4B5MUDFR.js} +2 -2
  3. package/dist/Prebuilt/common/constants.d.ts +4 -3
  4. package/dist/Prebuilt/components/AudioVideoToggle.d.ts +9 -0
  5. package/dist/Prebuilt/components/IconButtonWithOptions/IconButtonWithOptions.d.ts +11 -0
  6. package/dist/Prebuilt/components/hooks/useAudioOutputTest.d.ts +8 -0
  7. package/dist/{chunk-KUVM2TEZ.js → chunk-KST24BRA.js} +4227 -3807
  8. package/dist/chunk-KST24BRA.js.map +7 -0
  9. package/dist/index.cjs.js +5518 -5092
  10. package/dist/index.cjs.js.map +4 -4
  11. package/dist/index.js +1 -1
  12. package/dist/meta.cjs.json +666 -597
  13. package/dist/meta.esbuild.json +673 -604
  14. package/package.json +7 -6
  15. package/src/Prebuilt/common/constants.ts +7 -4
  16. package/src/Prebuilt/components/AppData/useUISettings.js +1 -1
  17. package/src/Prebuilt/components/AudioVideoToggle.tsx +308 -0
  18. package/src/Prebuilt/components/Footer/RoleOptions.tsx +1 -0
  19. package/src/Prebuilt/components/IconButtonWithOptions/IconButtonWithOptions.tsx +159 -0
  20. package/src/Prebuilt/components/Leave/DesktopLeaveRoom.tsx +0 -2
  21. package/src/Prebuilt/components/Notifications/HandRaisedNotifications.tsx +13 -2
  22. package/src/Prebuilt/components/Notifications/Notifications.tsx +1 -18
  23. package/src/Prebuilt/components/Settings/DeviceSettings.jsx +10 -17
  24. package/src/Prebuilt/components/hooks/useAudioOutputTest.tsx +20 -0
  25. package/dist/chunk-KUVM2TEZ.js.map +0 -7
  26. package/src/Prebuilt/components/AudioVideoToggle.jsx +0 -171
  27. package/src/Prebuilt/components/IconButtonWithOptions/IconButtonWithOptions.jsx +0 -121
  28. /package/dist/{HLSView-A5DVXKEU.js.map → HLSView-4B5MUDFR.js.map} +0 -0
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "prebuilt",
11
11
  "roomkit"
12
12
  ],
13
- "version": "0.2.6",
13
+ "version": "0.2.7-alpha.1",
14
14
  "author": "100ms",
15
15
  "license": "MIT",
16
16
  "repository": {
@@ -82,10 +82,11 @@
82
82
  "react": ">=17.0.2 <19.0.0"
83
83
  },
84
84
  "dependencies": {
85
- "@100mslive/hls-player": "0.2.6",
86
- "@100mslive/hms-virtual-background": "1.12.6",
87
- "@100mslive/react-icons": "0.9.6",
88
- "@100mslive/react-sdk": "0.9.6",
85
+ "@100mslive/hls-player": "0.2.7-alpha.1",
86
+ "@100mslive/hms-noise-cancellation": "0.0.0-alpha.1",
87
+ "@100mslive/hms-virtual-background": "1.12.7-alpha.1",
88
+ "@100mslive/react-icons": "0.9.7-alpha.1",
89
+ "@100mslive/react-sdk": "0.9.7-alpha.1",
89
90
  "@100mslive/types-prebuilt": "0.12.6",
90
91
  "@emoji-mart/data": "^1.0.6",
91
92
  "@emoji-mart/react": "^1.0.1",
@@ -120,5 +121,5 @@
120
121
  "uuid": "^8.3.2",
121
122
  "worker-timers": "^7.0.40"
122
123
  },
123
- "gitHead": "928f9172a44fe6c47232ab84d2c991b04cd9f707"
124
+ "gitHead": "d9ed012d59612c39bc68dc427b5749405e4eeb38"
124
125
  }
@@ -92,11 +92,12 @@ export const HLS_TIMED_METADATA_DOC_URL =
92
92
 
93
93
  export const REMOTE_STOP_SCREENSHARE_TYPE = 'REMOTE_STOP_SCREENSHARE';
94
94
 
95
- export const isChrome = parsedUserAgent.getBrowser()?.name?.toLowerCase() === 'chrome';
96
- export const isFirefox = parsedUserAgent.getBrowser()?.name?.toLowerCase() === 'firefox';
97
- export const isSafari = parsedUserAgent.getBrowser()?.name?.toLowerCase() === 'safari';
95
+ // mweb could have browser name as "Mobile Chrome" or "Mobile Firefox"
96
+ export const isChrome = parsedUserAgent.getBrowser()?.name?.toLowerCase().includes('chrome');
97
+ export const isFirefox = parsedUserAgent.getBrowser()?.name?.toLowerCase().includes('firefox');
98
+ export const isSafari = parsedUserAgent.getBrowser()?.name?.toLowerCase().includes('safari');
98
99
  export const isIOS = parsedUserAgent.getOS()?.name?.toLowerCase() === 'ios';
99
- export const isMacOS = parsedUserAgent.getOS()?.name?.toLowerCase() === 'mac os';
100
+ export const isMacOS = parsedUserAgent.getOS()?.name?.toLowerCase() === 'macos';
100
101
  export const isAndroid = parsedUserAgent.getOS()?.name?.toLowerCase() === 'android';
101
102
  export const isIPadOS = false;
102
103
  // typeof navigator !== "undefined" &&
@@ -136,3 +137,5 @@ export enum QUESTION_TYPE {
136
137
  export const ROLE_CHANGE_DECLINED = 'role_change_declined';
137
138
 
138
139
  export const DEFAULT_PORTAL_CONTAINER = '.prebuilt-container';
140
+
141
+ export const TEST_AUDIO_URL = 'https://100ms.live/test-audio.wav';
@@ -90,7 +90,7 @@ export const usePinnedTrack = () => {
90
90
 
91
91
  export const useSubscribedNotifications = notificationKey => {
92
92
  const notificationPreference = useHMSStore(selectAppDataByPath(APP_DATA.subscribedNotifications, notificationKey));
93
- return notificationPreference;
93
+ return notificationPreference || {};
94
94
  };
95
95
 
96
96
  export const useIsNotificationDisabled = () => {
@@ -0,0 +1,308 @@
1
+ import React, { Fragment, useEffect, useState } from 'react';
2
+ import { HMSKrispPlugin } from '@100mslive/hms-noise-cancellation';
3
+ import {
4
+ DeviceType,
5
+ HMSRoomState,
6
+ selectIsLocalAudioPluginPresent,
7
+ selectLocalAudioTrackID,
8
+ selectLocalVideoTrackID,
9
+ selectRoomState,
10
+ selectVideoTrackByID,
11
+ useAVToggle,
12
+ useDevices,
13
+ useHMSActions,
14
+ useHMSStore,
15
+ } from '@100mslive/react-sdk';
16
+ import {
17
+ AudioLevelIcon,
18
+ CameraFlipIcon,
19
+ CheckIcon,
20
+ MicOffIcon,
21
+ MicOnIcon,
22
+ SettingsIcon,
23
+ SpeakerIcon,
24
+ VideoOffIcon,
25
+ VideoOnIcon,
26
+ } from '@100mslive/react-icons';
27
+ import { IconButtonWithOptions } from './IconButtonWithOptions/IconButtonWithOptions';
28
+ // @ts-ignore: No implicit Any
29
+ import SettingsModal from './Settings/SettingsModal';
30
+ // @ts-ignore: No implicit Any
31
+ import { ToastManager } from './Toast/ToastManager';
32
+ import { Dropdown } from '../../Dropdown';
33
+ import { Box, Flex } from '../../Layout';
34
+ import { Switch } from '../../Switch';
35
+ import { Text } from '../../Text';
36
+ import { Tooltip } from '../../Tooltip';
37
+ import IconButton from '../IconButton';
38
+ import { useRoomLayoutConferencingScreen } from '../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
39
+ import { useAudioOutputTest } from './hooks/useAudioOutputTest';
40
+ import { isMacOS, TEST_AUDIO_URL } from '../common/constants';
41
+
42
+ // const optionsCSS = { fontWeight: '$semiBold', color: '$on_surface_high', w: '100%' };
43
+
44
+ export const Options = ({
45
+ options,
46
+ selectedDeviceId,
47
+ onClick,
48
+ }: {
49
+ options?: Array<MediaDeviceInfo | InputDeviceInfo>;
50
+ selectedDeviceId?: string;
51
+ onClick: (deviceId: string) => Promise<void>;
52
+ }) => {
53
+ return (
54
+ <>
55
+ {options?.map(option => (
56
+ <Dropdown.Item
57
+ key={option.label}
58
+ css={{
59
+ backgroundColor: selectedDeviceId === option.deviceId ? '$surface_bright' : '$surface_dim',
60
+ p: '$4 $8',
61
+ h: '$15',
62
+ fontSize: '$xs',
63
+ justifyContent: 'space-between',
64
+ }}
65
+ onClick={() => {
66
+ onClick(option.deviceId);
67
+ }}
68
+ >
69
+ {option.label}
70
+ {selectedDeviceId === option.deviceId ? <CheckIcon width={16} height={16} /> : null}
71
+ </Dropdown.Item>
72
+ ))}
73
+ </>
74
+ );
75
+ };
76
+
77
+ const OptionLabel = ({ children, icon }: { children: React.ReactNode; icon: React.ReactNode }) => {
78
+ return (
79
+ <Dropdown.Label
80
+ css={{
81
+ h: '$16',
82
+ p: '$4 $8',
83
+ color: '$on_surface_medium',
84
+ bg: 'transparent',
85
+ fontSize: '$xs',
86
+ gap: '$4',
87
+ alignItems: 'center',
88
+ }}
89
+ >
90
+ <Flex css={{ alignItems: 'center', justifyContent: 'center', '& svg': { size: '$8' } }}>{icon}</Flex> {children}
91
+ </Dropdown.Label>
92
+ );
93
+ };
94
+
95
+ const plugin = new HMSKrispPlugin();
96
+ const NoiseCancellation = () => {
97
+ const localPeerAudioTrackID = useHMSStore(selectLocalAudioTrackID);
98
+ const isPluginAdded = useHMSStore(selectIsLocalAudioPluginPresent(plugin.getName()));
99
+ const [active, setActive] = useState(isPluginAdded);
100
+ const [inProgress, setInProgress] = useState(false);
101
+ const actions = useHMSActions();
102
+
103
+ useEffect(() => {
104
+ (async () => {
105
+ setInProgress(true);
106
+ if (active && !isPluginAdded) {
107
+ await actions.addPluginToAudioTrack(plugin);
108
+ }
109
+ if (!active && isPluginAdded) {
110
+ await actions.removePluginFromAudioTrack(plugin);
111
+ }
112
+ setInProgress(false);
113
+ })();
114
+ }, [actions, active, isPluginAdded]);
115
+
116
+ if (!plugin.isSupported() || !localPeerAudioTrackID) {
117
+ return null;
118
+ }
119
+
120
+ return (
121
+ <Dropdown.Item
122
+ css={{
123
+ p: '$4 $8',
124
+ h: '$15',
125
+ fontSize: '$xs',
126
+ justifyContent: 'space-between',
127
+ }}
128
+ onClick={e => {
129
+ e.preventDefault();
130
+ setActive(value => !value);
131
+ }}
132
+ >
133
+ <Text css={{ display: 'flex', alignItems: 'center', gap: '$2', fontSize: '$xs', '& svg': { size: '$8' } }}>
134
+ <AudioLevelIcon />
135
+ Reduce Noise
136
+ </Text>
137
+ <Switch
138
+ id="noise_cancellation"
139
+ checked={active}
140
+ disabled={inProgress}
141
+ onClick={e => e.stopPropagation()}
142
+ onCheckedChange={value => {
143
+ setActive(value);
144
+ }}
145
+ />
146
+ </Dropdown.Item>
147
+ );
148
+ };
149
+
150
+ const AudioOutputLabel = ({ deviceId }: { deviceId: string }) => {
151
+ const { playing, setPlaying, audioRef } = useAudioOutputTest({ deviceId });
152
+ return (
153
+ <OptionLabel icon={<SpeakerIcon />}>
154
+ <Box css={{ flex: '1 1 0' }}>Speakers</Box>
155
+ <Text
156
+ variant="xs"
157
+ css={{ color: '$primary_bright', '&:hover': { cursor: 'pointer' } }}
158
+ onClick={async () => {
159
+ if (playing) {
160
+ return;
161
+ }
162
+ await audioRef.current?.play();
163
+ }}
164
+ >
165
+ <audio
166
+ ref={audioRef}
167
+ src={TEST_AUDIO_URL}
168
+ onEnded={() => setPlaying(false)}
169
+ onPlay={() => setPlaying(true)}
170
+ style={{ display: 'none' }}
171
+ />
172
+ {playing ? 'Playing Sound...' : 'Play Test Sound'}
173
+ </Text>
174
+ </OptionLabel>
175
+ );
176
+ };
177
+
178
+ const AudioSettings = ({ onClick }: { onClick: () => void }) => {
179
+ return (
180
+ <>
181
+ <Dropdown.Item
182
+ css={{
183
+ backgroundColor: '$surface_dim',
184
+ p: '$4 $8',
185
+ h: '$15',
186
+ alignItems: 'center',
187
+ gap: '$2',
188
+ fontSize: '$xs',
189
+ '& svg': { size: '$8' },
190
+ }}
191
+ onClick={onClick}
192
+ >
193
+ <SettingsIcon /> Audio Settings
194
+ </Dropdown.Item>
195
+ </>
196
+ );
197
+ };
198
+
199
+ export const AudioVideoToggle = ({ hideOptions = false }) => {
200
+ const { allDevices, selectedDeviceIDs, updateDevice } = useDevices();
201
+ const { videoInput, audioInput, audioOutput } = allDevices;
202
+ const { isLocalVideoEnabled, isLocalAudioEnabled, toggleAudio, toggleVideo } = useAVToggle();
203
+ const actions = useHMSActions();
204
+ const videoTrackId = useHMSStore(selectLocalVideoTrackID);
205
+ const localVideoTrack = useHMSStore(selectVideoTrackByID(videoTrackId));
206
+ const roomState = useHMSStore(selectRoomState);
207
+ const hasAudioDevices = Number(audioInput?.length) > 0;
208
+ const hasVideoDevices = Number(videoInput?.length) > 0;
209
+ const shouldShowAudioOutput = 'setSinkId' in HTMLMediaElement.prototype && Number(audioOutput?.length) > 0;
210
+ const { screenType } = useRoomLayoutConferencingScreen();
211
+ const [showSettings, setShowSettings] = useState(false);
212
+
213
+ if (!toggleAudio && !toggleVideo) {
214
+ return null;
215
+ }
216
+
217
+ return (
218
+ <Fragment>
219
+ {toggleAudio ? (
220
+ <IconButtonWithOptions
221
+ disabled={!toggleAudio}
222
+ hideOptions={hideOptions || !hasAudioDevices}
223
+ onDisabledClick={toggleAudio}
224
+ tooltipMessage={`Turn ${isLocalAudioEnabled ? 'off' : 'on'} audio (${isMacOS ? '⌘' : 'ctrl'} + d)`}
225
+ icon={
226
+ !isLocalAudioEnabled ? <MicOffIcon data-testid="audio_off_btn" /> : <MicOnIcon data-testid="audio_on_btn" />
227
+ }
228
+ active={isLocalAudioEnabled}
229
+ onClick={toggleAudio}
230
+ key="toggleAudio"
231
+ >
232
+ <Dropdown.Group>
233
+ <OptionLabel icon={<MicOnIcon />}>{!shouldShowAudioOutput ? 'Audio' : 'Microphone'}</OptionLabel>
234
+ <Options
235
+ options={audioInput}
236
+ selectedDeviceId={selectedDeviceIDs.audioInput}
237
+ onClick={deviceId => updateDevice({ deviceId, deviceType: DeviceType.audioInput })}
238
+ />
239
+ </Dropdown.Group>
240
+ <Dropdown.ItemSeparator css={{ mx: 0 }} />
241
+ {shouldShowAudioOutput && (
242
+ <>
243
+ <AudioOutputLabel deviceId={selectedDeviceIDs.audioOutput || ''} />
244
+ <Dropdown.Group>
245
+ <Options
246
+ options={audioOutput}
247
+ selectedDeviceId={selectedDeviceIDs.audioOutput}
248
+ onClick={deviceId => updateDevice({ deviceId, deviceType: DeviceType.audioOutput })}
249
+ />
250
+ </Dropdown.Group>
251
+ </>
252
+ )}
253
+ <Dropdown.ItemSeparator css={{ mx: 0 }} />
254
+ <NoiseCancellation />
255
+ <Dropdown.ItemSeparator css={{ mx: 0 }} />
256
+ <AudioSettings onClick={() => setShowSettings(true)} />
257
+ </IconButtonWithOptions>
258
+ ) : null}
259
+
260
+ {toggleVideo ? (
261
+ <IconButtonWithOptions
262
+ disabled={!toggleVideo}
263
+ hideOptions={hideOptions || !hasVideoDevices}
264
+ onDisabledClick={toggleVideo}
265
+ tooltipMessage={`Turn ${isLocalVideoEnabled ? 'off' : 'on'} video (${isMacOS ? '⌘' : 'ctrl'} + e)`}
266
+ icon={
267
+ !isLocalVideoEnabled ? (
268
+ <VideoOffIcon data-testid="video_off_btn" />
269
+ ) : (
270
+ <VideoOnIcon data-testid="video_on_btn" />
271
+ )
272
+ }
273
+ key="toggleVideo"
274
+ active={isLocalVideoEnabled}
275
+ onClick={toggleVideo}
276
+ >
277
+ <Options
278
+ options={videoInput}
279
+ selectedDeviceId={selectedDeviceIDs.videoInput}
280
+ onClick={deviceId => updateDevice({ deviceId, deviceType: DeviceType.videoInput })}
281
+ />
282
+ </IconButtonWithOptions>
283
+ ) : null}
284
+
285
+ {localVideoTrack?.facingMode && roomState === HMSRoomState.Preview ? (
286
+ <Tooltip title="Switch Camera" key="switchCamera">
287
+ <IconButton
288
+ onClick={async () => {
289
+ try {
290
+ await actions.switchCamera();
291
+ } catch (e) {
292
+ ToastManager.addToast({
293
+ title: `Error while flipping camera ${(e as Error).message || ''}`,
294
+ variant: 'error',
295
+ });
296
+ }
297
+ }}
298
+ >
299
+ <CameraFlipIcon />
300
+ </IconButton>
301
+ </Tooltip>
302
+ ) : null}
303
+ {showSettings && (
304
+ <SettingsModal open={showSettings} onOpenChange={() => setShowSettings(false)} screenType={screenType} />
305
+ )}
306
+ </Fragment>
307
+ );
308
+ };
@@ -80,6 +80,7 @@ export const RoleOptions = ({ roleName, peerList }: { roleName: string; peerList
80
80
  return (
81
81
  <Dropdown.Root open={openOptions} onOpenChange={setOpenOptions}>
82
82
  <Dropdown.Trigger
83
+ data-testid="role_group_options"
83
84
  onClick={e => e.stopPropagation()}
84
85
  className="role_actions"
85
86
  asChild
@@ -0,0 +1,159 @@
1
+ import React from 'react';
2
+ import { VerticalMenuIcon } from '@100mslive/react-icons';
3
+ import { Dropdown } from '../../../Dropdown';
4
+ import { Flex } from '../../../Layout';
5
+ import { styled } from '../../../Theme';
6
+ import { Tooltip } from '../../../Tooltip';
7
+ import IconButton from '../../IconButton';
8
+
9
+ const variants = {
10
+ disabled: {
11
+ true: {
12
+ bg: '$surface_brighter',
13
+ },
14
+ },
15
+ active: {
16
+ false: {
17
+ bg: '$secondary_dim',
18
+ },
19
+ },
20
+ };
21
+
22
+ const IconSection = styled(IconButton, {
23
+ w: 'unset',
24
+ h: '$14',
25
+ p: '$4',
26
+ r: '$1',
27
+ bg: '$transparent',
28
+ borderTopRightRadius: 0,
29
+ borderColor: '$border_bright',
30
+ borderBottomRightRadius: 0,
31
+ position: 'relative',
32
+ '&:not([disabled]):focus-visible': {
33
+ zIndex: 1,
34
+ },
35
+ '@md': {
36
+ mx: 0,
37
+ borderTopRightRadius: '$1',
38
+ borderBottomRightRadius: '$1',
39
+ },
40
+ variants: {
41
+ ...variants,
42
+ hideOptions: {
43
+ true: {
44
+ borderTopRightRadius: '$1',
45
+ borderBottomRightRadius: '$1',
46
+ },
47
+ },
48
+ },
49
+ });
50
+
51
+ const OptionsSection = styled(IconButton, {
52
+ w: 'unset',
53
+ h: '$14',
54
+ p: '$4 $2',
55
+ r: '$1',
56
+ borderTopLeftRadius: 0,
57
+ borderColor: '$border_bright',
58
+ borderBottomLeftRadius: 0,
59
+ borderLeftWidth: 0,
60
+ position: 'relative',
61
+ '&:not([disabled]):focus-visible': {
62
+ zIndex: 1,
63
+ },
64
+ '@md': {
65
+ display: 'none',
66
+ },
67
+ variants,
68
+ });
69
+
70
+ const Icon = styled(Flex, {
71
+ alignItems: 'center',
72
+ justifyContent: 'center',
73
+ color: '$on_primary_high',
74
+ variants: {
75
+ disabled: {
76
+ true: {
77
+ color: '$on_surface_low',
78
+ },
79
+ },
80
+ active: {
81
+ true: {
82
+ color: '$on_surface_high',
83
+ },
84
+ },
85
+ },
86
+ });
87
+
88
+ export const IconButtonWithOptions = ({
89
+ disabled = false,
90
+ onDisabledClick = () => {
91
+ return;
92
+ },
93
+ tooltipMessage = '',
94
+ icon,
95
+ children,
96
+ active,
97
+ hideOptions = false,
98
+ onClick = () => {
99
+ return;
100
+ },
101
+ }: {
102
+ onClick: () => void;
103
+ onDisabledClick: () => void;
104
+ icon: React.ReactNode;
105
+ children: React.ReactNode;
106
+ hideOptions?: boolean;
107
+ active: boolean;
108
+ disabled?: boolean;
109
+ tooltipMessage?: string;
110
+ }) => {
111
+ const commonProps = { disabled, active };
112
+ return (
113
+ <Flex>
114
+ <IconSection {...commonProps} onClick={onClick} hideOptions={hideOptions}>
115
+ <Tooltip disabled={!tooltipMessage} title={tooltipMessage}>
116
+ <Icon {...commonProps}>{icon}</Icon>
117
+ </Tooltip>
118
+ </IconSection>
119
+ {!hideOptions && children ? (
120
+ <Dropdown.Root>
121
+ <Dropdown.Trigger
122
+ asChild
123
+ // onClick does not work
124
+ onPointerDown={e => {
125
+ if (disabled) {
126
+ e.preventDefault();
127
+ onDisabledClick();
128
+ }
129
+ }}
130
+ >
131
+ <OptionsSection {...commonProps}>
132
+ <Tooltip title="View Options">
133
+ <Icon {...commonProps}>
134
+ <VerticalMenuIcon />
135
+ </Icon>
136
+ </Tooltip>
137
+ </OptionsSection>
138
+ </Dropdown.Trigger>
139
+ <Dropdown.Content
140
+ sideOffset={5}
141
+ alignOffset={-44}
142
+ align="start"
143
+ side="top"
144
+ css={{
145
+ w: 344,
146
+ maxWidth: '100%',
147
+ maxHeight: 'unset',
148
+ p: 0,
149
+ border: 'none',
150
+ bg: '$surface_dim',
151
+ }}
152
+ >
153
+ {children}
154
+ </Dropdown.Content>
155
+ </Dropdown.Root>
156
+ ) : null}
157
+ </Flex>
158
+ );
159
+ };
@@ -1,8 +1,6 @@
1
1
  import React, { Fragment, useState } from 'react';
2
2
  import { ConferencingScreen } from '@100mslive/types-prebuilt';
3
- // @ts-ignore: No implicit Any
4
3
  import { selectIsConnectedToRoom, selectPermissions, useHMSStore, useRecordingStreaming } from '@100mslive/react-sdk';
5
- // @ts-ignore: No implicit Any
6
4
  import { ExitIcon, StopIcon, VerticalMenuIcon } from '@100mslive/react-icons';
7
5
  import { Dropdown } from '../../../Dropdown';
8
6
  import { Box, Flex } from '../../../Layout';
@@ -11,25 +11,36 @@ import {
11
11
  // @ts-ignore: No implicit Any
12
12
  import { ToastBatcher } from '../Toast/ToastBatcher';
13
13
  import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
14
+ // @ts-ignore: No implicit Any
15
+ import { useSubscribedNotifications } from '../AppData/useUISettings';
14
16
 
15
17
  export const HandRaisedNotifications = () => {
16
18
  const notification = useHMSNotifications(HMSNotificationTypes.HAND_RAISE_CHANGED);
17
19
  const roomState = useHMSStore(selectRoomState);
18
20
  const vanillaStore = useHMSVanillaStore();
19
21
  const { on_stage_exp } = useRoomLayoutConferencingScreen().elements || {};
22
+ const subscribedNotifications = useSubscribedNotifications();
20
23
 
21
24
  useEffect(() => {
22
25
  if (!notification?.data) {
23
26
  return;
24
27
  }
25
- if (roomState !== HMSRoomState.Connected || notification.data.isLocal || !on_stage_exp) {
28
+
29
+ // Don't show toast message in case of local peer.
30
+ if (
31
+ roomState !== HMSRoomState.Connected ||
32
+ notification.data.isLocal ||
33
+ !on_stage_exp ||
34
+ !subscribedNotifications.METADATA_UPDATED
35
+ ) {
26
36
  return;
27
37
  }
28
38
  const hasPeerHandRaised = vanillaStore.getState(selectHasPeerHandRaised(notification.data.id));
29
39
  if (hasPeerHandRaised) {
30
40
  ToastBatcher.showToast({ notification, type: 'RAISE_HAND' });
41
+ console.debug('Metadata updated', notification.data);
31
42
  }
32
- }, [notification, on_stage_exp, roomState, vanillaStore]);
43
+ }, [notification, on_stage_exp, roomState, subscribedNotifications.METADATA_UPDATED, vanillaStore]);
33
44
 
34
45
  return null;
35
46
  };
@@ -16,8 +16,6 @@ import { GroupIcon } from '@100mslive/react-icons';
16
16
  import { Box, Button } from '../../..';
17
17
  import { useUpdateRoomLayout } from '../../provider/roomLayoutProvider';
18
18
  // @ts-ignore: No implicit Any
19
- import { ToastBatcher } from '../Toast/ToastBatcher';
20
- // @ts-ignore: No implicit Any
21
19
  import { ToastManager } from '../Toast/ToastManager';
22
20
  import { AutoplayBlockedModal } from './AutoplayBlockedModal';
23
21
  import { ChatNotifications } from './ChatNotifications';
@@ -34,8 +32,6 @@ import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvid
34
32
  import { usePollViewToggle } from '../AppData/useSidepane';
35
33
  // @ts-ignore: No implicit Any
36
34
  import { useIsNotificationDisabled, useSubscribedNotifications } from '../AppData/useUISettings';
37
- // @ts-ignore: No implicit Any
38
- import { getMetadata } from '../../common/utils';
39
35
  import { ROLE_CHANGE_DECLINED } from '../../common/constants';
40
36
 
41
37
  const pollToastKey: Record<string, string> = {};
@@ -43,7 +39,7 @@ const pollToastKey: Record<string, string> = {};
43
39
  export function Notifications() {
44
40
  const localPeerID = useHMSStore(selectLocalPeerID);
45
41
  const notification = useHMSNotifications();
46
- const subscribedNotifications = useSubscribedNotifications() || {};
42
+ const subscribedNotifications = useSubscribedNotifications();
47
43
  const roomState = useHMSStore(selectRoomState);
48
44
  const updateRoomLayoutForRole = useUpdateRoomLayout();
49
45
  const isNotificationDisabled = useIsNotificationDisabled();
@@ -65,19 +61,6 @@ export function Notifications() {
65
61
  return;
66
62
  }
67
63
  switch (notification.type) {
68
- case HMSNotificationTypes.METADATA_UPDATED:
69
- if (roomState !== HMSRoomState.Connected) {
70
- return;
71
- }
72
- // Don't show toast message when metadata is updated and raiseHand is false.
73
- // Don't show toast message in case of local peer.
74
- const metadata = getMetadata(notification.data?.metadata);
75
- if (!metadata?.isHandRaised || notification.data.isLocal) return;
76
-
77
- console.debug('Metadata updated', notification.data);
78
- if (!subscribedNotifications.METADATA_UPDATED) return;
79
- ToastBatcher.showToast({ notification, type: 'RAISE_HAND' });
80
- break;
81
64
  case HMSNotificationTypes.NAME_UPDATED:
82
65
  console.log(notification.data.id + ' changed their name to ' + notification.data.name);
83
66
  break;
@@ -14,9 +14,10 @@ import { Box, Button, Dropdown, Flex, StyledVideoTile, Text, Video } from '../..
14
14
  import { config as cssConfig } from '../../../Theme';
15
15
  import { DialogDropdownTrigger } from '../../primitives/DropdownTrigger';
16
16
  import { useUISettings } from '../AppData/useUISettings';
17
+ import { useAudioOutputTest } from '../hooks/useAudioOutputTest';
17
18
  import { useDropdownSelection } from '../hooks/useDropdownSelection';
18
19
  import { settingOverflow } from './common';
19
- import { UI_SETTINGS } from '../../common/constants';
20
+ import { TEST_AUDIO_URL, UI_SETTINGS } from '../../common/constants';
20
21
 
21
22
  /**
22
23
  * wrap the button on click of whom settings should open, this component will take care of the rest,
@@ -182,22 +183,8 @@ const DeviceSelector = ({ title, devices, selection, onChange, icon, children =
182
183
  );
183
184
  };
184
185
 
185
- const TEST_AUDIO_URL = 'https://100ms.live/test-audio.wav';
186
-
187
186
  const TestAudio = ({ id }) => {
188
- const audioRef = useRef(null);
189
- const [playing, setPlaying] = useState(false);
190
- useEffect(() => {
191
- if (audioRef.current && id) {
192
- try {
193
- if (typeof audioRef.current.setSinkId !== 'undefined') {
194
- audioRef.current.setSinkId(id);
195
- }
196
- } catch (error) {
197
- console.log(error);
198
- }
199
- }
200
- }, [id]);
187
+ const { playing, setPlaying, audioRef } = useAudioOutputTest({ deviceId: id });
201
188
  return (
202
189
  <>
203
190
  <Button
@@ -218,7 +205,13 @@ const TestAudio = ({ id }) => {
218
205
  &nbsp; speaker
219
206
  </Text>
220
207
  </Button>
221
- <audio ref={audioRef} src={TEST_AUDIO_URL} onEnded={() => setPlaying(false)} onPlay={() => setPlaying(true)} />
208
+ <audio
209
+ ref={audioRef}
210
+ src={TEST_AUDIO_URL}
211
+ onEnded={() => setPlaying(false)}
212
+ onPlay={() => setPlaying(true)}
213
+ css={{ display: 'none' }}
214
+ />
222
215
  </>
223
216
  );
224
217
  };