@100mslive/roomkit-react 0.2.7-alpha.0 → 0.2.7-alpha.2
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.
- package/README.md +30 -33
- package/dist/{HLSView-E65JZSU4.js → HLSView-DQJBCEW4.js} +2 -2
- package/dist/Prebuilt/common/constants.d.ts +4 -3
- package/dist/Prebuilt/components/AudioVideoToggle.d.ts +9 -0
- package/dist/Prebuilt/components/IconButtonWithOptions/IconButtonWithOptions.d.ts +11 -0
- package/dist/Prebuilt/components/hooks/useAudioOutputTest.d.ts +8 -0
- package/dist/{chunk-DWLYDCFC.js → chunk-GEXL6KL2.js} +4355 -3936
- package/dist/chunk-GEXL6KL2.js.map +7 -0
- package/dist/index.cjs.js +5991 -5566
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.js +1 -1
- package/dist/meta.cjs.json +668 -599
- package/dist/meta.esbuild.json +675 -606
- package/package.json +7 -6
- package/src/Prebuilt/common/constants.ts +7 -4
- package/src/Prebuilt/components/AppData/useUISettings.js +1 -1
- package/src/Prebuilt/components/AudioVideoToggle.tsx +308 -0
- package/src/Prebuilt/components/Chat/ChatFooter.tsx +0 -1
- package/src/Prebuilt/components/Footer/RoleOptions.tsx +1 -0
- package/src/Prebuilt/components/IconButtonWithOptions/IconButtonWithOptions.tsx +159 -0
- package/src/Prebuilt/components/Leave/DesktopLeaveRoom.tsx +0 -2
- package/src/Prebuilt/components/Notifications/HandRaisedNotifications.tsx +13 -2
- package/src/Prebuilt/components/Notifications/Notifications.tsx +1 -18
- package/src/Prebuilt/components/Settings/DeviceSettings.jsx +10 -17
- package/src/Prebuilt/components/hooks/useAudioOutputTest.tsx +20 -0
- package/dist/chunk-DWLYDCFC.js.map +0 -7
- package/src/Prebuilt/components/AudioVideoToggle.jsx +0 -171
- package/src/Prebuilt/components/IconButtonWithOptions/IconButtonWithOptions.jsx +0 -121
- /package/dist/{HLSView-E65JZSU4.js.map → HLSView-DQJBCEW4.js.map} +0 -0
package/package.json
CHANGED
@@ -10,7 +10,7 @@
|
|
10
10
|
"prebuilt",
|
11
11
|
"roomkit"
|
12
12
|
],
|
13
|
-
"version": "0.2.7-alpha.
|
13
|
+
"version": "0.2.7-alpha.2",
|
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.7-alpha.
|
86
|
-
"@100mslive/hms-
|
87
|
-
"@100mslive/
|
88
|
-
"@100mslive/react-
|
85
|
+
"@100mslive/hls-player": "0.2.7-alpha.2",
|
86
|
+
"@100mslive/hms-noise-cancellation": "0.0.0-alpha.1",
|
87
|
+
"@100mslive/hms-virtual-background": "1.12.7-alpha.2",
|
88
|
+
"@100mslive/react-icons": "0.9.7-alpha.2",
|
89
|
+
"@100mslive/react-sdk": "0.9.7-alpha.2",
|
89
90
|
"@100mslive/types-prebuilt": "0.12.7",
|
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": "
|
124
|
+
"gitHead": "4098033b045e93a14929ec5f7480ca9463bbf59b"
|
124
125
|
}
|
@@ -98,11 +98,12 @@ export const HLS_TIMED_METADATA_DOC_URL =
|
|
98
98
|
|
99
99
|
export const REMOTE_STOP_SCREENSHARE_TYPE = 'REMOTE_STOP_SCREENSHARE';
|
100
100
|
|
101
|
-
|
102
|
-
export const
|
103
|
-
export const
|
101
|
+
// mweb could have browser name as "Mobile Chrome" or "Mobile Firefox"
|
102
|
+
export const isChrome = parsedUserAgent.getBrowser()?.name?.toLowerCase().includes('chrome');
|
103
|
+
export const isFirefox = parsedUserAgent.getBrowser()?.name?.toLowerCase().includes('firefox');
|
104
|
+
export const isSafari = parsedUserAgent.getBrowser()?.name?.toLowerCase().includes('safari');
|
104
105
|
export const isIOS = parsedUserAgent.getOS()?.name?.toLowerCase() === 'ios';
|
105
|
-
export const isMacOS = parsedUserAgent.getOS()?.name?.toLowerCase() === '
|
106
|
+
export const isMacOS = parsedUserAgent.getOS()?.name?.toLowerCase() === 'macos';
|
106
107
|
export const isAndroid = parsedUserAgent.getOS()?.name?.toLowerCase() === 'android';
|
107
108
|
export const isIPadOS = false;
|
108
109
|
// typeof navigator !== "undefined" &&
|
@@ -142,3 +143,5 @@ export enum QUESTION_TYPE {
|
|
142
143
|
export const ROLE_CHANGE_DECLINED = 'role_change_declined';
|
143
144
|
|
144
145
|
export const DEFAULT_PORTAL_CONTAINER = '.prebuilt-container';
|
146
|
+
|
147
|
+
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
|
+
};
|
@@ -219,7 +219,6 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo
|
|
219
219
|
placeholder={message_placeholder}
|
220
220
|
ref={inputRef}
|
221
221
|
required
|
222
|
-
autoFocus={!isMobile}
|
223
222
|
onKeyPress={async event => {
|
224
223
|
if (event.key === 'Enter') {
|
225
224
|
if (!event.shiftKey) {
|
@@ -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
|
-
|
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;
|