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

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. package/dist/Diagnostics/AudioTest.d.ts +2 -0
  2. package/dist/Diagnostics/ConnectivityTest.d.ts +7 -0
  3. package/dist/Diagnostics/Diagnostics.d.ts +2 -0
  4. package/dist/Diagnostics/VideoTest.d.ts +2 -0
  5. package/dist/Diagnostics/components.d.ts +16 -0
  6. package/dist/Diagnostics/hms.d.ts +9 -0
  7. package/dist/Diagnostics/index.d.ts +1 -0
  8. package/dist/{HLSView-USRUP6VG.js → HLSView-42MLSPUI.js} +2 -2
  9. package/dist/{HLSView-7LHIA6HH.css → HLSView-4UFNO3KZ.css} +3 -3
  10. package/dist/{HLSView-7LHIA6HH.css.map → HLSView-4UFNO3KZ.css.map} +1 -1
  11. package/dist/Prebuilt/App.d.ts +3 -1
  12. package/dist/Stats/index.d.ts +1 -0
  13. package/dist/{chunk-DYDYPNYY.js → chunk-VNHKDQOM.js} +12334 -1483
  14. package/dist/chunk-VNHKDQOM.js.map +7 -0
  15. package/dist/index.cjs.css +2 -2
  16. package/dist/index.cjs.css.map +1 -1
  17. package/dist/index.cjs.js +16767 -5828
  18. package/dist/index.cjs.js.map +4 -4
  19. package/dist/index.css +2 -2
  20. package/dist/index.css.map +1 -1
  21. package/dist/index.d.ts +1 -0
  22. package/dist/index.js +5 -1
  23. package/dist/meta.cjs.json +896 -190
  24. package/dist/meta.esbuild.json +908 -198
  25. package/package.json +7 -7
  26. package/src/Diagnostics/AudioTest.tsx +153 -0
  27. package/src/Diagnostics/ConnectivityTest.tsx +355 -0
  28. package/src/Diagnostics/DeviceSelector.jsx +71 -0
  29. package/src/Diagnostics/Diagnostics.tsx +112 -0
  30. package/src/Diagnostics/VideoTest.tsx +60 -0
  31. package/src/Diagnostics/components.tsx +84 -0
  32. package/src/Diagnostics/hms.ts +9 -0
  33. package/src/Diagnostics/index.ts +1 -0
  34. package/src/Prebuilt/App.tsx +12 -1
  35. package/src/Prebuilt/components/Chat/ChatFooter.tsx +16 -2
  36. package/src/Prebuilt/components/StatsForNerds.jsx +1 -13
  37. package/src/Stats/index.tsx +1 -0
  38. package/src/index.ts +1 -0
  39. package/dist/chunk-DYDYPNYY.js.map +0 -7
  40. /package/dist/{HLSView-USRUP6VG.js.map → HLSView-42MLSPUI.js.map} +0 -0
