@100mslive/roomkit-react 0.1.4-alpha.1 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (152) hide show
  1. package/dist/{HLSView-F2K5VSTS.js → HLSView-CTAJQUU4.js} +7 -11
  2. package/dist/{HLSView-F2K5VSTS.js.map → HLSView-CTAJQUU4.js.map} +1 -1
  3. package/dist/PinnedTrackView-CQKONH4O.js +102 -0
  4. package/dist/PinnedTrackView-CQKONH4O.js.map +7 -0
  5. package/dist/Popover/index.d.ts +1 -0
  6. package/dist/Prebuilt/App.d.ts +25 -0
  7. package/dist/Prebuilt/index.d.ts +1 -0
  8. package/dist/Prebuilt/provider/roomLayoutProvider/index.d.ts +1 -1
  9. package/dist/Sheet/Sheet.d.ts +3093 -0
  10. package/dist/Sheet/index.d.ts +1 -0
  11. package/dist/Theme/ThemeProvider.d.ts +4 -286
  12. package/dist/Theme/stitches.config.d.ts +1 -1
  13. package/dist/{VirtualBackground-S3XEPZ2T.js → VirtualBackground-GGGBJYVY.js} +31 -7
  14. package/dist/VirtualBackground-GGGBJYVY.js.map +7 -0
  15. package/dist/chunk-I2FJWE74.js +827 -0
  16. package/dist/chunk-I2FJWE74.js.map +7 -0
  17. package/dist/{chunk-42SWPN2C.js → chunk-L2SX7GBO.js} +3020 -2189
  18. package/dist/chunk-L2SX7GBO.js.map +7 -0
  19. package/dist/chunk-NOKIGB6Y.js +1100 -0
  20. package/dist/chunk-NOKIGB6Y.js.map +7 -0
  21. package/dist/chunk-TJNDX446.js +67 -0
  22. package/dist/chunk-TJNDX446.js.map +7 -0
  23. package/dist/{chunk-ESUJK7AT.js → conference-OEO7VOJD.js} +3136 -653
  24. package/dist/conference-OEO7VOJD.js.map +7 -0
  25. package/dist/index.cjs.js +15733 -15498
  26. package/dist/index.cjs.js.map +4 -4
  27. package/dist/index.js +4 -8
  28. package/dist/meta.cjs.json +3355 -3017
  29. package/dist/meta.esbuild.json +3534 -3329
  30. package/dist/utils/animations.d.ts +16 -0
  31. package/package.json +8 -10
  32. package/src/Button/Button.tsx +4 -4
  33. package/src/Dropdown/Dropdown.tsx +2 -2
  34. package/src/IconButton/IconButton.tsx +4 -2
  35. package/src/Pagination/StyledPagination.tsx +1 -0
  36. package/src/Popover/index.tsx +2 -1
  37. package/src/Prebuilt/{App.jsx → App.tsx} +95 -48
  38. package/src/Prebuilt/Prebuilt.stories.tsx +22 -8
  39. package/src/Prebuilt/common/constants.js +1 -2
  40. package/src/Prebuilt/common/hooks.js +8 -0
  41. package/src/Prebuilt/common/utils.js +15 -0
  42. package/src/Prebuilt/components/AppData/AppData.jsx +1 -2
  43. package/src/Prebuilt/components/AppData/useUISettings.js +0 -5
  44. package/src/Prebuilt/components/AudioVideoToggle.jsx +69 -26
  45. package/src/Prebuilt/components/AuthToken.jsx +3 -2
  46. package/src/Prebuilt/components/Chat/ChatSelector.jsx +1 -1
  47. package/src/Prebuilt/components/Connection/TileConnection.jsx +0 -1
  48. package/src/Prebuilt/components/EmojiReaction.jsx +23 -73
  49. package/src/Prebuilt/components/EndSessionContent.jsx +57 -0
  50. package/src/Prebuilt/components/EqualProminence.jsx +180 -0
  51. package/src/Prebuilt/components/ErrorBoundary.jsx +4 -10
  52. package/src/Prebuilt/components/Footer/EmojiCard.jsx +34 -0
  53. package/src/Prebuilt/components/Footer/Footer.jsx +73 -0
  54. package/src/Prebuilt/components/{Header → Footer}/ParticipantList.jsx +5 -5
  55. package/src/Prebuilt/components/Header/ConferencingHeader.jsx +27 -7
  56. package/src/Prebuilt/components/Header/HeaderComponents.jsx +16 -14
  57. package/src/Prebuilt/components/Header/StreamActions.jsx +101 -36
  58. package/src/Prebuilt/components/Header/StreamingHeader.jsx +1 -1
  59. package/src/Prebuilt/components/Header/common.jsx +164 -0
  60. package/src/Prebuilt/components/IconButtonWithOptions/IconButtonWithOptions.jsx +1 -2
  61. package/src/Prebuilt/components/LeaveCard.jsx +19 -0
  62. package/src/Prebuilt/components/LeaveRoom.jsx +35 -143
  63. package/src/Prebuilt/components/LeaveSessionContent.jsx +45 -0
  64. package/src/Prebuilt/components/MoreSettings/ActionTile.jsx +55 -0
  65. package/src/Prebuilt/components/MoreSettings/ChangeNameContent.jsx +96 -0
  66. package/src/Prebuilt/components/MoreSettings/ChangeNameModal.jsx +31 -54
  67. package/src/Prebuilt/components/MoreSettings/EmbedUrl.jsx +48 -73
  68. package/src/Prebuilt/components/MoreSettings/MoreSettings.jsx +5 -221
  69. package/src/Prebuilt/components/MoreSettings/MuteAllContent.jsx +61 -0
  70. package/src/Prebuilt/components/MoreSettings/MuteAllModal.jsx +32 -49
  71. package/src/Prebuilt/components/MoreSettings/SplitComponents/DesktopLeaveRoom.jsx +129 -0
  72. package/src/Prebuilt/components/MoreSettings/SplitComponents/DesktopOptions.jsx +219 -0
  73. package/src/Prebuilt/components/MoreSettings/SplitComponents/MwebLeaveRoom.jsx +100 -0
  74. package/src/Prebuilt/components/MoreSettings/SplitComponents/MwebOptions.jsx +259 -0
  75. package/src/Prebuilt/components/Notifications/Notifications.jsx +0 -2
  76. package/src/Prebuilt/components/Notifications/ReconnectNotifications.jsx +0 -4
  77. package/src/Prebuilt/components/PIP/PIPComponent.jsx +30 -26
  78. package/src/Prebuilt/components/PIP/PIPManager.js +13 -0
  79. package/src/Prebuilt/components/PIP/index.jsx +2 -7
  80. package/src/Prebuilt/components/Pagination.jsx +4 -4
  81. package/src/Prebuilt/components/Preview/PreviewContainer.jsx +5 -13
  82. package/src/Prebuilt/components/Preview/PreviewForm.jsx +9 -5
  83. package/src/Prebuilt/components/Preview/PreviewJoin.jsx +20 -27
  84. package/src/Prebuilt/components/RaiseHand.jsx +27 -0
  85. package/src/Prebuilt/components/ScreenShare.jsx +1 -1
  86. package/src/Prebuilt/components/ScreenshareDisplay.jsx +2 -2
  87. package/src/Prebuilt/components/ScreenshareTile.jsx +2 -2
  88. package/src/Prebuilt/components/Settings/DeviceSettings.jsx +2 -1
  89. package/src/Prebuilt/components/Settings/LayoutSettings.jsx +1 -24
  90. package/src/Prebuilt/components/Settings/SettingsModal.jsx +152 -17
  91. package/src/Prebuilt/components/ShareMenuIcon.jsx +1 -0
  92. package/src/Prebuilt/components/TileMenu/TileMenu.jsx +133 -0
  93. package/src/Prebuilt/components/TileMenu/TileMenuContent.jsx +313 -0
  94. package/src/Prebuilt/components/VideoList.jsx +5 -33
  95. package/src/Prebuilt/components/VideoTile.jsx +30 -8
  96. package/src/Prebuilt/components/conference.jsx +14 -1
  97. package/src/Prebuilt/components/init/Init.jsx +0 -27
  98. package/src/Prebuilt/components/init/initUtils.js +0 -23
  99. package/src/Prebuilt/components/pdfAnnotator/pdfFileOptions.jsx +2 -1
  100. package/src/Prebuilt/components/pdfAnnotator/pdfInfo.jsx +1 -1
  101. package/src/Prebuilt/components/pdfAnnotator/shareScreenOptions.jsx +19 -8
  102. package/src/Prebuilt/components/pdfAnnotator/uploadedFile.jsx +1 -0
  103. package/src/Prebuilt/images/pdf-share.png +0 -0
  104. package/src/Prebuilt/images/screen-share.png +0 -0
  105. package/src/Prebuilt/index.ts +1 -0
  106. package/src/Prebuilt/layouts/EmbedView.jsx +0 -1
  107. package/src/Prebuilt/layouts/InsetView.jsx +65 -24
  108. package/src/Prebuilt/layouts/PDFView.jsx +0 -1
  109. package/src/Prebuilt/layouts/SidePane.jsx +8 -7
  110. package/src/Prebuilt/layouts/mainView.jsx +22 -31
  111. package/src/Prebuilt/layouts/screenShareView.jsx +0 -2
  112. package/src/Prebuilt/plugins/VirtualBackground/VirtualBackground.jsx +25 -1
  113. package/src/Prebuilt/primitives/DialogContent.jsx +1 -1
  114. package/src/Prebuilt/provider/roomLayoutProvider/index.tsx +1 -1
  115. package/src/Sheet/Sheet.mdx +19 -0
  116. package/src/Sheet/Sheet.stories.tsx +103 -0
  117. package/src/Sheet/Sheet.tsx +118 -0
  118. package/src/Sheet/index.ts +1 -0
  119. package/src/Theme/ThemeProvider.tsx +10 -13
  120. package/src/Theme/base.config.ts +1 -1
  121. package/src/Theme/stitches.config.ts +1 -1
  122. package/src/TileMenu/StyledMenuTile.tsx +2 -2
  123. package/src/TileMenu/TileMenu.tsx +2 -0
  124. package/src/VideoTile/StyledVideoTile.tsx +5 -0
  125. package/src/utils/animations.ts +18 -0
  126. package/dist/ActiveSpeakerView-V6O4K3BV.js +0 -39
  127. package/dist/ActiveSpeakerView-V6O4K3BV.js.map +0 -7
  128. package/dist/PinnedTrackView-7YQG4QKC.js +0 -70
  129. package/dist/PinnedTrackView-7YQG4QKC.js.map +0 -7
  130. package/dist/VirtualBackground-S3XEPZ2T.js.map +0 -7
  131. package/dist/chunk-42SWPN2C.js.map +0 -7
  132. package/dist/chunk-4NEZLVVH.js +0 -811
  133. package/dist/chunk-4NEZLVVH.js.map +0 -7
  134. package/dist/chunk-4ZBEFSRC.js +0 -58
  135. package/dist/chunk-4ZBEFSRC.js.map +0 -7
  136. package/dist/chunk-ESUJK7AT.js.map +0 -7
  137. package/dist/chunk-R6PDR5WZ.js +0 -243
  138. package/dist/chunk-R6PDR5WZ.js.map +0 -7
  139. package/dist/conference-7QKOMJPP.js +0 -3697
  140. package/dist/conference-7QKOMJPP.js.map +0 -7
  141. package/dist/transcription-RJA4V6PC.js +0 -356
  142. package/dist/transcription-RJA4V6PC.js.map +0 -7
  143. package/src/Prebuilt/common/useSortedPeers.js +0 -28
  144. package/src/Prebuilt/components/BottomActionSheet/BottomActionSheet.jsx +0 -96
  145. package/src/Prebuilt/components/BottomActionSheet/BottomActionSheet.stories.tsx +0 -46
  146. package/src/Prebuilt/components/Footer/ConferencingFooter.jsx +0 -101
  147. package/src/Prebuilt/components/Footer/StreamingFooter.jsx +0 -71
  148. package/src/Prebuilt/components/Footer.jsx +0 -8
  149. package/src/Prebuilt/components/MoreSettings/ChangeSelfRole.jsx +0 -67
  150. package/src/Prebuilt/components/TileMenu.jsx +0 -268
  151. package/src/Prebuilt/index.d.ts +0 -20
  152. package/src/Prebuilt/index.js +0 -2
