@100mslive/roomkit-react 0.1.7-alpha.0 → 0.1.8-alpha.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. package/dist/AudioLevel/AudioLevel.d.ts +5 -8
  2. package/dist/AudioLevel/index.d.ts +2 -1
  3. package/dist/AudioLevel/useBorderAudioLevel.d.ts +8 -0
  4. package/dist/{HLSView-F5BDZVT2.js → HLSView-IQRPLYNH.js} +8 -6
  5. package/dist/{HLSView-F5BDZVT2.js.map → HLSView-IQRPLYNH.js.map} +2 -2
  6. package/dist/Prebuilt/components/Chip.d.ts +12 -0
  7. package/dist/Prebuilt/components/PrebuiltDialogPortal.d.ts +4 -0
  8. package/dist/Prebuilt/components/RoleChangeRequest/RequestPrompt.d.ts +9 -0
  9. package/dist/VideoTile/StyledVideoTile.d.ts +445 -3
  10. package/dist/{VirtualBackground-THDRYDRA.js → VirtualBackground-GP4ATXD3.js} +3 -3
  11. package/dist/{chunk-JSH7SKEH.js → chunk-2H5NIZB7.js} +2 -2
  12. package/dist/{chunk-CDYRVICT.js → chunk-GLYGPYNS.js} +574 -1196
  13. package/dist/chunk-GLYGPYNS.js.map +7 -0
  14. package/dist/{chunk-U3G743OY.js → chunk-Z3O2WGWV.js} +2 -2
  15. package/dist/{chunk-U3G743OY.js.map → chunk-Z3O2WGWV.js.map} +1 -1
  16. package/dist/{conference-6IVZHILI.js → conference-JD35TNH4.js} +1545 -840
  17. package/dist/conference-JD35TNH4.js.map +7 -0
  18. package/dist/index.cjs.js +5975 -5849
  19. package/dist/index.cjs.js.map +4 -4
  20. package/dist/index.js +4 -2
  21. package/dist/meta.cjs.json +1633 -1399
  22. package/dist/meta.esbuild.json +1689 -1454
  23. package/package.json +6 -6
  24. package/src/AudioLevel/AudioLevel.tsx +79 -30
  25. package/src/AudioLevel/audio-level.png +0 -0
  26. package/src/AudioLevel/index.ts +2 -1
  27. package/src/AudioLevel/useBorderAudioLevel.tsx +34 -0
  28. package/src/Input/Input.tsx +1 -1
  29. package/src/Prebuilt/App.tsx +1 -0
  30. package/src/Prebuilt/components/Chat/ChatBody.jsx +125 -106
  31. package/src/Prebuilt/components/{Chip.jsx → Chip.tsx} +13 -2
  32. package/src/Prebuilt/components/Footer/ParticipantList.jsx +24 -13
  33. package/src/Prebuilt/components/Footer/RoleAccordion.tsx +43 -3
  34. package/src/Prebuilt/components/Leave/DesktopLeaveRoom.tsx +41 -46
  35. package/src/Prebuilt/components/Leave/MwebLeaveRoom.tsx +23 -35
  36. package/src/Prebuilt/components/MoreSettings/ChangeNameModal.jsx +3 -2
  37. package/src/Prebuilt/components/MoreSettings/EmbedUrl.jsx +3 -2
  38. package/src/Prebuilt/components/MwebLandscapePrompt.jsx +58 -0
  39. package/src/Prebuilt/components/Notifications/HLSFailureModal.jsx +3 -2
  40. package/src/Prebuilt/components/Notifications/PermissionErrorModal.jsx +3 -2
  41. package/src/Prebuilt/components/PrebuiltDialogPortal.tsx +6 -0
  42. package/src/Prebuilt/components/Preview/PreviewJoin.tsx +9 -6
  43. package/src/Prebuilt/components/RaiseHand.jsx +4 -11
  44. package/src/Prebuilt/components/RoleChangeModal.jsx +3 -2
  45. package/src/Prebuilt/components/RoleChangeRequest/RequestPrompt.tsx +67 -0
  46. package/src/Prebuilt/components/{RoleChangeRequestModal.tsx → RoleChangeRequest/RoleChangeRequestModal.tsx} +18 -50
  47. package/src/Prebuilt/components/Settings/SettingsModal.jsx +3 -2
  48. package/src/Prebuilt/components/Settings/StartRecording.jsx +3 -2
  49. package/src/Prebuilt/components/StatsForNerds.jsx +3 -2
  50. package/src/Prebuilt/components/VideoTile.jsx +34 -75
  51. package/src/Prebuilt/components/conference.jsx +1 -1
  52. package/src/Prebuilt/components/hooks/useMetadata.jsx +2 -1
  53. package/src/Prebuilt/components/pdfAnnotator/pdfFileOptions.jsx +3 -2
  54. package/src/Prebuilt/components/pdfAnnotator/shareScreenOptions.jsx +4 -29
  55. package/src/Prebuilt/components/pdfAnnotator/uploadedFile.jsx +3 -2
  56. package/src/Prebuilt/layouts/HLSView.jsx +4 -2
  57. package/src/Prebuilt/layouts/SidePane.tsx +0 -1
  58. package/src/Prebuilt/primitives/DialogContent.jsx +5 -4
  59. package/src/VideoTile/StyledVideoTile.tsx +10 -14
  60. package/dist/chunk-CDYRVICT.js.map +0 -7
  61. package/dist/conference-6IVZHILI.js.map +0 -7
  62. /package/dist/Prebuilt/components/{RoleChangeRequestModal.d.ts → RoleChangeRequest/RoleChangeRequestModal.d.ts} +0 -0
  63. /package/dist/{VirtualBackground-THDRYDRA.js.map → VirtualBackground-GP4ATXD3.js.map} +0 -0
  64. /package/dist/{chunk-JSH7SKEH.js.map → chunk-2H5NIZB7.js.map} +0 -0
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "prebuilt",
11
11
  "roomkit"
