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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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',