@100mslive/roomkit-react 0.3.14-alpha.0 → 0.3.14-alpha.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. package/dist/Diagnostics/AudioTest.d.ts +2 -0
  2. package/dist/Diagnostics/BrowserTest.d.ts +81 -0
  3. package/dist/Diagnostics/ConnectivityTest.d.ts +7 -0
  4. package/dist/Diagnostics/Diagnostics.d.ts +2 -0
  5. package/dist/Diagnostics/VideoTest.d.ts +2 -0
  6. package/dist/Diagnostics/components.d.ts +18 -0
  7. package/dist/Diagnostics/hms.d.ts +9 -0
  8. package/dist/Diagnostics/index.d.ts +1 -0
  9. package/dist/{HLSView-USRUP6VG.js → HLSView-PL2BEA32.js} +2 -2
  10. package/dist/{HLSView-7LHIA6HH.css → HLSView-TAAU7UCF.css} +3 -3
  11. package/dist/{HLSView-7LHIA6HH.css.map → HLSView-TAAU7UCF.css.map} +1 -1
  12. package/dist/Prebuilt/App.d.ts +1 -0
  13. package/dist/Prebuilt/components/Notifications/PermissionErrorModal.d.ts +5 -1
  14. package/dist/Stats/index.d.ts +1 -0
  15. package/dist/{chunk-DYDYPNYY.js → chunk-EKH2S2VL.js} +13835 -2768
  16. package/dist/chunk-EKH2S2VL.js.map +7 -0
  17. package/dist/index.cjs.css +2 -2
  18. package/dist/index.cjs.css.map +1 -1
  19. package/dist/index.cjs.js +14870 -3708
  20. package/dist/index.cjs.js.map +4 -4
  21. package/dist/index.css +2 -2
  22. package/dist/index.css.map +1 -1
  23. package/dist/index.d.ts +1 -0
  24. package/dist/index.js +5 -1
  25. package/dist/meta.cjs.json +972 -197
  26. package/dist/meta.esbuild.json +984 -205
  27. package/package.json +7 -7
  28. package/src/Diagnostics/AudioTest.tsx +176 -0
  29. package/src/Diagnostics/BrowserTest.tsx +139 -0
  30. package/src/Diagnostics/ConnectivityTest.tsx +359 -0
  31. package/src/Diagnostics/DeviceSelector.jsx +71 -0
  32. package/src/Diagnostics/Diagnostics.tsx +134 -0
  33. package/src/Diagnostics/VideoTest.tsx +68 -0
  34. package/src/Diagnostics/components.tsx +96 -0
  35. package/src/Diagnostics/hms.ts +9 -0
  36. package/src/Diagnostics/index.ts +1 -0
  37. package/src/Prebuilt/App.tsx +3 -0
  38. package/src/Prebuilt/components/Chat/ChatFooter.tsx +20 -3
  39. package/src/Prebuilt/components/Header/common.jsx +3 -0
  40. package/src/Prebuilt/components/Notifications/Notifications.tsx +2 -2
  41. package/src/Prebuilt/components/Notifications/PermissionErrorModal.tsx +13 -11
  42. package/src/Prebuilt/components/StatsForNerds.jsx +1 -13
  43. package/src/Stats/index.tsx +1 -0
  44. package/src/index.ts +1 -0
  45. package/dist/chunk-DYDYPNYY.js.map +0 -7
  46. /package/dist/{HLSView-USRUP6VG.js.map → HLSView-PL2BEA32.js.map} +0 -0
