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

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 (37) hide show
  1. package/dist/{HLSView-MR7RYQZB.js → HLSView-XU2WQ4VV.js} +2 -2
  2. package/dist/Prebuilt/components/Chat/Chat.d.ts +2 -0
  3. package/dist/Prebuilt/components/Chat/ChatActions.d.ts +12 -0
  4. package/dist/Prebuilt/components/Chat/ChatBody.d.ts +8 -0
  5. package/dist/Prebuilt/components/Chat/ChatFooter.d.ts +2 -2
  6. package/dist/Prebuilt/components/Chat/ChatStates.d.ts +1 -1
  7. package/dist/Prebuilt/components/Chat/MwebChatOption.d.ts +1 -1
  8. package/dist/Prebuilt/components/Chat/PinnedMessage.d.ts +1 -3
  9. package/dist/Prebuilt/components/hooks/useChatBlacklist.d.ts +1 -0
  10. package/dist/Prebuilt/components/hooks/useSetPinnedMessages.d.ts +3 -10
  11. package/dist/Prebuilt/components/hooks/useUnreadPollQuizPresent.d.ts +5 -0
  12. package/dist/{chunk-WFHOR7AP.js → chunk-2FX6MFDM.js} +4535 -4495
  13. package/dist/chunk-2FX6MFDM.js.map +7 -0
  14. package/dist/index.cjs.js +5110 -5055
  15. package/dist/index.cjs.js.map +4 -4
  16. package/dist/index.js +1 -1
  17. package/dist/meta.cjs.json +341 -223
  18. package/dist/meta.esbuild.json +351 -233
  19. package/package.json +6 -6
  20. package/src/Prebuilt/components/Chat/Chat.tsx +108 -0
  21. package/src/Prebuilt/components/Chat/ChatActions.tsx +295 -0
  22. package/src/Prebuilt/components/Chat/ChatBody.tsx +444 -0
  23. package/src/Prebuilt/components/Chat/ChatFooter.tsx +9 -3
  24. package/src/Prebuilt/components/Chat/ChatStates.tsx +5 -0
  25. package/src/Prebuilt/components/Chat/MwebChatOption.tsx +1 -1
  26. package/src/Prebuilt/components/Chat/PinnedMessage.tsx +4 -2
  27. package/src/Prebuilt/components/Footer/PollsToggle.tsx +12 -3
  28. package/src/Prebuilt/components/MoreSettings/SplitComponents/MwebOptions.tsx +5 -1
  29. package/src/Prebuilt/components/Polls/common/MultipleChoiceOptions.jsx +1 -1
  30. package/src/Prebuilt/components/SidePaneTabs.tsx +33 -11
  31. package/src/Prebuilt/components/hooks/useChatBlacklist.ts +7 -1
  32. package/src/Prebuilt/components/hooks/useSetPinnedMessages.ts +19 -11
  33. package/src/Prebuilt/components/hooks/useUnreadPollQuizPresent.tsx +20 -0
  34. package/dist/chunk-WFHOR7AP.js.map +0 -7
  35. package/src/Prebuilt/components/Chat/Chat.jsx +0 -124
  36. package/src/Prebuilt/components/Chat/ChatBody.jsx +0 -726
  37. /package/dist/{HLSView-MR7RYQZB.js.map → HLSView-XU2WQ4VV.js.map} +0 -0