@@ -0,0 +1,112 @@
1
+ import React, { useContext } from 'react';
2
+ import { HMSRoomProvider } from '@100mslive/react-sdk';
3
+ import { 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 { ConnectivityTest } from './ConnectivityTest';
10
+ import { hmsActions, hmsNotifications, hmsStats, hmsStore } from './hms';
11
+ import { VideoTest } from './VideoTest';
12
+
13
+ const DiagnosticsStepIcon: Record<string, React.ReactNode> = {
14
+ video: <VideoOnIcon width="2rem" height="2rem" />,
15
+ audio: <MicOnIcon width="2rem" height="2rem" />,
16
+ browser: <GlobeIcon width="2rem" height="2rem" />,
17
+ connectivity: <ConnectivityIcon width="2rem" height="2rem" />,
18
+ };
19
+
20
+ const Container = ({ children }: { children: React.ReactNode }) => (
21
+ <Box
22
+ css={{
23
+ px: '155px',
24
+ py: '160px',
25
+ bg: '$background_dim',
26
+ lineHeight: '1.5',
27
+ '-webkit-text-size-adjust': '100%',
28
+ position: 'relative',
29
+ minHeight: '100vh',
30
+ '@lg': {
31
+ p: '$12',
32
+ },
33
+ }}
34
+ >
35
+ {children}
36
+ </Box>
37
+ );
38
+
39
+ const DiagnosticsStepTest = () => {
40
+ const { activeStep } = useContext(DiagnosticsContext);
41
+
42
+ let TestComponent = () => <></>;
43
+
44
+ if (activeStep === 'audio') {
45
+ TestComponent = AudioTest;
46
+ } else if (activeStep === 'video') {
47
+ TestComponent = VideoTest;
48
+ } else if (activeStep === 'connectivity') {
49
+ TestComponent = ConnectivityTest;
50
+ }
51
+
52
+ return <TestComponent key={activeStep} />;
53
+ };
54
+
55
+ const DiagnosticsStepHeader = () => {
56
+ const { activeStep } = useContext(DiagnosticsContext);
57
+ return (
58
+ <Flex css={{ py: '$8', px: '$10', alignItems: 'center', borderBottom: '1px solid $border_default' }}>
59
+ <Text css={{ c: '$primary_bright', mt: '$xs' }}>{DiagnosticsStepIcon[activeStep]}</Text>
60
+ <Text css={{ fontSize: '$h6', ml: '$9' }}>{DiagnosticsSteps[activeStep]}</Text>
61
+ </Flex>
62
+ );
63
+ };
64
+
65
+ const DiagnosticsStep = () => {
66
+ return (
67
+ <Box css={{ border: '1px solid $border_default', r: '$1', w: '75%', '@lg': { w: '100%' } }}>
68
+ <DiagnosticsStepHeader />
69
+ <DiagnosticsStepTest />
70
+ </Box>
71
+ );
72
+ };
73
+
74
+ const DiagnosticsStepsList = () => {
75
+ const { activeStep } = useContext(DiagnosticsContext);
76
+
77
+ return (
78
+ <Box css={{ w: '25%', '@lg': { display: 'none' } }}>
79
+ <ul>
80
+ {Object.keys(DiagnosticsSteps).map(key => (
81
+ <li key={key}>
82
+ <Text variant="md" css={{ mb: '$10', c: activeStep === key ? '$on_primary_high' : '$on_primary_low' }}>
83
+ {DiagnosticsSteps[key]}
84
+ </Text>
85
+ </li>
86
+ ))}
87
+ </ul>
88
+ </Box>
89
+ );
90
+ };
91
+
92
+ export const Diagnostics = () => {
93
+ const [activeStep, setActiveStep] = React.useState(Object.keys(DiagnosticsSteps)[0]);
94
+ return (
95
+ <HMSRoomProvider store={hmsStore} actions={hmsActions} notifications={hmsNotifications} stats={hmsStats}>
96
+ <HMSThemeProvider themeType="default">
97
+ <DiagnosticsContext.Provider value={{ activeStep, setActiveStep }}>
98
+ <Container>
99
+ <Text variant="h4">Pre-call Test</Text>
100
+ <Text variant="md" css={{ c: '$on_primary_medium' }}>
101
+ Make sure your devices and network are good to go, let's get started.
102
+ </Text>
103
+ <Flex css={{ direction: 'column', mt: '$12', justifyItems: 'center' }}>
104
+ <DiagnosticsStepsList />
105
+ <DiagnosticsStep />
106
+ </Flex>
107
+ </Container>
108
+ </DiagnosticsContext.Provider>
109
+ </HMSThemeProvider>
110
+ </HMSRoomProvider>
111
+ );
112
+ };
@@ -0,0 +1,60 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { selectDevices, selectLocalMediaSettings, selectLocalVideoTrackID, useHMSStore } from '@100mslive/react-sdk';
3
+ import { VideoOnIcon } from '@100mslive/react-icons';
4
+ import { TestContainer, TestFooter } from './components';
5
+ import { Flex } from '../Layout';
6
+ import { Text } from '../Text';
7
+ import { Video } from '../Video';
8
+ import { StyledVideoTile } from '../VideoTile';
9
+ // @ts-ignore: No implicit any
10
+ import { DeviceSelector } from './DeviceSelector';
11
+ import { hmsDiagnostics } from './hms';
12
+
13
+ export const VideoTest = () => {
14
+ const allDevices = useHMSStore(selectDevices);
15
+ const { videoInput } = allDevices;
16
+ const trackID = useHMSStore(selectLocalVideoTrackID);
17
+ const sdkSelectedDevices = useHMSStore(selectLocalMediaSettings);
18
+ const [error, setError] = useState<Error | undefined>();
19
+
20
+ useEffect(() => {
21
+ hmsDiagnostics.startCameraCheck().catch(err => setError(err));
22
+ }, []);
23
+
24
+ return (
25
+ <>
26
+ <TestContainer css={{ display: 'flex', '@lg': { flexDirection: 'column', alignItems: 'center' } }}>
27
+ {trackID && (
28
+ <StyledVideoTile.Container
29
+ css={{
30
+ w: '90%',
31
+ height: '$48',
32
+ mr: '$10',
33
+ '@lg': { mr: 0, mb: '$10' },
34
+ }}
35
+ >
36
+ <Video mirror={true} trackId={trackID} />
37
+ </StyledVideoTile.Container>
38
+ )}
39
+ <Flex direction="column" css={{ w: '100%' }}>
40
+ <Text variant="body2" css={{ c: '$on_primary_medium', mb: '$10' }}>
41
+ Move in front of your camera to make sure it's working. If you don't see your video, try changing the
42
+ selected camera. If the camera isn't part of your computer, check your settings to make sure your system
43
+ recognizes it.
44
+ </Text>
45
+ <DeviceSelector
46
+ title="Video"
47
+ devices={videoInput || []}
48
+ icon={<VideoOnIcon />}
49
+ selection={sdkSelectedDevices.videoInputDeviceId}
50
+ onChange={async (deviceId: string) => {
51
+ hmsDiagnostics.stopCameraCheck();
52
+ hmsDiagnostics.startCameraCheck(deviceId);
53
+ }}
54
+ />
55
+ </Flex>
56
+ </TestContainer>
57
+ <TestFooter error={error} ctaText="Does your video look good?" />
58
+ </>
59
+ );
60
+ };
@@ -0,0 +1,84 @@
1
+ import React, { useContext } from 'react';
2
+ import { Button } from '../Button';
3
+ import { Box, Flex } from '../Layout';
4
+ import { Text } from '../Text';
5
+ import { CSS } from '../Theme';
6
+ import { hmsDiagnostics } from './hms';
7
+
8
+ export const DiagnosticsSteps: Record<string, string> = {
9
+ video: 'Test Video',
10
+ audio: 'Test Audio',
11
+ // browser: 'Browser Support',
12
+ connectivity: 'Connection Quality',
13
+ };
14
+
15
+ export const DiagnosticsContext = React.createContext<{
16
+ activeStep: string;
17
+ setActiveStep: React.Dispatch<React.SetStateAction<string>>;
18
+ }>({
19
+ activeStep: 'video',
20
+ setActiveStep: () => {
21
+ return;
22
+ },
23
+ });
24
+
25
+ export const TestContainer = ({ css, children }: { css?: CSS; children: React.ReactNode }) => {
26
+ return <Box css={{ p: '$10', ...css }}>{children}</Box>;
27
+ };
28
+
29
+ export const TestFooter = ({
30
+ error,
31
+ ctaText,
32
+ children,
33
+ }: {
34
+ ctaText?: string;
35
+ error?: Error;
36
+ children?: React.ReactNode;
37
+ }) => {
38
+ const { activeStep, setActiveStep } = useContext(DiagnosticsContext);
39
+
40
+ const onNextStep = () => {
41
+ if (activeStep === 'audio') {
42
+ hmsDiagnostics.stopMicCheck();
43
+ } else if (activeStep === 'video') {
44
+ hmsDiagnostics.stopCameraCheck();
45
+ } else if (activeStep === 'connectivity') {
46
+ hmsDiagnostics.stopConnectivityCheck();
47
+ }
48
+
49
+ const keys = Object.keys(DiagnosticsSteps);
50
+ setActiveStep(step => keys[keys.indexOf(step) + 1]);
51
+ };
52
+
53
+ return (
54
+ <Flex
55
+ css={{
56
+ py: '$8',
57
+ px: '$10',
58
+ justifyContent: 'space-between',
59
+ alignItems: 'center',
60
+ borderTop: '1px solid $border_default',
61
+ fontSize: '$sm',
62
+ lineHeight: '$sm',
63
+ '@lg': { flexDirection: 'column', gap: '$8' },
64
+ }}
65
+ >
66
+ <Box>{error && <Text css={{ c: '$alert_error_default' }}>Error: {error.message}</Text>}</Box>
67
+ {children ? (
68
+ children
69
+ ) : (
70
+ <Flex align="center" css={{ gap: '$8', '@lg': { flexDirection: 'column' } }}>
71
+ <Text css={{ c: '$on_primary_medium' }}>{ctaText}</Text>
72
+ <Flex align="center" gap="4">
73
+ <Button onClick={onNextStep} variant="standard" outlined={true}>
74
+ Skip
75
+ </Button>
76
+ <Button disabled={!!error} onClick={onNextStep}>
77
+ Yes
78
+ </Button>
79
+ </Flex>
80
+ </Flex>
81
+ )}
82
+ </Flex>
83
+ );
84
+ };
@@ -0,0 +1,9 @@
1
+ // @ts-check
2
+ import { HMSReactiveStore } from '@100mslive/hms-video-store';
3
+
4
+ const hms = new HMSReactiveStore();
5
+ export const hmsStore = hms.getStore();
6
+ export const hmsActions = hms.getActions();
7
+ export const hmsNotifications = hms.getNotifications();
8
+ export const hmsStats = hms.getStats();
9
+ export const hmsDiagnostics = hms.getDiagnosticsSDK();
@@ -0,0 +1 @@
1
+ export { Diagnostics } from './Diagnostics';
@@ -1,5 +1,10 @@
1
1
  import React, { MutableRefObject, useEffect, useRef } from 'react';
2
- import { HMSStatsStoreWrapper, HMSStoreWrapper, IHMSNotifications } from '@100mslive/hms-video-store';
2
+ import {
3
+ HMSDiagnosticsInterface,
4
+ HMSStatsStoreWrapper,
5
+ HMSStoreWrapper,
6
+ IHMSNotifications,
7
+ } from '@100mslive/hms-video-store';
3
8
  import { Layout, Logo, Screens, Theme, Typography } from '@100mslive/types-prebuilt';
4
9
  import { match } from 'ts-pattern';
5
10
  import {
@@ -65,6 +70,7 @@ export type HMSPrebuiltProps = {
65
70
  options?: HMSPrebuiltOptions;
66
71
  screens?: Screens;
67
72
  authToken?: string;
73
+ leaveOnUnload?: boolean;
68
74
  onLeave?: () => void;
69
75
  onJoin?: () => void;
70
76
  /**
@@ -79,6 +85,7 @@ export type HMSPrebuiltRefType = {
79
85
  hmsStore: HMSStoreWrapper;
80
86
  hmsStats: HMSStatsStoreWrapper;
81
87
  hmsNotifications: IHMSNotifications;
88
+ hmsDiagnostics: HMSDiagnosticsInterface;
82
89
  };
83
90
 
84
91
  export const HMSPrebuilt = React.forwardRef<HMSPrebuiltRefType, HMSPrebuiltProps>(
@@ -92,6 +99,7 @@ export const HMSPrebuilt = React.forwardRef<HMSPrebuiltRefType, HMSPrebuiltProps
92
99
  themes,
93
100
  options: { userName = '', userId = '', endpoints } = {},
94
101
  screens,
102
+ leaveOnUnload = true,
95
103
  onLeave,
96
104
  onJoin,
97
105
  },
@@ -107,6 +115,7 @@ export const HMSPrebuilt = React.forwardRef<HMSPrebuiltRefType, HMSPrebuiltProps
107
115
  const hmsActions = hms.getActions();
108
116
  const hmsNotifications = hms.getNotifications();
109
117
  const hmsStats = hms.getStats();
118
+ const hmsDiagnostics = hms.getDiagnosticsSDK();
110
119
  hms.triggerOnSubscribe();
111
120
 
112
121
  reactiveStore.current = {
@@ -114,6 +123,7 @@ export const HMSPrebuilt = React.forwardRef<HMSPrebuiltRefType, HMSPrebuiltProps
114
123
  hmsStats,
115
124
  hmsStore,
116
125
  hmsNotifications,
126
+ hmsDiagnostics,
117
127
  };
118
128
  }, []);
119
129
 
@@ -188,6 +198,7 @@ export const HMSPrebuilt = React.forwardRef<HMSPrebuiltRefType, HMSPrebuiltProps
188
198
  store={reactiveStore.current?.hmsStore}
189
199
  notifications={reactiveStore.current?.hmsNotifications}
190
200
  stats={reactiveStore.current?.hmsStats}
201
+ leaveOnUnload={leaveOnUnload}
191
202
  >
192
203
  <RoomLayoutProvider roomLayoutEndpoint={roomLayoutEndpoint} overrideLayout={overrideLayout}>
193
204
  <RoomLayoutContext.Consumer>
@@ -22,6 +22,8 @@ import { useEmojiPickerStyles } from './useEmojiPickerStyles';
22
22
  import { useDefaultChatSelection, useLandscapeHLSStream, useMobileHLSStream } from '../../common/hooks';
23
23
  import { CHAT_SELECTOR, SESSION_STORE_KEY } from '../../common/constants';
24
24
 
25
+ const CHAT_MESSAGE_LIMIT = 2000;
26
+
25
27
  const TextArea = styled('textarea', {
26
28
  width: '100%',
27
29
  bg: 'transparent',
@@ -90,6 +92,7 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo
90
92
  const selection = selectedPeer.name || selectedRole || defaultSelection;
91
93
  const isLocalPeerBlacklisted = useIsPeerBlacklisted({ local: true });
92
94
  const isMwebHLSStream = useMobileHLSStream();
95
+ const [messageLengthExceeded, setMessageLengthExceeded] = useState(false);
93
96
  const isLandscapeHLSStream = useLandscapeHLSStream();
94
97
 
95
98
  useEffect(() => {
@@ -146,6 +149,7 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo
146
149
  if (messageElement) {
147
150
  messageElement.value = draftMessage;
148
151
  updateInputHeight();
152
+ setMessageLengthExceeded(draftMessage.length > CHAT_MESSAGE_LIMIT);
149
153
  }
150
154
  }, [draftMessage]);
151
155
 
@@ -230,6 +234,7 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo
230
234
  >
231
235
  {children}
232
236
  <TextArea
237
+ maxLength={CHAT_MESSAGE_LIMIT + 10}
233
238
  css={{
234
239
  c: '$on_surface_high',
235
240
  '&:valid ~ .send-msg': { color: '$on_surface_high' },
@@ -243,7 +248,7 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo
243
248
  autoFocus={!(isMobile || isLandscapeHLSStream)}
244
249
  onKeyPress={async event => {
245
250
  if (event.key === 'Enter') {
246
- if (!event.shiftKey) {
251
+ if (!event.shiftKey && !messageLengthExceeded) {
247
252
  event.preventDefault();
248
253
  await sendMessage();
249
254
  }
@@ -251,7 +256,10 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo
251
256
  }}
252
257
  autoComplete="off"
253
258
  aria-autocomplete="none"
254
- onChange={updateInputHeight}
259
+ onChange={e => {
260
+ updateInputHeight();
261
+ setMessageLengthExceeded(e.target.value.length > CHAT_MESSAGE_LIMIT);
262
+ }}
255
263
  onBlur={resetInputHeight}
256
264
  onPaste={e => e.stopPropagation()}
257
265
  onCut={e => e.stopPropagation()}
@@ -269,6 +277,7 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo
269
277
  <BaseIconButton
270
278
  className="send-msg"
271
279
  onClick={sendMessage}
280
+ disabled={messageLengthExceeded}
272
281
  css={{
273
282
  ml: 'auto',
274
283
  height: 'max-content',
@@ -295,6 +304,11 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo
295
304
  )}
296
305
  </Flex>
297
306
  )}
307
+ {messageLengthExceeded && (
308
+ <Text variant="xs" css={{ color: '$alert_error_default', fontWeight: '$semiBold', mt: '$1', ml: '$7' }}>
309
+ Message cannot exceed 2000 characters
310
+ </Text>
311
+ )}
298
312
  </Box>
299
313
  );
300
314
  };
@@ -13,6 +13,7 @@ import { Dropdown } from '../../Dropdown';
13
13
  import { Label } from '../../Label';
14
14
  import { Box, Flex } from '../../Layout';
15
15
  import { Dialog } from '../../Modal';
16
+ import { formatBytes } from '../../Stats';
16
17
  import { Switch } from '../../Switch';
17
18
  import { Text } from '../../Text';
18
19
  import { DialogDropdownTrigger } from '../primitives/DropdownTrigger';
@@ -245,16 +246,3 @@ const StatsRow = React.memo(({ label, value }) => (
245
246
  </Text>
246
247
  </Box>
247
248
  ));
248
-
249
- const formatBytes = (bytes, unit = 'B', decimals = 2) => {
250
- if (bytes === undefined) return '-';
251
- if (bytes === 0) return '0 ' + unit;
252
-
253
- const k = 1024;
254
- const dm = decimals < 0 ? 0 : decimals;
255
- const sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'].map(size => size + unit);
256
-
257
- const i = Math.floor(Math.log(bytes) / Math.log(k));
258
-
259
- return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
260
- };
@@ -1 +1,2 @@
1
1
  export { VideoTileStats } from './Stats';
2
+ export { formatBytes } from './formatBytes';
package/src/index.ts CHANGED
@@ -38,3 +38,4 @@ export * from './Prebuilt';
38
38
  export * from './Progress';
39
39
  export * from './context/DialogContext';
40
40
  export * from './TextArea';
41
+ export * from './Diagnostics';