12
12
  ],
13
- "version": "0.1.7-alpha.0",
13
+ "version": "0.1.8-alpha.0",
14
14
  "author": "100ms",
15
15
  "license": "MIT",
16
16
  "files": [
@@ -76,10 +76,10 @@
76
76
  "react": ">=17.0.2 <19.0.0"
77
77
  },
78
78
  "dependencies": {
79
- "@100mslive/hls-player": "0.1.16-alpha.0",
80
- "@100mslive/hms-virtual-background": "1.11.16-alpha.0",
81
- "@100mslive/react-icons": "0.8.16-alpha.0",
82
- "@100mslive/react-sdk": "0.8.16-alpha.0",
79
+ "@100mslive/hls-player": "0.1.17-alpha.0",
80
+ "@100mslive/hms-virtual-background": "1.11.17-alpha.0",
81
+ "@100mslive/react-icons": "0.8.17-alpha.0",
82
+ "@100mslive/react-sdk": "0.8.17-alpha.0",
83
83
  "@100mslive/types-prebuilt": "0.12.0",
84
84
  "@emoji-mart/data": "^1.0.6",
85
85
  "@emoji-mart/react": "^1.0.1",
@@ -115,5 +115,5 @@
115
115
  "uuid": "^8.3.2",
116
116
  "worker-timers": "^7.0.40"
117
117
  },
118
- "gitHead": "02a796684045c2468c14db4cb894c56a17c6c2ba"
118
+ "gitHead": "d6e072de081b508a297ebc1684a33a8e9db048ef"
119
119
  }
@@ -1,34 +1,83 @@
1
- import { useCallback, useRef } from 'react';
2
- import { HMSTrackID } from '@100mslive/hms-video-store';
3
- import { useAudioLevelStyles } from '@100mslive/react-sdk';
4
- import { useTheme } from '../Theme';
1
+ import React, { useEffect, useRef } from 'react';
2
+ import { selectTrackAudioByID, useHMSVanillaStore } from '@100mslive/react-sdk';
3
+ import { Box, Flex } from '../Layout';
4
+ import { keyframes } from '../Theme';
5
+ //@ts-ignore
6
+ import bg from './audio-level.png';
5
7
 
