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

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 (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
+ };