@100mslive/roomkit-react 0.1.12 → 0.1.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. package/dist/{HLSView-PWVM4OIP.js → HLSView-IENE4HRP.js} +2 -2
  2. package/dist/Prebuilt/App.d.ts +0 -2
  3. package/dist/Prebuilt/AppContext.d.ts +0 -2
  4. package/dist/Prebuilt/common/constants.d.ts +110 -0
  5. package/dist/Prebuilt/components/AppData/AppData.d.ts +2 -0
  6. package/dist/Prebuilt/components/Chat/MwebChatOption.d.ts +7 -0
  7. package/dist/Prebuilt/components/Chat/Navigation.d.ts +8 -0
  8. package/dist/Prebuilt/components/Chat/PinnedMessage.d.ts +4 -0
  9. package/dist/Prebuilt/components/Footer/Footer.d.ts +3 -2
  10. package/dist/Prebuilt/components/Notifications/ChatNotifications.d.ts +2 -0
  11. package/dist/Prebuilt/components/Preview/PreviewJoin.d.ts +2 -1
  12. package/dist/Prebuilt/components/VirtualBackground/VBCollection.d.ts +2 -1
  13. package/dist/Prebuilt/components/VirtualBackground/VBPicker.d.ts +3 -0
  14. package/dist/Prebuilt/components/VirtualBackground/constants.d.ts +0 -6
  15. package/dist/Prebuilt/components/hooks/useChatBlacklist.d.ts +4 -0
  16. package/dist/Prebuilt/components/hooks/useSetPinnedMessages.d.ts +16 -0
  17. package/dist/Prebuilt/provider/roomLayoutProvider/hooks/useRoomLayoutScreen.d.ts +3 -2
  18. package/dist/{chunk-WREKGNP6.js → chunk-U4AB6X6M.js} +2178 -1148
  19. package/dist/chunk-U4AB6X6M.js.map +7 -0
  20. package/dist/index.cjs.js +2746 -1638
  21. package/dist/index.cjs.js.map +4 -4
  22. package/dist/index.js +1 -1
  23. package/dist/meta.cjs.json +803 -208
  24. package/dist/meta.esbuild.json +812 -217
  25. package/package.json +8 -7
  26. package/src/Prebuilt/App.tsx +3 -14
  27. package/src/Prebuilt/AppContext.tsx +0 -2
  28. package/src/Prebuilt/common/{constants.js → constants.ts} +22 -20
  29. package/src/Prebuilt/components/AppData/{AppData.jsx → AppData.tsx} +8 -22
  30. package/src/Prebuilt/components/AppData/useUISettings.js +0 -4
  31. package/src/Prebuilt/components/AuthToken.jsx +15 -5
  32. package/src/Prebuilt/components/Chat/Chat.jsx +49 -73
  33. package/src/Prebuilt/components/Chat/ChatBody.jsx +241 -51
  34. package/src/Prebuilt/components/Chat/ChatFooter.tsx +56 -6
  35. package/src/Prebuilt/components/Chat/ChatStates.jsx +68 -0
  36. package/src/Prebuilt/components/Chat/MwebChatOption.tsx +24 -0
  37. package/src/Prebuilt/components/Chat/Navigation.tsx +60 -0
  38. package/src/Prebuilt/components/Chat/PinnedMessage.tsx +116 -0
  39. package/src/Prebuilt/components/Footer/Footer.tsx +4 -7
  40. package/src/Prebuilt/components/Header/common.jsx +1 -1
  41. package/src/Prebuilt/components/Notifications/ChatNotifications.tsx +34 -0
  42. package/src/Prebuilt/components/Notifications/Notifications.tsx +2 -0
  43. package/src/Prebuilt/components/Polls/Voting/QuestionCard.jsx +6 -4
  44. package/src/Prebuilt/components/Polls/Voting/TimedVoting.jsx +3 -2
  45. package/src/Prebuilt/components/Polls/common/VoteCount.jsx +6 -4
  46. package/src/Prebuilt/components/Preview/PreviewJoin.tsx +7 -9
  47. package/src/Prebuilt/components/RoleChangeRequest/RoleChangeRequestModal.tsx +4 -1
  48. package/src/Prebuilt/components/Settings/DeviceSettings.jsx +1 -1
  49. package/src/Prebuilt/components/SidePaneTabs.tsx +3 -2
  50. package/src/Prebuilt/components/Toast/ToastConfig.jsx +1 -0
  51. package/src/Prebuilt/components/VirtualBackground/VBCollection.tsx +4 -4
  52. package/src/Prebuilt/components/VirtualBackground/{VBPicker.jsx → VBPicker.tsx} +64 -44
  53. package/src/Prebuilt/components/VirtualBackground/constants.ts +0 -8
  54. package/src/Prebuilt/components/hooks/useAutoStartStreaming.tsx +10 -1
  55. package/src/Prebuilt/components/hooks/useChatBlacklist.ts +21 -0
  56. package/src/Prebuilt/components/hooks/useSetPinnedMessages.ts +75 -0
  57. package/src/Prebuilt/components/hooks/useUserPreferences.jsx +1 -0
  58. package/src/Prebuilt/layouts/SidePane.tsx +1 -1
  59. package/src/Prebuilt/layouts/VideoStreamingSection.tsx +16 -8
  60. package/src/Prebuilt/plugins/whiteboard/PusherCommunicationProvider.js +1 -1
  61. package/src/Prebuilt/plugins/whiteboard/README.md +1 -1
  62. package/src/Prebuilt/primitives/DialogContent.jsx +1 -1
  63. package/src/Prebuilt/provider/roomLayoutProvider/constants/index.ts +12 -1
  64. package/src/Prebuilt/provider/roomLayoutProvider/hooks/useRoomLayoutScreen.ts +5 -1
  65. package/dist/chunk-WREKGNP6.js.map +0 -7
  66. package/src/Prebuilt/components/hooks/useSetPinnedMessage.js +0 -38
  67. package/src/Prebuilt/services/tokenService.js +0 -49
  68. /package/dist/{HLSView-PWVM4OIP.js.map → HLSView-IENE4HRP.js.map} +0 -0