@@ -0,0 +1,73 @@
1
+ import React from 'react';
2
+ import { useMedia } from 'react-use';
3
+ import { selectLocalPeerRoleName, useHMSStore } from '@100mslive/react-sdk';
4
+ import { config as cssConfig, Footer as AppFooter } from '../../..';
5
+ import { AudioVideoToggle } from '../AudioVideoToggle';
6
+ import { EmojiReaction } from '../EmojiReaction';
7
+ import { LeaveRoom } from '../LeaveRoom';
8
+ import { MoreSettings } from '../MoreSettings/MoreSettings';
9
+ import { RaiseHand } from '../RaiseHand';
10
+ import { ScreenshareToggle } from '../ScreenShare';
11
+ import { ChatToggle } from './ChatToggle';
12
+ import { ParticipantCount } from './ParticipantList';
13
+ import { useHLSViewerRole } from '../AppData/useUISettings';
14
+
15
+ export const Footer = () => {
16
+ const isMobile = useMedia(cssConfig.media.md);
17
+ const localPeerRole = useHMSStore(selectLocalPeerRoleName);
18
+ const hlsViewerRole = useHLSViewerRole();
19
+ const isHlsViewer = hlsViewerRole === localPeerRole;
20
+
21
+ return (
22
+ <AppFooter.Root
23
+ css={{
24
+ flexWrap: 'nowrap',
25
+ '@md': {
26
+ justifyContent: 'center',
27
+ gap: '$10',
28
+ },
29
+ }}
30
+ >
31
+ <AppFooter.Left
32
+ css={{
33
+ '@md': {
34
+ w: 'unset',
35
+ p: '0',
36
+ gap: '$10',
37
+ },
38
+ }}
39
+ >
40
+ {isMobile ? <LeaveRoom /> : null}
41
+ <AudioVideoToggle />
42
+ </AppFooter.Left>
43
+ <AppFooter.Center
44
+ css={{
45
+ '@md': {
46
+ w: 'unset',
47
+ gap: '$10',
48
+ },
49
+ }}
50
+ >
51
+ {isMobile ? (
52
+ <>
53
+ {isHlsViewer ? <RaiseHand /> : null}
54
+ <ChatToggle />
55
+ <MoreSettings />
56
+ </>
57
+ ) : (
58
+ <>
59
+ <ScreenshareToggle />
60
+ {isHlsViewer ? <RaiseHand /> : null}
61
+ <EmojiReaction />
62
+ <LeaveRoom />
63
+ </>
64
+ )}
65
+ </AppFooter.Center>
66
+ <AppFooter.Right>
67
+ <ChatToggle />
68
+ <ParticipantCount />
69
+ <MoreSettings />
70
+ </AppFooter.Right>
71
+ </AppFooter.Root>
72
+ );
73
+ };
@@ -21,11 +21,11 @@ import {
21
21
  SpeakerIcon,
22
22
  VerticalMenuIcon,
23
23
  } from '@100mslive/react-icons';
