@100mslive/roomkit-react 0.1.6-alpha.0 → 0.1.6-alpha.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. package/dist/{HLSView-PY2FKWX3.js → HLSView-HNVYG5VE.js} +208 -118
  2. package/dist/HLSView-HNVYG5VE.js.map +7 -0
  3. package/dist/Prebuilt/AppContext.d.ts +1 -1
  4. package/dist/Prebuilt/components/Chat/ChatFooter.d.ts +7 -0
  5. package/dist/Prebuilt/components/Connection/ConnectionIndicator.d.ts +6 -0
  6. package/dist/Prebuilt/components/Connection/TileConnection.d.ts +10 -0
  7. package/dist/Prebuilt/components/Footer/ChatToggle.d.ts +4 -0
  8. package/dist/Prebuilt/components/Footer/RoleAccordion.d.ts +14 -0
  9. package/dist/Prebuilt/components/Footer/RoleOptions.d.ts +6 -0
  10. package/dist/Prebuilt/components/Header/StreamActions.d.ts +11 -0
  11. package/dist/Prebuilt/components/Leave/DesktopLeaveRoom.d.ts +4 -3
  12. package/dist/Prebuilt/components/Leave/EndSessionContent.d.ts +4 -3
  13. package/dist/Prebuilt/components/Leave/LeaveCard.d.ts +1 -2
  14. package/dist/Prebuilt/components/Leave/LeaveSessionContent.d.ts +3 -1
  15. package/dist/Prebuilt/components/Leave/MwebLeaveRoom.d.ts +4 -3
  16. package/dist/Prebuilt/components/MoreSettings/SplitComponents/MwebOptions.d.ts +6 -0
  17. package/dist/Prebuilt/components/Preview/PreviewContainer.d.ts +3 -0
  18. package/dist/Prebuilt/components/Preview/PreviewJoin.d.ts +16 -0
  19. package/dist/Prebuilt/components/RoleChangeRequestModal.d.ts +2 -0
  20. package/dist/Prebuilt/components/SecondaryTiles.d.ts +1 -1
  21. package/dist/Prebuilt/components/VideoLayouts/EqualProminence.d.ts +1 -1
  22. package/dist/Prebuilt/components/VideoLayouts/Grid.d.ts +1 -0
  23. package/dist/Prebuilt/components/VideoLayouts/GridLayout.d.ts +5 -3
  24. package/dist/Prebuilt/components/VideoLayouts/ProminenceLayout.d.ts +6 -3
  25. package/dist/Prebuilt/components/VideoLayouts/RoleProminence.d.ts +1 -1
  26. package/dist/Prebuilt/components/VideoLayouts/ScreenshareLayout.d.ts +1 -1
  27. package/dist/Prebuilt/components/VideoLayouts/interface.d.ts +1 -0
  28. package/dist/Prebuilt/components/hooks/useAutoStartStreaming.d.ts +1 -0
  29. package/dist/Prebuilt/components/hooks/useRedirectToLeave.d.ts +3 -0
  30. package/dist/Prebuilt/components/hooks/useTileLayout.d.ts +2 -1
  31. package/dist/Prebuilt/components/hooks/useVideoTileLayout.d.ts +2 -0
  32. package/dist/Prebuilt/layouts/SidePane.d.ts +4 -1
  33. package/dist/Prebuilt/layouts/VideoStreamingSection.d.ts +2 -1
  34. package/dist/{VirtualBackground-AYDHYLIZ.js → VirtualBackground-UM2FOUHQ.js} +3 -3
  35. package/dist/{chunk-E2M2ZSOL.js → chunk-364HP22I.js} +2 -2
  36. package/dist/{chunk-RXTHJUMZ.js → chunk-LYSAET4G.js} +946 -390
  37. package/dist/chunk-LYSAET4G.js.map +7 -0
  38. package/dist/{chunk-GQD2AGWW.js → chunk-POE7H4IE.js} +12 -2
  39. package/dist/{chunk-GQD2AGWW.js.map → chunk-POE7H4IE.js.map} +2 -2
  40. package/dist/{conference-V2XZGTKU.js → conference-UWLJHMB2.js} +1116 -1316
  41. package/dist/conference-UWLJHMB2.js.map +7 -0
  42. package/dist/index.cjs.js +6080 -5631
  43. package/dist/index.cjs.js.map +4 -4
  44. package/dist/index.js +2 -2
  45. package/dist/meta.cjs.json +741 -493
  46. package/dist/meta.esbuild.json +782 -529
  47. package/package.json +8 -7
  48. package/src/Prebuilt/App.tsx +10 -21
  49. package/src/Prebuilt/AppContext.tsx +1 -1
  50. package/src/Prebuilt/IconButton.jsx +10 -0
  51. package/src/Prebuilt/common/PeersSorter.ts +1 -1
  52. package/src/Prebuilt/common/constants.js +1 -2
  53. package/src/Prebuilt/common/utils.js +1 -1
  54. package/src/Prebuilt/components/AppData/AppData.jsx +8 -2
  55. package/src/Prebuilt/components/AppData/useUISettings.js +6 -6
  56. package/src/Prebuilt/components/AudioVideoToggle.jsx +8 -6
  57. package/src/Prebuilt/components/Chat/Chat.jsx +23 -6
  58. package/src/Prebuilt/components/Chat/ChatBody.jsx +20 -21
  59. package/src/Prebuilt/components/Chat/{ChatFooter.jsx → ChatFooter.tsx} +38 -13
  60. package/src/Prebuilt/components/Chat/ChatParticipantHeader.jsx +38 -27
  61. package/src/Prebuilt/components/Chat/useEmojiPickerStyles.js +5 -4
  62. package/src/Prebuilt/components/Connection/{ConnectionIndicator.jsx → ConnectionIndicator.tsx} +12 -4
  63. package/src/Prebuilt/components/Connection/{TileConnection.jsx → TileConnection.tsx} +20 -6
  64. package/src/Prebuilt/components/EmojiReaction.jsx +2 -6
  65. package/src/Prebuilt/components/Footer/{ChatToggle.jsx → ChatToggle.tsx} +13 -3
  66. package/src/Prebuilt/components/Footer/Footer.tsx +15 -6
  67. package/src/Prebuilt/components/Footer/ParticipantList.jsx +15 -47
  68. package/src/Prebuilt/components/Footer/{RoleAccordion.jsx → RoleAccordion.tsx} +33 -17
  69. package/src/Prebuilt/components/Footer/RoleOptions.tsx +155 -0
  70. package/src/Prebuilt/components/FullPageProgress.jsx +3 -3
  71. package/src/Prebuilt/components/HMSVideo/Controls.jsx +1 -0
  72. package/src/Prebuilt/components/HMSVideo/HLSQualitySelector.jsx +39 -17
  73. package/src/Prebuilt/components/HMSVideo/HMSVideo.jsx +2 -2
  74. package/src/Prebuilt/components/HMSVideo/VideoProgress.jsx +5 -6
  75. package/src/Prebuilt/components/HMSVideo/VolumeControl.jsx +1 -1
  76. package/src/Prebuilt/components/Header/{StreamActions.jsx → StreamActions.tsx} +23 -9
  77. package/src/Prebuilt/components/Header/common.jsx +5 -2
  78. package/src/Prebuilt/components/IconButtonWithOptions/IconButtonWithOptions.jsx +6 -1
  79. package/src/Prebuilt/components/InsetTile.tsx +14 -8
  80. package/src/Prebuilt/components/Leave/DesktopLeaveRoom.tsx +21 -11
  81. package/src/Prebuilt/components/Leave/EndSessionContent.tsx +2 -5
  82. package/src/Prebuilt/components/Leave/LeaveCard.tsx +1 -3
  83. package/src/Prebuilt/components/Leave/LeaveRoom.tsx +28 -25
  84. package/src/Prebuilt/components/Leave/LeaveSessionContent.tsx +8 -2
  85. package/src/Prebuilt/components/Leave/MwebLeaveRoom.tsx +8 -8
  86. package/src/Prebuilt/components/MoreSettings/ChangeNameContent.jsx +4 -0
  87. package/src/Prebuilt/components/MoreSettings/MoreSettings.tsx +1 -1
  88. package/src/Prebuilt/components/MoreSettings/SplitComponents/DesktopOptions.tsx +9 -23
  89. package/src/Prebuilt/components/MoreSettings/SplitComponents/{MwebOptions.jsx → MwebOptions.tsx} +88 -27
  90. package/src/Prebuilt/components/Notifications/Notifications.jsx +30 -21
  91. package/src/Prebuilt/components/Notifications/ReconnectNotifications.jsx +5 -11
  92. package/src/Prebuilt/components/Pagination.tsx +14 -12
  93. package/src/Prebuilt/components/Preview/{PreviewContainer.jsx → PreviewContainer.tsx} +11 -2
  94. package/src/Prebuilt/components/Preview/PreviewForm.tsx +6 -8
  95. package/src/Prebuilt/components/Preview/{PreviewJoin.jsx → PreviewJoin.tsx} +43 -19
  96. package/src/Prebuilt/components/{RoleChangeRequestModal.jsx → RoleChangeRequestModal.tsx} +32 -15
  97. package/src/Prebuilt/components/ScreenshareTile.jsx +6 -7
  98. package/src/Prebuilt/components/SecondaryTiles.tsx +12 -10
  99. package/src/Prebuilt/components/TileMenu/TileMenu.jsx +1 -1
  100. package/src/Prebuilt/components/TileMenu/TileMenuContent.jsx +14 -10
  101. package/src/Prebuilt/components/Toast/ToastConfig.jsx +5 -4
  102. package/src/Prebuilt/components/VideoLayouts/EqualProminence.tsx +13 -10
  103. package/src/Prebuilt/components/VideoLayouts/Grid.tsx +36 -34
  104. package/src/Prebuilt/components/VideoLayouts/GridLayout.tsx +33 -15
  105. package/src/Prebuilt/components/VideoLayouts/ProminenceLayout.tsx +45 -31
  106. package/src/Prebuilt/components/VideoLayouts/RoleProminence.tsx +12 -9
  107. package/src/Prebuilt/components/VideoLayouts/ScreenshareLayout.tsx +25 -9
  108. package/src/Prebuilt/components/VideoLayouts/interface.ts +1 -0
  109. package/src/Prebuilt/components/VideoTile.jsx +45 -53
  110. package/src/Prebuilt/components/conference.jsx +71 -74
  111. package/src/Prebuilt/components/hooks/useAutoStartStreaming.tsx +57 -0
  112. package/src/Prebuilt/components/hooks/useMetadata.jsx +12 -3
  113. package/src/Prebuilt/components/hooks/useRedirectToLeave.tsx +34 -0
  114. package/src/Prebuilt/components/hooks/useRoleProminencePeers.tsx +1 -1
  115. package/src/Prebuilt/components/hooks/useTileLayout.tsx +24 -18
  116. package/src/Prebuilt/components/hooks/useVideoTileLayout.ts +4 -0
  117. package/src/Prebuilt/layouts/EmbedView.jsx +1 -11
  118. package/src/Prebuilt/layouts/HLSView.jsx +152 -82
  119. package/src/Prebuilt/layouts/SidePane.tsx +15 -3
  120. package/src/Prebuilt/layouts/VideoStreamingSection.tsx +11 -47
  121. package/src/Prebuilt/plugins/FlyingEmoji.jsx +14 -2
  122. package/src/Prebuilt/services/FeatureFlags.jsx +0 -1
  123. package/src/VideoTile/StyledVideoTile.tsx +1 -0
  124. package/dist/HLSView-PY2FKWX3.js.map +0 -7
  125. package/dist/chunk-RXTHJUMZ.js.map +0 -7
  126. package/dist/conference-V2XZGTKU.js.map +0 -7
  127. package/src/Prebuilt/components/AudioLevel/BeamSpeakerLabelsLogging.jsx +0 -16
  128. package/src/Prebuilt/components/VideoList.jsx +0 -73
  129. /package/dist/{VirtualBackground-AYDHYLIZ.js.map → VirtualBackground-UM2FOUHQ.js.map} +0 -0
  130. /package/dist/{chunk-E2M2ZSOL.js.map → chunk-364HP22I.js.map} +0 -0