@@ -0,0 +1,359 @@
1
+ import React, { useContext, useState } from 'react';
2
+ import { ConnectivityCheckResult, ConnectivityState, DiagnosticsRTCStats } from '@100mslive/react-sdk';
3
+ import { CheckCircleIcon, CrossCircleIcon, LinkIcon } from '@100mslive/react-icons';
4
+ import { DiagnosticsContext, TestContainer, TestFooter } from './components';
5
+ import { Button } from '../Button';
6
+ import { Box, Flex } from '../Layout';
7
+ import { Loading } from '../Loading';
8
+ import { formatBytes } from '../Stats';
9
+ import { Text } from '../Text';
10
+ import { hmsDiagnostics } from './hms';
11
+
12
+ const Regions = {
13
+ in: 'India',
14
+ eu: 'Europe',
15
+ us: 'United States',
16
+ };
17
+
18
+ const ConnectivityStateMessage = {
19
+ [ConnectivityState.STARTING]: 'Fetching Init',
20
+ [ConnectivityState.INIT_FETCHED]: 'Connecting to signal server',
21
+ [ConnectivityState.SIGNAL_CONNECTED]: 'Establishing ICE connection',
22
+ [ConnectivityState.ICE_ESTABLISHED]: 'Capturing Media',
23
+ [ConnectivityState.MEDIA_CAPTURED]: 'Publishing Media',
24
+ [ConnectivityState.MEDIA_PUBLISHED]: 'Finishing Up',
25
+ [ConnectivityState.COMPLETED]: 'Completed',
26
+ };
27
+
28
+ export const ConnectivityTestStepResult = ({
29
+ title,
30
+ success,
31
+ children,
32
+ }: {
33
+ title: string;
34
+ success?: boolean;
35
+ children: React.ReactNode;
36
+ }) => {
37
+ return (
38
+ <Box css={{ my: '$10', p: '$10', r: '$1', bg: '$surface_bright' }}>
39
+ <Text css={{ c: '$on_primary_medium', mb: '$6' }}>{title}</Text>
40
+ {success ? (
41
+ <Flex>
42
+ <Text css={{ c: '$alert_success' }}>
43
+ <CheckCircleIcon width="1.5rem" height="1.5rem" />
44
+ </Text>
45
+ <Text variant="lg" css={{ ml: '$4' }}>
46
+ Connected
47
+ </Text>
48
+ </Flex>
49
+ ) : (
50
+ <Flex>
51
+ <Text css={{ c: '$alert_error_bright' }}>
52
+ <CrossCircleIcon width="1.5rem" height="1.5rem" />
53
+ </Text>
54
+ <Text variant="lg" css={{ ml: '$4' }}>
55
+ Failed
56
+ </Text>
57
+ </Flex>
58
+ )}
59
+ <Box>{children}</Box>
60
+ </Box>
61
+ );
62
+ };
63
+
64
+ const DetailedInfo = ({
65
+ title,
66
+ value,
67
+ Icon,
68
+ }: {
69
+ title: string;
70
+ value: string;
71
+ Icon?: (props: React.SVGProps<SVGSVGElement>) => React.JSX.Element;
72
+ }) => {
73
+ return (
74
+ <Box css={{ flex: '50%', mt: '$6' }}>
75
+ <Text variant="caption" css={{ fontWeight: '$semiBold', c: '$on_primary_medium' }}>
76
+ {title}
77
+ </Text>
78
+ <Flex css={{ mt: '$xs', alignItems: 'flex-start' }}>
79
+ {Icon && (
80
+ <Text css={{ mr: '$4' }}>
81
+ <Icon width="1rem" height="1rem" />
82
+ </Text>
83
+ )}
84
+ <Text variant="caption">{value}</Text>
85
+ </Flex>
86
+ </Box>
87
+ );
88
+ };
89
+
90
+ const MediaServerResult = ({ result }: { result?: ConnectivityCheckResult['mediaServerReport'] }) => {
91
+ return (
92
+ <ConnectivityTestStepResult
93
+ title="Media server connection test"
94
+ success={result?.isPublishICEConnected && result.isSubscribeICEConnected}
95
+ >
96
+ <Flex css={{ flexWrap: 'wrap' }}>
97
+ <DetailedInfo
98
+ title="Media Captured"
99
+ value={result?.stats?.audio.bytesSent ? 'Yes' : 'No'}
100
+ Icon={result?.stats?.audio.bytesSent ? CheckCircleIcon : CrossCircleIcon}
101
+ />
102
+ <DetailedInfo
103
+ title="Media Published"
104
+ value={result?.stats?.audio.bitrateSent ? 'Yes' : 'No'}
105
+ Icon={result?.stats?.audio.bytesSent ? CheckCircleIcon : CrossCircleIcon}
106
+ />
107
+ {result?.connectionQualityScore ? (
108
+ <DetailedInfo
109
+ title="Connection Quality Score (CQS)"
110
+ value={`${result.connectionQualityScore.toFixed(2)} (out of 5)`}
111
+ />
112
+ ) : null}
113
+ </Flex>
114
+ </ConnectivityTestStepResult>
115
+ );
116
+ };
117
+
118
+ const SignallingResult = ({ result }: { result?: ConnectivityCheckResult['signallingReport'] }) => {
119
+ return (
120
+ <ConnectivityTestStepResult title="Signalling server connection test" success={result?.isConnected}>
121
+ <Flex css={{ flexWrap: 'wrap' }}>
122
+ <DetailedInfo
123
+ title="Signalling Gateway"
124
+ value={result?.isConnected ? 'Reachable' : 'Unreachable'}
125
+ Icon={result?.isConnected ? CheckCircleIcon : CrossCircleIcon}
126
+ />
127
+ <DetailedInfo title="Websocket URL" value={result?.websocketUrl || 'N/A'} Icon={LinkIcon} />
128
+ </Flex>
129
+ </ConnectivityTestStepResult>
130
+ );
131
+ };
132
+
133
+ const AudioStats = ({ stats }: { stats: DiagnosticsRTCStats | undefined }) => {
134
+ return (
135
+ <ConnectivityTestStepResult title="Audio" success={!!stats?.bytesSent}>
136
+ {stats && (
137
+ <Flex css={{ flexWrap: 'wrap' }}>
138
+ <DetailedInfo title="Bytes Sent" value={formatBytes(stats.bytesSent)} />
139
+ <DetailedInfo title="Bytes Received" value={formatBytes(stats.bytesReceived)} />
140
+ <DetailedInfo title="Packets Received" value={stats.packetsReceived.toString()} />
141
+ <DetailedInfo title="Packets Lost" value={stats.packetsLost.toString()} />
142
+ <DetailedInfo title="Bitrate Sent" value={formatBytes(stats.bitrateSent, 'b/s')} />
143
+ <DetailedInfo title="Bitrate Received" value={formatBytes(stats.bitrateReceived, 'b/s')} />
144
+ <DetailedInfo title="Round Trip Time" value={`${stats.roundTripTime} ms`} />
145
+ </Flex>
146
+ )}
147
+ </ConnectivityTestStepResult>
148
+ );
149
+ };
150
+
151
+ const VideoStats = ({ stats }: { stats: DiagnosticsRTCStats | undefined }) => {
152
+ return (
153
+ <ConnectivityTestStepResult title="Video" success={!!stats?.bytesSent}>
154
+ {stats && (
155
+ <Flex css={{ flexWrap: 'wrap' }}>
156
+ <DetailedInfo title="Bytes Sent" value={formatBytes(stats.bytesSent)} />
157
+ <DetailedInfo title="Bytes Received" value={formatBytes(stats.bytesReceived)} />
158
+ <DetailedInfo title="Packets Received" value={stats.packetsReceived.toString()} />
159
+ <DetailedInfo title="Packets Lost" value={stats.packetsLost.toString()} />
160
+ <DetailedInfo title="Bitrate Sent" value={formatBytes(stats.bitrateSent, 'b/s')} />
161
+ <DetailedInfo title="Bitrate Received" value={formatBytes(stats.bitrateReceived, 'b/s')} />
162
+ <DetailedInfo title="Round Trip Time" value={`${stats.roundTripTime} ms`} />
163
+ </Flex>
164
+ )}
165
+ </ConnectivityTestStepResult>
166
+ );
167
+ };
168
+
169
+ const Footer = ({
170
+ error,
171
+ result,
172
+ restart,
173
+ }: {
174
+ result?: ConnectivityCheckResult;
175
+ restart: () => void;
176
+ error?: Error;
177
+ }) => {
178
+ return (
179
+ <TestFooter error={error}>
180
+ <Flex css={{ gap: '$8', '@lg': { flexDirection: 'column' } }}>
181
+ <Button variant="standard" onClick={restart}>
182
+ Restart Test
183
+ </Button>
184
+ <Button disabled={!result} onClick={() => result && downloadJson(result, 'hms_diagnostics_results')}>
185
+ Download Test Report
186
+ </Button>
187
+ </Flex>
188
+ </TestFooter>
189
+ );
190
+ };
191
+
192
+ const ConnectivityTestReport = ({
193
+ error,
194
+ result,
195
+ progress,
196
+ startTest,
197
+ }: {
198
+ error?: Error;
199
+ result?: ConnectivityCheckResult;
200
+ progress?: ConnectivityState;
201
+ startTest: () => void;
202
+ }) => {
203
+ if (error) {
204
+ return (
205
+ <>
206
+ <TestContainer css={{ textAlign: 'center' }}>
207
+ <Text css={{ c: '$alert_error_default', mb: '$4' }}>
208
+ <CrossCircleIcon />
209
+ </Text>
210
+ <Text variant="h6">Connectivity Test Failed</Text>
211
+ <Text variant="body2" css={{ c: '$on_primary_medium' }}>
212
+ {error.message}
213
+ </Text>
214
+ </TestContainer>
215
+ <Footer restart={startTest} error={error} />
216
+ </>
217
+ );
218
+ }
219
+
220
+ if (result) {
221
+ // for debugging and quick view of results
222
+ console.log(result);
223
+ return (
224
+ <>
225
+ <TestContainer>
226
+ <Text css={{ c: '$on_primary_medium' }}>Connectivity test has been completed.</Text>
227
+ <SignallingResult result={result?.signallingReport} />
228
+ <MediaServerResult result={result?.mediaServerReport} />
229
+ <AudioStats stats={result?.mediaServerReport?.stats?.audio} />
230
+ <VideoStats stats={result?.mediaServerReport?.stats?.video} />
231
+ </TestContainer>
232
+ <Footer result={result} restart={startTest} error={error} />
233
+ </>
234
+ );
235
+ }
236
+
237
+ if (progress !== undefined) {
238
+ return (
239
+ <TestContainer css={{ textAlign: 'center' }}>
240
+ <Text css={{ c: '$primary_bright', textAlign: 'center' }}>
241
+ <Loading size="3.5rem" color="currentColor" />
242
+ </Text>
243
+ <Text variant="h6" css={{ mt: '$8' }}>
244
+ Checking your connection...
245
+ </Text>
246
+ <Text
247
+ variant="body2"
248
+ css={{ c: '$on_primary_medium', mt: '$4' }}
249
+ >{`${ConnectivityStateMessage[progress]}...`}</Text>
250
+ </TestContainer>
251
+ );
252
+ }
253
+
254
+ return null;
255
+ };
256
+
257
+ const RegionSelector = ({
258
+ region,
259
+ setRegion,
260
+ startTest,
261
+ }: {
262
+ region?: string;
263
+ startTest?: () => void;
264
+ setRegion: (region: string) => void;
265
+ }) => {
266
+ return (
267
+ <TestContainer css={{ borderBottom: '1px solid $border_default' }}>
268
+ <Text variant="body1">Select a region</Text>
269
+ <Text variant="body2" css={{ c: '$on_secondary_low' }}>
270
+ Select the closest region for best results
271
+ </Text>
272
+ <Flex
273
+ justify="between"
274
+ css={{
275
+ mt: '$md',
276
+ '@lg': {
277
+ flexDirection: 'column',
278
+ gap: '$8',
279
+ },
280
+ }}
281
+ >
282
+ <Flex
283
+ css={{
284
+ gap: '$4',
285
+ '@lg': {
286
+ flexDirection: 'column',
287
+ },
288
+ }}
289
+ >
290
+ {Object.entries(Regions).map(([key, value]) => (
291
+ <Button
292
+ key={key}
293
+ outlined={region !== key}
294
+ variant={region === key ? 'primary' : 'standard'}
295
+ css={region === key ? { bg: '$primary_dim' } : {}}
296
+ onClick={() => setRegion(key)}
297
+ >
298
+ {value}
299
+ </Button>
300
+ ))}
301
+ </Flex>
302
+ <Flex css={{ '@lg': { flexDirection: 'column' } }}>
303
+ <Button variant="primary" onClick={startTest} disabled={!startTest}>
304
+ {startTest ? 'Start Test' : 'Testing...'}
305
+ </Button>
306
+ </Flex>
307
+ </Flex>
308
+ </TestContainer>
309
+ );
310
+ };
311
+
312
+ export const ConnectivityTest = () => {
313
+ const { setConnectivityTested } = useContext(DiagnosticsContext);
314
+ const [region, setRegion] = useState<string | undefined>(Object.keys(Regions)[0]);
315
+ const [error, setError] = useState<Error | undefined>();
316
+ const [progress, setProgress] = useState<ConnectivityState>();
317
+ const [result, setResult] = useState<ConnectivityCheckResult | undefined>();
318
+
319
+ const startTest = () => {
320
+ setConnectivityTested(false);
321
+ setError(undefined);
322
+ setResult(undefined);
323
+ hmsDiagnostics
324
+ .startConnectivityCheck(
325
+ state => {
326
+ setProgress(state);
327
+ },
328
+ result => {
329
+ setConnectivityTested(true);
330
+ setResult(result);
331
+ },
332
+ region,
333
+ )
334
+ .catch(error => {
335
+ setError(error);
336
+ });
337
+ };
338
+
339
+ return (
340
+ <>
341
+ <RegionSelector
342
+ region={region}
343
+ setRegion={setRegion}
344
+ startTest={progress === undefined || progress === ConnectivityState.COMPLETED ? startTest : undefined}
345
+ />
346
+ <ConnectivityTestReport error={error} result={result} progress={progress} startTest={startTest} />
347
+ </>
348
+ );
349
+ };
350
+
351
+ const downloadJson = (obj: object, fileName: string) => {
352
+ const a = document.createElement('a');
353
+ const file = new Blob([JSON.stringify(obj, null, 2)], {
354
+ type: 'application/json',
355
+ });
356
+ a.href = URL.createObjectURL(file);
357
+ a.download = `${fileName}.json`;
358
+ a.click();
359
+ };
@@ -0,0 +1,71 @@
1
+ import React, { useRef, useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Dropdown } from '../Dropdown';
4
+ import { Box, Flex } from '../Layout';
5
+ import { DialogDropdownTrigger } from '../Prebuilt/primitives/DropdownTrigger';
6
+ import { Text } from '../Text';
7
+
8
+ export const DeviceSelector = ({ title, devices, selection, onChange, icon, children = null }) => {
9
+ const [open, setOpen] = useState(false);
10
+ const ref = useRef(null);
11
+ return (
12
+ <Box css={{ mb: '$6' }}>
13
+ <Text css={{ mb: '$4' }}>{title}</Text>
14
+ <Flex
15
+ align="center"
16
+ css={{
17
+ gap: '$4',
18
+ '@md': {
19
+ flexDirection: children ? 'column' : 'row',
20
+ alignItems: children ? 'start' : 'center',
21
+ },
22
+ }}
23
+ >
24
+ <Dropdown.Root open={open} onOpenChange={setOpen}>
25
+ <DialogDropdownTrigger
26
+ ref={ref}
27
+ icon={icon}
28
+ title={devices.find(({ deviceId }) => deviceId === selection)?.label || 'Select device from list'}
29
+ open={open}
30
+ />
31
+ <Dropdown.Portal>
32
+ <Dropdown.Content
33
+ align="start"
34
+ sideOffset={8}
35
+ css={{
36
+ w:
37
+ // @ts-ignore
38
+ ref.current?.clientWidth,
39
+ zIndex: 1001,
40
+ }}
41
+ >
42
+ {devices.map(device => {
43
+ return (
44
+ <Dropdown.Item
45
+ key={device.label}
46
+ onSelect={() => onChange(device.deviceId)}
47
+ css={{
48
+ px: '$9',
49
+ }}
50
+ >
51
+ {device.label}
52
+ </Dropdown.Item>
53
+ );
54
+ })}
55
+ </Dropdown.Content>
56
+ </Dropdown.Portal>
57
+ </Dropdown.Root>
58
+ {children}
59
+ </Flex>
60
+ </Box>
61
+ );
62
+ };
63
+
64
+ DeviceSelector.propTypes = {
65
+ title: PropTypes.string.isRequired,
66
+ devices: PropTypes.array.isRequired,
67
+ selection: PropTypes.string,
68
+ onChange: PropTypes.func.isRequired,
69
+ icon: PropTypes.node,
70
+ children: PropTypes.node,
71
+ };
@@ -0,0 +1,134 @@
1
+ import React, { useContext } from 'react';
2
+ import { HMSRoomProvider } from '@100mslive/react-sdk';
3
+ import { CheckCircleIcon, ConnectivityIcon, GlobeIcon, MicOnIcon, VideoOnIcon } from '@100mslive/react-icons';
4
+ import { DiagnosticsContext, DiagnosticsSteps } from './components';
5
+ import { Box, Flex } from '../Layout';
6
+ import { Text } from '../Text';
7
+ import { HMSThemeProvider } from '../Theme';
8
+ import { AudioTest } from './AudioTest';
9
+ import { BrowserTest } from './BrowserTest';
10
+ import { ConnectivityTest } from './ConnectivityTest';
11
+ import { hmsActions, hmsNotifications, hmsStats, hmsStore } from './hms';
12
+ import { VideoTest } from './VideoTest';
13
+
14
+ const DiagnosticsStepIcon: Record<string, React.ReactNode> = {
15
+ video: <VideoOnIcon width="2rem" height="2rem" />,
16
+ audio: <MicOnIcon width="2rem" height="2rem" />,
17
+ browser: <GlobeIcon width="2rem" height="2rem" />,
18
+ connectivity: <ConnectivityIcon width="2rem" height="2rem" />,
19
+ };
20
+
21
+ const Container = ({ children }: { children: React.ReactNode }) => (
22
+ <Box
23
+ css={{
24
+ px: '120px',
25
+ py: '120px',
26
+ bg: '$background_dim',
27
+ lineHeight: '1.5',
28
+ '-webkit-text-size-adjust': '100%',
29
+ position: 'relative',
30
+ minHeight: '100vh',
31
+ '@lg': {
32
+ p: '$12',
33
+ },
34
+ }}
35
+ >
36
+ {children}
37
+ </Box>
38
+ );
39
+
40
+ const DiagnosticsStepTest = () => {
41
+ const { activeStep } = useContext(DiagnosticsContext);
42
+
43
+ let TestComponent = () => <></>;
44
+
45
+ if (activeStep === 'audio') {
46
+ TestComponent = AudioTest;
47
+ } else if (activeStep === 'video') {
48
+ TestComponent = VideoTest;
49
+ } else if (activeStep === 'browser') {
50
+ TestComponent = BrowserTest;
51
+ } else if (activeStep === 'connectivity') {
52
+ TestComponent = ConnectivityTest;
53
+ }
54
+
55
+ return <TestComponent key={activeStep} />;
56
+ };
57
+
58
+ const DiagnosticsStepHeader = () => {
59
+ const { activeStep } = useContext(DiagnosticsContext);
60
+ return (
61
+ <Flex css={{ py: '$8', px: '$10', alignItems: 'center', borderBottom: '1px solid $border_default' }}>
62
+ <Text css={{ c: '$primary_bright', mt: '$xs' }}>{DiagnosticsStepIcon[activeStep]}</Text>
63
+ <Text css={{ fontSize: '$h6', ml: '$9' }}>{DiagnosticsSteps[activeStep]}</Text>
64
+ </Flex>
65
+ );
66
+ };
67
+
68
+ const DiagnosticsStep = () => {
69
+ return (
70
+ <Box css={{ border: '1px solid $border_default', r: '$1', w: '75%', maxWidth: '65rem', '@lg': { w: '100%' } }}>
71
+ <DiagnosticsStepHeader />
72
+ <Box css={{ maxHeight: '55vh', overflowY: 'auto' }}>
73
+ <DiagnosticsStepTest />
74
+ </Box>
75
+ </Box>
76
+ );
77
+ };
78
+
79
+ const DiagnosticsStepsList = () => {
80
+ const { activeStep, connectivityTested } = useContext(DiagnosticsContext);
81
+
82
+ return (
83
+ <Box css={{ w: '25%', '@lg': { display: 'none' } }}>
84
+ {Object.keys(DiagnosticsSteps).map(key => {
85
+ const keys = Object.keys(DiagnosticsSteps);
86
+ const activeStepIndex = keys.indexOf(activeStep);
87
+ const keyIndex = keys.indexOf(key);
88
+ const isStepCompleted = activeStepIndex > keyIndex || (activeStep === 'connectivity' && connectivityTested);
89
+
90
+ let color = '$on_primary_low';
91
+ if (activeStep === key) {
92
+ color = '$on_primary_high';
93
+ }
94
+ if (isStepCompleted) {
95
+ color = '$primary_bright';
96
+ }
97
+
98
+ return (
99
+ <Flex key={key} css={{ mb: '$10', c: color, gap: '$4', alignItems: 'center' }}>
100
+ {isStepCompleted ? (
101
+ <CheckCircleIcon width="1rem" height="1rem" />
102
+ ) : (
103
+ <Text css={{ c: color, fontSize: '1.75rem' }}>&bull;</Text>
104
+ )}
105
+ <Text css={{ c: color }}>{DiagnosticsSteps[key]}</Text>
106
+ </Flex>
107
+ );
108
+ })}
109
+ </Box>
110
+ );
111
+ };
112
+
113
+ export const Diagnostics = () => {
114
+ const [activeStep, setActiveStep] = React.useState(Object.keys(DiagnosticsSteps)[0]);
115
+ const [connectivityTested, setConnectivityTested] = React.useState(false);
116
+ return (
117
+ <HMSRoomProvider store={hmsStore} actions={hmsActions} notifications={hmsNotifications} stats={hmsStats}>
118
+ <HMSThemeProvider themeType="default">
119
+ <DiagnosticsContext.Provider value={{ activeStep, setActiveStep, connectivityTested, setConnectivityTested }}>
120
+ <Container>
121
+ <Text variant="h4">Pre-call Test</Text>
122
+ <Text variant="md" css={{ c: '$on_primary_medium' }}>
123
+ Make sure your devices and network are good to go, let's get started.
124
+ </Text>
125
+ <Flex css={{ direction: 'column', mt: '$12', justifyItems: 'center' }}>
126
+ <DiagnosticsStepsList />
127
+ <DiagnosticsStep />
128
+ </Flex>
129
+ </Container>
130
+ </DiagnosticsContext.Provider>
131
+ </HMSThemeProvider>
132
+ </HMSRoomProvider>
133
+ );
134
+ };
@@ -0,0 +1,68 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import {
3
+ HMSException,
4
+ selectDevices,
5
+ selectLocalMediaSettings,
6
+ selectLocalVideoTrackID,
7
+ useHMSStore,
8
+ } from '@100mslive/react-sdk';
9
+ import { VideoOnIcon } from '@100mslive/react-icons';
10
+ import { PermissionErrorModal } from '../Prebuilt/components/Notifications/PermissionErrorModal';
11
+ import { TestContainer, TestFooter } from './components';
12
+ import { Flex } from '../Layout';
13
+ import { Text } from '../Text';
14
+ import { Video } from '../Video';
15
+ import { StyledVideoTile } from '../VideoTile';
16
+ // @ts-ignore: No implicit any
17
+ import { DeviceSelector } from './DeviceSelector';
18
+ import { hmsDiagnostics } from './hms';
19
+
20
+ export const VideoTest = () => {
21
+ const allDevices = useHMSStore(selectDevices);
22
+ const { videoInput } = allDevices;
23
+ const trackID = useHMSStore(selectLocalVideoTrackID);
24
+ const sdkSelectedDevices = useHMSStore(selectLocalMediaSettings);
25
+ const [error, setError] = useState<HMSException | undefined>();
26
+
27
+ useEffect(() => {
28
+ hmsDiagnostics.startCameraCheck().catch(err => setError(err));
29
+ }, []);
30
+
31
+ return (
32
+ <>
33
+ <TestContainer css={{ display: 'flex', '@lg': { flexDirection: 'column', alignItems: 'center' } }}>
34
+ {trackID && (
35
+ <StyledVideoTile.Container
36
+ css={{
37
+ width: '90%',
38
+ aspectRatio: '16/9',
39
+ mr: '$10',
40
+ '@lg': { mr: 0, mb: '$10', aspectRatio: '1/1' },
41
+ }}
42
+ >
43
+ <Video mirror={true} trackId={trackID} />
44
+ </StyledVideoTile.Container>
45
+ )}
46
+ <Flex direction="column" css={{ w: '100%' }}>
47
+ <Text variant="body2" css={{ c: '$on_primary_medium', mb: '$10' }}>
48
+ Move in front of your camera to make sure it's working. If you don't see your video, try changing the
49
+ selected camera. If the camera isn't part of your computer, check your settings to make sure your system
50
+ recognizes it.
51
+ </Text>
52
+ <DeviceSelector
53
+ title="Video"
54
+ devices={videoInput || []}
55
+ icon={<VideoOnIcon />}
56
+ selection={sdkSelectedDevices.videoInputDeviceId}
57
+ onChange={async (deviceId: string) => {
58
+ hmsDiagnostics.stopCameraCheck();
59
+ hmsDiagnostics.startCameraCheck(deviceId);
60
+ }}
61
+ />
62
+ </Flex>
63
+ </TestContainer>
64
+ <TestFooter error={error} ctaText="Does your video look good?" />
65
+ <PermissionErrorModal error={error} />
66
+ </>
67
+ );
68
+ };