6
- /**
7
- * pass in a track id and get a ref. That ref can be attached to an element which will have border
8
- * as per audio level post that.
9
- */
10
- export function useBorderAudioLevel(audioTrackId?: HMSTrackID) {
11
- const { theme } = useTheme();
12
- const color = theme.colors.primary_default.value;
13
- const getStyle = useCallback(
14
- (level: number) => {
15
- const style: Record<string, string> = {
16
- transition: 'outline 0.4s ease-in-out',
17
- };
18
- style['outline'] = level ? `${sigmoid(level) * 4}px solid ${color}` : '0px solid transparent';
19
- return style;
20
- },
21
- [color],
8
+ // keep the calculated values before hand to avoid recalcuation everytime
9
+ const positionValues = new Array(101).fill(0).reduce((acc, _, index) => {
10
+ acc[index] = Math.round((index / 100) * 4) / 4; // convert to 0.25 multiples
11
+ return acc;
12
+ }, {});
13
+
14
+ const barAnimation = keyframes({
15
+ from: {
16
+ maskSize: '4em .8em',
17
+ '-webkit-mask-position-y': '.1em',
18
+ maskPosition: 'initial .1em',
19
+ },
20
+
21
+ '50%': {
22
+ maskSize: '4em 1em',
23
+ '-webkit-mask-position-y': 0,
24
+ maskPosition: 'initial 0',
25
+ },
26
+
27
+ to: {
28
+ maskSize: '4em .8em',
29
+ '-webkit-mask-position-y': '.1em',
30
+ maskPosition: 'initial 0',
31
+ },
32
+ });
33
+
34
+ const AudioBar = () => {
35
+ return (
36
+ <Box
37
+ css={{
38
+ width: '.25em',
39
+ height: '1em',
40
+ maskImage: `url(${bg})`,
41
+ '-webkit-mask-repeat': 'no-repeat',
42
+ backgroundColor: '$on_primary_high',
43
+ maskSize: '4em 1em',
44
+ }}
45
+ />
22
46
  );
23
- const ref = useRef(null);
24
- useAudioLevelStyles({
25
- trackId: audioTrackId,
26
- getStyle,
27
- ref,
28
- });
29
- return ref;
30
- }
47
+ };
48
+
49
+ export const AudioLevel = ({ trackId, size }: { trackId?: string; size?: 'small' | 'medium' }) => {
50
+ const ref = useRef<HTMLDivElement | null>(null);
51
+ const vanillaStore = useHMSVanillaStore();
31
52
 
32
- export const sigmoid = (z: number) => {
33
- return 1 / (1 + Math.exp(-z));
53
+ useEffect(() => {
54
+ const unsubscribe = vanillaStore.subscribe(audioLevel => {
55
+ if (ref.current) {
56
+ let index = 0;
57
+ //@ts-ignore
58
+ for (const child of ref.current.children) {
59
+ const positionX = `-${positionValues[audioLevel] * (index === 1 ? 2.5 : 1.25)}em`;
60
+ child.style['-webkit-mask-position-x'] = positionX;
61
+ child.style['mask-position'] = `${positionX} 0`;
62
+ child.style['animation'] =
63
+ positionValues[audioLevel] > 0 ? `${barAnimation} 0.6s steps(3,jump-none) 0s infinite` : 'none';
64
+ index++;
65
+ }
66
+ }
67
+ }, selectTrackAudioByID(trackId));
68
+ return unsubscribe;
69
+ }, [vanillaStore, trackId]);
70
+ return (
71
+ <Flex
72
+ ref={ref}
73
+ css={{
74
+ fontSize: size === 'small' ? '0.75rem' : '1rem',
75
+ gap: size === 'small' ? '$1' : '$2',
76
+ }}
77
+ >
78
+ <AudioBar />
79
+ <AudioBar />
80
+ <AudioBar />
81
+ </Flex>
82
+ );
34
83
  };
Binary file
@@ -1 +1,2 @@
1
- export { useBorderAudioLevel } from './AudioLevel';
1
+ export { useBorderAudioLevel } from './useBorderAudioLevel';
2
+ export { AudioLevel } from './AudioLevel';
@@ -0,0 +1,34 @@
1
+ import { useCallback, useRef } from 'react';
2
+ import { HMSTrackID } from '@100mslive/hms-video-store';
3
+ import { useAudioLevelStyles } from '@100mslive/react-sdk';
4
+ import { useTheme } from '../Theme';
5
+
6
+ /**
7
+ * pass in a track id and get a ref. That ref can be attached to an element which will have border
8
+ * as per audio level post that.
9
+ */
10
+ export function useBorderAudioLevel(audioTrackId?: HMSTrackID) {
11
+ const { theme } = useTheme();
12
+ const color = theme.colors.primary_default.value;
13
+ const getStyle = useCallback(
14
+ (level: number) => {
15
+ const style: Record<string, string> = {
16
+ transition: 'outline 0.4s ease-in-out',
17
+ };
18
+ style['outline'] = level ? `${sigmoid(level) * 4}px solid ${color}` : '0px solid transparent';
19
+ return style;
20
+ },
21
+ [color],
22
+ );
23
+ const ref = useRef(null);
24
+ useAudioLevelStyles({
25
+ trackId: audioTrackId,
26
+ getStyle,
27
+ ref,
28
+ });
29
+ return ref;
30
+ }
31
+
32
+ export const sigmoid = (z: number) => {
33
+ return 1 / (1 + Math.exp(-z));
34
+ };
@@ -20,7 +20,7 @@ export const Input = styled('input', {
20
20
  border: '1px solid transparent',
21
21
  },
22
22
  '&::placeholder': {
23
- color: '$on_surface_low',
23
+ color: '$on_surface_medium',
24
24
  },
25
25
  variants: {
26
26
  alert_error_default: {
@@ -218,6 +218,7 @@ export const HMSPrebuilt = React.forwardRef<HMSPrebuiltRefType, HMSPrebuiltProps
218
218
  <AppData appDetails={metadata} tokenEndpoint={tokenByRoomIdRoleEndpoint} />
219
219
  <Init />
220
220
  <Box
221
+ id="prebuilt-container"
221
222
  css={{
222
223
  bg: '$background_dim',
223
224
  size: '100%',
@@ -24,6 +24,7 @@ import { Tooltip } from '../../../Tooltip';
24
24
  import emptyChat from '../../images/empty-chat.svg';
25
25
  import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
26
26
  import { useSetPinnedMessage } from '../hooks/useSetPinnedMessage';
27
+ import { useUnreadCount } from './useUnreadCount';
27
28
 
28
29
  const formatTime = date => {
29
30
  if (!(date instanceof Date)) {
@@ -192,119 +193,127 @@ const SenderName = styled(Text, {
192
193
  fontWeight: '$semiBold',
193
194
  });
194
195
 
195
- const ChatMessage = React.memo(({ index, style = {}, message, setRowHeight, onPin }) => {
196
- const { ref, inView } = useInView({ threshold: 0.5, triggerOnce: true });
197
- const rowRef = useRef(null);
198
- useEffect(() => {
199
- if (rowRef.current) {
200
- setRowHeight(index, rowRef.current.clientHeight);
201
- }
202
- }, [index, setRowHeight]);
203
- const isMobile = useMedia(cssConfig.media.md);
204
- const { elements } = useRoomLayoutConferencingScreen();
205
- const isOverlay = elements?.chat?.is_overlay && isMobile;
206
- const hmsActions = useHMSActions();
207
- const localPeerId = useHMSStore(selectLocalPeerID);
208
- const permissions = useHMSStore(selectPermissions);
209
- const messageType = getMessageType({
210
- roles: message.recipientRoles,
211
- receiver: message.recipientPeer,
212
- });
213
- // show pin action only if peer has remove others permission and the message is of broadcast type
214
- const showPinAction = permissions.removeOthers && !messageType && elements?.chat?.allow_pinning_messages;
196
+ const ChatMessage = React.memo(
197
+ ({ index, style = {}, message, setRowHeight, isLast = false, unreadCount = 0, scrollToBottom, onPin }) => {
198
+ const { ref, inView } = useInView({ threshold: 0.5, triggerOnce: true });
199
+ const rowRef = useRef(null);
200
+ useEffect(() => {
201
+ if (rowRef.current) {
202
+ setRowHeight(index, rowRef.current.clientHeight);
203
+ }
204
+ }, [index, setRowHeight]);
205
+ const isMobile = useMedia(cssConfig.media.md);
206
+ const { elements } = useRoomLayoutConferencingScreen();
207
+ const isOverlay = elements?.chat?.is_overlay && isMobile;
208
+ const hmsActions = useHMSActions();
209
+ const localPeerId = useHMSStore(selectLocalPeerID);
210
+ const permissions = useHMSStore(selectPermissions);
211
+ const messageType = getMessageType({
212
+ roles: message.recipientRoles,
213
+ receiver: message.recipientPeer,
214
+ });
215
+ // show pin action only if peer has remove others permission and the message is of broadcast type
216
+ const showPinAction = permissions.removeOthers && !messageType && elements?.chat?.allow_pinning_messages;
215
217
 
216
- useEffect(() => {
217
- if (message.id && !message.read && inView) {
218
- hmsActions.setMessageRead(true, message.id);
219
- }
220
- }, [message.read, hmsActions, inView, message.id]);
218
+ useEffect(() => {
219
+ if (message.id && !message.read && inView) {
220
+ hmsActions.setMessageRead(true, message.id);
221
+ }
222
+ }, [message.read, hmsActions, inView, message.id]);
221
223
 
222
- return (
223
- <Box
224
- ref={ref}
225
- as="div"
226
- css={{ mb: '$10', pr: '$10', mt: '$8', '&:hover .chat_actions': { opacity: 1 } }}
227
- style={style}
228
- >
229
- <Flex
230
- ref={rowRef}
231
- align="center"
232
- css={{
233
- flexWrap: 'wrap',
234
- // Theme independent color, token should not be used for transparent chat
235
- bg: messageType ? (isOverlay ? 'rgba(0, 0, 0, 0.64)' : '$surface_default') : undefined,
236
- r: messageType ? '$1' : undefined,
237
- px: messageType ? '$4' : '$2',
238
- py: messageType ? '$4' : 0,
239
- userSelect: 'none',
240
- }}
241
- key={message.time}
242
- data-testid="chat_msg"
224
+ useEffect(() => {
225
+ if (isLast && inView && unreadCount >= 1) {
226
+ scrollToBottom(1);
227
+ }
228
+ }, [inView, isLast, scrollToBottom, unreadCount]);
229
+
230
+ return (
231
+ <Box
232
+ ref={ref}
233
+ as="div"
234
+ css={{ mb: '$10', pr: '$10', mt: '$8', '&:hover .chat_actions': { opacity: 1 } }}
235
+ style={style}
243
236
  >
244
- <Text
237
+ <Flex
238
+ ref={rowRef}
239
+ align="center"
245
240
  css={{
246
- color: isOverlay ? '#FFF' : '$on_surface_high',
247
- fontWeight: '$semiBold',
248
- display: 'inline-flex',
249
- alignItems: 'center',
250
- justifyContent: 'space-between',
251
- width: '100%',
241
+ flexWrap: 'wrap',
242
+ // Theme independent color, token should not be used for transparent chat
243
+ bg: messageType ? (isOverlay ? 'rgba(0, 0, 0, 0.64)' : '$surface_default') : undefined,
244
+ r: messageType ? '$1' : undefined,
245
+ px: messageType ? '$4' : '$2',
246
+ py: messageType ? '$4' : 0,
247
+ userSelect: 'none',
252
248
  }}
253
- as="div"
249
+ key={message.time}
250
+ data-testid="chat_msg"
254
251
  >
255
- <Flex align="baseline">
256
- {message.senderName === 'You' || !message.senderName ? (
257
- <SenderName as="span" variant="sm" css={{ color: isOverlay ? '#FFF' : '$on_surface_high' }}>
258
- {message.senderName || 'Anonymous'}
259
- </SenderName>
260
- ) : (
261
- <Tooltip title={message.senderName} side="top" align="start">
252
+ <Text
253
+ css={{
254
+ color: isOverlay ? '#FFF' : '$on_surface_high',
255
+ fontWeight: '$semiBold',
256
+ display: 'inline-flex',
257
+ alignItems: 'center',
258
+ justifyContent: 'space-between',
259
+ width: '100%',
260
+ }}
261
+ as="div"
262
+ >
263
+ <Flex align="baseline">
264
+ {message.senderName === 'You' || !message.senderName ? (
262
265
  <SenderName as="span" variant="sm" css={{ color: isOverlay ? '#FFF' : '$on_surface_high' }}>
263
- {message.senderName}
266
+ {message.senderName || 'Anonymous'}
264
267
  </SenderName>
265
- </Tooltip>
266
- )}
267
- {!isOverlay ? (
268
- <Text
269
- as="span"
270
- variant="xs"
271
- css={{
272
- ml: '$4',
273
- color: '$on_surface_medium',
274
- flexShrink: 0,
275
- }}
276
- >
277
- {formatTime(message.time)}
278
- </Text>
279
- ) : null}
280
- </Flex>
281
- <MessageType
282
- hasCurrentUserSent={message.sender === localPeerId}
283
- receiver={message.recipientPeer}
284
- roles={message.recipientRoles}
285
- />
286
- {!isOverlay ? <ChatActions onPin={onPin} showPinAction={showPinAction} /> : null}
287
- </Text>
288
- <Text
289
- variant="sm"
290
- css={{
291
- w: '100%',
292
- mt: '$2',
293
- wordBreak: 'break-word',
294
- whiteSpace: 'pre-wrap',
295
- userSelect: 'all',
296
- color: isOverlay ? '#FFF' : '$on_surface_high',
297
- }}
298
- onClick={e => e.stopPropagation()}
299
- >
300
- <AnnotisedMessage message={message.message} />
301
- </Text>
302
- </Flex>
303
- </Box>
304
- );
305
- });
268
+ ) : (
269
+ <Tooltip title={message.senderName} side="top" align="start">
270
+ <SenderName as="span" variant="sm" css={{ color: isOverlay ? '#FFF' : '$on_surface_high' }}>
271
+ {message.senderName}
272
+ </SenderName>
273
+ </Tooltip>
274
+ )}
275
+ {!isOverlay ? (
276
+ <Text
277
+ as="span"
278
+ variant="xs"
279
+ css={{
280
+ ml: '$4',
281
+ color: '$on_surface_medium',
282
+ flexShrink: 0,
283
+ }}
284
+ >
285
+ {formatTime(message.time)}
286
+ </Text>
287
+ ) : null}
288
+ </Flex>
289
+ <MessageType
290
+ hasCurrentUserSent={message.sender === localPeerId}
291
+ receiver={message.recipientPeer}
292
+ roles={message.recipientRoles}
293
+ />
294
+ {!isOverlay ? <ChatActions onPin={onPin} showPinAction={showPinAction} /> : null}
295
+ </Text>
296
+ <Text
297
+ variant="sm"
298
+ css={{
299
+ w: '100%',
300
+ mt: '$2',
301
+ wordBreak: 'break-word',
302
+ whiteSpace: 'pre-wrap',
303
+ userSelect: 'all',
304
+ color: isOverlay ? '#FFF' : '$on_surface_high',
305
+ }}
306
+ onClick={e => e.stopPropagation()}
307
+ >
308
+ <AnnotisedMessage message={message.message} />
309
+ </Text>
310
+ </Flex>
311
+ </Box>
312
+ );
313
+ },
314
+ );
306
315
  const ChatList = React.forwardRef(
307
- ({ width, height, setRowHeight, getRowHeight, messages, scrollToBottom }, listRef) => {
316
+ ({ width, height, setRowHeight, getRowHeight, messages, unreadCount = 0, scrollToBottom }, listRef) => {
308
317
  const { setPinnedMessage } = useSetPinnedMessage();
309
318
  useLayoutEffect(() => {
310
319
  if (listRef.current && listRef.current.scrollToItem) {
@@ -331,6 +340,9 @@ const ChatList = React.forwardRef(
331
340
  key={messages[index].id}
332
341
  message={messages[index]}
333
342
  setRowHeight={setRowHeight}
343
+ unreadCount={unreadCount}
344
+ isLast={index >= messages.length - 2}
345
+ scrollToBottom={scrollToBottom}
334
346
  onPin={() => setPinnedMessage(messages[index])}
335
347
  />
336
348
  )}
@@ -338,7 +350,7 @@ const ChatList = React.forwardRef(
338
350
  );
339
351
  },
340
352
  );
341
- const VirtualizedChatMessages = React.forwardRef(({ messages, scrollToBottom }, listRef) => {
353
+ const VirtualizedChatMessages = React.forwardRef(({ messages, unreadCount = 0, scrollToBottom }, listRef) => {
342
354
  const rowHeights = useRef({});
343
355
 
344
356
  function getRowHeight(index) {
@@ -377,6 +389,7 @@ const VirtualizedChatMessages = React.forwardRef(({ messages, scrollToBottom },
377
389
  getRowHeight={getRowHeight}
378
390
  scrollToBottom={scrollToBottom}
379
391
  ref={listRef}
392
+ unreadCount={unreadCount}
380
393
  />
381
394
  )}
382
395
  </AutoSizer>
@@ -394,6 +407,7 @@ export const ChatBody = React.forwardRef(({ role, peerId, scrollToBottom }, list
394
407
  messages = useMemo(() => messages?.filter(message => message.type === 'chat') || [], [messages]);
395
408
  const isMobile = useMedia(cssConfig.media.md);
396
409
  const { elements } = useRoomLayoutConferencingScreen();
410
+ const unreadCount = useUnreadCount({ role, peerId });
397
411
 
398
412
  if (messages.length === 0 && !(isMobile && elements?.chat?.is_overlay)) {
399
413
  return (
@@ -425,7 +439,12 @@ export const ChatBody = React.forwardRef(({ role, peerId, scrollToBottom }, list
425
439
 
426
440
  return (
427
441
  <Fragment>
428
- <VirtualizedChatMessages messages={messages} scrollToBottom={scrollToBottom} ref={listRef} />
442
+ <VirtualizedChatMessages
443
+ messages={messages}
444
+ scrollToBottom={scrollToBottom}
445
+ unreadCount={unreadCount}
446
+ ref={listRef}
447
+ />
429
448
  </Fragment>
430
449
  );
431
450
  });
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import { Flex } from '../../Layout';
3
3
  import { Text } from '../../Text';
4
+ import { CSS } from '../../Theme';
4
5
 
5
6
  const Chip = ({
6
7
  icon = <></>,
@@ -8,12 +9,22 @@ const Chip = ({
8
9
  backgroundColor = '$surface_default',
9
10
  textColor = '$on_surface_high',
10
11
  hideIfNoContent = false,
12
+ onClick,
13
+ css = {},
14
+ }: {
15
+ icon?: React.JSX.Element;
16
+ content: string;
17
+ backgroundColor?: string;
18
+ textColor?: string;
19
+ hideIfNoContent?: boolean;
20
+ onClick?: () => void | Promise<void>;
21
+ css?: CSS;
11
22
  }) => {
12
23
  if (hideIfNoContent && !content) {
13
- return;
24
+ return null;
14
25
  }
15
26
  return (
16
- <Flex align="center" css={{ backgroundColor, p: '$4 $6', borderRadius: '$4' }}>
27
+ <Flex align="center" css={{ backgroundColor, p: '$4 $6', borderRadius: '$4', ...css }} onClick={() => onClick?.()}>
17
28
  {icon}
18
29
  <Text variant="sm" css={{ fontWeight: '$semiBold', color: textColor, ml: '$2' }}>
19
30
  {content}
@@ -127,13 +127,15 @@ const VirtualizedParticipants = ({ peersOrderedByRoles = {}, isConnected, filter
127
127
  flex: '1 1 0',
128
128
  }}
129
129
  >
130
- <RoleAccordion
131
- peerList={handRaisedList}
132
- roleName="Hand Raised"
133
- filter={filter}
134
- isConnected={isConnected}
135
- isHandRaisedAccordion
136
- />
130
+ {handRaisedList.length > 0 ? (
131
+ <RoleAccordion
132
+ peerList={handRaisedList}
133
+ roleName="Hand Raised"
134
+ filter={filter}
135
+ isConnected={isConnected}
136
+ isHandRaisedAccordion
137
+ />
138
+ ) : null}
137
139
  {Object.keys(peersOrderedByRoles).map(role => (
138
140
  <RoleAccordion
139
141
  key={role}
@@ -179,7 +181,10 @@ export const Participant = ({ peer, isConnected }) => {
179
181
  const ParticipantActions = React.memo(({ peerId, role, isLocal }) => {
180
182
  const isHandRaised = useHMSStore(selectHasPeerHandRaised(peerId));
181
183
  const canChangeRole = useHMSStore(selectPermissions)?.changeRole;
182
- const shouldShowMoreActions = canChangeRole;
184
+ const canRemoveOthers = useHMSStore(selectPermissions)?.removeOthers;
185
+ const { elements } = useRoomLayoutConferencingScreen();
186
+ const { on_stage_exp } = elements || {};
187
+ const shouldShowMoreActions = (on_stage_exp && canChangeRole) || canRemoveOthers;
183
188
  const isAudioMuted = !useHMSStore(selectIsPeerAudioEnabled(peerId));
184
189
 
185
190
  return (
@@ -210,15 +215,21 @@ const ParticipantActions = React.memo(({ peerId, role, isLocal }) => {
210
215
  </Flex>
211
216
  ) : null}
212
217
 
213
- {shouldShowMoreActions && !isLocal ? <ParticipantMoreActions peerId={peerId} role={role} /> : null}
218
+ {shouldShowMoreActions && !isLocal ? (
219
+ <ParticipantMoreActions
220
+ peerId={peerId}
221
+ role={role}
222
+ elements={elements}
223
+ canChangeRole={canChangeRole}
224
+ canRemoveOthers={canRemoveOthers}
225
+ />
226
+ ) : null}
214
227
  </Flex>
215
228
  );
216
229
  });
217
230
 
218
- const ParticipantMoreActions = ({ peerId, role }) => {
231
+ const ParticipantMoreActions = ({ peerId, role, elements, canChangeRole, canRemoveOthers }) => {
219
232
  const hmsActions = useHMSActions();
220
- const { changeRole: canChangeRole, removeOthers: canRemoveOthers } = useHMSStore(selectPermissions);
221
- const { elements } = useRoomLayoutConferencingScreen();
222
233
  const {
223
234
  bring_to_stage_label,
224
235
  remove_from_stage_label,
@@ -327,7 +338,7 @@ export const ParticipantSearch = ({ onSearch, placeholder, inSidePane = false })
327
338
  <Input
328
339
  type="text"
329
340
  placeholder={placeholder || 'Search for participants'}
330
- css={{ w: '100%', p: '$6', pl: '$14', mr: '$4', bg: inSidePane ? '$surface_default' : '$surface_dim' }}
341
+ css={{ w: '100%', p: '$6', pl: '$16', mr: '$4', bg: inSidePane ? '$surface_default' : '$surface_dim' }}
331
342
  value={value}
332
343
  onKeyDown={event => {
333
344
  event.stopPropagation();