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

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. package/dist/HLSView-FBEGJ3L7.js +1396 -0
  2. package/dist/HLSView-FBEGJ3L7.js.map +7 -0
  3. package/dist/Prebuilt/common/hooks.d.ts +3 -0
  4. package/dist/Prebuilt/components/Chat/MwebChatOption.d.ts +1 -1
  5. package/dist/Prebuilt/components/HMSVideo/FullscreenButton.d.ts +5 -0
  6. package/dist/Prebuilt/components/HMSVideo/HLSAutoplayBlockedPrompt.d.ts +5 -0
  7. package/dist/Prebuilt/components/HMSVideo/HLSCaptionSelector.d.ts +1 -2
  8. package/dist/Prebuilt/components/HMSVideo/HLSQualitySelector.d.ts +13 -0
  9. package/dist/Prebuilt/components/HMSVideo/MwebHLSViewTitle.d.ts +2 -0
  10. package/dist/Prebuilt/components/HMSVideo/PlayButton.d.ts +6 -0
  11. package/dist/Prebuilt/components/HMSVideo/PlayPauseButton.d.ts +6 -0
  12. package/dist/Prebuilt/components/HMSVideo/PlayerContext.d.ts +8 -0
  13. package/dist/Prebuilt/components/HMSVideo/SeekControls.d.ts +7 -0
  14. package/dist/Prebuilt/components/HMSVideo/VideoProgress.d.ts +5 -0
  15. package/dist/Prebuilt/components/HMSVideo/VideoTime.d.ts +2 -0
  16. package/dist/Prebuilt/components/HMSVideo/VolumeControl.d.ts +2 -0
  17. package/dist/Prebuilt/components/HMSVideo/index.d.ts +26 -0
  18. package/dist/Prebuilt/components/HMSVideo/utils.d.ts +8 -0
  19. package/dist/Prebuilt/components/Leave/DesktopLeaveRoom.d.ts +2 -1
  20. package/dist/Prebuilt/components/Leave/LeaveRoom.d.ts +2 -1
  21. package/dist/Prebuilt/components/Leave/MwebLeaveRoom.d.ts +2 -3
  22. package/dist/Prebuilt/components/MwebLandscapePrompt.d.ts +1 -1
  23. package/dist/Prebuilt/components/RaiseHand.d.ts +5 -0
  24. package/dist/Prebuilt/components/SidePaneTabs.d.ts +1 -1
  25. package/dist/Sheet/Sheet.d.ts +1 -0
  26. package/dist/{chunk-72B32WVR.js → chunk-R2JJJQR3.js} +1684 -1316
  27. package/dist/chunk-R2JJJQR3.js.map +7 -0
  28. package/dist/index.cjs.js +2866 -2053
  29. package/dist/index.cjs.js.map +4 -4
  30. package/dist/index.js +1 -1
  31. package/dist/meta.cjs.json +786 -299
  32. package/dist/meta.esbuild.json +805 -307
  33. package/package.json +7 -6
  34. package/src/Button/Button.tsx +4 -4
  35. package/src/Fieldset/Fieldset.tsx +1 -1
  36. package/src/Input/PasswordInput.stories.tsx +1 -1
  37. package/src/Pagination/StyledPagination.stories.tsx +2 -2
  38. package/src/Prebuilt/IconButton.tsx +1 -1
  39. package/src/Prebuilt/common/hooks.ts +21 -0
  40. package/src/Prebuilt/components/AppData/useSidepane.js +34 -7
  41. package/src/Prebuilt/components/AudioVideoToggle.tsx +2 -1
  42. package/src/Prebuilt/components/AuthToken.jsx +1 -1
  43. package/src/Prebuilt/components/Chat/Chat.tsx +41 -1
  44. package/src/Prebuilt/components/Chat/ChatFooter.tsx +33 -13
  45. package/src/Prebuilt/components/Chat/MwebChatOption.tsx +1 -1
  46. package/src/Prebuilt/components/ConferenceScreen.tsx +48 -7
  47. package/src/Prebuilt/components/EmojiReaction.jsx +33 -23
  48. package/src/Prebuilt/components/Footer/Footer.tsx +0 -1
  49. package/src/Prebuilt/components/Footer/RoleOptions.tsx +138 -125
  50. package/src/Prebuilt/components/HMSVideo/Controls.jsx +1 -1
  51. package/src/Prebuilt/components/HMSVideo/FullscreenButton.tsx +13 -0
  52. package/src/Prebuilt/components/HMSVideo/HLSAutoplayBlockedPrompt.tsx +72 -0
  53. package/src/Prebuilt/components/HMSVideo/HLSCaptionSelector.tsx +4 -2
  54. package/src/Prebuilt/components/HMSVideo/HLSQualitySelector.tsx +248 -0
  55. package/src/Prebuilt/components/HMSVideo/HMSVideo.jsx +17 -7
  56. package/src/Prebuilt/components/HMSVideo/MwebHLSViewTitle.tsx +84 -0
  57. package/src/Prebuilt/components/HMSVideo/PlayButton.tsx +27 -0
  58. package/src/Prebuilt/components/HMSVideo/PlayPauseButton.tsx +27 -0
  59. package/src/Prebuilt/components/HMSVideo/PlayerContext.tsx +15 -0
  60. package/src/Prebuilt/components/HMSVideo/SeekControls.tsx +22 -0
  61. package/src/Prebuilt/components/HMSVideo/VideoProgress.tsx +95 -0
  62. package/src/Prebuilt/components/HMSVideo/VideoTime.tsx +43 -0
  63. package/src/Prebuilt/components/HMSVideo/{VolumeControl.jsx → VolumeControl.tsx} +6 -4
  64. package/src/Prebuilt/components/HMSVideo/{index.js → index.ts} +6 -2
  65. package/src/Prebuilt/components/HMSVideo/{HMSVIdeoUtils.js → utils.ts} +5 -5
  66. package/src/Prebuilt/components/Header/StreamActions.tsx +1 -1
  67. package/src/Prebuilt/components/IconButtonWithOptions/IconButtonWithOptions.tsx +1 -1
  68. package/src/Prebuilt/components/Leave/DesktopLeaveRoom.tsx +50 -46
  69. package/src/Prebuilt/components/Leave/LeaveRoom.tsx +15 -4
  70. package/src/Prebuilt/components/Leave/MwebLeaveRoom.tsx +46 -27
  71. package/src/Prebuilt/components/MoreSettings/MoreSettings.tsx +3 -1
  72. package/src/Prebuilt/components/MoreSettings/SplitComponents/DesktopOptions.tsx +37 -31
  73. package/src/Prebuilt/components/MoreSettings/SplitComponents/MwebOptions.tsx +12 -8
  74. package/src/Prebuilt/components/MwebLandscapePrompt.tsx +14 -3
  75. package/src/Prebuilt/components/Notifications/HandRaisedNotifications.tsx +5 -2
  76. package/src/Prebuilt/components/Notifications/PeerNotifications.tsx +1 -1
  77. package/src/Prebuilt/components/Polls/Voting/QuestionCard.jsx +19 -8
  78. package/src/Prebuilt/components/Polls/Voting/Voting.tsx +3 -2
  79. package/src/Prebuilt/components/Polls/common/OptionInputWithDelete.tsx +1 -1
  80. package/src/Prebuilt/components/Polls/common/utils.ts +2 -2
  81. package/src/Prebuilt/components/RaiseHand.tsx +24 -0
  82. package/src/Prebuilt/components/RoomDetails/RoomDetailsPane.tsx +41 -14
  83. package/src/Prebuilt/components/SidePaneTabs.tsx +56 -48
  84. package/src/Prebuilt/components/StatsForNerds.jsx +14 -6
  85. package/src/Prebuilt/components/Streaming/Common.jsx +1 -1
  86. package/src/Prebuilt/components/TileMenu/TileMenuContent.tsx +2 -2
  87. package/src/Prebuilt/components/Toast/ToastBatcher.js +8 -1
  88. package/src/Prebuilt/components/Toast/ToastConfig.jsx +17 -0
  89. package/src/Prebuilt/components/VideoLayouts/RoleProminence.tsx +1 -1
  90. package/src/Prebuilt/components/hooks/useRoleProminencePeers.tsx +2 -1
  91. package/src/Prebuilt/components/pdfAnnotator/shareScreenOptions.jsx +2 -2
  92. package/src/Prebuilt/components/pdfAnnotator/uploadedFile.jsx +1 -1
  93. package/src/Prebuilt/layouts/HLSView.jsx +359 -178
  94. package/src/Prebuilt/layouts/SidePane.tsx +145 -59
  95. package/src/Prebuilt/layouts/VideoStreamingSection.tsx +22 -2
  96. package/src/Prebuilt/primitives/DialogContent.jsx +1 -1
  97. package/src/Prebuilt/provider/roomLayoutProvider/index.tsx +1 -1
  98. package/src/Sheet/Sheet.tsx +7 -3
  99. package/dist/HLSView-37B2YVTC.js +0 -987
  100. package/dist/HLSView-37B2YVTC.js.map +0 -7
  101. package/dist/chunk-72B32WVR.js.map +0 -7
  102. package/src/Prebuilt/components/HMSVideo/FullscreenButton.jsx +0 -18
  103. package/src/Prebuilt/components/HMSVideo/HLSAutoplayBlockedPrompt.jsx +0 -35
  104. package/src/Prebuilt/components/HMSVideo/HLSQualitySelector.jsx +0 -127
  105. package/src/Prebuilt/components/HMSVideo/PlayButton.jsx +0 -13
  106. package/src/Prebuilt/components/HMSVideo/VideoProgress.jsx +0 -76
  107. package/src/Prebuilt/components/HMSVideo/VideoTime.jsx +0 -33
  108. package/src/Prebuilt/components/RaiseHand.jsx +0 -17