@@ -1,53 +1,61 @@
1
1
  import React from 'react';
2
2
  import { useMeasure } from 'react-use';
3
3
  import { FixedSizeList } from 'react-window';
4
+ import { HMSPeer } from '@100mslive/react-sdk';
4
5
  import { Accordion } from '../../../Accordion';
5
6
  import { Box, Flex } from '../../../Layout';
7
+ import { Text } from '../../../Text';
8
+ // @ts-ignore: No implicit Any
6
9
  import { Participant } from './ParticipantList';
10
+ import { RoleOptions } from './RoleOptions';
11
+ // @ts-ignore: No implicit Any
7
12
  import { getFormattedCount } from '../../common/utils';
8
13
 
9
14
  const ROW_HEIGHT = 50;
10
15
 
11
- function itemKey(index, data) {
16
+ interface ItemData {
17
+ peerList: HMSPeer[];
18
+ isConnected: boolean;
19
+ }
20
+
21
+ function itemKey(index: number, data: ItemData) {
12
22
  return data.peerList[index].id;
13
23
  }
14
24
 
15
- const VirtualizedParticipantItem = React.memo(({ index, data }) => {
16
- return (
17
- <Participant
18
- key={data.peerList[index].id}
19
- peer={data.peerList[index]}
20
- isConnected={data.isConnected}
21
- setSelectedPeerId={data.setSelectedPeerId}
22
- />
23
- );
25
+ const VirtualizedParticipantItem = React.memo(({ index, data }: { index: number; data: ItemData }) => {
26
+ return <Participant key={data.peerList[index].id} peer={data.peerList[index]} isConnected={data.isConnected} />;
24
27
  });
25
28
 
26
29
  export const RoleAccordion = ({
27
30
  peerList = [],
28
31
  roleName,
29
- setSelectedPeerId,
30
32
  isConnected,
31
33
  filter,
32
34
  isHandRaisedAccordion = false,
35
+ }: ItemData & {
36
+ roleName: string;
37
+ isHandRaisedAccordion?: boolean;
38
+ filter?: { search: string };
33
39
  }) => {
34
- const [ref, { width }] = useMeasure();
35
- const height = ROW_HEIGHT * peerList.length;
40
+ const [ref, { width }] = useMeasure<HTMLDivElement>();
36
41
  const showAcordion = filter?.search ? peerList.some(peer => peer.name.toLowerCase().includes(filter.search)) : true;
42
+
37
43
  if (!showAcordion || (isHandRaisedAccordion && filter?.search) || peerList.length === 0) {
38
44
  return null;
39
45
  }
46
+ const height = ROW_HEIGHT * peerList.length;
40
47
 
41
48
  return (
42
- <Flex direction="column" css={{ flexGrow: 1 }} ref={ref}>
49
+ <Flex direction="column" css={{ flexGrow: 1, '&:hover .role_actions': { visibility: 'visible' } }} ref={ref}>
43
50
  <Accordion.Root
44
51
  type="single"
45
52
  collapsible
46
53
  defaultValue={roleName}
47
- css={{ borderRadius: '$3', border: '1px solid $border_bright' }}
54
+ css={{ borderRadius: '$1', border: '1px solid $border_bright' }}
48
55
  >
49
56
  <Accordion.Item value={roleName}>
50
57
  <Accordion.Header
58
+ iconStyles={{ c: '$on_surface_high' }}
51
59
  css={{
52
60
  textTransform: 'capitalize',
53
61
  p: '$6 $8',
@@ -56,13 +64,21 @@ export const RoleAccordion = ({
56
64
  c: '$on_surface_medium',
57
65
  }}
58
66
  >
59
- {roleName} {`(${getFormattedCount(peerList.length)})`}
67
+ <Flex justify="between" css={{ flexGrow: 1, pr: '$6' }}>
68
+ <Text
69
+ variant="sm"
70
+ css={{ fontWeight: '$semiBold', textTransform: 'capitalize', color: '$on_surface_medium' }}
71
+ >
72
+ {roleName} {`(${getFormattedCount(peerList.length)})`}
73
+ </Text>
74
+ <RoleOptions roleName={roleName} peerList={peerList} />
75
+ </Flex>
60
76
  </Accordion.Header>
61
77
  <Accordion.Content>
62
78
  <Box css={{ borderTop: '1px solid $border_default' }} />
63
79
  <FixedSizeList
64
80
  itemSize={ROW_HEIGHT}
65
- itemData={{ peerList, isConnected, setSelectedPeerId }}
81
+ itemData={{ peerList, isConnected }}
66
82
  itemKey={itemKey}
67
83
  itemCount={peerList.length}
68
84
  width={width}
@@ -0,0 +1,155 @@
1
+ import React, { useState } from 'react';
2
+ import { DefaultConferencingScreen_Elements } from '@100mslive/types-prebuilt';
3
+ import { HMSPeer, selectPermissions, useHMSActions, useHMSStore, useHMSVanillaStore } from '@100mslive/react-sdk';
4
+ import {
5
+ MicOffIcon,
6
+ MicOnIcon,
7
+ PersonRectangleIcon,
8
+ RemoveUserIcon,
9
+ VerticalMenuIcon,
10
+ VideoOffIcon,
11
+ VideoOnIcon,
12
+ } from '@100mslive/react-icons';
13
+ import { Dropdown } from '../../../Dropdown';
14
+ import { Flex } from '../../../Layout';
15
+ import { Text } from '../../../Text';
16
+ import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
17
+ // @ts-ignore: No implicit Any
18
+ import { getMetadata } from '../../common/utils';
19
+
20
+ const dropdownItemCSS = { backgroundColor: '$surface_default', gap: '$4', p: '$8' };
21
+ const optionTextCSS = { fontWeight: '$semiBold', color: '$on_surface_high', textTransform: 'none' };
22
+
23
+ export const RoleOptions = ({ roleName, peerList }: { roleName: string; peerList: HMSPeer[] }) => {
24
+ const [openOptions, setOpenOptions] = useState(false);
25
+ const permissions = useHMSStore(selectPermissions);
26
+ const hmsActions = useHMSActions();
27
+ const { elements } = useRoomLayoutConferencingScreen();
28
+ const { on_stage_role, off_stage_roles = [] } = (elements as DefaultConferencingScreen_Elements)?.on_stage_exp || {};
29
+
30
+ const vanillaStore = useHMSVanillaStore();
31
+ const store = vanillaStore.getState();
32
+
33
+ let allPeersHaveVideoOn = true;
34
+ let allPeersHaveAudioOn = true;
35
+
36
+ peerList.forEach(peer => {
37
+ const isAudioOn = !!peer.audioTrack && store.tracks[peer.audioTrack]?.enabled;
38
+ const isVideoOn = !!peer.videoTrack && store.tracks[peer.videoTrack]?.enabled;
39
+ allPeersHaveAudioOn = allPeersHaveAudioOn && isAudioOn;
40
+ allPeersHaveVideoOn = allPeersHaveVideoOn && isVideoOn;
41
+ });
42
+
43
+ const canMuteRole = permissions?.mute && roleName === on_stage_role;
44
+ const canRemoveRoleFromStage = permissions?.changeRole && roleName === on_stage_role;
45
+ // on stage and off stage roles
46
+ const canRemoveRoleFromRoom =
47
+ permissions?.removeOthers && (on_stage_role === roleName || off_stage_roles?.includes(roleName));
48
+
49
+ if (!(canMuteRole || canRemoveRoleFromStage || canRemoveRoleFromRoom)) {
50
+ return null;
51
+ }
52
+
53
+ const removeAllFromStage = () => {
54
+ peerList.forEach(peer => {
55
+ const prevRole = getMetadata(peer.metadata).prevRole;
56
+ if (prevRole) {
57
+ hmsActions.changeRoleOfPeer(peer.id, prevRole, true);
58
+ }
59
+ });
60
+ };
61
+
62
+ const setTrackEnabled = async (type: 'audio' | 'video', enabled = false) => {
63
+ try {
64
+ await hmsActions.setRemoteTracksEnabled({ roles: [roleName], source: 'regular', type, enabled });
65
+ } catch (e) {
66
+ console.error(e);
67
+ }
68
+ };
69
+
70
+ const removePeersFromRoom = async () => {
71
+ try {
72
+ peerList.forEach(async peer => {
73
+ await hmsActions.removePeer(peer.id, '');
74
+ });
75
+ } catch (e) {
76
+ console.error(e);
77
+ }
78
+ };
79
+
80
+ return (
81
+ <Dropdown.Root open={openOptions} onOpenChange={setOpenOptions}>
82
+ <Dropdown.Trigger
83
+ onClick={e => e.stopPropagation()}
84
+ className="role_actions"
85
+ asChild
86
+ css={{
87
+ p: '$1',
88
+ r: '$0',
89
+ c: '$on_surface_high',
90
+ visibility: openOptions ? 'visible' : 'hidden',
91
+ '&:hover': {
92
+ c: '$on_surface_medium',
93
+ },
94
+ '@md': {
95
+ visibility: 'visible',
96
+ },
97
+ }}
98
+ >
99
+ <Flex>
100
+ <VerticalMenuIcon />
101
+ </Flex>
102
+ </Dropdown.Trigger>
103
+ <Dropdown.Content
104
+ onClick={e => e.stopPropagation()}
105
+ css={{ w: 'max-content', maxWidth: '$64', bg: '$surface_default', py: 0 }}
106
+ align="end"
107
+ >
108
+ {canRemoveRoleFromStage && (
109
+ <Dropdown.Item
110
+ css={{ ...dropdownItemCSS, borderBottom: '1px solid $border_bright' }}
111
+ onClick={removeAllFromStage}
112
+ >
113
+ <PersonRectangleIcon />
114
+ <Text variant="sm" css={optionTextCSS}>
115
+ Remove all from Stage
116
+ </Text>
117
+ </Dropdown.Item>
118
+ )}
119
+
120
+ {canMuteRole && (
121
+ <>
122
+ <Dropdown.Item css={dropdownItemCSS} onClick={() => setTrackEnabled('audio', !allPeersHaveAudioOn)}>
123
+ {allPeersHaveAudioOn ? <MicOffIcon /> : <MicOnIcon />}
124
+ <Text variant="sm" css={optionTextCSS}>
125
+ {allPeersHaveAudioOn ? 'Mute' : 'Unmute'} Audio
126
+ </Text>
127
+ </Dropdown.Item>
128
+
129
+ <Dropdown.Item
130
+ css={{ ...dropdownItemCSS, borderTop: '1px solid $border_bright' }}
131
+ onClick={() => setTrackEnabled('video', !allPeersHaveVideoOn)}
132
+ >
133
+ {allPeersHaveVideoOn ? <VideoOffIcon /> : <VideoOnIcon />}
134
+ <Text variant="sm" css={optionTextCSS}>
135
+ {allPeersHaveVideoOn ? 'Mute' : 'Unmute'} Video
136
+ </Text>
137
+ </Dropdown.Item>
138
+ </>
139
+ )}
140
+
141
+ {canRemoveRoleFromRoom && (
142
+ <Dropdown.Item
143
+ css={{ ...dropdownItemCSS, borderTop: '1px solid $border_bright', color: '$alert_error_default' }}
144
+ onClick={removePeersFromRoom}
145
+ >
146
+ <RemoveUserIcon />
147
+ <Text variant="sm" css={{ ...optionTextCSS, color: 'inherit' }}>
148
+ Remove all from Room
149
+ </Text>
150
+ </Dropdown.Item>
151
+ )}
152
+ </Dropdown.Content>
153
+ </Dropdown.Root>
154
+ );
155
+ };
@@ -3,10 +3,10 @@ import { Flex } from '../../Layout';
3
3
  import { Loading } from '../../Loading';
4
4
  import { Text } from '../../Text';
5
5
 
6
- const FullPageProgress = ({ loaderColor = '$primary_default', loadingText = '' }) => (
7
- <Flex direction="column" justify="center" align="center" css={{ size: '100%', color: loaderColor }}>
6
+ const FullPageProgress = ({ loaderColor = '$primary_default', text = '', css = {} }) => (
7
+ <Flex direction="column" justify="center" align="center" css={{ size: '100%', color: loaderColor, ...css }}>
8
8
  <Loading color="currentColor" size={100} />
9
- {loadingText ? <Text css={{ mt: '$10', color: '$on_surface_high' }}>{loadingText}</Text> : null}
9
+ {text ? <Text css={{ mt: '$10', color: '$on_surface_high' }}>{text}</Text> : null}
10
10
  </Flex>
11
11
  );
12
12
 
@@ -3,6 +3,7 @@ import { Flex, styled } from '../../../';
3
3
  export const VideoControls = styled(Flex, {
4
4
  justifyContent: 'center',
5
5
  alignItems: 'center',
6
+ alignSelf: 'stretch',
6
7
  width: '100%',
7
8
  gap: '$2',
8
9
  });
@@ -1,12 +1,10 @@
1
- import React, { useState } from 'react';
2
- import { CheckCircleIcon, SettingsIcon } from '@100mslive/react-icons';
1
+ import React from 'react';
2
+ import { CheckIcon, SettingsIcon } from '@100mslive/react-icons';
3
3
  import { Box, Dropdown, Flex, Text, Tooltip } from '../../../';
4
4
 
5
- export function HLSQualitySelector({ layers, onQualityChange, selection, isAuto }) {
6
- const [qualityDropDownOpen, setQualityDropDownOpen] = useState(false);
7
-
5
+ export function HLSQualitySelector({ open, onOpen, layers, onQualityChange, selection, isAuto }) {
8
6
  return (
9
- <Dropdown.Root open={qualityDropDownOpen} onOpenChange={value => setQualityDropDownOpen(value)}>
7
+ <Dropdown.Root open={open} onOpenChange={value => onOpen(value)}>
10
8
  <Dropdown.Trigger asChild data-testid="quality_selector">
11
9
  <Flex
12
10
  css={{
@@ -24,6 +22,7 @@ export function HLSQualitySelector({ layers, onQualityChange, selection, isAuto
24
22
  h: '$9',
25
23
  display: 'inline-flex',
26
24
  alignItems: 'center',
25
+ c: '$on_surface_high',
27
26
  }}
28
27
  >
29
28
  <SettingsIcon />
@@ -44,7 +43,7 @@ export function HLSQualitySelector({ layers, onQualityChange, selection, isAuto
44
43
  mx: '$2',
45
44
  w: '$2',
46
45
  h: '$2',
47
- background: '$on_primary_high',
46
+ background: '$on_surface_medium',
48
47
  r: '$1',
49
48
  }}
50
49
  />
@@ -60,7 +59,15 @@ export function HLSQualitySelector({ layers, onQualityChange, selection, isAuto
60
59
  <Dropdown.Content
61
60
  sideOffset={5}
62
61
  align="end"
63
- css={{ height: 'auto', maxHeight: '$96', w: '$64', bg: '$surface_bright' }}
62
+ css={{
63
+ height: 'auto',
64
+ maxHeight: '$52',
65
+ w: '$40',
66
+ bg: '$surface_bright',
67
+ py: '$4',
68
+ gap: '$4',
69
+ display: 'grid',
70
+ }}
64
71
  >
65
72
  {layers.map(layer => {
66
73
  return (
@@ -68,16 +75,26 @@ export function HLSQualitySelector({ layers, onQualityChange, selection, isAuto
68
75
  onClick={() => onQualityChange(layer)}
69
76
  key={layer.width}
70
77
  css={{
71
- bg: '$surface_bright',
78
+ bg:
79
+ !isAuto && layer.width === selection?.width && layer.height === selection?.height
80
+ ? '$surface_default'
81
+ : '$surface_bright',
72
82
  '&:hover': {
73
- bg: '$surface_default',
83
+ bg: '$surface_brighter',
74
84
  },
85
+ p: '$2 $4 $2 $8',
86
+ h: '$12',
87
+ gap: '$2',
75
88
  }}
76
89
  >
77
- <Text>{getQualityText(layer)}</Text>
78
- <Text css={{ flex: '1 1 0', c: '$on_surface_low', pl: '$2' }}>{getBitrateText(layer)}</Text>
90
+ <Text variant="caption" css={{ fontWeight: '$semiBold' }}>
91
+ {getQualityText(layer)}
92
+ </Text>
93
+ <Text variant="caption" css={{ flex: '1 1 0', c: '$on_surface_low', pl: '$2' }}>
94
+ {getBitrateText(layer)}
95
+ </Text>
79
96
  {!isAuto && layer.width === selection?.width && layer.height === selection?.height && (
80
- <CheckCircleIcon />
97
+ <CheckIcon width="16px" height="16px" />
81
98
  )}
82
99
  </Dropdown.Item>
83
100
  );
@@ -86,14 +103,19 @@ export function HLSQualitySelector({ layers, onQualityChange, selection, isAuto
86
103
  onClick={() => onQualityChange({ height: 'auto' })}
87
104
  key="auto"
88
105
  css={{
89
- bg: '$surface_bright',
106
+ bg: !isAuto ? '$surface_bright' : '$surface_default',
90
107
  '&:hover': {
91
- bg: '$surface_default',
108
+ bg: '$surface_brighter',
92
109
  },
110
+ p: '$2 $4 $2 $8',
111
+ h: '$12',
112
+ gap: '$2',
93
113
  }}
94
114
  >
95
- <Text css={{ flex: '1 1 0' }}>Auto</Text>
96
- {isAuto && <CheckCircleIcon />}
115
+ <Text variant="caption" css={{ fontWeight: '$semiBold', flex: '1 1 0' }}>
116
+ Auto
117
+ </Text>
118
+ {isAuto && <CheckIcon width="16px" height="16px" />}
97
119
  </Dropdown.Item>
98
120
  </Dropdown.Content>
99
121
  )}
@@ -1,9 +1,9 @@
1
1
  import React, { forwardRef } from 'react';
2
2
  import { Flex } from '../../../';
3
3
 
4
- export const HMSVideo = forwardRef(({ children }, videoRef) => {
4
+ export const HMSVideo = forwardRef(({ children, ...props }, videoRef) => {
5
5
  return (
6
- <Flex data-testid="hms-video" css={{ size: '100%' }} direction="column">
6
+ <Flex data-testid="hms-video" css={{ size: '100%', position: 'relative' }} direction="column" {...props}>
7
7
  <video style={{ flex: '1 1 0', margin: '0 auto', minHeight: '0' }} ref={videoRef} playsInline />
8
8
  {children}
9
9
  </Flex>
@@ -44,7 +44,7 @@ export const VideoProgress = ({ onValueChange, hlsPlayer }) => {
44
44
  return hlsPlayer.getVideoElement() ? (
45
45
  <Flex
46
46
  ref={progressRootRef}
47
- css={{ paddingLeft: '$8', paddingRight: '$8', cursor: 'pointer' }}
47
+ css={{ cursor: 'pointer', h: '$4', alignSelf: 'stretch' }}
48
48
  onClick={onProgressChangeHandler}
49
49
  >
50
50
  <Box
@@ -53,23 +53,22 @@ export const VideoProgress = ({ onValueChange, hlsPlayer }) => {
53
53
  display: 'inline',
54
54
  width: `${videoProgress}%`,
55
55
  background: '$primary_default',
56
- height: '0.3rem',
57
56
  }}
58
57
  />
59
58
  <Box
60
59
  id="video-buffer"
61
60
  css={{
62
61
  width: `${bufferProgress - videoProgress}%`,
63
- background: '$primary_dim',
64
- height: '0.3rem',
62
+ background: '$on_surface_high',
63
+ opacity: '25%',
65
64
  }}
66
65
  />
67
66
  <Box
68
67
  id="video-rest"
69
68
  css={{
70
69
  width: `${100 - bufferProgress}%`,
71
- background: '$surface_brighter',
72
- height: '0.3rem',
70
+ background: '$on_surface_high',
71
+ opacity: '10%',
73
72
  }}
74
73
  />
75
74
  </Flex>
@@ -9,7 +9,7 @@ export const VolumeControl = ({ hlsPlayer }) => {
9
9
  return (
10
10
  <Flex
11
11
  align="center"
12
- css={{ color: '$on_primary_high' }}
12
+ css={{ color: '$on_surface_high' }}
13
13
  onMouseOver={event => {
14
14
  event.stopPropagation();
15
15
  setShowSlider(true);
@@ -11,26 +11,31 @@ import {
11
11
  useRecordingStreaming,
12
12
  } from '@100mslive/react-sdk';
13
13
  import { AlertTriangleIcon, CrossIcon, RecordIcon } from '@100mslive/react-icons';
14
- import { Box, Button, config as cssConfig, Flex, HorizontalDivider, Loading, Popover, Text, Tooltip } from '../../../';
14
+ import { Box, Button, config as cssConfig, Flex, HorizontalDivider, Loading, Popover, Text, Tooltip } from '../../..';
15
15
  import { Sheet } from '../../../Sheet';
16
+ // @ts-ignore
16
17
  import { ToastManager } from '../Toast/ToastManager';
18
+ // @ts-ignore
17
19
  import { AdditionalRoomState, getRecordingText } from './AdditionalRoomState';
20
+ // @ts-ignore
18
21
  import { useSetAppDataByKey } from '../AppData/useUISettings';
22
+ // @ts-ignore
19
23
  import { formatTime } from '../../common/utils';
24
+ // @ts-ignore
20
25
  import { APP_DATA } from '../../common/constants';
21
26
 
22
27
  export const LiveStatus = () => {
23
28
  const { isHLSRunning, isRTMPRunning } = useRecordingStreaming();
24
29
  const hlsState = useHMSStore(selectHLSState);
25
30
  const isMobile = useMedia(cssConfig.media.md);
26
- const intervalRef = useRef(null);
31
+ const intervalRef = useRef<NodeJS.Timeout | null>(null);
27
32
 
28
33
  const [liveTime, setLiveTime] = useState(0);
29
34
 
30
35
  const startTimer = useCallback(() => {
31
36
  intervalRef.current = setInterval(() => {
32
- if (hlsState?.running) {
33
- setLiveTime(Date.now() - hlsState?.variants[0]?.startedAt.getTime());
37
+ if (hlsState?.running && hlsState?.variants[0]?.startedAt) {
38
+ setLiveTime(Date.now() - hlsState.variants[0].startedAt.getTime());
34
39
  }
35
40
  }, 1000);
36
41
  }, [hlsState?.running, hlsState?.variants]);
@@ -90,6 +95,7 @@ export const RecordingStatus = () => {
90
95
 
91
96
  return (
92
97
  <Tooltip
98
+ boxCss={{ zIndex: 1 }}
93
99
  title={getRecordingText({
94
100
  isBrowserRecordingOn,
95
101
  isServerRecordingOn,
@@ -130,7 +136,7 @@ const StartRecording = () => {
130
136
  </Popover.Trigger>
131
137
  <Popover.Portal>
132
138
  <Popover.Content align="end" sideOffset={8} css={{ w: '$64' }}>
133
- <Text variant="body" css={{ color: '$on_surface_medium' }}>
139
+ <Text variant="body1" css={{ color: '$on_surface_medium' }}>
134
140
  Are you sure you want to end the recording?
135
141
  </Text>
136
142
  <Button
@@ -142,8 +148,9 @@ const StartRecording = () => {
142
148
  try {
143
149
  await hmsActions.stopRTMPAndRecording();
144
150
  } catch (error) {
151
+ const err = error as Error;
145
152
  ToastManager.addToast({
146
- title: error.message,
153
+ title: err.message,
147
154
  variant: 'error',
148
155
  });
149
156
  }
@@ -170,14 +177,15 @@ const StartRecording = () => {
170
177
  record: true,
171
178
  });
172
179
  } catch (error) {
173
- if (error.message.includes('stream already running')) {
180
+ const err = error as Error;
181
+ if (err.message.includes('stream already running')) {
174
182
  ToastManager.addToast({
175
183
  title: 'Recording already running',
176
184
  variant: 'error',
177
185
  });
178
186
  } else {
179
187
  ToastManager.addToast({
180
- title: error.message,
188
+ title: err.message,
181
189
  variant: 'error',
182
190
  });
183
191
  }
@@ -215,7 +223,13 @@ export const StreamActions = () => {
215
223
  );
216
224
  };
217
225
 
218
- export const StopRecordingInSheet = ({ onStopRecording, onClose }) => {
226
+ export const StopRecordingInSheet = ({
227
+ onStopRecording,
228
+ onClose,
229
+ }: {
230
+ onStopRecording: () => void;
231
+ onClose: () => void;
232
+ }) => {
219
233
  return (
220
234
  <Sheet.Root open={true}>
221
235
  <Sheet.Content>
@@ -25,11 +25,13 @@ export const CamaraFlipActions = () => {
25
25
 
26
26
  const videoTrackId = useHMSStore(selectLocalVideoTrackID);
27
27
  const localVideoTrack = useHMSStore(selectVideoTrackByID(videoTrackId));
28
-
28
+ if (!videoInput || !videoInput?.length || !localVideoTrack?.facingMode) {
29
+ return null;
30
+ }
29
31
  return (
30
32
  <Box>
31
33
  <IconButton
32
- disabled={!videoInput?.length || !isVideoOn || !localVideoTrack?.facingMode}
34
+ disabled={!isVideoOn}
33
35
  onClick={async () => {
34
36
  try {
35
37
  await actions.switchCamera();
@@ -117,6 +119,7 @@ const AudioOutputSelectionSheet = ({ outputDevices, outputSelected, onChange, ch
117
119
  {outputDevices.map(audioDevice => {
118
120
  return (
119
121
  <SelectWithLabel
122
+ key={audioDevice.deviceId}
120
123
  label={audioDevice.label}
121
124
  id={audioDevice.deviceId}
122
125
  checked={audioDevice.deviceId === outputSelected}
@@ -14,6 +14,10 @@ const IconSection = styled(IconButton, {
14
14
  borderTopRightRadius: 0,
15
15
  borderColor: '$border_default',
16
16
  borderBottomRightRadius: 0,
17
+ position: 'relative',
18
+ '&:not([disabled]):focus-visible': {
19
+ zIndex: 1,
20
+ },
17
21
  '@md': {
18
22
  mx: 0,
19
23
  borderTopRightRadius: '$1',
@@ -30,8 +34,9 @@ const OptionsSection = styled(IconButton, {
30
34
  borderColor: '$border_default',
31
35
  borderBottomLeftRadius: 0,
32
36
  borderLeftWidth: 0,
37
+ position: 'relative',
33
38
  '&:not([disabled]):focus-visible': {
34
- boxShadow: 'none',
39
+ zIndex: 1,
35
40
  },
36
41
  '@md': {
37
42
  display: 'none',
@@ -14,6 +14,7 @@ import { AudioVideoToggle } from './AudioVideoToggle';
14
14
  import VideoTile from './VideoTile';
15
15
  // @ts-ignore: No implicit Any
16
16
  import { useSetAppDataByKey } from './AppData/useUISettings';
17
+ import { useVideoTileContext } from './hooks/useVideoTileLayout';
17
18
  // @ts-ignore: No implicit Any
18
19
  import { APP_DATA } from '../common/constants';
19
20
 
@@ -22,7 +23,11 @@ const MinimisedTile = ({ setMinimised }: { setMinimised: (value: boolean) => voi
22
23
  <Flex align="center" css={{ gap: '$6', r: '$1', bg: '$surface_default', p: '$4', color: '$on_surface_high' }}>
23
24
  <AudioVideoToggle hideOptions={true} />
24
25
  <Text>You</Text>
25
- <IconButton onClick={() => setMinimised(false)} css={{ bg: 'transparent', border: 'transparent' }}>
26
+ <IconButton
27
+ className="__cancel-drag-event"
28
+ onClick={() => setMinimised(false)}
29
+ css={{ bg: 'transparent', border: 'transparent' }}
30
+ >
26
31
  <ExpandIcon />
27
32
  </IconButton>
28
33
  </Flex>
@@ -41,12 +46,11 @@ export const InsetTile = () => {
41
46
  const [minimised, setMinimised] = useSetAppDataByKey(APP_DATA.minimiseInset);
42
47
  const videoTrack = useHMSStore(selectVideoTrackByID(localPeer?.videoTrack));
43
48
  const isAllowedToPublish = useHMSStore(selectIsAllowedToPublish);
44
- const aspectRatio =
45
- videoTrack?.width && videoTrack?.height
46
- ? videoTrack.width / videoTrack.height
47
- : isMobile
48
- ? defaultMobileAspectRatio
49
- : desktopAspectRatio;
49
+ const videoTileProps = useVideoTileContext();
50
+ let aspectRatio = isMobile ? defaultMobileAspectRatio : desktopAspectRatio;
51
+ if (videoTrack?.width && videoTrack?.height && !isMobile) {
52
+ aspectRatio = videoTrack.width / videoTrack.height;
53
+ }
50
54
  let height = insetHeightPx;
51
55
  let width = height * aspectRatio;
52
56
  // Convert to 16/9 in landscape mode with a max width of 240
@@ -82,7 +86,7 @@ export const InsetTile = () => {
82
86
  }
83
87
 
84
88
  return (
85
- <Draggable bounds="parent" nodeRef={nodeRef}>
89
+ <Draggable bounds="parent" nodeRef={nodeRef} cancel=".__cancel-drag-event">
86
90
  <Box
87
91
  ref={nodeRef}
88
92
  css={{
@@ -114,6 +118,8 @@ export const InsetTile = () => {
114
118
  height={height}
115
119
  containerCSS={{ background: '$surface_default' }}
116
120
  canMinimise
121
+ isDragabble
122
+ {...videoTileProps}
117
123
  />
118
124
  )}
119
125
  </Box>