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

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 (44) hide show
  1. package/dist/{HLSView-MR7RYQZB.js → HLSView-GG4WVUQY.js} +2 -2
  2. package/dist/Prebuilt/common/constants.d.ts +2 -6
  3. package/dist/Prebuilt/components/Chat/Chat.d.ts +2 -0
  4. package/dist/Prebuilt/components/Chat/ChatActions.d.ts +12 -0
  5. package/dist/Prebuilt/components/Chat/ChatBody.d.ts +8 -0
  6. package/dist/Prebuilt/components/Chat/ChatFooter.d.ts +2 -2
  7. package/dist/Prebuilt/components/Chat/ChatStates.d.ts +1 -1
  8. package/dist/Prebuilt/components/Chat/MwebChatOption.d.ts +1 -1
  9. package/dist/Prebuilt/components/Chat/PinnedMessage.d.ts +1 -3
  10. package/dist/Prebuilt/components/hooks/useChatBlacklist.d.ts +4 -0
  11. package/dist/Prebuilt/components/hooks/useSetPinnedMessages.d.ts +3 -10
  12. package/dist/Prebuilt/components/hooks/useUnreadPollQuizPresent.d.ts +5 -0
  13. package/dist/{chunk-WFHOR7AP.js → chunk-GXJIUWTP.js} +10083 -4574
  14. package/dist/chunk-GXJIUWTP.js.map +7 -0
  15. package/dist/index.cjs.js +12716 -7192
  16. package/dist/index.cjs.js.map +4 -4
  17. package/dist/index.js +1 -1
  18. package/dist/meta.cjs.json +367 -240
  19. package/dist/meta.esbuild.json +377 -250
  20. package/package.json +6 -6
  21. package/src/Prebuilt/common/constants.ts +4 -4
  22. package/src/Prebuilt/components/Chat/Chat.tsx +108 -0
  23. package/src/Prebuilt/components/Chat/ChatActions.tsx +297 -0
  24. package/src/Prebuilt/components/Chat/ChatBody.tsx +444 -0
  25. package/src/Prebuilt/components/Chat/ChatFooter.tsx +9 -3
  26. package/src/Prebuilt/components/Chat/ChatStates.tsx +5 -0
  27. package/src/Prebuilt/components/Chat/MwebChatOption.tsx +1 -1
  28. package/src/Prebuilt/components/Chat/PinnedMessage.tsx +4 -2
  29. package/src/Prebuilt/components/Footer/PollsToggle.tsx +12 -3
  30. package/src/Prebuilt/components/MoreSettings/SplitComponents/MwebOptions.tsx +5 -1
  31. package/src/Prebuilt/components/Polls/CreateQuestions/QuestionForm.jsx +3 -3
  32. package/src/Prebuilt/components/Polls/Voting/Leaderboard.tsx +9 -1
  33. package/src/Prebuilt/components/Polls/Voting/LeaderboardEntry.tsx +6 -6
  34. package/src/Prebuilt/components/Polls/Voting/QuestionCard.jsx +20 -23
  35. package/src/Prebuilt/components/Polls/common/MultipleChoiceOptions.jsx +1 -1
  36. package/src/Prebuilt/components/SidePaneTabs.tsx +33 -11
  37. package/src/Prebuilt/components/hooks/useChatBlacklist.ts +7 -1
  38. package/src/Prebuilt/components/hooks/useSetPinnedMessages.ts +19 -11
  39. package/src/Prebuilt/components/hooks/useUnreadPollQuizPresent.tsx +20 -0
  40. package/src/Prebuilt/provider/roomLayoutProvider/index.tsx +11 -2
  41. package/dist/chunk-WFHOR7AP.js.map +0 -7
  42. package/src/Prebuilt/components/Chat/Chat.jsx +0 -124
  43. package/src/Prebuilt/components/Chat/ChatBody.jsx +0 -726
  44. /package/dist/{HLSView-MR7RYQZB.js.map → HLSView-GG4WVUQY.js.map} +0 -0