@@ -0,0 +1,444 @@
1
+ import React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { useMedia } from 'react-use';
3
+ import AutoSizer from 'react-virtualized-auto-sizer';
4
+ import { VariableSizeList } from 'react-window';
5
+ import {
6
+ HMSMessage,
7
+ HMSPeerID,
8
+ HMSRoleName,
9
+ selectHMSMessages,
10
+ selectHMSMessagesCount,
11
+ selectLocalPeerID,
12
+ selectLocalPeerRoleName,
13
+ selectPeerNameByID,
14
+ selectSessionStore,
15
+ useHMSActions,
16
+ useHMSStore,
17
+ useHMSVanillaStore,
18
+ } from '@100mslive/react-sdk';
19
+ import { Box, Flex } from '../../../Layout';
20
+ import { Text } from '../../../Text';
21
+ import { config as cssConfig, styled } from '../../../Theme';
22
+ import { Tooltip } from '../../../Tooltip';
23
+ // @ts-ignore
24
+ import emptyChat from '../../images/empty-chat.svg';
25
+ import { ChatActions } from './ChatActions';
26
+ import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
27
+ // @ts-ignore: No implicit Any
28
+ import { useSetSubscribedChatSelector } from '../AppData/useUISettings';
29
+ import { CHAT_SELECTOR, SESSION_STORE_KEY } from '../../common/constants';
30
+
31
+ const formatTime = (date: Date) => {
32
+ if (!(date instanceof Date)) {
33
+ return '';
34
+ }
35
+ const hours = date.getHours();
36
+ const minutes = date.getMinutes();
37
+ const suffix = hours > 11 ? 'PM' : 'AM';
38
+ return `${hours < 10 ? '0' : ''}${hours}:${minutes < 10 ? '0' : ''}${minutes} ${suffix}`;
39
+ };
40
+
41
+ const rowHeights: Record<number, number> = {};
42
+ function getRowHeight(index: number) {
43
+ // 72 will be default row height for any message length
44
+ // 16 will add margin value as clientHeight don't include margin
45
+ return rowHeights[index] + 16 || 72;
46
+ }
47
+
48
+ const setRowHeight = (index: number, size: number) => {
49
+ Object.assign(rowHeights, { [index]: size });
50
+ };
51
+
52
+ const MessageTypeContainer = ({ left, right }: { left?: string; right?: string }) => {
53
+ return (
54
+ <Flex
55
+ align="center"
56
+ css={{
57
+ ml: '$2',
58
+ mr: '$4',
59
+ gap: '$space$2',
60
+ }}
61
+ >
62
+ {left && (
63
+ <SenderName
64
+ variant="xs"
65
+ as="span"
66
+ css={{ color: '$on_surface_medium', textTransform: 'capitalize', fontWeight: '$regular' }}
67
+ >
68
+ {left}
69
+ </SenderName>
70
+ )}
71
+ {right && (
72
+ <SenderName
73
+ as="span"
74
+ variant="overline"
75
+ css={{
76
+ color: '$on_surface_medium',
77
+ fontWeight: '$regular',
78
+ }}
79
+ >
80
+ {right}
81
+ </SenderName>
82
+ )}
83
+ </Flex>
84
+ );
85
+ };
86
+
87
+ const MessageType = ({
88
+ roles,
89
+ hasCurrentUserSent,
90
+ receiver,
91
+ }: {
92
+ roles?: HMSRoleName[];
93
+ hasCurrentUserSent: boolean;
94
+ receiver?: HMSPeerID;
95
+ }) => {
96
+ const peerName = useHMSStore(selectPeerNameByID(receiver));
97
+ const localPeerRoleName = useHMSStore(selectLocalPeerRoleName);
98
+ if (receiver) {
99
+ return (
100
+ <MessageTypeContainer left={hasCurrentUserSent ? `${peerName ? `to ${peerName}` : ''}` : 'to You'} right="(DM)" />
101
+ );
102
+ }
103
+
104
+ if (roles && roles.length) {
105
+ return <MessageTypeContainer left={`to ${hasCurrentUserSent ? roles[0] : localPeerRoleName}`} right="(Group)" />;
106
+ }
107
+ return null;
108
+ };
109
+
110
+ const URL_REGEX =
111
+ /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/;
112
+
113
+ const Link = styled('a', {
114
+ color: '$primary_default',
115
+ wordBreak: 'break-word',
116
+ '&:hover': {
117
+ textDecoration: 'underline',
118
+ },
119
+ });
120
+
121
+ export const AnnotisedMessage = ({ message }: { message: string }) => {
122
+ if (!message) {
123
+ return <Fragment />;
124
+ }
125
+
126
+ return (
127
+ <Fragment>
128
+ {message
129
+ .trim()
130
+ .split(/(\s)/)
131
+ .map(part =>
132
+ URL_REGEX.test(part) ? (
133
+ <Link href={part} key={part} target="_blank" rel="noopener noreferrer">
134
+ {part}
135
+ </Link>
136
+ ) : (
137
+ part
138
+ ),
139
+ )}
140
+ </Fragment>
141
+ );
142
+ };
143
+
144
+ const getMessageType = ({ roles, receiver }: { roles?: HMSRoleName[]; receiver?: HMSPeerID }) => {
145
+ if (roles && roles.length > 0) {
146
+ return 'role';
147
+ }
148
+ return receiver ? 'private' : '';
149
+ };
150
+ const SenderName = styled(Text, {
151
+ overflow: 'hidden',
152
+ textOverflow: 'ellipsis',
153
+ whiteSpace: 'nowrap',
154
+ maxWidth: '14ch',
155
+ minWidth: 0,
156
+ color: '$on_surface_high',
157
+ fontWeight: '$semiBold',
158
+ });
159
+
160
+ const ChatMessage = React.memo(
161
+ ({ index, style = {}, message }: { message: HMSMessage; index: number; style: React.CSSProperties }) => {
162
+ const { elements } = useRoomLayoutConferencingScreen();
163
+ const rowRef = useRef<HTMLDivElement | null>(null);
164
+ const isMobile = useMedia(cssConfig.media.md);
165
+ const isPrivateChatEnabled = !!elements?.chat?.private_chat_enabled;
166
+ const roleWhiteList = elements?.chat?.roles_whitelist || [];
167
+ const isOverlay = elements?.chat?.is_overlay && isMobile;
168
+ const localPeerId = useHMSStore(selectLocalPeerID);
169
+ const [selectedRole, setRoleSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.ROLE);
170
+ const [selectedPeer, setPeerSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.PEER);
171
+ const messageType = getMessageType({
172
+ roles: message.recipientRoles,
173
+ receiver: message.recipientPeer,
174
+ });
175
+ const [openSheet, setOpenSheet] = useState(false);
176
+ const showPinAction = !!elements?.chat?.allow_pinning_messages;
177
+ let showReply = false;
178
+ if (message.recipientRoles && roleWhiteList.includes(message.recipientRoles[0])) {
179
+ showReply = true;
180
+ } else if (message.sender !== selectedPeer.id && message.sender !== localPeerId && isPrivateChatEnabled) {
181
+ showReply = true;
182
+ }
183
+
184
+ useEffect(() => {
185
+ if (rowRef.current) {
186
+ setRowHeight(index, rowRef.current.clientHeight);
187
+ }
188
+ }, [index]);
189
+
190
+ return (
191
+ <Box
192
+ css={{
193
+ mb: '$5',
194
+ pr: '$10',
195
+ mt: '$4',
196
+ '&:not(:hover} .chat_actions': { display: 'none' },
197
+ '&:hover .chat_actions': { display: 'flex', opacity: 1 },
198
+ }}
199
+ style={style}
200
+ >
201
+ <Flex
202
+ ref={rowRef}
203
+ align="center"
204
+ css={{
205
+ flexWrap: 'wrap',
206
+ position: 'relative',
207
+ // Theme independent color, token should not be used for transparent chat
208
+ bg:
209
+ messageType && !(selectedPeer.id || selectedRole)
210
+ ? isOverlay
211
+ ? 'rgba(0, 0, 0, 0.64)'
212
+ : '$surface_default'
213
+ : undefined,
214
+ r: '$1',
215
+ p: '$4',
216
+ userSelect: 'none',
217
+ '@md': {
218
+ cursor: 'pointer',
219
+ },
220
+ '&:hover': {
221
+ background: 'linear-gradient(277deg, $surface_default 0%, $surface_dim 60.87%)',
222
+ },
223
+ }}
224
+ data-testid="chat_msg"
225
+ onClick={() => {
226
+ if (isMobile) {
227
+ setOpenSheet(true);
228
+ }
229
+ }}
230
+ >
231
+ <Text
232
+ css={{
233
+ color: isOverlay ? '#FFF' : '$on_surface_high',
234
+ fontWeight: '$semiBold',
235
+ display: 'flex',
236
+ alignItems: 'center',
237
+ alignSelf: 'stretch',
238
+ width: '100%',
239
+ }}
240
+ as="div"
241
+ >
242
+ <Flex align="baseline">
243
+ {message.senderName === 'You' || !message.senderName ? (
244
+ <SenderName
245
+ as="span"
246
+ variant="sub2"
247
+ css={{ color: isOverlay ? '#FFF' : '$on_surface_high', fontWeight: '$semiBold' }}
248
+ >
249
+ {message.senderName || 'Anonymous'}
250
+ </SenderName>
251
+ ) : (
252
+ <Tooltip title={message.senderName} side="top" align="start">
253
+ <SenderName
254
+ as="span"
255
+ variant="sub2"
256
+ css={{ color: isOverlay ? '#FFF' : '$on_surface_high', fontWeight: '$semiBold' }}
257
+ >
258
+ {message.senderName}
259
+ </SenderName>
260
+ </Tooltip>
261
+ )}
262
+ <MessageType
263
+ hasCurrentUserSent={message.sender === localPeerId}
264
+ receiver={message.recipientPeer}
265
+ roles={message.recipientRoles}
266
+ />
267
+ </Flex>
268
+
269
+ {!isOverlay ? (
270
+ <Text
271
+ as="span"
272
+ variant="caption"
273
+ css={{
274
+ color: '$on_surface_medium',
275
+ flexShrink: 0,
276
+ position: 'absolute',
277
+ right: 0,
278
+ zIndex: 1,
279
+ mr: '$4',
280
+ p: '$2',
281
+ }}
282
+ >
283
+ {formatTime(message.time)}
284
+ </Text>
285
+ ) : null}
286
+ <ChatActions
287
+ showPinAction={showPinAction}
288
+ message={message}
289
+ sentByLocalPeer={message.sender === localPeerId}
290
+ onReply={() => {
291
+ if (message.recipientRoles?.length) {
292
+ setRoleSelector(message.recipientRoles[0]);
293
+ setPeerSelector({});
294
+ } else {
295
+ setRoleSelector('');
296
+ setPeerSelector({ id: message.sender, name: message.senderName });
297
+ }
298
+ }}
299
+ showReply={showReply}
300
+ isMobile={isMobile}
301
+ openSheet={openSheet}
302
+ setOpenSheet={setOpenSheet}
303
+ />
304
+ </Text>
305
+ <Text
306
+ variant="sm"
307
+ css={{
308
+ w: '100%',
309
+ mt: '$2',
310
+ wordBreak: 'break-word',
311
+ whiteSpace: 'pre-wrap',
312
+ userSelect: 'all',
313
+ color: isOverlay ? '#FFF' : '$on_surface_high',
314
+ }}
315
+ onClick={e => {
316
+ e.stopPropagation();
317
+ setOpenSheet(true);
318
+ }}
319
+ >
320
+ <AnnotisedMessage message={message.message} />
321
+ </Text>
322
+ </Flex>
323
+ </Box>
324
+ );
325
+ },
326
+ );
327
+
328
+ const MessageWrapper = React.memo(
329
+ ({ index, style, data }: { index: number; style: React.CSSProperties; data: HMSMessage[] }) => {
330
+ return <ChatMessage style={style} index={index} key={data[index].id} message={data[index]} />;
331
+ },
332
+ );
333
+
334
+ const VirtualizedChatMessages = React.forwardRef<
335
+ VariableSizeList,
336
+ { messages: HMSMessage[]; scrollToBottom: (count: number) => void }
337
+ >(({ messages, scrollToBottom }, listRef) => {
338
+ const hmsActions = useHMSActions();
339
+ const itemKey = useCallback((index: number, data: HMSMessage[]) => {
340
+ return data[index].id;
341
+ }, []);
342
+ useEffect(() => {
343
+ requestAnimationFrame(() => scrollToBottom(1));
344
+ }, [scrollToBottom]);
345
+ return (
346
+ <Box
347
+ css={{
348
+ mr: '-$10',
349
+ h: '100%',
350
+ }}
351
+ >
352
+ <AutoSizer
353
+ style={{
354
+ width: '90%',
355
+ }}
356
+ >
357
+ {({ height, width }: { height: number; width: number }) => (
358
+ <VariableSizeList
359
+ ref={listRef}
360
+ itemCount={messages.length}
361
+ itemSize={getRowHeight}
362
+ itemData={messages}
363
+ width={width}
364
+ height={height}
365
+ style={{
366
+ overflowX: 'hidden',
367
+ }}
368
+ itemKey={itemKey}
369
+ onItemsRendered={({ visibleStartIndex, visibleStopIndex }) => {
370
+ for (let i = visibleStartIndex; i <= visibleStopIndex; i++) {
371
+ if (!messages[i].read) {
372
+ hmsActions.setMessageRead(true, messages[i].id);
373
+ }
374
+ }
375
+ }}
376
+ >
377
+ {MessageWrapper}
378
+ </VariableSizeList>
379
+ )}
380
+ </AutoSizer>
381
+ </Box>
382
+ );
383
+ });
384
+
385
+ export const ChatBody = React.forwardRef<VariableSizeList, { scrollToBottom: (count: number) => void }>(
386
+ ({ scrollToBottom }: { scrollToBottom: (count: number) => void }, listRef) => {
387
+ const messages = useHMSStore(selectHMSMessages);
388
+ const blacklistedMessageIDs = useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_MESSAGE_BLACKLIST));
389
+ const filteredMessages = useMemo(() => {
390
+ const blacklistedMessageIDSet = new Set(blacklistedMessageIDs || []);
391
+ return messages?.filter(message => message.type === 'chat' && !blacklistedMessageIDSet.has(message.id)) || [];
392
+ }, [blacklistedMessageIDs, messages]);
393
+
394
+ const isMobile = useMedia(cssConfig.media.md);
395
+ const { elements } = useRoomLayoutConferencingScreen();
396
+ const vanillaStore = useHMSVanillaStore();
397
+
398
+ useEffect(() => {
399
+ const unsubscribe = vanillaStore.subscribe(() => {
400
+ // @ts-ignore
401
+ if (!listRef?.current) {
402
+ return;
403
+ }
404
+ // @ts-ignore
405
+ const outerElement = listRef.current._outerRef;
406
+ // @ts-ignore
407
+ if (outerElement.scrollHeight - (listRef.current.state.scrollOffset + outerElement.offsetHeight) <= 10) {
408
+ scrollToBottom(1);
409
+ }
410
+ }, selectHMSMessagesCount);
411
+ return unsubscribe;
412
+ }, [vanillaStore, listRef, scrollToBottom]);
413
+
414
+ if (filteredMessages.length === 0 && !(isMobile && elements?.chat?.is_overlay)) {
415
+ return (
416
+ <Flex
417
+ css={{
418
+ width: '100%',
419
+ flex: '1 1 0',
420
+ textAlign: 'center',
421
+ px: '$4',
422
+ }}
423
+ align="center"
424
+ justify="center"
425
+ >
426
+ <Box>
427
+ <img src={emptyChat} alt="Empty Chat" height={132} width={185} style={{ margin: '0 auto' }} />
428
+ <Text variant="h5" css={{ mt: '$8', c: '$on_surface_high' }}>
429
+ Start a conversation
430
+ </Text>
431
+ <Text
432
+ variant="sm"
433
+ css={{ mt: '$4', maxWidth: '80%', textAlign: 'center', mx: 'auto', c: '$on_surface_medium' }}
434
+ >
435
+ There are no messages here yet. Start a conversation by sending a message.
436
+ </Text>
437
+ </Box>
438
+ </Flex>
439
+ );
440
+ }
441
+
442
+ return <VirtualizedChatMessages messages={filteredMessages} ref={listRef} scrollToBottom={scrollToBottom} />;
443
+ },
444
+ );
@@ -10,11 +10,11 @@ import { IconButton } from '../../../IconButton';
10
10
  import { ToastManager } from '../Toast/ToastManager';
