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

Sign up to get free protection for your applications and to get access to all the features.
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
- });