@100mslive/roomkit-react 0.1.18-alpha.0 → 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 (38) hide show
  1. package/dist/{HLSView-GYHK4ION.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-75O2M72S.js → chunk-2FX6MFDM.js} +4541 -4502
  13. package/dist/chunk-2FX6MFDM.js.map +7 -0
  14. package/dist/index.cjs.js +5113 -5059
  15. package/dist/index.cjs.js.map +4 -4
  16. package/dist/index.js +1 -1
  17. package/dist/meta.cjs.json +343 -225
  18. package/dist/meta.esbuild.json +353 -235
  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/Leave/DesktopLeaveRoom.tsx +4 -3
  29. package/src/Prebuilt/components/MoreSettings/SplitComponents/MwebOptions.tsx +5 -1
  30. package/src/Prebuilt/components/Polls/common/MultipleChoiceOptions.jsx +1 -1
  31. package/src/Prebuilt/components/SidePaneTabs.tsx +33 -11
  32. package/src/Prebuilt/components/hooks/useChatBlacklist.ts +7 -1
  33. package/src/Prebuilt/components/hooks/useSetPinnedMessages.ts +19 -11
  34. package/src/Prebuilt/components/hooks/useUnreadPollQuizPresent.tsx +20 -0
  35. package/dist/chunk-75O2M72S.js.map +0 -7
  36. package/src/Prebuilt/components/Chat/Chat.jsx +0 -124
  37. package/src/Prebuilt/components/Chat/ChatBody.jsx +0 -726
  38. /package/dist/{HLSView-GYHK4ION.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
  );
@@ -77,8 +77,8 @@ export const DesktopLeaveRoom = ({
77
77
  bg: '$surface_dim',
78
78
  color: '$on_surface_medium',
79
79
  '&:hover': { bg: '$surface_default', color: '$on_surface_high' },
80
+ p: '0',
80
81
  }}
81
- onClick={async () => await leaveRoom()}
82
82
  data-testid="just_leave_btn"
83
83
  >
84
84
  <LeaveCard
@@ -90,7 +90,7 @@ export const DesktopLeaveRoom = ({
90
90
  titleColor="$on_surface_high"
91
91
  icon={<ExitIcon height={24} width={24} style={{ transform: 'rotate(180deg)' }} />}
92
92
  onClick={async () => await leaveRoom()}
93
- css={{ p: 0 }}
93
+ css={{ p: '$8 $4' }}
94
94
  />
95
95
  </Dropdown.Item>
96
96
 
@@ -99,6 +99,7 @@ export const DesktopLeaveRoom = ({
99
99
  bg: '$alert_error_dim',
100
100
  color: '$alert_error_bright',
101
101
  '&:hover': { bg: '$alert_error_dim', color: '$alert_error_brighter' },
102
+ p: '0',
102
103
  }}
103
104
  data-testid="end_room_btn"
104
105
  >
@@ -114,7 +115,7 @@ export const DesktopLeaveRoom = ({
114
115
  setOpen(false);
115
116
  setShowEndStreamAlert(true);
116
117
  }}
117
- css={{ p: 0 }}
118
+ css={{ p: '$8 $4' }}
118
119
  />
119
120
  </Dropdown.Item>
120
121
  </Dropdown.Content>
@@ -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>