@@ -1,4 +1,4 @@
1
- import React, { Fragment, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
1
+ import React, { Fragment, useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
2
2
  import { useInView } from 'react-intersection-observer';
3
3
  import { useMedia } from 'react-use';
4
4
  import AutoSizer from 'react-virtualized-auto-sizer';
@@ -6,25 +6,41 @@ import { VariableSizeList } from 'react-window';
6
6
  import {
7
7
  selectHMSMessages,
8
8
  selectLocalPeerID,
9
+ selectLocalPeerName,
9
10
  selectLocalPeerRoleName,
10
11
  selectMessagesByPeerID,
11
12
  selectMessagesByRole,
12
13
  selectPeerNameByID,
13
14
  selectPermissions,
15
+ selectSessionStore,
14
16
  useHMSActions,
15
17
  useHMSStore,
16
18
  } from '@100mslive/react-sdk';
17
- import { PinIcon, VerticalMenuIcon } from '@100mslive/react-icons';
19
+ import { CopyIcon, CrossCircleIcon, CrossIcon, EyeCloseIcon, PinIcon, VerticalMenuIcon } from '@100mslive/react-icons';
18
20
  import { Dropdown } from '../../../Dropdown';
19
21
  import { IconButton } from '../../../IconButton';
20
22
  import { Box, Flex } from '../../../Layout';
23
+ import { Sheet } from '../../../Sheet';
21
24
  import { Text } from '../../../Text';
22
25
  import { config as cssConfig, styled } from '../../../Theme';
23
26
  import { Tooltip } from '../../../Tooltip';
24
27
  import emptyChat from '../../images/empty-chat.svg';
28
+ import { ToastManager } from '../Toast/ToastManager';
29
+ import { MwebChatOption } from './MwebChatOption';
25
30
  import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
26
- import { useSetPinnedMessage } from '../hooks/useSetPinnedMessage';
31
+ import { useChatBlacklist } from '../hooks/useChatBlacklist';
32
+ import { useSetPinnedMessages } from '../hooks/useSetPinnedMessages';
27
33
  import { useUnreadCount } from './useUnreadCount';
34
+ import { SESSION_STORE_KEY } from '../../common/constants';
35
+
36
+ const iconStyle = { height: '1.125rem', width: '1.125rem' };
37
+ const tooltipBoxCSS = {
38
+ fontSize: '$xs',
39
+ backgroundColor: '$surface_default',
40
+ p: '$1 $5',
41
+ fontWeight: '$regular',
42
+ borderRadius: '$3',
43
+ };
28
44
 
29
45
  const formatTime = date => {
30
46
  if (!(date instanceof Date)) {
@@ -127,56 +143,188 @@ const getMessageType = ({ roles, receiver }) => {
127
143
  }
128
144
  return receiver ? 'private' : '';
129
145
  };
130
- const ChatActions = ({ onPin, showPinAction }) => {
146
+ const ChatActions = ({ onPin, showPinAction, message, sentByLocalPeer, isMobile, openSheet, setOpenSheet }) => {
147
+ const { elements } = useRoomLayoutConferencingScreen();
148
+ const { can_hide_message, can_block_user } = elements?.chat?.real_time_controls || {
149
+ can_hide_message: false,
150
+ can_block_user: false,
151
+ };
131
152
  const [open, setOpen] = useState(false);
132
- if (!showPinAction) {
133
- return null;
153
+ const blacklistedPeerIDs = useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_PEER_BLACKLIST)) || [];
154
+ const { blacklistItem: blacklistPeer } = useChatBlacklist(SESSION_STORE_KEY.CHAT_PEER_BLACKLIST);
155
+
156
+ const blacklistedMessageIDs = useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_MESSAGE_BLACKLIST)) || [];
157
+ const { blacklistItem: blacklistMessage } = useChatBlacklist(SESSION_STORE_KEY.CHAT_MESSAGE_BLACKLIST);
158
+
159
+ const { unpinBlacklistedMessages } = useSetPinnedMessages();
160
+
161
+ const pinnedMessages = useHMSStore(selectSessionStore(SESSION_STORE_KEY.PINNED_MESSAGES)) || [];
162
+
163
+ const updatePinnedMessages = useCallback(
164
+ ({ messageID = '', peerID = '' }) => {
165
+ const blacklistedMessageIDSet = new Set([...blacklistedMessageIDs, messageID]);
166
+ const blacklistedPeerIDSet = new Set([...blacklistedPeerIDs, peerID]);
167
+ unpinBlacklistedMessages(pinnedMessages, blacklistedPeerIDSet, blacklistedMessageIDSet);
168
+ },
169
+ [pinnedMessages, blacklistedMessageIDs, blacklistedPeerIDs],
170
+ );
171
+
172
+ const copyMessageContent = useCallback(() => {
173
+ try {
174
+ navigator?.clipboard.writeText(message.message);
175
+ ToastManager.addToast({
176
+ title: 'Message copied successfully',
177
+ });
178
+ } catch (e) {
179
+ console.log(e);
180
+ ToastManager.addToast({
181
+ title: 'Could not copy message',
182
+ });
183
+ }
184
+ }, [message]);
185
+
186
+ const options = {
187
+ pin: {
188
+ text: 'Pin message',
189
+ tooltipText: 'Pin',
190
+ icon: <PinIcon style={iconStyle} />,
191
+ onClick: onPin,
192
+ show: showPinAction,
193
+ },
194
+ copy: {
195
+ text: 'Copy text',
196
+ tooltipText: 'Copy',
197
+ icon: <CopyIcon style={iconStyle} />,
198
+ onClick: copyMessageContent,
199
+ show: true,
200
+ },
201
+ hide: {
202
+ text: 'Hide for everyone',
203
+ icon: <EyeCloseIcon style={iconStyle} />,
204
+ onClick: async () => {
205
+ blacklistMessage(blacklistedMessageIDs, message.id);
206
+ updatePinnedMessages({ messageID: message.id });
207
+ },
208
+ show: can_hide_message,
209
+ },
210
+ block: {
211
+ text: 'Block from chat',
212
+ icon: <CrossCircleIcon style={iconStyle} />,
213
+ onClick: async () => {
214
+ blacklistPeer(blacklistedPeerIDs, message?.senderUserId);
215
+ updatePinnedMessages({ peerID: message?.senderUserId });
216
+ },
217
+ color: '$alert_error_default',
218
+ show: can_block_user && !sentByLocalPeer,
219
+ },
220
+ };
221
+
222
+ if (isMobile) {
223
+ return (
224
+ <Sheet.Root open={openSheet} onOpenChange={setOpenSheet}>
225
+ <Sheet.Content css={{ bg: '$surface_default', pb: '$14' }} onClick={() => setOpenSheet(false)}>
226
+ <Sheet.Title
227
+ css={{
228
+ display: 'flex',
229
+ color: '$on_surface_high',
230
+ w: '100%',
231
+ justifyContent: 'space-between',
232
+ mt: '$8',
233
+ fontSize: '$md',
234
+ px: '$10',
235
+ pb: '$8',
236
+ borderBottom: '1px solid $border_bright',
237
+ alignItems: 'center',
238
+ }}
239
+ >
240
+ Message options
241
+ <Sheet.Close css={{ color: '$on_surface_high' }} onClick={() => setOpenSheet(false)}>
242
+ <CrossIcon />
243
+ </Sheet.Close>
244
+ </Sheet.Title>
245
+
246
+ {Object.keys(options).map(optionKey => {
247
+ const option = options[optionKey];
248
+ return option.show ? (
249
+ <MwebChatOption
250
+ key={optionKey}
251
+ text={option.text}
252
+ icon={option.icon}
253
+ onClick={option.onClick}
254
+ color={option?.color}
255
+ />
256
+ ) : null;
257
+ })}
258
+ </Sheet.Content>
259
+ </Sheet.Root>
260
+ );
134
261
  }
135
262
 
136
263
  return (
137
- <Dropdown.Root open={open} onOpenChange={setOpen}>
138
- <Dropdown.Trigger className="chat_actions" css={{ opacity: open ? 1 : 0, '@md': { opacity: 1 } }} asChild>
139
- <IconButton>
140
- <Tooltip title="More options">
141
- <VerticalMenuIcon />
264
+ <Dropdown.Root open={open} onOpenChange={setOpen} css={{ '@md': { display: 'none' } }}>
265
+ <Flex
266
+ className="chat_actions"
267
+ css={{
268
+ background: '$surface_bright',
269
+ borderRadius: '$1',
270
+ p: '$2',
271
+ opacity: open ? 1 : 0,
272
+ '@md': { opacity: 1 },
273
+ }}
274
+ >
275
+ {options.pin.show ? (
276
+ <Tooltip boxCss={tooltipBoxCSS} title={options.pin.tooltipText}>
277
+ <IconButton data-testid="pin_message_btn" onClick={options.pin.onClick}>
278
+ {options.pin.icon}
279
+ </IconButton>
280
+ </Tooltip>
281
+ ) : null}
282
+
283
+ {options.copy.show ? (
284
+ <Tooltip boxCss={tooltipBoxCSS} title={options.copy.tooltipText}>
285
+ <IconButton onClick={options.copy.onClick} data-testid="copy_message_btn">
286
+ <CopyIcon style={iconStyle} />
287
+ </IconButton>
288
+ </Tooltip>
289
+ ) : null}
290
+
291
+ {options.block.show || options.hide.show ? (
292
+ <Tooltip boxCss={tooltipBoxCSS} title="More actions">
293
+ <Dropdown.Trigger asChild>
294
+ <IconButton>
295
+ <VerticalMenuIcon style={iconStyle} />
296
+ </IconButton>
297
+ </Dropdown.Trigger>
142
298
  </Tooltip>
143
- </IconButton>
144
- </Dropdown.Trigger>
299
+ ) : null}
300
+ </Flex>
145
301
  <Dropdown.Portal>
146
302
  <Dropdown.Content
147
303
  sideOffset={5}
148
304
  align="end"
149
305
  css={{ width: '$48', backgroundColor: '$surface_bright', py: '$0', border: '1px solid $border_bright' }}
150
306
  >
151
- <Dropdown.Item data-testid="pin_message_btn" onClick={onPin}>
152
- <PinIcon />
153
- <Text variant="sm" css={{ ml: '$4' }}>
154
- Pin Message
155
- </Text>
156
- </Dropdown.Item>
157
- {/* {isMobile ? (
307
+ {options.hide.show ? (
308
+ <Dropdown.Item data-testid="hide_message_btn" onClick={options.hide.onClick}>
309
+ {options.hide.icon}
310
+ <Text variant="sm" css={{ ml: '$4', fontWeight: '$semiBold' }}>
311
+ {options.hide.text}
312
+ </Text>
313
+ </Dropdown.Item>
314
+ ) : null}
315
+
316
+ {options.block.show ? (
158
317
  <Dropdown.Item
159
- data-testid="copy_message_btn"
160
- onClick={() => {
161
- try {
162
- navigator?.clipboard.writeText(messageContent);
163
- ToastManager.addToast({
164
- title: 'Message copied successfully',
165
- });
166
- } catch (e) {
167
- console.log(e);
168
- ToastManager.addToast({
169
- title: 'Could not copy message',
170
- });
171
- }
172
- }}
318
+ data-testid="block_peer_btn"
319
+ onClick={options.block.onClick}
320
+ css={{ color: options.block.color }}
173
321
  >
174
- <CopyIcon />
175
- <Text variant="sm" css={{ ml: '$4' }}>
176
- Copy Message
322
+ {options.block.icon}
323
+ <Text variant="sm" css={{ ml: '$4', color: 'inherit', fontWeight: '$semiBold' }}>
324
+ {options.block.text}
177
325
  </Text>
178
326
  </Dropdown.Item>
179
- ) : null} */}
327
+ ) : null}
180
328
  </Dropdown.Content>
181
329
  </Dropdown.Portal>
182
330
  </Dropdown.Root>
@@ -212,9 +360,9 @@ const ChatMessage = React.memo(
212
360
  roles: message.recipientRoles,
213
361
  receiver: message.recipientPeer,
214
362
  });