@@ -1,726 +0,0 @@
1
- import React, { Fragment, useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
2
- import { useInView } from 'react-intersection-observer';
3
- import { useMedia } from 'react-use';
4
- import AutoSizer from 'react-virtualized-auto-sizer';
5
- import { VariableSizeList } from 'react-window';
6
- import {
7
- selectHMSMessages,
8
- selectLocalPeerID,
9
- selectLocalPeerName,
10
- selectLocalPeerRoleName,
11
- selectPeerNameByID,
12
- selectPermissions,
13
- selectSessionStore,
14
- selectUnreadHMSMessagesCount,
15
- useHMSActions,
16
- useHMSStore,
17
- } from '@100mslive/react-sdk';
18
- import {
19
- CopyIcon,
20
- CrossCircleIcon,
21
- CrossIcon,
22
- EyeCloseIcon,
23
- PeopleRemoveIcon,
24
- PinIcon,
25
- ReplyIcon,
26
- VerticalMenuIcon,
27
- } from '@100mslive/react-icons';
28
- import { Dropdown } from '../../../Dropdown';
29
- import { IconButton } from '../../../IconButton';
30
- import { Box, Flex } from '../../../Layout';
31
- import { Sheet } from '../../../Sheet';
32
- import { Text } from '../../../Text';
33
- import { config as cssConfig, styled } from '../../../Theme';
34
- import { Tooltip } from '../../../Tooltip';
35
- import emptyChat from '../../images/empty-chat.svg';
36
- import { ToastManager } from '../Toast/ToastManager';
37
- import { MwebChatOption } from './MwebChatOption';
38
- import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
39
- import { useSetSubscribedChatSelector } from '../AppData/useUISettings';
40
- import { useChatBlacklist } from '../hooks/useChatBlacklist';
41
- import { useSetPinnedMessages } from '../hooks/useSetPinnedMessages';
42
- import { CHAT_SELECTOR, SESSION_STORE_KEY } from '../../common/constants';
43
-
44
- const iconStyle = { height: '1.125rem', width: '1.125rem' };
45
- const tooltipBoxCSS = {
46
- fontSize: '$xs',
47
- backgroundColor: '$surface_default',
48
- p: '$1 $5',
49
- fontWeight: '$regular',
50
- borderRadius: '$3',
51
- };
52
-
53
- const formatTime = date => {
54
- if (!(date instanceof Date)) {
55
- return '';
56
- }
57
- let hours = date.getHours();
58
- let mins = date.getMinutes();
59
- const suffix = hours > 11 ? 'PM' : 'AM';
60
- if (hours < 10) {
61
- hours = '0' + hours;
62
- }
63
- if (mins < 10) {
64
- mins = '0' + mins;
65
- }
66
- return `${hours}:${mins} ${suffix}`;
67
- };
68
-
69
- const MessageTypeContainer = ({ left, right }) => {
70
- return (
71
- <Flex
72
- align="center"
73
- css={{
74
- ml: '$2',
75
- mr: '$4',
76
- gap: '$space$2',
77
- }}
78
- >
79
- {left && (
80
- <SenderName
81
- variant="xs"
82
- as="span"
83
- css={{ color: '$on_surface_medium', textTransform: 'capitalize', fontWeight: '$regular' }}
84
- >
85
- {left}
86
- </SenderName>
87
- )}
88
- {right && (
89
- <SenderName
90
- as="span"
91
- variant="overline"
92
- css={{
93
- color: '$on_surface_medium',
94
- fontWeight: '$regular',
95
- }}
96
- >
97
- {right}
98
- </SenderName>
99
- )}
100
- </Flex>
101
- );
102
- };
103
-
104
- const MessageType = ({ roles, hasCurrentUserSent, receiver }) => {
105
- const peerName = useHMSStore(selectPeerNameByID(receiver));
106
- const localPeerRoleName = useHMSStore(selectLocalPeerRoleName);
107
- if (receiver) {
108
- return (
109
- <MessageTypeContainer left={hasCurrentUserSent ? `${peerName ? `to ${peerName}` : ''}` : 'to You'} right="(DM)" />
110
- );
111
- }
112
-
113
- if (roles && roles.length) {
114
- return <MessageTypeContainer left={`to ${hasCurrentUserSent ? roles[0] : localPeerRoleName}`} right="(Group)" />;
115
- }
116
- return null;
117
- };
118
-
119
- const URL_REGEX =
120
- /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/;
121
-
122
- const Link = styled('a', {
123
- color: '$primary_default',
124
- wordBreak: 'break-word',
125
- '&:hover': {
126
- textDecoration: 'underline',
127
- },
128
- });
129
-
130
- export const AnnotisedMessage = ({ message }) => {
131
- if (!message) {
132
- return <Fragment />;
133
- }
134
-
135
- return (
136
- <Fragment>
137
- {message
138
- .trim()
139
- .split(/(\s)/)
140
- .map(part =>
141
- URL_REGEX.test(part) ? (
142
- <Link href={part} key={part} target="_blank" rel="noopener noreferrer">
143
- {part}
144
- </Link>
145
- ) : (
146
- part
147
- ),
148
- )}
149
- </Fragment>
150
- );
151
- };
152
-
153
- const getMessageType = ({ roles, receiver }) => {
154
- if (roles && roles.length > 0) {
155
- return 'role';
156
- }
157
- return receiver ? 'private' : '';
158
- };
159
- const ChatActions = ({
160
- onPin,
161
- showPinAction,
162
- onReply,
163
- showReply,
164
- message,
165
- sentByLocalPeer,
166
- isMobile,
167
- openSheet,
168
- setOpenSheet,
169
- }) => {
170
- const { elements } = useRoomLayoutConferencingScreen();
171
- const { can_hide_message, can_block_user } = elements?.chat?.real_time_controls || {
172
- can_hide_message: false,
173
- can_block_user: false,
174
- };
175
- const [open, setOpen] = useState(false);
176
- const actions = useHMSActions();
177
- const canRemoveOthers = useHMSStore(selectPermissions)?.removeOthers;
178
- const { blacklistItem: blacklistPeer } = useChatBlacklist(SESSION_STORE_KEY.CHAT_PEER_BLACKLIST);
179
-
180
- const { blacklistItem: blacklistMessage, blacklistedIDs: blacklistedMessageIDs = [] } = useChatBlacklist(
181
- SESSION_STORE_KEY.CHAT_MESSAGE_BLACKLIST,
182
- );
183
- const { unpinBlacklistedMessages } = useSetPinnedMessages();
184
-
185
- const pinnedMessages = useHMSStore(selectSessionStore(SESSION_STORE_KEY.PINNED_MESSAGES));
186
- const updatePinnedMessages = useCallback(
187
- (messageID = '') => {
188
- const blacklistedMessageIDSet = new Set([...blacklistedMessageIDs, messageID]);
189
- unpinBlacklistedMessages(pinnedMessages, blacklistedMessageIDSet);
190
- },
191
- [blacklistedMessageIDs, unpinBlacklistedMessages, pinnedMessages],
192
- );
193
-
194
- const copyMessageContent = useCallback(() => {
195
- try {
196
- navigator?.clipboard.writeText(message.message);
197
- ToastManager.addToast({
198
- title: 'Message copied successfully',
199
- });
200
- } catch (e) {
201
- console.log(e);
202
- ToastManager.addToast({
203
- title: 'Could not copy message',
204
- });
205
- }
206
- }, [message]);
207
-
208
- const options = {
209
- reply: {
210
- text: message.recipientRoles?.length ? 'Reply to Group' : 'Reply Privately',
211
- tooltipText: message.recipientRoles?.length ? 'Reply to Group' : 'Reply Privately',
212
- icon: <ReplyIcon style={iconStyle} />,
213
- onClick: onReply,
214
- show: showReply,
215
- },
216
- pin: {
217
- text: 'Pin message',
218
- tooltipText: 'Pin',
219
- icon: <PinIcon style={iconStyle} />,
220
- onClick: onPin,
221
- show: showPinAction,
222
- },
223
- copy: {
224
- text: 'Copy text',
225
- tooltipText: 'Copy',
226
- icon: <CopyIcon style={iconStyle} />,
227
- onClick: copyMessageContent,
228
- show: true,
229
- },
230
- hide: {
231
- text: message.recipientPeer ? 'Hide for both' : 'Hide for everyone',
232
- icon: <EyeCloseIcon style={iconStyle} />,
233
- onClick: async () => {
234
- blacklistMessage(message.id);
235
- updatePinnedMessages(message.id);
236
- },
237
- show: can_hide_message,
238
- },
239
- block: {
240
- text: 'Block from chat',
241
- icon: <CrossCircleIcon style={iconStyle} />,
242
- onClick: async () => blacklistPeer(message?.senderUserId),
243
- color: '$alert_error_default',
244
- show: can_block_user && !sentByLocalPeer,
245
- },
246
- remove: {
247
- text: 'Remove Partipant',
248
- icon: <PeopleRemoveIcon style={iconStyle} />,
249
- color: '$alert_error_default',
250
- show: canRemoveOthers && !sentByLocalPeer,
251
- onClick: async () => {
252
- try {
253
- await actions.removePeer(message.sender, '');
254
- } catch (error) {
255
- ToastManager.addToast({ title: error.message, variant: 'error' });
256
- }
257
- },
258
- },
259
- };
260
-
261
- if (isMobile) {
262
- return (
263
- <Sheet.Root open={openSheet} onOpenChange={setOpenSheet}>
264
- <Sheet.Content css={{ bg: '$surface_default', pb: '$14' }} onClick={() => setOpenSheet(false)}>
265
- <Sheet.Title
266
- css={{
267
- display: 'flex',
268
- color: '$on_surface_high',
269
- w: '100%',
270
- justifyContent: 'space-between',
271
- mt: '$8',
272
- fontSize: '$md',
273
- px: '$10',
274
- pb: '$8',
275
- borderBottom: '1px solid $border_bright',
276
- alignItems: 'center',
277
- }}
278
- >
279
- Message options
280
- <Sheet.Close css={{ color: '$on_surface_high' }} onClick={() => setOpenSheet(false)}>
281
- <CrossIcon />
282
- </Sheet.Close>
283
- </Sheet.Title>
284
-
285
- {Object.keys(options).map(optionKey => {
286
- const option = options[optionKey];
287
- return option.show ? (
288
- <MwebChatOption
289
- key={optionKey}
290
- text={option.text}
291
- icon={option.icon}
292
- onClick={option.onClick}
293
- color={option?.color}
294
- />
295
- ) : null;
296
- })}
297
- </Sheet.Content>
298
- </Sheet.Root>
299
- );
300
- }
301
-
302
- return (
303
- <Dropdown.Root open={open} onOpenChange={setOpen} css={{ '@md': { display: 'none' } }}>
304
- <Flex
305
- className="chat_actions"
306
- css={{
307
- background: '$surface_bright',
308
- borderRadius: '$1',
309
- p: '$2',
310
- opacity: open ? 1 : 0,
311
- position: 'absolute',
312
- right: 0,
313
- zIndex: 1,
314
- '@md': { opacity: 1 },
315
- }}
316
- >
317
- {options.reply.show ? (
318
- <Tooltip boxCss={tooltipBoxCSS} title={options.reply.tooltipText}>
319
- <IconButton data-testid="reply_message_btn" onClick={options.reply.onClick}>
320
- {options.reply.icon}
321
- </IconButton>
322
- </Tooltip>
323
- ) : null}
324
- {options.pin.show ? (
325
- <Tooltip boxCss={tooltipBoxCSS} title={options.pin.tooltipText}>
326
- <IconButton data-testid="pin_message_btn" onClick={options.pin.onClick}>
327
- {options.pin.icon}
328
- </IconButton>
329
- </Tooltip>
330
- ) : null}
331
-
332
- {options.copy.show ? (
333
- <Tooltip boxCss={tooltipBoxCSS} title={options.copy.tooltipText}>
334
- <IconButton onClick={options.copy.onClick} data-testid="copy_message_btn">
335
- <CopyIcon style={iconStyle} />
336
- </IconButton>
337
- </Tooltip>
338
- ) : null}
339
-
340
- {options.block.show || options.hide.show || options.remove.show ? (
341
- <Tooltip boxCss={tooltipBoxCSS} title="More actions">
342
- <Dropdown.Trigger asChild>
343
- <IconButton>
344
- <VerticalMenuIcon style={iconStyle} />
345
- </IconButton>
346
- </Dropdown.Trigger>
347
- </Tooltip>
348
- ) : null}
349
- </Flex>
350
- <Dropdown.Portal>
351
- <Dropdown.Content
352
- sideOffset={5}
353
- align="end"
354
- css={{ width: '$48', backgroundColor: '$surface_bright', py: '$0', border: '1px solid $border_bright' }}
355
- >
356
- {options.hide.show ? (
357
- <Dropdown.Item data-testid="hide_message_btn" onClick={options.hide.onClick}>
358
- {options.hide.icon}
359
- <Text variant="sm" css={{ ml: '$4', fontWeight: '$semiBold' }}>
360
- {options.hide.text}
361
- </Text>
362
- </Dropdown.Item>
363
- ) : null}
364
-
365
- {options.block.show ? (
366
- <Dropdown.Item
367
- data-testid="block_peer_btn"
368
- onClick={options.block.onClick}
369
- css={{ color: options.block.color }}
370
- >
371
- {options.block.icon}
372
- <Text variant="sm" css={{ ml: '$4', color: 'inherit', fontWeight: '$semiBold' }}>
373
- {options.block.text}
374
- </Text>
375
- </Dropdown.Item>
376
- ) : null}
377
- {options.remove.show ? (
378
- <Dropdown.Item
379
- data-testid="remove_peer_btn"
380
- onClick={options.remove.onClick}
381
- css={{ color: options.remove.color }}
382
- >
383
- {options.remove.icon}
384
- <Text variant="sm" css={{ ml: '$4', color: 'inherit', fontWeight: '$semiBold' }}>
385
- {options.remove.text}
386
- </Text>
387
- </Dropdown.Item>
388
- ) : null}
389
- </Dropdown.Content>
390
- </Dropdown.Portal>
391
- </Dropdown.Root>
392
- );
393
- };
394
-
395
- const SenderName = styled(Text, {
396
- overflow: 'hidden',
397
- textOverflow: 'ellipsis',
398
- whiteSpace: 'nowrap',
399
- maxWidth: '14ch',
400
- minWidth: 0,
401
- color: '$on_surface_high',
402
- fontWeight: '$semiBold',
403
- });
404
-
405
- const ChatMessage = React.memo(
406
- ({ index, style = {}, message, setRowHeight, isLast = false, unreadCount = 0, scrollToBottom, onPin }) => {
407
- const { ref, inView } = useInView({ threshold: 0.5, triggerOnce: true });
408
- const { elements } = useRoomLayoutConferencingScreen();
409
- const rowRef = useRef(null);
410
- useEffect(() => {
411
- if (rowRef.current) {
412
- setRowHeight(index, rowRef.current.clientHeight);
413
- }
414
- }, [index, setRowHeight]);
415
- const isMobile = useMedia(cssConfig.media.md);
416
- const isPrivateChatEnabled = !!elements?.chat?.private_chat_enabled;
417
- const roleWhiteList = elements?.chat?.roles_whitelist || [];
418
- const isOverlay = elements?.chat?.is_overlay && isMobile;
419
- const hmsActions = useHMSActions();
420
- const localPeerId = useHMSStore(selectLocalPeerID);
421
- const [selectedRole, setRoleSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.ROLE);
422
- const [selectedPeer, setPeerSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.PEER);
423
- const messageType = getMessageType({
424
- roles: message.recipientRoles,
425
- receiver: message.recipientPeer,
426
- });
427
- const [openSheet, setOpenSheet] = useState(false);
428
- const showPinAction = !!elements?.chat?.allow_pinning_messages;
429
- let showReply = false;
430
- if (message.recipientRoles && roleWhiteList.includes(message.recipientRoles[0])) {
431
- showReply = true;
432
- } else if (message.sender !== selectedPeer.id && message.sender !== localPeerId && isPrivateChatEnabled) {
433
- showReply = true;
434
- }
435
-
436
- useEffect(() => {
437
- if (message.id && !message.read && inView) {
438
- hmsActions.setMessageRead(true, message.id);
439
- }
440
- }, [message.read, hmsActions, inView, message.id]);
441
-
442
- useEffect(() => {
443
- if (isLast && inView && unreadCount >= 1) {
444
- scrollToBottom(1);
445
- }
446
- }, [inView, isLast, scrollToBottom, unreadCount]);
447
-
448
- return (
449
- <Box
450
- ref={ref}
451
- as="div"
452
- css={{
453
- mb: '$5',
454
- pr: '$10',
455
- mt: '$4',
456
- '&:not(:hover} .chat_actions': { display: 'none' },
457
- '&:hover .chat_actions': { display: 'flex', opacity: 1 },
458
- }}
459
- style={style}
460
- >
461
- <Flex
462
- ref={rowRef}
463
- align="center"
464
- css={{
465
- flexWrap: 'wrap',
466
- position: 'relative',
467
- // Theme independent color, token should not be used for transparent chat
468
- bg:
469
- messageType && !(selectedPeer.id || selectedRole)
470
- ? isOverlay
471
- ? 'rgba(0, 0, 0, 0.64)'
472
- : '$surface_default'
473
- : undefined,
474
- r: '$1',
475
- p: '$4',
476
- userSelect: 'none',
477
- '@md': {
478
- cursor: 'pointer',
479
- },
480
- '&:hover': {
481
- background: 'linear-gradient(277deg, $surface_default 0%, $surface_dim 60.87%)',
482
- },
483
- }}
484
- key={message.time}
485
- data-testid="chat_msg"
486
- onClick={() => {
487
- if (isMobile) {
488
- setOpenSheet(true);
489
- }
490
- }}
491
- >
492
- <Text
493
- css={{
494
- color: isOverlay ? '#FFF' : '$on_surface_high',
495
- fontWeight: '$semiBold',
496
- display: 'flex',
497
- alignItems: 'center',
498
- alignSelf: 'stretch',
499
- width: '100%',
500
- }}
501
- as="div"
502
- >
503
- <Flex align="baseline">
504
- {message.senderName === 'You' || !message.senderName ? (
505
- <SenderName
506
- as="span"
507
- variant="sub2"
508
- css={{ color: isOverlay ? '#FFF' : '$on_surface_high', fontWeight: '$semiBold' }}
509
- >
510
- {message.senderName || 'Anonymous'}
511
- </SenderName>
512
- ) : (
513
- <Tooltip title={message.senderName} side="top" align="start">
514
- <SenderName
515
- as="span"
516
- variant="sub2"
517
- css={{ color: isOverlay ? '#FFF' : '$on_surface_high', fontWeight: '$semiBold' }}
518
- >
519
- {message.senderName}
520
- </SenderName>
521
- </Tooltip>
522
- )}
523
- <MessageType
524
- hasCurrentUserSent={message.sender === localPeerId}
525
- receiver={message.recipientPeer}
526
- roles={message.recipientRoles}
527
- />
528
- </Flex>
529
-
530
- {!isOverlay ? (
531
- <Text
532
- as="span"
533
- variant="caption"
534
- css={{
535
- color: '$on_surface_medium',
536
- flexShrink: 0,
537
- position: 'absolute',
538
- right: 0,
539
- zIndex: 1,
540
- mr: '$4',
541
- p: '$2',
542
- }}
543
- >
544
- {formatTime(message.time)}
545
- </Text>
546
- ) : null}
547
- <ChatActions
548
- onPin={onPin}
549
- showPinAction={showPinAction}
550
- message={message}
551
- sentByLocalPeer={message.sender === localPeerId}
552
- onReply={() => {
553
- if (message.recipientRoles?.length) {
554
- setRoleSelector(message.recipientRoles[0]);
555
- setPeerSelector({});
556
- } else {
557
- setRoleSelector('');
558
- setPeerSelector({ id: message.sender, name: message.senderName });
559
- }
560
- }}
561
- showReply={showReply}
562
- isMobile={isMobile}
563
- openSheet={openSheet}
564
- setOpenSheet={setOpenSheet}
565
- />
566
- </Text>
567
- <Text
568
- variant="sm"
569
- css={{
570
- w: '100%',
571
- mt: '$2',
572
- wordBreak: 'break-word',
573
- whiteSpace: 'pre-wrap',
574
- userSelect: 'all',
575
- color: isOverlay ? '#FFF' : '$on_surface_high',
576
- }}
577
- onClick={e => {
578
- e.stopPropagation();
579
- setOpenSheet(true);
580
- }}
581
- >
582
- <AnnotisedMessage message={message.message} />
583
- </Text>
584
- </Flex>
585
- </Box>
586
- );
587
- },
588
- );
589
- const ChatList = React.forwardRef(
590
- ({ width, height, setRowHeight, getRowHeight, messages, unreadCount = 0, scrollToBottom }, listRef) => {
591
- const { setPinnedMessages } = useSetPinnedMessages();
592
- const pinnedMessages = useHMSStore(selectSessionStore(SESSION_STORE_KEY.PINNED_MESSAGES)) || [];
593
- const localPeerName = useHMSStore(selectLocalPeerName);
594
- useLayoutEffect(() => {
595
- if (listRef.current && listRef.current.scrollToItem) {
596
- scrollToBottom(1);
597
- }
598
- // eslint-disable-next-line react-hooks/exhaustive-deps
599
- }, [listRef]);
600
-
601
- return (
602
- <VariableSizeList
603
- ref={listRef}
604
- itemCount={messages.length}
605
- itemSize={getRowHeight}
606
- width={width}
607
- height={height - 1}
608
- style={{
609
- overflowX: 'hidden',
610
- }}
611
- >
612
- {({ index, style }) => (
613
- <ChatMessage
614
- style={style}
615
- index={index}
616
- key={messages[index].id}
617
- message={messages[index]}
618
- setRowHeight={setRowHeight}
619
- unreadCount={unreadCount}
620
- isLast={index >= messages.length - 2}
621
- scrollToBottom={scrollToBottom}
622
- onPin={() => setPinnedMessages(pinnedMessages, messages[index], localPeerName)}
623
- />
624
- )}
625
- </VariableSizeList>
626
- );
627
- },
628
- );
629
- const VirtualizedChatMessages = React.forwardRef(({ messages, unreadCount = 0, scrollToBottom }, listRef) => {
630
- const rowHeights = useRef({});
631
-
632
- function getRowHeight(index) {
633
- // 72 will be default row height for any message length
634
- // 16 will add margin value as clientHeight don't include margin
635
- return rowHeights.current[index] + 16 || 72;
636
- }
637
-
638
- const setRowHeight = useCallback(
639
- (index, size) => {
640
- listRef.current.resetAfterIndex(0);
641
- rowHeights.current = { ...rowHeights.current, [index]: size };
642
- },
643
- [listRef],
644
- );
645
-
646
- return (
647
- <Box
648
- css={{
649
- mr: '-$10',
650
- h: '100%',
651
- }}
652
- as="div"
653
- >
654
- <AutoSizer
655
- style={{
656
- width: '90%',
657
- }}
658
- >
659
- {({ height, width }) => (
660
- <ChatList
661
- width={width}
662
- height={height}
663
- messages={messages}
664
- setRowHeight={setRowHeight}
665
- getRowHeight={getRowHeight}
666
- scrollToBottom={scrollToBottom}
667
- ref={listRef}
668
- unreadCount={unreadCount}
669
- />
670
- )}
671
- </AutoSizer>
672
- </Box>
673
- );
674
- });
675
-
676
- export const ChatBody = React.forwardRef(({ scrollToBottom }, listRef) => {
677
- let messages = useHMSStore(selectHMSMessages);
678
- const blacklistedMessageIDs = useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_MESSAGE_BLACKLIST)) || [];
679
- const getFilteredMessages = () => {
680
- const blacklistedMessageIDSet = new Set(blacklistedMessageIDs);
681
- return messages?.filter(message => message.type === 'chat' && !blacklistedMessageIDSet.has(message.id)) || [];
682
- };
683
-
684
- const isMobile = useMedia(cssConfig.media.md);
685
- const { elements } = useRoomLayoutConferencingScreen();
686
- const unreadCount = useHMSStore(selectUnreadHMSMessagesCount);
687
-
688
- if (messages.length === 0 && !(isMobile && elements?.chat?.is_overlay)) {
689
- return (
690
- <Flex
691
- css={{
692
- width: '100%',
693
- flex: '1 1 0',
694
- textAlign: 'center',
695
- px: '$4',
696
- }}
697
- align="center"
698
- justify="center"
699
- >
700
- <Box>
701
- <img src={emptyChat} alt="Empty Chat" height={132} width={185} style={{ margin: '0 auto' }} />
702
- <Text variant="h5" css={{ mt: '$8', c: '$on_surface_high' }}>
703
- Start a conversation
704
- </Text>
705
- <Text
706
- variant="sm"
707
- css={{ mt: '$4', maxWidth: '80%', textAlign: 'center', mx: 'auto', c: '$on_surface_medium' }}
708
- >
709
- There are no messages here yet. Start a conversation by sending a message.
710
- </Text>
711
- </Box>
712
- </Flex>
713
- );
714
- }
715
-
716
- return (
717
- <Fragment>
718
- <VirtualizedChatMessages
719
- messages={getFilteredMessages()}
720
- scrollToBottom={scrollToBottom}
721
- unreadCount={unreadCount}
722
- ref={listRef}
723
- />
724
- </Fragment>
725
- );
726
- });