@@ -38,9 +38,9 @@ export const Voting = ({ id, toggleVoting }: { id: string; toggleVoting: () => v
38
38
  <Flex
39
39
  align="center"
40
40
  css={{
41
- gap: '$6',
41
+ gap: '$4',
42
42
  py: '$6',
43
- px: '$10',
43
+ px: '$8',
44
44
  my: '$4',
45
45
  w: '100%',
46
46
  color: '$on_surface_high',
@@ -60,6 +60,7 @@ export const Voting = ({ id, toggleVoting }: { id: string; toggleVoting: () => v
60
60
  marginLeft: 'auto',
61
61
  cursor: 'pointer',
62
62
  '&:hover': { opacity: '0.8' },
63
+ height: 'fit-content',
63
64
  }}
64
65
  >
65
66
  <CrossIcon onClick={toggleVoting} />
@@ -29,7 +29,7 @@ export const OptionInputWithDelete = ({
29
29
  key={index}
30
30
  onChange={event => handleOptionTextChange(index, event.target.value)}
31
31
  />
32
- <IconButton onClick={() => removeOption(index)} css={{ bg: '$transparent', border: 'none' }}>
32
+ <IconButton onClick={() => removeOption(index)} css={{ bg: 'transparent', border: 'none' }}>
33
33
  <TrashIcon />
34
34
  </IconButton>
35
35
  </>
@@ -4,7 +4,7 @@ export const getFormattedTime = (milliseconds: number | undefined, precise = tru
4
4
  const totalSeconds = milliseconds / 1000;
5
5
  const hours = Math.floor(totalSeconds / 3600);
6
6
  const minutes = Math.floor((totalSeconds % 3600) / 60);
7
- const seconds = precise ? totalSeconds % 60 : Math.floor(totalSeconds % 60);
7
+ const seconds = totalSeconds % 60;
8
8
 
9
9
  let formattedTime = '';
10
10
  if (hours) {
@@ -16,7 +16,7 @@ export const getFormattedTime = (milliseconds: number | undefined, precise = tru
16
16
  if (!precise && (hours || minutes)) {
17
17
  return formattedTime;
18
18
  }
19
- formattedTime += `${seconds}s`;
19
+ formattedTime += `${precise ? seconds.toFixed(3) : Math.floor(seconds)}s`;
20
20
 
21
21
  return formattedTime;
22
22
  };
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ import { HandIcon, HandRaiseSlashedIcon } from '@100mslive/react-icons';
3
+ import { CSS } from '../../Theme';
4
+ import { Tooltip } from '../../Tooltip';
5
+ // @ts-ignore: No implicit Any
6
+ import IconButton from '../IconButton';
7
+ // @ts-ignore: No implicit Any
8
+ import { useMyMetadata } from './hooks/useMetadata';
9
+
10
+ export const RaiseHand = ({ css }: { css?: CSS }) => {
11
+ const { isHandRaised, toggleHandRaise } = useMyMetadata();
12
+ return (
13
+ <Tooltip title={isHandRaised ? 'Lower hand' : 'Raise hand'}>
14
+ <IconButton
15
+ data-testid="hand_raise_btn"
16
+ css={css}
17
+ active={!isHandRaised}
18
+ onClick={async () => await toggleHandRaise()}
19
+ >
20
+ {isHandRaised ? <HandRaiseSlashedIcon /> : <HandIcon />}
21
+ </IconButton>
22
+ </Tooltip>
23
+ );
24
+ };
@@ -2,31 +2,32 @@ import React from 'react';
2
2
  import { CrossIcon } from '@100mslive/react-icons';
3
3
  import { Box, Flex } from '../../../Layout';
4
4
  import { Text } from '../../../Text';
5
+ // @ts-ignore: No implicit any
6
+ import { Logo } from '../Header/HeaderComponents';
5
7
  import { RoomDetailsRow } from './RoomDetailsRow';
6
8
  import { useRoomLayoutHeader } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
7
9
  // @ts-ignore
8
10
  import { useSidepaneToggle } from '../AppData/useSidepane';
11
+ import { useMobileHLSStream } from '../../common/hooks';
9
12
  import { SIDE_PANE_OPTIONS } from '../../common/constants';
10
13
 
11
14
  export const RoomDetailsPane = () => {
12
- const { title, description, details } = useRoomLayoutHeader();
13
- const toggleDetailsPane = useSidepaneToggle(SIDE_PANE_OPTIONS.ROOM_DETAILS);
15
+ const { description } = useRoomLayoutHeader();
16
+ const isMwebHLSStream = useMobileHLSStream();
14
17
  return (
15
18
  <Box css={{ flex: '1 1 0' }}>
16
- <Flex justify="between" align="center" css={{ w: '100%' }}>
17
- <Text variant="h6">{title}</Text>
18
- <Flex
19
- onClick={toggleDetailsPane}
20
- css={{ color: '$on_surface_high', cursor: 'pointer', '&:hover': { opacity: '0.8' } }}
21
- >
22
- <CrossIcon />
19
+ {isMwebHLSStream ? (
20
+ <Flex direction="row" align="center" gap="2">
21
+ <Logo />
22
+ <ShowRoomDetailHeader />
23
23
  </Flex>
24
- </Flex>
25
-
26
- <RoomDetailsRow details={details} />
27
-
24
+ ) : (
25
+ <ShowRoomDetailHeader />
26
+ )}
28
27
  <Box css={{ mt: '$10' }}>
29
- <Text css={{ color: '$on_surface_high', fontWeight: '$semiBold' }}>Description</Text>
28
+ <Text css={{ color: '$on_surface_high', fontWeight: '$semiBold', display: isMwebHLSStream ? 'none' : '' }}>
29
+ Description
30
+ </Text>
30
31
  <Text variant="sm" css={{ c: '$on_surface_medium' }}>
31
32
  {description}
32
33
  </Text>
@@ -34,3 +35,29 @@ export const RoomDetailsPane = () => {
34
35
  </Box>
35
36
  );
36
37
  };
38
+
39
+ const ShowRoomDetailHeader = () => {
40
+ const { title, details } = useRoomLayoutHeader();
41
+ const toggleDetailsPane = useSidepaneToggle(SIDE_PANE_OPTIONS.ROOM_DETAILS);
42
+ const isMwebHLSStream = useMobileHLSStream();
43
+ return (
44
+ <Flex direction="column">
45
+ <Flex justify="between" align="center" css={{ w: '100%' }}>
46
+ <Text variant="h6">{title}</Text>
47
+ {!isMwebHLSStream && (
48
+ <Flex
49
+ onClick={toggleDetailsPane}
50
+ css={{
51
+ color: '$on_surface_high',
52
+ cursor: 'pointer',
53
+ '&:hover': { opacity: '0.8' },
54
+ }}
55
+ >
56
+ <CrossIcon />
57
+ </Flex>
58
+ )}
59
+ </Flex>
60
+ <RoomDetailsRow details={details} />
61
+ </Flex>
62
+ );
63
+ };
@@ -1,16 +1,15 @@
1
1
  import React, { useEffect, useState } from 'react';
2
2
  import { useMedia } from 'react-use';
3
3
  import { DefaultConferencingScreen_Elements } from '@100mslive/types-prebuilt';
4
+ import { match } from 'ts-pattern';
4
5
  import { selectPeerCount, useHMSStore } from '@100mslive/react-sdk';
5
6
  import { CrossIcon } from '@100mslive/react-icons';
6
- // @ts-ignore: No implicit Any
7
7
  import { Chat } from './Chat/Chat';
8
8
  import { PaginatedParticipants } from './Footer/PaginatedParticipants';
9
9
  import { ParticipantList } from './Footer/ParticipantList';
10
10
  import { Box, config as cssConfig, Flex, IconButton, Tabs, Text } from '../..';
11
11
  import { Tooltip } from '../../Tooltip';
12
12
  import { ChatSettings } from './ChatSettings';
13
- // @ts-ignore: No implicit Any
14
13
  import { useRoomLayoutConferencingScreen } from '../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
15
14
  // @ts-ignore: No implicit Any
16
15
  import { useIsSidepaneTypeOpen, useSidepaneReset, useSidepaneToggle } from './AppData/useSidepane';
@@ -19,12 +18,15 @@ import { getFormattedCount } from '../common/utils';
19
18
  import { SIDE_PANE_OPTIONS } from '../common/constants';
20
19
 
21
20
  const tabTriggerCSS = {
22
- color: '$on_surface_high',
21
+ color: '$on_surface_low',
23
22
  p: '$4',
24
23
  fontWeight: '$semiBold',
25
24
  fontSize: '$sm',
26
25
  w: '100%',
27
26
  justifyContent: 'center',
27
+ '&[data-state="active"]': {
28
+ color: '$on_surface_high',
29
+ },
28
30
  };
29
31
 
30
32
  const ParticipantCount = ({ count }: { count: number }) => {
@@ -39,19 +41,19 @@ const ParticipantCount = ({ count }: { count: number }) => {
39
41
 
40
42
  export const SidePaneTabs = React.memo<{
41
43
  active: 'Participants | Chat';
42
- hideControls?: boolean;
43
- }>(({ active = SIDE_PANE_OPTIONS.CHAT, hideControls }) => {
44
+ hideTab?: boolean;
45
+ }>(({ active = SIDE_PANE_OPTIONS.CHAT, hideTab = false }) => {
44
46
  const toggleChat = useSidepaneToggle(SIDE_PANE_OPTIONS.CHAT);
45
47
  const toggleParticipants = useSidepaneToggle(SIDE_PANE_OPTIONS.PARTICIPANTS);
46
48
  const resetSidePane = useSidepaneReset();
47
49
  const [activeTab, setActiveTab] = useState(active);
48
50
  const [activeRole, setActiveRole] = useState('');
49
51
  const peerCount = useHMSStore(selectPeerCount);
50
- const { elements } = useRoomLayoutConferencingScreen();
52
+ const { elements, screenType } = useRoomLayoutConferencingScreen();
51
53
  const chat_title = elements?.chat?.chat_title || 'Chat';
52
54
  const showChat = !!elements?.chat;
53
55
  const showParticipants = !!elements?.participant_list;
54
- const hideTabs = !(showChat && showParticipants);
56
+ const hideTabs = !(showChat && showParticipants) || hideTab;
55
57
  const isMobile = useMedia(cssConfig.media.md);
56
58
  const isOverlayChat = !!elements?.chat?.is_overlay && isMobile;
57
59
  const { off_stage_roles = [] } = (elements as DefaultConferencingScreen_Elements)?.on_stage_exp || {};
@@ -59,13 +61,16 @@ export const SidePaneTabs = React.memo<{
59
61
  const showChatSettings = showChat && isChatOpen && (!isMobile || !isOverlayChat);
60
62
 
61
63
  useEffect(() => {
62
- if (activeTab === SIDE_PANE_OPTIONS.CHAT && !showChat && showParticipants) {
63
- setActiveTab(SIDE_PANE_OPTIONS.PARTICIPANTS);
64
- } else if (activeTab === SIDE_PANE_OPTIONS.PARTICIPANTS && showChat && !showParticipants) {
65
- setActiveTab(SIDE_PANE_OPTIONS.CHAT);
66
- } else if (!showChat && !showParticipants) {
67
- resetSidePane();
68
- }
64
+ match({ activeTab, showChat, showParticipants })
65
+ .with({ activeTab: SIDE_PANE_OPTIONS.CHAT, showChat: false, showParticipants: true }, () => {
66
+ setActiveTab(SIDE_PANE_OPTIONS.PARTICIPANTS);
67
+ })
68
+ .with({ activeTab: SIDE_PANE_OPTIONS.PARTICIPANTS, showChat: true, showParticipants: false }, () => {
69
+ setActiveTab(SIDE_PANE_OPTIONS.CHAT);
70
+ })
71
+ .with({ showChat: false, showParticipants: false }, () => {
72
+ resetSidePane();
73
+ });
69
74
  }, [showChat, activeTab, showParticipants, resetSidePane]);
70
75
 
71
76
  useEffect(() => {
@@ -79,7 +84,6 @@ export const SidePaneTabs = React.memo<{
79
84
  css={{
80
85
  color: '$on_primary_high',
81
86
  h: '100%',
82
- marginTop: hideControls && isOverlayChat ? '$17' : '0',
83
87
  transition: 'margin 0.3s ease-in-out',
84
88
  position: 'relative',
85
89
  }}
@@ -97,20 +101,27 @@ export const SidePaneTabs = React.memo<{
97
101
  css={{
98
102
  color: '$on_primary_high',
99
103
  h: '100%',
100
- marginTop: hideControls && isOverlayChat ? '$17' : '0',
101
104
  transition: 'margin 0.3s ease-in-out',
102
105
  }}
103
106
  >
104
- {isOverlayChat && isChatOpen && showChat ? (
105
- <Chat />
106
- ) : (
107
- <>
108
- {hideTabs ? (
107
+ {match({ isOverlayChat, isChatOpen, showChat, hideTabs })
108
+ .with({ isOverlayChat: true, isChatOpen: true, showChat: true }, () => <Chat />)
109
+ .with({ hideTabs: true }, () => {
110
+ return (
109
111
  <>
110
- <Flex justify="between" css={{ w: '100%' }}>
111
- <Text variant="sm" css={{ fontWeight: '$semiBold', p: '$4', c: '$on_surface_high', pr: '$12' }}>
112
- {showChat ? (
113
- chat_title
112
+ <Flex justify="between" css={{ w: '100%', '&:empty': { display: 'none' } }}>
113
+ <Text
114
+ variant="sm"
115
+ css={{
116
+ fontWeight: '$semiBold',
117
+ p: '$4',
118
+ c: '$on_surface_high',
119
+ pr: '$12',
120
+ '&:empty': { display: 'none' },
121
+ }}
122
+ >
123
+ {activeTab === SIDE_PANE_OPTIONS.CHAT ? (
124
+ screenType !== 'hls_live_streaming' && chat_title
114
125
  ) : (
115
126
  <span>
116
127
  Participants&nbsp;
@@ -122,7 +133,12 @@ export const SidePaneTabs = React.memo<{
122
133
  {showChatSettings ? <ChatSettings /> : null}
123
134
  {isOverlayChat && isChatOpen ? null : (
124
135
  <IconButton
125
- css={{ my: '$1', color: '$on_surface_medium', '&:hover': { color: '$on_surface_high' } }}
136
+ css={{
137
+ my: '$1',
138
+ color: '$on_surface_medium',
139
+ '&:hover': { color: '$on_surface_high' },
140
+ '&:empty': { display: 'none' },
141
+ }}
126
142
  onClick={e => {
127
143
  e.stopPropagation();
128
144
  if (activeTab === SIDE_PANE_OPTIONS.CHAT) {
@@ -133,14 +149,21 @@ export const SidePaneTabs = React.memo<{
133
149
  }}
134
150
  data-testid="close_chat"
135
151
  >
136
- <CrossIcon />
152
+ {screenType === 'hls_live_streaming' && isChatOpen ? null : <CrossIcon />}
137
153
  </IconButton>
138
154
  )}
139
155
  </Flex>
140
156
  </Flex>
141
- {showChat ? <Chat /> : <ParticipantList offStageRoles={off_stage_roles} onActive={setActiveRole} />}
157
+ {activeTab === SIDE_PANE_OPTIONS.CHAT ? (
158
+ <Chat />
159
+ ) : (
160
+ <ParticipantList offStageRoles={off_stage_roles} onActive={setActiveRole} />
161
+ )}
142
162
  </>
143
- ) : (
163
+ );
164
+ })
165
+ .otherwise(() => {
166
+ return (
144
167
  <Tabs.Root
145
168
  value={activeTab}
146
169
  onValueChange={setActiveTab}
@@ -151,24 +174,10 @@ export const SidePaneTabs = React.memo<{
151
174
  >
152
175
  <Flex css={{ w: '100%' }}>
153
176
  <Tabs.List css={{ flexGrow: 1, borderRadius: '$2', bg: '$surface_default' }}>
154
- <Tabs.Trigger
155
- value={SIDE_PANE_OPTIONS.CHAT}
156
- onClick={toggleChat}
157
- css={{
158
- ...tabTriggerCSS,
159
- color: activeTab !== SIDE_PANE_OPTIONS.CHAT ? '$on_surface_low' : '$on_surface_high',
160
- }}
161
- >
177
+ <Tabs.Trigger value={SIDE_PANE_OPTIONS.CHAT} onClick={toggleChat} css={tabTriggerCSS}>
162
178
  {chat_title}
163
179
  </Tabs.Trigger>
164
- <Tabs.Trigger
165
- value={SIDE_PANE_OPTIONS.PARTICIPANTS}
166
- onClick={toggleParticipants}
167
- css={{
168
- ...tabTriggerCSS,
169
- color: activeTab !== SIDE_PANE_OPTIONS.PARTICIPANTS ? '$on_surface_low' : '$on_surface_high',
170
- }}
171
- >
180
+ <Tabs.Trigger value={SIDE_PANE_OPTIONS.PARTICIPANTS} onClick={toggleParticipants} css={tabTriggerCSS}>
172
181
  Participants&nbsp;
173
182
  <ParticipantCount count={peerCount} />
174
183
  </Tabs.Trigger>
@@ -198,9 +207,8 @@ export const SidePaneTabs = React.memo<{
198
207
  <Chat />
199
208
  </Tabs.Content>
200
209
  </Tabs.Root>
201
- )}
202
- </>
203
- )}
210
+ );
211
+ })}
204
212
  </Flex>
205
213
  );
206
214
  });
@@ -1,4 +1,5 @@
1
1
  import React, { useEffect, useMemo, useRef, useState } from 'react';
2
+ import { match, P } from 'ts-pattern';
2
3
  import {
3
4
  selectHMSStats,
4
5
  selectLocalPeerID,
@@ -188,11 +189,15 @@ const LocalPeerStats = () => {
188
189
  };
189
190
 
190
191
  const TrackStats = ({ trackID, layer, local }) => {
191
- const selector = layer
192
- ? selectHMSStats.localVideoTrackStatsByLayer(layer)(trackID)
193
- : local
194
- ? selectHMSStats.localAudioTrackStatsByID(trackID)
195
- : selectHMSStats.trackStatsByID(trackID);
192
+ const selector = match({ trackID, layer, local })
193
+ .with(
194
+ {
195
+ layer: P.when(layer => !!layer),
196
+ },
197
+ () => selectHMSStats.localVideoTrackStatsByLayer(layer)(trackID),
198
+ )
199
+ .with({ local: P.when(local => !!local) }, () => selectHMSStats.localAudioTrackStatsByID(trackID))
200
+ .otherwise(() => selectHMSStats.trackStatsByID(trackID));
196
201
  const stats = useHMSStatsStore(selector);
197
202
  if (!stats) {
198
203
  return null;
@@ -215,7 +220,10 @@ const TrackStats = ({ trackID, layer, local }) => {
215
220
  {!inbound && <StatsRow label="Quality Limitation Reason" value={stats.qualityLimitationReason} />}
216
221
  </>
217
222
  )}
218
- <StatsRow label="Round Trip Time" value={stats.roundTripTime ? `${stats.roundTripTime * 1000} ms` : '-'} />
223
+ <StatsRow
224
+ label="Round Trip Time"
225
+ value={stats.roundTripTime ? `${(stats.roundTripTime * 1000).toFixed(3)} ms` : '-'}
226
+ />
219
227
  </Flex>
220
228
  );
221
229
  };
@@ -20,7 +20,7 @@ export const StreamCard = ({ title, subtitle, Icon, imgSrc = '', css = {}, onCli
20
20
  onClick={onClick}
21
21
  >
22
22
  <Text css={{ alignSelf: 'center', p: '$4' }}>
23
- {imgSrc ? <img src={imgSrc} height={40} width={40} /> : <Icon width={40} height={40} />}
23
+ {imgSrc ? <img src={imgSrc} height={40} width={40} alt="Streaming" /> : <Icon width={40} height={40} />}
24
24
  </Text>
25
25
  <Box css={{ flex: '1 1 0', mx: '$8' }}>
26
26
  <Text variant="h6" css={{ mb: '$4' }}>
@@ -289,7 +289,7 @@ export const TileMenuContent = ({
289
289
  data-testid={isVideoEnabled ? 'mute_video_participant_btn' : 'unmute_video_participant_btn'}
290
290
  >
291
291
  {isVideoEnabled ? <VideoOnIcon height={20} width={20} /> : <VideoOffIcon height={20} width={20} />}
292
- <span>{isVideoEnabled ? 'Mute' : 'Request Unmute'}</span>
292
+ <span>{isVideoEnabled ? 'Mute Video' : 'Request to Unmute Video'}</span>
293
293
  </StyledMenuTile.ItemButton>
294
294
  ) : null}
295
295
 
@@ -304,7 +304,7 @@ export const TileMenuContent = ({
304
304
  data-testid={isAudioEnabled ? 'mute_audio_participant_btn' : 'unmute_audio_participant_btn'}
305
305
  >
306
306
  {isAudioEnabled ? <MicOnIcon height={20} width={20} /> : <MicOffIcon height={20} width={20} />}
307
- <span>{isAudioEnabled ? 'Mute' : 'Request Unmute'}</span>
307
+ <span>{isAudioEnabled ? 'Mute Audio' : 'Request to Unmute Audio'}</span>
308
308
  </StyledMenuTile.ItemButton>
309
309
  ) : null}
310
310
 
@@ -3,6 +3,7 @@ import { ToastManager } from './ToastManager';
3
3
 
4
4
  export const ToastBatcher = {
5
5
  toastsType: new Map(),
6
+ toastCache: {},
6
7
  showToastInternal({ notification, duration, type }) {
7
8
  let notificationType = type;
8
9
  if (!type) {
@@ -40,7 +41,13 @@ export const ToastBatcher = {
40
41
  },
41
42
  showToast({ notification, duration = 3000, type }) {
42
43
  try {
43
- this.showToastInternal({ notification, duration, type });
44
+ if (!this.toastCache[notification.id]) {
45
+ this.showToastInternal({ notification, duration, type });
46
+ }
47
+ this.toastCache[notification.id] = true;
48
+ if (Object.keys(this.toastCache).length > 100) {
49
+ this.toastCache = {};
50
+ }
44
51
  } catch (err) {
45
52
  console.debug('Notifications', err);
46
53
  }
@@ -123,6 +123,23 @@ export const ToastConfig = {
123
123
  },
124
124
  },
125
125
  RAISE_HAND: {
126
+ single: notification => {
127
+ return {
128
+ title: `${notification.data?.name} raised hand`,
129
+ icon: <HandIcon />,
130
+ };
131
+ },
132
+ multiple: notifications => {
133
+ const count = new Set(notifications.map(notification => notification.data?.id)).size;
134
+ return {
135
+ title: `${notifications[notifications.length - 1].data?.name} ${
136
+ count > 1 ? `${count} and others` : ''
137
+ } raised hand`,
138
+ icon: <HandIcon />,
139
+ };
140
+ },
141
+ },
142
+ RAISE_HAND_HLS: {
126
143
  single: notification => {
127
144
  return {
128
145
  title: `${notification.data?.name} raised hand`,
@@ -67,7 +67,7 @@ export function RoleProminence({
67
67
  edgeToEdge={edgeToEdge}
68
68
  hasSidebar={layoutMode === LayoutMode.SIDEBAR}
69
69
  />
70
- {isInsetEnabled && localPeer && !prominentPeers.includes(localPeer) && <InsetTile />}
70
+ {isInsetEnabled && localPeer && prominentPeers.length > 0 && !prominentPeers.includes(localPeer) && <InsetTile />}
71
71
  </ProminenceLayout.Root>
72
72
  );
73
73
  }
@@ -17,7 +17,7 @@ export const useRoleProminencePeers = (prominentRoles: string[], peers: HMSPeer[
17
17
  }
18
18
  return acc;
19
19
  }
20
- if (peer.isLocal && isInsetEnabled) {
20
+ if (peer.isLocal && isInsetEnabled && !prominentRoles?.includes(peer.roleName || '')) {
21
21
  return acc;
22
22
  }
23
23
  if (prominentRoles?.includes(peer.roleName || '')) {
@@ -25,6 +25,7 @@ export const useRoleProminencePeers = (prominentRoles: string[], peers: HMSPeer[
25
25
  } else {
26
26
  acc[1].push(peer);
27
27
  }
28
+
28
29
  return acc;
29
30
  },
30
31
  [[], []],
@@ -61,7 +61,7 @@ export function ShareScreenOptions() {
61
61
  pt: '$10',
62
62
  pb: '$6',
63
63
  '&:hover': {
64
- bg: '$transparent',
64
+ bg: 'transparent',
65
65
  cursor: 'default',
66
66
  },
67
67
  }}
@@ -78,7 +78,7 @@ export function ShareScreenOptions() {
78
78
  pt: '$6',
79
79
  pb: '$10',
80
80
  '&:hover': {
81
- bg: '$transparent',
81
+ bg: 'transparent',
82
82
  cursor: 'default',
83
83
  },
84
84
  }}
@@ -36,7 +36,7 @@ export const UploadedFile = ({ pdfFile, setPDFFile, onOpenChange }) => {
36
36
  w: '100%',
37
37
  '&:focus': {
38
38
  boxShadow: '0 0 0 1px $colors$primary_default',
39
- border: '1px solid $transparent',
39
+ border: '1px solid transparent',
40
40
  },
41
41
  mb: 0,
42
42
  mt: '$6',