363
+ const [openSheet, setOpenSheet] = useState(false);
215
364
  // show pin action only if peer has remove others permission and the message is of broadcast type
216
365
  const showPinAction = permissions.removeOthers && !messageType && elements?.chat?.allow_pinning_messages;
217
-
218
366
  useEffect(() => {
219
367
  if (message.id && !message.read && inView) {
220
368
  hmsActions.setMessageRead(true, message.id);
@@ -231,7 +379,12 @@ const ChatMessage = React.memo(
231
379
  <Box
232
380
  ref={ref}
233
381
  as="div"
234
- css={{ mb: '$10', pr: '$10', mt: '$8', '&:hover .chat_actions': { opacity: 1 } }}
382
+ css={{
383
+ mb: '$5',
384
+ pr: '$10',
385
+ mt: '$4',
386
+ '&:hover .chat_actions': { opacity: 1 },
387
+ }}
235
388
  style={style}
236
389
  >
237
390
  <Flex
@@ -241,13 +394,23 @@ const ChatMessage = React.memo(
241
394
  flexWrap: 'wrap',
242
395
  // Theme independent color, token should not be used for transparent chat
243
396
  bg: messageType ? (isOverlay ? 'rgba(0, 0, 0, 0.64)' : '$surface_default') : undefined,
244
- r: messageType ? '$1' : undefined,
245
- px: messageType ? '$4' : '$2',
246
- py: messageType ? '$4' : 0,
397
+ r: '$1',
398
+ p: '$1 $2',
247
399
  userSelect: 'none',
400
+ '@md': {
401
+ cursor: 'pointer',
402
+ },
403
+ '&:hover': {
404
+ background: 'linear-gradient(277deg, $surface_default 0%, $surface_dim 60.87%)',
405
+ },
248
406
  }}
249
407
  key={message.time}
250
408
  data-testid="chat_msg"
409
+ onClick={() => {
410
+ if (isMobile) {
411
+ setOpenSheet(true);
412
+ }
413
+ }}
251
414
  >
252
415
  <Text
253
416
  css={{
@@ -291,7 +454,16 @@ const ChatMessage = React.memo(
291
454
  receiver={message.recipientPeer}
292
455
  roles={message.recipientRoles}
293
456
  />
294
- {!isOverlay ? <ChatActions onPin={onPin} showPinAction={showPinAction} /> : null}
457
+
458
+ <ChatActions
459
+ onPin={onPin}
460
+ showPinAction={showPinAction}
461
+ message={message}
462
+ sentByLocalPeer={message.sender === localPeerId}
463
+ isMobile={isMobile}
464
+ openSheet={openSheet}
465
+ setOpenSheet={setOpenSheet}
466
+ />
295
467
  </Text>
296
468
  <Text
297
469
  variant="sm"
@@ -303,7 +475,10 @@ const ChatMessage = React.memo(
303
475
  userSelect: 'all',
304
476
  color: isOverlay ? '#FFF' : '$on_surface_high',
305
477
  }}
306
- onClick={e => e.stopPropagation()}
478
+ onClick={e => {
479
+ e.stopPropagation();
480
+ setOpenSheet(true);
481
+ }}
307
482
  >
308
483
  <AnnotisedMessage message={message.message} />
309
484
  </Text>
@@ -314,7 +489,9 @@ const ChatMessage = React.memo(
314
489
  );
315
490
  const ChatList = React.forwardRef(
316
491
  ({ width, height, setRowHeight, getRowHeight, messages, unreadCount = 0, scrollToBottom }, listRef) => {
317
- const { setPinnedMessage } = useSetPinnedMessage();
492
+ const { setPinnedMessages } = useSetPinnedMessages();
493
+ const pinnedMessages = useHMSStore(selectSessionStore(SESSION_STORE_KEY.PINNED_MESSAGES)) || [];
494
+ const localPeerName = useHMSStore(selectLocalPeerName);
318
495
  useLayoutEffect(() => {
319
496
  if (listRef.current && listRef.current.scrollToItem) {
320
497
  scrollToBottom(1);
@@ -343,7 +520,7 @@ const ChatList = React.forwardRef(
343
520
  unreadCount={unreadCount}
344
521
  isLast={index >= messages.length - 2}
345
522
  scrollToBottom={scrollToBottom}
346
- onPin={() => setPinnedMessage(messages[index])}
523
+ onPin={() => setPinnedMessages(pinnedMessages, messages[index], localPeerName)}
347
524
  />
348
525
  )}
349
526
  </VariableSizeList>
@@ -397,14 +574,27 @@ const VirtualizedChatMessages = React.forwardRef(({ messages, unreadCount = 0, s
397
574
  );
398
575
  });
399
576
 
400
- export const ChatBody = React.forwardRef(({ role, peerId, scrollToBottom }, listRef) => {
577
+ export const ChatBody = React.forwardRef(({ role, peerId, scrollToBottom, blacklistedPeerIDs }, listRef) => {
401
578
  const storeMessageSelector = role
402
579
  ? selectMessagesByRole(role)
403
580
  : peerId
404
581
  ? selectMessagesByPeerID(peerId)
405
582
  : selectHMSMessages;
406
- let messages = useHMSStore(storeMessageSelector);
407
- messages = useMemo(() => messages?.filter(message => message.type === 'chat') || [], [messages]);
583
+ let messages = useHMSStore(storeMessageSelector) || [];
584
+ const blacklistedMessageIDs = useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_MESSAGE_BLACKLIST)) || [];
585
+ const getFilteredMessages = () => {
586
+ const blacklistedMessageIDSet = new Set(blacklistedMessageIDs);
587
+ const blacklistedPeerIDSet = new Set(blacklistedPeerIDs);
588
+ return (
589
+ messages?.filter(
590
+ message =>
591
+ message.type === 'chat' &&
592
+ !blacklistedMessageIDSet.has(message.id) &&
593
+ !blacklistedPeerIDSet.has(message?.senderUserId),
594
+ ) || []
595
+ );
596
+ };
597
+
408
598
  const isMobile = useMedia(cssConfig.media.md);
409
599
  const { elements } = useRoomLayoutConferencingScreen();
410
600
  const unreadCount = useUnreadCount({ role, peerId });
@@ -440,7 +630,7 @@ export const ChatBody = React.forwardRef(({ role, peerId, scrollToBottom }, list
440
630
  return (
441
631
  <Fragment>
442
632
  <VirtualizedChatMessages
443
- messages={messages}
633
+ messages={getFilteredMessages()}
444
634
  scrollToBottom={scrollToBottom}
445
635
  unreadCount={unreadCount}
446
636
  ref={listRef}
@@ -2,9 +2,10 @@ import React, { ReactNode, useCallback, useEffect, useRef, useState } from 'reac
2
2
  import { useMedia } from 'react-use';
3
3
  import data from '@emoji-mart/data';
4
4
  import Picker from '@emoji-mart/react';
5
- import { useHMSActions } from '@100mslive/react-sdk';
6
- import { EmojiIcon, SendIcon } from '@100mslive/react-icons';
7
- import { Box, config as cssConfig, Flex, IconButton as BaseIconButton, Popover, styled } from '../../..';
5
+ import { selectLocalPeer, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
6
+ import { EmojiIcon, PauseCircleIcon, SendIcon, VerticalMenuIcon } from '@100mslive/react-icons';
7
+ import { Box, config as cssConfig, Flex, IconButton as BaseIconButton, Popover, styled, Text } from '../../..';
8
+ import { IconButton } from '../../../IconButton';
8
9
  // @ts-ignore
9
10
  import { ToastManager } from '../Toast/ToastManager';
10
11
  import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
@@ -13,6 +14,8 @@ import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvid
13
14
  import { useChatDraftMessage } from '../AppData/useChatState';
14
15
  // @ts-ignore
15
16
  import { useEmojiPickerStyles } from './useEmojiPickerStyles';
17
+ // @ts-ignore
18
+ import { SESSION_STORE_KEY } from '../../common/constants';
16
19
 
17
20
  const TextArea = styled('textarea', {
18
21
  width: '100%',
@@ -82,7 +85,10 @@ export const ChatFooter = ({
82
85
  const [draftMessage, setDraftMessage] = useChatDraftMessage();
83
86
  const isMobile = useMedia(cssConfig.media.md);
84
87
  const { elements } = useRoomLayoutConferencingScreen();
88
+ const message_placeholder = elements?.chat?.message_placeholder || 'Send a message';
89
+ const localPeer = useHMSStore(selectLocalPeer);
85
90
  const isOverlayChat = elements?.chat?.is_overlay;
91
+ const canDisableChat = !!elements?.chat?.real_time_controls?.can_disable_chat;
86
92
 
87
93
  const sendMessage = useCallback(async () => {
88
94
  const message = inputRef?.current?.value;
@@ -122,10 +128,54 @@ export const ChatFooter = ({
122
128
  }, [setDraftMessage]);
123
129
 
124
130
  return (
125
- <>
131
+ <Box>
126
132
  {/* {screenType !== 'hls_live_streaming' ? (
127
133
  <ChatSelectorContainer onSelect={onSelect} role={role} peerId={peerId} selection={selection} />
128
134
  ) : null} */}
135
+ {canDisableChat ? (
136
+ <Flex align="center" justify="end" css={{ w: '100%', mb: '$4' }}>
137
+ <Popover.Root>
138
+ <Popover.Trigger asChild>
139
+ <IconButton css={{ border: '1px solid $border_bright' }}>
140
+ <VerticalMenuIcon height="16" width="16" />
141
+ </IconButton>
142
+ </Popover.Trigger>
143
+ <Popover.Portal>
144
+ <Popover.Content
145
+ align="end"
146
+ side="top"
147
+ onClick={() => {
148
+ const chatState = {
149
+ enabled: false,
150
+ updatedBy: {
151
+ peerId: localPeer?.id,
152
+ userId: localPeer?.customerUserId,
153
+ userName: localPeer?.name,
154
+ },
155
+ updatedAt: Date.now(),
156
+ };
157
+ hmsActions.sessionStore.set(SESSION_STORE_KEY.CHAT_STATE, chatState);
158
+ }}
159
+ css={{
160
+ backgroundColor: '$surface_default',
161
+ display: 'flex',
162
+ alignItems: 'center',
163
+ gap: '$4',
164
+ borderRadius: '$1',
165
+ color: '$on_surface_high',
166
+ cursor: 'pointer',
167
+ '&:hover': { backgroundColor: '$surface_dim' },
168
+ }}
169
+ >
170
+ <PauseCircleIcon />
171
+ <Text variant="sm" css={{ fontWeight: '$semiBold' }}>
172
+ Pause Chat
173
+ </Text>
174
+ </Popover.Content>
175
+ </Popover.Portal>
176
+ </Popover.Root>
177
+ </Flex>
178
+ ) : null}
129
179
  <Flex align="center" css={{ gap: '$4', w: '100%' }}>
130
180
  <Flex
131
181
  align="center"
@@ -153,7 +203,7 @@ export const ChatFooter = ({
153
203
  '& ~ .send-msg': { color: '$on_surface_low' },
154
204
  '&::placeholder': { color: '$on_surface_medium' },
155
205
  }}
156
- placeholder="Send a message...."
206
+ placeholder={message_placeholder}
157
207
  ref={inputRef}
158
208
  required
159
209
  autoFocus={!isMobile}
@@ -195,6 +245,6 @@ export const ChatFooter = ({
195
245
  </BaseIconButton>
196
246
  </Flex>
197
247
  </Flex>
198
- </>
248
+ </Box>
199
249
  );
200
250
  };
@@ -0,0 +1,68 @@
1
+ import React, { useCallback } from 'react';
2
+ import { selectLocalPeer, selectSessionStore, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
3
+ import { Button } from '../../../Button';
4
+ import { Box, Flex } from '../../../Layout';
5
+ import { Text } from '../../../Text';
6
+ import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
7
+ import { SESSION_STORE_KEY } from '../../common/constants';
8
+
9
+ export const ChatPaused = () => {
10
+ const hmsActions = useHMSActions();
11
+ const { elements } = useRoomLayoutConferencingScreen();
12
+ const { can_disable_chat } = elements?.chat.real_time_controls || false;
13
+ const { enabled: isChatEnabled = true, updatedBy: chatStateUpdatedBy } =
14
+ useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_STATE)) || {};
15
+
16
+ const localPeer = useHMSStore(selectLocalPeer);
17
+
18
+ const unPauseChat = useCallback(
19
+ async () =>
20
+ await hmsActions.sessionStore.set(SESSION_STORE_KEY.CHAT_STATE, {
21
+ enabled: true,
22
+ updatedBy: { userName: localPeer.name, userId: localPeer?.customerUserId, peerId: localPeer.id },
23
+ updatedAt: Date.now(),
24
+ }),
25
+ [hmsActions, localPeer],
26
+ );
27
+
28
+ return isChatEnabled ? null : (
29
+ <Flex
30
+ align="center"
31
+ justify="between"
32
+ css={{ borderRadius: '$1', bg: '$surface_default', p: '$4 $4 $4 $8', w: '100%' }}
33
+ >
34
+ <Box>
35
+ <Text variant="sm" css={{ fontWeight: '$semiBold', color: '$on_surface_high' }}>
36
+ Chat paused
37
+ </Text>
38
+ <Text
39
+ variant="xs"
40
+ css={{ color: '$on_surface_medium', maxWidth: '100%', overflow: 'hidden', textOverflow: 'ellipsis' }}
41
+ >
42
+ Chat has been paused by {chatStateUpdatedBy?.peerId === localPeer.id ? 'you' : chatStateUpdatedBy?.userName}
43
+ </Text>
44
+ </Box>
45
+ {can_disable_chat ? (
46
+ <Button css={{ fontWeight: '$semiBold', fontSize: '$sm', borderRadius: '$2' }} onClick={unPauseChat}>
47
+ Resume
48
+ </Button>
49
+ ) : (
50
+ <></>
51
+ )}
52
+ </Flex>
53
+ );
54
+ };
55
+
56
+ export const ChatBlocked = () => {
57
+ return (
58
+ <Flex
59
+ align="center"
60
+ justify="between"
61
+ css={{ borderRadius: '$1', bg: '$surface_default', p: '$4 $4 $4 $8', w: '100%' }}
62
+ >
63
+ <Text variant="sm" css={{ color: '$on_surface_medium', textAlign: 'center', w: '100%' }}>
64
+ You've been blocked from sending messages
65
+ </Text>
66
+ </Flex>
67
+ );
68
+ };
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ import { Flex } from '../../../Layout';
3
+ import { Text } from '../../../Text';
4
+
5
+ export const MwebChatOption = ({
6
+ icon,
7
+ text,
8
+ onClick,
9
+ color = '$on_surface_high',
10
+ }: {
11
+ icon: any;
12
+ text: string;
13
+ onClick: () => void | Promise<void>;
14
+ color: string;
15
+ }) => {
16
+ return (
17
+ <Flex align="center" css={{ w: '100%', color, cursor: 'pointer', gap: '$4', p: '$8' }} onClick={onClick}>
18
+ {icon}
19
+ <Text variant="sm" css={{ color, fontWeight: '$semiBold' }}>
20
+ {text}
21
+ </Text>
22
+ </Flex>
23
+ );
24
+ };
@@ -0,0 +1,60 @@
1
+ import React from 'react';
2
+ import { ChevronDownIcon, ChevronUpIcon } from '@100mslive/react-icons';
3
+ import { Box, Flex } from '../../../Layout';
4
+
5
+ export const Navigation = ({
6
+ total,
7
+ index,
8
+ showPrevious,
9
+ showNext,
10
+ isMobile,
11
+ }: {
12
+ total: number;
13
+ index: number;
14
+ showPrevious: () => void;
15
+ showNext: () => void;
16
+ isMobile: boolean;
17
+ }) => {
18
+ const sticksCount = Math.min(3, total);
19
+
20
+ if (total < 2) {
21
+ return null;
22
+ }
23
+
24
+ return isMobile ? (
25
+ <Flex direction="column" css={{ gap: '$1' }}>
26
+ {[...Array(sticksCount)].map((_, i) => (
27
+ <Box
28
+ css={{
29
+ borderLeft: '2px solid',
30
+ height: '$4',
31
+ borderColor: i === index ? '$on_surface_high' : '$on_surface_low',
32
+ }}
33
+ />
34
+ ))}
35
+ </Flex>
36
+ ) : (
37
+ <Flex direction="column" css={{ gap: '$4' }}>
38
+ <Flex
39
+ onClick={showPrevious}
40
+ css={
41
+ index === 0
42
+ ? { cursor: 'not-allowed', color: '$on_surface_low' }
43
+ : { cursor: 'pointer', color: '$on_surface_medium' }
44
+ }
45
+ >
46
+ <ChevronUpIcon height={20} width={20} />
47
+ </Flex>
48
+ <Flex
49
+ onClick={showNext}
50
+ css={
51
+ index === total - 1
52
+ ? { cursor: 'not-allowed', color: '$on_surface_low' }
53
+ : { cursor: 'pointer', color: '$on_surface_medium' }
54
+ }
55
+ >
56
+ <ChevronDownIcon height={20} width={20} />
57
+ </Flex>
58
+ </Flex>
59
+ );
60
+ };