11
11
  import { ChatSelectorContainer } from './ChatSelectorContainer';
12
12
  import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
13
- // import { ChatSelectorContainer } from './ChatSelectorContainer';
14
13
  // @ts-ignore
15
14
  import { useChatDraftMessage } from '../AppData/useChatState';
16
15
  // @ts-ignore
17
16
  import { useSetSubscribedChatSelector, useSubscribeChatSelector } from '../AppData/useUISettings';
17
+ import { useIsLocalPeerBlacklisted } from '../hooks/useChatBlacklist';
18
18
  // @ts-ignore
19
19
  import { useEmojiPickerStyles } from './useEmojiPickerStyles';
20
20
  import { useDefaultChatSelection } from '../../common/hooks';
@@ -72,7 +72,7 @@ function EmojiPicker({ onSelect }: { onSelect: (emoji: any) => void }) {
72
72
  );
73
73
  }
74
74
 
75
- export const ChatFooter = ({ onSend, children }: { onSend: () => void; children: ReactNode }) => {
75
+ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => void; children: ReactNode }) => {
76
76
  const hmsActions = useHMSActions();
77
77
  const inputRef = useRef<HTMLTextAreaElement>(null);
78
78
  const [draftMessage, setDraftMessage] = useChatDraftMessage();
@@ -86,6 +86,8 @@ export const ChatFooter = ({ onSend, children }: { onSend: () => void; children:
86
86
  const [selectedRole, setRoleSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.ROLE);
87
87
  const defaultSelection = useDefaultChatSelection();
88
88
  const selection = selectedPeer.name || selectedRole || defaultSelection;
89
+ const isLocalPeerBlacklisted = useIsLocalPeerBlacklisted();
90
+
89
91
  useEffect(() => {
90
92
  if (!selectedPeer.id && !selectedRole && !['Everyone', ''].includes(defaultSelection)) {
91
93
  setRoleSelector(defaultSelection);
@@ -108,7 +110,7 @@ export const ChatFooter = ({ onSend, children }: { onSend: () => void; children:
108
110
  }
109
111
  inputRef.current.value = '';
110
112
  setTimeout(() => {
111
- onSend();
113
+ onSend(1);
112
114
  }, 0);
113
115
  } catch (error) {
114
116
  const err = error as HMSException;
@@ -132,6 +134,10 @@ export const ChatFooter = ({ onSend, children }: { onSend: () => void; children:
132
134
  };
133
135
  }, [setDraftMessage]);
134
136
 
137
+ if (isLocalPeerBlacklisted) {
138
+ return null;
139
+ }
140
+
135
141
  return (
136
142
  <Box>
137
143
  <Flex>
@@ -4,6 +4,7 @@ import { Button } from '../../../Button';
4
4
  import { Box, Flex } from '../../../Layout';
5
5
  import { Text } from '../../../Text';
6
6
  import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
7
+ import { useIsLocalPeerBlacklisted } from '../hooks/useChatBlacklist';
7
8
  import { SESSION_STORE_KEY } from '../../common/constants';
8
9
 
9
10
  export const ChatPaused = () => {
@@ -54,6 +55,10 @@ export const ChatPaused = () => {
54
55
  };
55
56
 
56
57
  export const ChatBlocked = () => {
58
+ const isLocalPeerBlacklisted = useIsLocalPeerBlacklisted();
59
+ if (!isLocalPeerBlacklisted) {
60
+ return null;
61
+ }
57
62
  return (
58
63
  <Flex
59
64
  align="center"
@@ -11,7 +11,7 @@ export const MwebChatOption = ({
11
11
  icon: any;
12
12
  text: string;
13
13
  onClick: () => void | Promise<void>;
14
- color: string;
14
+ color?: string;
15
15
  }) => {
16
16
  return (
17
17
  <Flex align="center" css={{ w: '100%', color, cursor: 'pointer', gap: '$4', p: '$8' }} onClick={onClick}>
@@ -11,13 +11,15 @@ import { ArrowNavigation } from './ArrowNavigation';
11
11
  import { AnnotisedMessage } from './ChatBody';
12
12
  import { StickIndicator } from './StickIndicator';
13
13
  import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
14
+ import { useSetPinnedMessages } from '../hooks/useSetPinnedMessages';
14
15
  import { SESSION_STORE_KEY } from '../../common/constants';
15
16
 
16
17
  const PINNED_MESSAGE_LENGTH = 75;
17
18
 
18
- export const PinnedMessage = ({ clearPinnedMessage }: { clearPinnedMessage: (index: number) => void }) => {
19
+ export const PinnedMessage = () => {
19
20
  const pinnedMessages = useHMSStore(selectSessionStore(SESSION_STORE_KEY.PINNED_MESSAGES));
20
21
  const [pinnedMessageIndex, setPinnedMessageIndex] = useState(0);
22
+ const { removePinnedMessage } = useSetPinnedMessages();
21
23
  const isMobile = useMedia(cssConfig.media.md);
22
24
 
23
25
  const { elements } = useRoomLayoutConferencingScreen();
@@ -117,7 +119,7 @@ export const PinnedMessage = ({ clearPinnedMessage }: { clearPinnedMessage: (ind
117
119
  {canUnpinMessage ? (
118
120
  <Flex
119
121
  onClick={() => {
120
- clearPinnedMessage(pinnedMessageIndex);
122
+ removePinnedMessage(pinnedMessageIndex);
121
123
  setPinnedMessageIndex(Math.max(0, pinnedMessageIndex - 1));
122
124
  }}
123
125
  css={{
@@ -1,21 +1,30 @@
1
1
  import React from 'react';
2
- import { QuizIcon } from '@100mslive/react-icons';
2
+ import { QuizActiveIcon, QuizIcon } from '@100mslive/react-icons';
3
3
  import { Tooltip } from '../../..';
4
4
  // @ts-ignore: No implicit Any
5
5
  import IconButton from '../../IconButton';
6
6
  // @ts-ignore: No implicit Any
7
7
  import { useIsSidepaneTypeOpen, usePollViewToggle } from '../AppData/useSidepane';
8
+ import { useUnreadPollQuizPresent } from '../hooks/useUnreadPollQuizPresent';
8
9
  // @ts-ignore: No implicit Any
9
10
  import { SIDE_PANE_OPTIONS } from '../../common/constants';
10
11
 
11
12
  export const PollsToggle = () => {
12
13
  const isPollsOpen = useIsSidepaneTypeOpen(SIDE_PANE_OPTIONS.POLLS);
13
14
  const togglePollView = usePollViewToggle();
15
+ const { unreadPollQuiz, setUnreadPollQuiz } = useUnreadPollQuizPresent();
14
16
 
15
17
  return (
16
18
  <Tooltip key="polls" title={`${isPollsOpen ? 'Close' : 'Open'} polls and quizzes`}>
17
- <IconButton onClick={togglePollView} active={!isPollsOpen} data-testid="polls_btn">
18
- <QuizIcon />
19
+ <IconButton
20
+ onClick={() => {
21
+ togglePollView();
22
+ setUnreadPollQuiz(false);
23
+ }}
24
+ active={!isPollsOpen}
25
+ data-testid="polls_btn"
26
+ >
27
+ {unreadPollQuiz ? <QuizActiveIcon /> : <QuizIcon />}
19
28
  </IconButton>
20
29
  </Tooltip>
21
30
  );
@@ -18,6 +18,7 @@ import {
18
18
  HandIcon,
19
19
  HandRaiseSlashedIcon,
20
20
  PeopleIcon,
21
+ QuizActiveIcon,
21
22
  QuizIcon,
22
23
  RecordIcon,
23
24
  SettingsIcon,
@@ -48,6 +49,7 @@ import { useShowPolls } from '../../AppData/useUISettings';
48
49
  import { useDropdownList } from '../../hooks/useDropdownList';
49
50
  // @ts-ignore: No implicit any
50
51
  import { useMyMetadata } from '../../hooks/useMetadata';
52
+ import { useUnreadPollQuizPresent } from '../../hooks/useUnreadPollQuizPresent';
51
53
  // @ts-ignore: No implicit any
52
54
  import { getFormattedCount } from '../../../common/utils';
53
55
  // @ts-ignore: No implicit any
@@ -92,6 +94,7 @@ export const MwebOptions = ({
92
94
  const { isBRBOn, toggleBRB, isHandRaised, toggleHandRaise } = useMyMetadata();
93
95
  const { toggleAudio, toggleVideo } = useAVToggle();
94
96
  const noAVPermissions = !(toggleAudio || toggleVideo);
97
+ const { unreadPollQuiz, setUnreadPollQuiz } = useUnreadPollQuizPresent();
95
98
  // const isVideoOn = useHMSStore(selectIsLocalVideoEnabled);
96
99
 
97
100
  useDropdownList({ open: openModals.size > 0 || openOptionsSheet || openSettingsSheet, name: 'MoreSettings' });
@@ -202,9 +205,10 @@ export const MwebOptions = ({
202
205
  onClick={() => {
203
206
  togglePollView();
204
207
  setOpenOptionsSheet(false);
208
+ setUnreadPollQuiz(false);
205
209
  }}
206
210
  >
207
- <QuizIcon />
211
+ {unreadPollQuiz ? <QuizActiveIcon /> : <QuizIcon />}
208
212
  <ActionTile.Title>Polls and Quizzes</ActionTile.Title>
209
213
  </ActionTile.Root>
210
214
  )}
@@ -51,7 +51,7 @@ export const MultipleChoiceOptions = ({
51
51
  </Checkbox.Root>
52
52
  ) : null}
53
53
 
54
- {isStopped && correctOptionIndexes.includes(option.index) ? (
54
+ {isStopped && correctOptionIndexes?.includes(option.index) ? (
55
55
  <Flex css={{ color: '$on_surface_high' }}>
56
56
  <CheckCircleIcon />
57
57
  </Flex>
@@ -107,16 +107,37 @@ export const SidePaneTabs = React.memo<{
107
107
  <>
108
108
  {hideTabs ? (
109
109
  <>
110
- <Text variant="sm" css={{ fontWeight: '$semiBold', p: '$4', c: '$on_surface_high', pr: '$12' }}>
111
- {showChat ? (
112
- chat_title
113
- ) : (
114
- <span>
115
- Participants <ParticipantCount count={peerCount} />
116
- </span>
117
- )}
118
- </Text>
119
-
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
114
+ ) : (
115
+ <span>
116
+ Participants&nbsp;
117
+ <ParticipantCount count={peerCount} />
118
+ </span>
119
+ )}
120
+ </Text>
121
+ <Flex>
122
+ {showChatSettings ? <ChatSettings /> : null}
123
+ {isOverlayChat && isChatOpen ? null : (
124
+ <IconButton
125
+ css={{ my: '$1', color: '$on_surface_medium', '&:hover': { color: '$on_surface_high' } }}
126
+ onClick={e => {
127
+ e.stopPropagation();
128
+ if (activeTab === SIDE_PANE_OPTIONS.CHAT) {
129
+ toggleChat();
130
+ } else {
131
+ toggleParticipants();
132
+ }
133
+ }}
134
+ data-testid="close_chat"
135
+ >
136
+ <CrossIcon />
137
+ </IconButton>
138
+ )}
139
+ </Flex>
140
+ </Flex>
120
141
  {showChat ? <Chat /> : <ParticipantList offStageRoles={off_stage_roles} onActive={setActiveRole} />}
121
142
  </>
122
143
  ) : (
@@ -148,7 +169,8 @@ export const SidePaneTabs = React.memo<{
148
169
  color: activeTab !== SIDE_PANE_OPTIONS.PARTICIPANTS ? '$on_surface_low' : '$on_surface_high',
149
170
  }}
150
171
  >
151
- Participants &nbsp; <ParticipantCount count={peerCount} />
172
+ Participants&nbsp;
173
+ <ParticipantCount count={peerCount} />
152
174
  </Tabs.Trigger>
153
175
  </Tabs.List>
154
176
  {showChatSettings ? <ChatSettings /> : null}