24
- import { Avatar, Box, Dropdown, Flex, Input, Slider, Text, textEllipsis } from '../../../';
24
+ import { Avatar, Box, Dropdown, Flex, Input, Slider, Text, textEllipsis } from '../../..';
25
25
  import IconButton from '../../IconButton';
26
26
  import { ConnectionIndicator } from '../Connection/ConnectionIndicator';
27
+ import { ParticipantFilter } from '../Header/ParticipantFilter';
27
28
  import { RoleChangeModal } from '../RoleChangeModal';
28
- import { ParticipantFilter } from './ParticipantFilter';
29
29
  import { useIsSidepaneTypeOpen, useSidepaneToggle } from '../AppData/useSidepane';
30
30
  import { isInternalRole } from '../../common/utils';
31
31
  import { SIDE_PANE_OPTIONS } from '../../common/constants';
@@ -232,7 +232,7 @@ const ParticipantMoreActions = ({ onRoleChange, peerId }) => {
232
232
  const actions = useHMSActions();
233
233
  const [open, setOpen] = useState(false);
234
234
  return (
235
- <Dropdown.Root open={open} onOpenChange={value => setOpen(value)}>
235
+ <Dropdown.Root open={open} onOpenChange={value => setOpen(value)} modal={false}>
236
236
  <Dropdown.Trigger asChild data-testid="participant_more_actions" css={{ p: '$2', r: '$0' }} tabIndex={0}>
237
237
  <Text>
238
238
  <VerticalMenuIcon />
@@ -320,8 +320,8 @@ export const ParticipantSearch = ({ onSearch, placeholder }) => {
320
320
  </Box>
321
321
  <Input
322
322
  type="text"
323
- placeholder={placeholder || 'Find what you are looking for'}
324
- css={{ w: '100%', pl: '$14' }}
323
+ placeholder={placeholder || 'Search among participants'}
324
+ css={{ w: '100%', pl: '$14', bg: '$surface_bright' }}
325
325
  value={value}
326
326
  onKeyDown={event => {
327
327
  event.stopPropagation();
@@ -1,16 +1,31 @@
1
1
  import React from 'react';
2
- import { Flex } from '../../../';
3
- import { SpeakerTag } from './HeaderComponents';
4
- import { ParticipantCount } from './ParticipantList';
5
- import { StreamActions } from './StreamActions';
2
+ import { useMedia } from 'react-use';
3
+ import { HMSRoomState, selectRoomState, useHMSStore } from '@100mslive/react-sdk';
4
+ import { config as cssConfig, Flex } from '../../../';
5
+ import { Logo, SpeakerTag } from './HeaderComponents';
6
+ import { LiveStatus, RecordingStatus, StreamActions } from './StreamActions';
7
+ import { AudioOutputActions, CamaraFlipActions } from './common';
6
8
 
7
9
  export const ConferencingHeader = () => {
10
+ const roomState = useHMSStore(selectRoomState);
11
+ const isMobile = useMedia(cssConfig.media.md);
12
+ const isPreview = roomState === HMSRoomState.Preview;
13
+ // no header if there in preview
14
+ if (isPreview) {
15
+ return <></>;
16
+ }
8
17
  return (
9
18
  <Flex justify="between" align="center" css={{ position: 'relative', height: '100%' }}>
10
- <Flex align="center" css={{ position: 'absolute', left: '$10' }}>
19
+ <Flex align="center" gap="2" css={{ position: 'absolute', left: '$10' }}>
20
+ <Logo />
11
21
  <SpeakerTag />
22
+ {isMobile && (
23
+ <Flex align="center" css={{ gap: '$4' }}>
24
+ <LiveStatus />
25
+ <RecordingStatus />
26
+ </Flex>
27
+ )}
12
28
  </Flex>
13
-
14
29
  <Flex
15
30
  align="center"
16
31
  css={{
@@ -20,7 +35,12 @@ export const ConferencingHeader = () => {
20
35
  }}
21
36
  >
22
37
  <StreamActions />
23
- <ParticipantCount />
38
+ {isMobile && (
39
+ <>
40
+ <CamaraFlipActions />
41
+ <AudioOutputActions />
42
+ </>
43
+ )}
24
44
  </Flex>
25
45
  </Flex>
26
46
  );
@@ -2,25 +2,27 @@ import React from 'react';
2
2
  import { useMedia } from 'react-use';
3
3
  import { selectDominantSpeaker, selectIsConnectedToRoom, useHMSStore } from '@100mslive/react-sdk';
4
4
  import { VolumeOneIcon } from '@100mslive/react-icons';
5
- import { config as cssConfig, Flex, styled, Text, textEllipsis } from '../../../';
5
+ import { config as cssConfig, Flex, styled, Text, textEllipsis, VerticalDivider } from '../../../';
6
6
  import { useRoomLayout } from '../../provider/roomLayoutProvider';
7
7
  import { isStreamingKit } from '../../common/utils';
8
8
 
9
9
  export const SpeakerTag = () => {
10
10
  const dominantSpeaker = useHMSStore(selectDominantSpeaker);
11
- return dominantSpeaker && dominantSpeaker.name ? (
12
- <Flex
13
- align="center"
14
- justify="center"
15
- css={{ flex: '1 1 0', color: '$on_primary_high', '@md': { display: 'none' } }}
16
- >
17
- <VolumeOneIcon />
18
- <Text variant="md" css={{ ...textEllipsis(200), ml: '$2' }} title={dominantSpeaker.name}>
19
- {dominantSpeaker.name}
20
- </Text>
21
- </Flex>
22
- ) : (
23
- <></>
11
+ return (
12
+ dominantSpeaker &&
13
+ dominantSpeaker.name && (
14
+ <Flex
15
+ align="center"
16
+ justify="center"
17
+ css={{ flex: '1 1 0', color: '$on_primary_high', '@md': { display: 'none' } }}
18
+ >
19
+ <VerticalDivider css={{ ml: '$8' }} />
20
+ <VolumeOneIcon />
21
+ <Text variant="md" css={{ ...textEllipsis(200), ml: '$2' }} title={dominantSpeaker.name}>
22
+ {dominantSpeaker.name}
23
+ </Text>
24
+ </Flex>
25
+ )
24
26
  );
25
27
  };
26
28
 
@@ -1,7 +1,8 @@
1
- import React, { Fragment, useState } from 'react';
1
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
2
2
  import { useMedia } from 'react-use';
3
3
  import {
4
4
  HMSRoomState,
5
+ selectHLSState,
5
6
  selectIsConnectedToRoom,
6
7
  selectPermissions,
7
8
  selectRoomState,
@@ -9,31 +10,65 @@ import {
9
10
  useHMSStore,
10
11
  useRecordingStreaming,
11
12
  } from '@100mslive/react-sdk';
12
- import { RecordIcon, WrenchIcon } from '@100mslive/react-icons';
13
- import { Box, Button, config as cssConfig, Flex, Loading, Popover, Text, Tooltip } from '../../../';
14
- import GoLiveButton from '../GoLiveButton';
13
+ import { AlertTriangleIcon, CrossIcon, RecordIcon } from '@100mslive/react-icons';
14
+ import { Box, Button, config as cssConfig, Flex, HorizontalDivider, Loading, Popover, Text, Tooltip } from '../../../';
15
+ import { Sheet } from '../../../Sheet';
15
16
  import { ResolutionInput } from '../Streaming/ResolutionInput';
16
17
  import { getResolution } from '../Streaming/RTMPStreaming';
17
18
  import { ToastManager } from '../Toast/ToastManager';
18
19
  import { AdditionalRoomState, getRecordingText } from './AdditionalRoomState';
19
- import { useSidepaneToggle } from '../AppData/useSidepane';
20
20
  import { useSetAppDataByKey } from '../AppData/useUISettings';
21
- import { APP_DATA, RTMP_RECORD_DEFAULT_RESOLUTION, SIDE_PANE_OPTIONS } from '../../common/constants';
21
+ import { formatTime } from '../../common/utils';
22
+ import { APP_DATA, RTMP_RECORD_DEFAULT_RESOLUTION } from '../../common/constants';
22
23
 
23
24
  export const LiveStatus = () => {
24
25
  const { isHLSRunning, isRTMPRunning } = useRecordingStreaming();
26
+ const hlsState = useHMSStore(selectHLSState);
27
+ const isMobile = useMedia(cssConfig.media.md);
28
+ const intervalRef = useRef(null);
29
+
30
+ const [liveTime, setLiveTime] = useState(0);
31
+
32
+ const startTimer = useCallback(() => {
33
+ intervalRef.current = setInterval(() => {
34
+ if (hlsState?.running) {
35
+ setLiveTime(Date.now() - hlsState?.variants[0]?.startedAt.getTime());
36
+ }
37
+ }, 1000);
38
+ }, [hlsState?.running, hlsState?.variants]);
39
+
40
+ useEffect(() => {
41
+ if (hlsState?.running) {
42
+ startTimer();
43
+ }
44
+ if (!hlsState?.running && intervalRef.current) {
45
+ clearInterval(intervalRef.current);
46
+ }
47
+ return () => {
48
+ if (intervalRef.current) {
49
+ clearInterval(intervalRef.current);
50
+ }
51
+ };
52
+ }, [hlsState.running, isMobile, startTimer]);
53
+
25
54
  if (!isHLSRunning && !isRTMPRunning) {
26
55
  return null;
27
56
  }
28
57
  return (
29
- <Flex align="center">
58
+ <Flex
59
+ align="center"
60
+ gap="1"
61
+ css={{
62
+ border: '1px solid $border_default',
63
+ padding: '$4 $6 $4 $6',
64
+ borderRadius: '$1',
65
+ }}
66
+ >
30
67
  <Box css={{ w: '$4', h: '$4', r: '$round', bg: '$alert_error_default', mr: '$2' }} />
31
- <Text>
32
- Live
33
- <Text as="span" css={{ '@md': { display: 'none' } }}>
34
- &nbsp;with {isHLSRunning ? 'HLS' : 'RTMP'}
35
- </Text>
36
- </Text>
68
+ <Flex align="center" gap="2">
69
+ <Text variant={!isMobile ? 'button' : 'body2'}>LIVE</Text>
70
+ <Text variant="caption">{hlsState?.variants?.length > 0 ? formatTime(liveTime) : ''}</Text>
71
+ </Flex>
37
72
  </Flex>
38
73
  );
39
74
  };
@@ -41,6 +76,7 @@ export const LiveStatus = () => {
41
76
  export const RecordingStatus = () => {
42
77
  const { isBrowserRecordingOn, isServerRecordingOn, isHLSRecordingOn, isRecordingOn } = useRecordingStreaming();
43
78
  const permissions = useHMSStore(selectPermissions);
79
+ const isMobile = useMedia(cssConfig.media.md);
44
80
 
45
81
  if (
46
82
  !isRecordingOn ||
@@ -50,8 +86,10 @@ export const RecordingStatus = () => {
50
86
  value => !!value,
51
87
  )
52
88
  ) {
53
- return null;
89
+ // show recording icon in mobile without popover
90
+ if (!(isMobile && isRecordingOn)) return null;
54
91
  }
92
+
55
93
  return (
56
94
  <Tooltip
57
95
  title={getRecordingText({
@@ -60,28 +98,18 @@ export const RecordingStatus = () => {
60
98
  isHLSRecordingOn,
61
99
  })}
62
100
  >
63
- <Box
101
+ <Flex
64
102
  css={{
65
103
  color: '$alert_error_default',
104
+ alignItems: 'center',
66
105
  }}
67
106
  >
68
107
  <RecordIcon width={24} height={24} />
69
- </Box>
108
+ </Flex>
70
109
  </Tooltip>
71
110
  );
72
111
  };
73
112
 
74
- const EndStream = () => {
75
- const toggleStreaming = useSidepaneToggle(SIDE_PANE_OPTIONS.STREAMING);
76
-
77
- return (
78
- <Button data-testid="end_stream" variant="danger" icon onClick={toggleStreaming}>
79
- <WrenchIcon />
80
- Manage Stream
81
- </Button>
82
- );
83
- };
84
-
85
113
  const StartRecording = () => {
86
114
  const permissions = useHMSStore(selectPermissions);
87
115
  const [resolution, setResolution] = useState(RTMP_RECORD_DEFAULT_RESOLUTION);
@@ -192,24 +220,61 @@ const StartRecording = () => {
192
220
  );
193
221
  };
194
222
 
223
+ /**
224
+ * @description only start recording button will be shown.
225
+ */
195
226
  export const StreamActions = () => {
196
227
  const isConnected = useHMSStore(selectIsConnectedToRoom);
197
- const permissions = useHMSStore(selectPermissions);
198
228
  const isMobile = useMedia(cssConfig.media.md);
199
- const { isStreamingOn } = useRecordingStreaming();
200
229
  const roomState = useHMSStore(selectRoomState);
201
230
 
202
231
  return (
203
232
  <Flex align="center" css={{ gap: '$4' }}>
204
233
  <AdditionalRoomState />
205
- <Flex align="center" css={{ gap: '$4', '@md': { display: 'none' } }}>
206
- {roomState !== HMSRoomState.Preview ? <LiveStatus /> : null}
207
- <RecordingStatus />
208
- </Flex>
209
- {isConnected && !isMobile ? <StartRecording /> : null}
210
- {isConnected && (permissions.hlsStreaming || permissions.rtmpStreaming) && (
211
- <Fragment>{isStreamingOn ? <EndStream /> : <GoLiveButton />}</Fragment>
234
+ {!isMobile && (
235
+ <Flex align="center" css={{ gap: '$4' }}>
236
+ <RecordingStatus />
237
+ {roomState !== HMSRoomState.Preview ? <LiveStatus /> : null}
238
+ </Flex>
212
239
  )}
240
+ {isConnected && !isMobile ? <StartRecording /> : null}
213
241
  </Flex>
214
242
  );
215
243
  };
244
+
245
+ export const StopRecordingInSheet = ({ onStopRecording, onClose }) => {
246
+ return (
247
+ <Sheet.Root open={true}>
248
+ <Sheet.Content>
249
+ <Sheet.Title css={{ p: '$10' }}>
250
+ <Flex direction="row" justify="between" css={{ w: '100%', c: '$alert_error_default' }}>
251
+ <Flex justify="start" align="center" gap="3">
252
+ <AlertTriangleIcon />
253
+ <Text variant="h5" css={{ c: '$alert_error_default' }}>
254
+ Stop Recording
255
+ </Text>
256
+ </Flex>
257
+ <Sheet.Close css={{ color: 'white' }} onClick={onClose}>
258
+ <CrossIcon />
259
+ </Sheet.Close>
260
+ </Flex>
261
+ </Sheet.Title>
262
+ <HorizontalDivider />
263
+ <Box as="div" css={{ p: '$10', overflowY: 'scroll', maxHeight: '70vh' }}>
264
+ <Text variant="caption" css={{ c: '$on_surface_medium', pb: '$8' }}>
265
+ Are you sure you want to stop recording? You can’t undo this action.
266
+ </Text>
267
+ <Button
268
+ variant="danger"
269
+ css={{ width: '100%' }}
270
+ type="submit"
271
+ data-testid="popup_change_btn"
272
+ onClick={onStopRecording}
273
+ >
274
+ Stop
275
+ </Button>
276
+ </Box>
277
+ </Sheet.Content>
278
+ </Sheet.Root>
279
+ );
280
+ };
@@ -2,10 +2,10 @@ import React from 'react';
2
2
  import { useMedia } from 'react-use';
3
3
  import { config as cssConfig, Flex } from '../../../';
4
4
  import { EmojiReaction } from '../EmojiReaction';
5
+ import { ParticipantCount } from '../Footer/ParticipantList';
5
6
  import { LeaveRoom } from '../LeaveRoom';
6
7
  import MetaActions from '../MetaActions';
7
8
  import { SpeakerTag } from './HeaderComponents';
8
- import { ParticipantCount } from './ParticipantList';
9
9
  import { LiveStatus, RecordingStatus, StreamActions } from './StreamActions';
10
10
 
11
11
  export const StreamingHeader = () => {
@@ -0,0 +1,164 @@
1
+ import React from 'react';
2
+ import {
3
+ DeviceType,
4
+ selectIsLocalVideoEnabled,
5
+ selectLocalVideoTrackID,
6
+ selectVideoTrackByID,
7
+ useDevices,
8
+ useHMSActions,
9
+ useHMSStore,
10
+ } from '@100mslive/react-sdk';
11
+ import { CameraFlipIcon, CheckIcon, CrossIcon, SpeakerIcon } from '@100mslive/react-icons';
12
+ import { HorizontalDivider } from '../../../Divider';
13
+ import { Label } from '../../../Label';
14
+ import { Box, Flex } from '../../../Layout';
15
+ import { Sheet } from '../../../Sheet';
16
+ import { Text } from '../../../Text';
17
+ import IconButton from '../../IconButton';
18
+ import { ToastManager } from '../Toast/ToastManager';
19
+
20
+ export const CamaraFlipActions = () => {
21
+ const actions = useHMSActions();
22
+ const { allDevices } = useDevices();
23
+ const { videoInput } = allDevices;
24
+ const isVideoOn = useHMSStore(selectIsLocalVideoEnabled);
25
+
26
+ const videoTrackId = useHMSStore(selectLocalVideoTrackID);
27
+ const localVideoTrack = useHMSStore(selectVideoTrackByID(videoTrackId));
28
+
29
+ return (
30
+ <Box>
31
+ <IconButton
32
+ disabled={!videoInput?.length || !isVideoOn || !localVideoTrack?.facingMode}
33
+ onClick={async () => {
34
+ try {
35
+ await actions.switchCamera();
36
+ } catch (e) {
37
+ ToastManager.addToast({
38
+ title: `Error while flipping camera ${e.message || ''}`,
39
+ variant: 'error',
40
+ });
41
+ }
42
+ }}
43
+ >
44
+ <CameraFlipIcon />
45
+ </IconButton>
46
+ </Box>
47
+ );
48
+ };
49
+
50
+ export const AudioOutputActions = () => {
51
+ const { allDevices, selectedDeviceIDs, updateDevice } = useDevices();
52
+ const { audioOutput } = allDevices;
53
+ // don't show speaker selector where the API is not supported, and use
54
+ // a generic word("Audio") for Mic. In some cases(Chrome Android for e.g.) this changes both mic and speaker keeping them in sync.
55
+ const shouldShowAudioOutput = 'setSinkId' in HTMLMediaElement.prototype;
56
+
57
+ /**
58
+ * Chromium browsers return an audioOutput with empty label when no permissions are given
59
+ */
60
+ const audioOutputFiltered = audioOutput?.filter(item => !!item.label) ?? [];
61
+ if (!shouldShowAudioOutput || !audioOutputFiltered?.length > 0) {
62
+ return null;
63
+ }
64
+ return (
65
+ <AudioOutputSelectionSheet
66
+ outputDevices={audioOutput}
67
+ outputSelected={selectedDeviceIDs.outputDevices}
68
+ onChange={async deviceId => {
69
+ try {
70
+ await updateDevice({
71
+ deviceId,
72
+ deviceType: DeviceType.audioOutput,
73
+ });
74
+ } catch (e) {
75
+ ToastManager.addToast({
76
+ title: `Error while changing audio output ${e.message || ''}`,
77
+ variant: 'error',
78
+ });
79
+ }
80
+ }}
81
+ >
82
+ <Box>
83
+ <IconButton>
84
+ <SpeakerIcon />
85
+ </IconButton>
86
+ </Box>
87
+ </AudioOutputSelectionSheet>
88
+ );
89
+ };
90
+
91
+ const AudioOutputSelectionSheet = ({ outputDevices, outputSelected, onChange, children }) => {
92
+ return (
93
+ <Sheet.Root>
94
+ <Sheet.Trigger asChild>{children}</Sheet.Trigger>
95
+ <Sheet.Content>
96
+ <Sheet.Title css={{ py: '$10', px: '$8', alignItems: 'center' }}>
97
+ <Flex direction="row" justify="between" css={{ w: '100%' }}>
98
+ <Text variant="h6" css={{ display: 'flex' }}>
99
+ Audio Output
100
+ </Text>
101
+ <Sheet.Close>
102
+ <IconButton as="div" data-testid="dialog_cross_icon">
103
+ <CrossIcon />
104
+ </IconButton>
105
+ </Sheet.Close>
106
+ </Flex>
107
+ </Sheet.Title>
108
+ <HorizontalDivider />
109
+ <Flex
110
+ direction="column"
111
+ css={{
112
+ px: '$8',
113
+ maxHeight: '80vh',
114
+ overflowY: 'scroll',
115
+ }}
116
+ >
117
+ {outputDevices.map(audioDevice => {
118
+ return (
119
+ <SelectWithLabel
120
+ label={audioDevice.label}
121
+ id={audioDevice.deviceId}
122
+ checked={audioDevice.deviceId === outputSelected}
123
+ onChange={() => onChange(audioDevice.deviceId)}
124
+ />
125
+ );
126
+ })}
127
+ </Flex>
128
+ </Sheet.Content>
129
+ </Sheet.Root>
130
+ );
131
+ };
132
+
133
+ const SelectWithLabel = ({ label, icon = <></>, checked, id, onChange }) => {
134
+ return (
135
+ <Flex
136
+ align="center"
137
+ css={{
138
+ my: '$2',
139
+ py: '$8',
140
+ w: '100%',
141
+ borderBottom: '1px solid $border_default',
142
+ }}
143
+ onClick={onChange}
144
+ >
145
+ <Label
146
+ htmlFor={id}
147
+ css={{
148
+ fontSize: '$md',
149
+ fontWeight: '$semiBold',
150
+ color: checked ? '$on_surface_high' : '$on_surface_low',
151
+ cursor: 'pointer',
152
+ display: 'flex',
153
+ alignItems: 'center',
154
+ gap: '$8',
155
+ flex: '1 1 0',
156
+ }}
157
+ >
158
+ {icon}
159
+ {label}
160
+ </Label>
161
+ {checked && <CheckIcon width={24} height={24} />}
162
+ </Flex>
163
+ );
164
+ };
@@ -46,13 +46,12 @@ export const IconButtonWithOptions = ({
46
46
  onClick = () => {
47
47
  return;
48
48
  },
49
- key = '',
50
49
  }) => {
51
50
  const bgCss = { backgroundColor: active ? '$transparent' : '$secondary_dim' };
52
51
  const iconCss = { color: active ? '$on_surface_high' : '$on_primary_high' };
53
52
  return (
54
53
  <Flex>
55
- <IconSection css={bgCss} onClick={onClick} key={key}>
54
+ <IconSection css={bgCss} onClick={onClick}>
56
55
  <Tooltip disabled={!tooltipMessage} title={tooltipMessage}>
57
56
  <Flex align="center" justify="center" css={iconCss}>
58
57
  {icon}
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+ import { Box, Flex } from '../../Layout';
3
+ import { Text } from '../../Text';
4
+
5
+ export const LeaveCard = ({ icon, title, subtitle, onClick, bg, titleColor, subtitleColor, css = {} }) => {
6
+ return (
7
+ <Flex css={{ p: '$10', flexGrow: 1, gap: '$8', bg, ...css }} onClick={onClick}>
8
+ <Box css={{ color: titleColor }}>{icon}</Box>
9
+ <Box css={{ gap: '$2' }}>
10
+ <Text variant="lg" css={{ color: titleColor }}>
11
+ {title}
12
+ </Text>
13
+ <Text variant="sm" css={{ color: subtitleColor }}>
14
+ {subtitle}
15
+ </Text>
16
+ </Box>
17
+ </Flex>
18
+ );
19
+ };