@100mslive/roomkit-react 0.1.12 → 0.1.13-alpha.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. package/dist/{HLSView-PWVM4OIP.js → HLSView-AIPLCDXY.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/Preview/PreviewJoin.d.ts +2 -1
  11. package/dist/Prebuilt/components/VirtualBackground/VBCollection.d.ts +2 -1
  12. package/dist/Prebuilt/components/VirtualBackground/VBPicker.d.ts +3 -0
  13. package/dist/Prebuilt/components/VirtualBackground/constants.d.ts +0 -6
  14. package/dist/Prebuilt/components/hooks/useChatBlacklist.d.ts +4 -0
  15. package/dist/Prebuilt/components/hooks/useSetPinnedMessages.d.ts +16 -0
  16. package/dist/Prebuilt/provider/roomLayoutProvider/hooks/useRoomLayoutScreen.d.ts +3 -2
  17. package/dist/{chunk-WREKGNP6.js → chunk-5DCII2TP.js} +2212 -1216
  18. package/dist/chunk-5DCII2TP.js.map +7 -0
  19. package/dist/index.cjs.js +2906 -1848
  20. package/dist/index.cjs.js.map +4 -4
  21. package/dist/index.js +1 -1
  22. package/dist/meta.cjs.json +802 -286
  23. package/dist/meta.esbuild.json +810 -294
  24. package/package.json +8 -7
  25. package/src/Prebuilt/App.tsx +3 -14
  26. package/src/Prebuilt/AppContext.tsx +0 -2
  27. package/src/Prebuilt/common/{constants.js → constants.ts} +22 -20
  28. package/src/Prebuilt/components/AppData/{AppData.jsx → AppData.tsx} +8 -22
  29. package/src/Prebuilt/components/AppData/useUISettings.js +0 -4
  30. package/src/Prebuilt/components/AuthToken.jsx +4 -4
  31. package/src/Prebuilt/components/Chat/Chat.jsx +51 -73
  32. package/src/Prebuilt/components/Chat/ChatBody.jsx +219 -48
  33. package/src/Prebuilt/components/Chat/ChatFooter.tsx +50 -6
  34. package/src/Prebuilt/components/Chat/ChatStates.jsx +66 -0
  35. package/src/Prebuilt/components/Chat/MwebChatOption.tsx +24 -0
  36. package/src/Prebuilt/components/Chat/Navigation.tsx +60 -0
  37. package/src/Prebuilt/components/Chat/PinnedMessage.tsx +118 -0
  38. package/src/Prebuilt/components/Footer/Footer.tsx +4 -7
  39. package/src/Prebuilt/components/Header/common.jsx +1 -1
  40. package/src/Prebuilt/components/Preview/PreviewJoin.tsx +6 -8
  41. package/src/Prebuilt/components/RoleChangeRequest/RoleChangeRequestModal.tsx +4 -1
  42. package/src/Prebuilt/components/Settings/DeviceSettings.jsx +1 -1
  43. package/src/Prebuilt/components/SidePaneTabs.tsx +3 -2
  44. package/src/Prebuilt/components/Toast/ToastConfig.jsx +20 -0
  45. package/src/Prebuilt/components/VirtualBackground/VBCollection.tsx +4 -4
  46. package/src/Prebuilt/components/VirtualBackground/{VBPicker.jsx → VBPicker.tsx} +27 -18
  47. package/src/Prebuilt/components/VirtualBackground/constants.ts +0 -8
  48. package/src/Prebuilt/components/hooks/useAutoStartStreaming.tsx +10 -1
  49. package/src/Prebuilt/components/hooks/useChatBlacklist.ts +21 -0
  50. package/src/Prebuilt/components/hooks/useSetPinnedMessages.ts +76 -0
  51. package/src/Prebuilt/layouts/SidePane.tsx +1 -1
  52. package/src/Prebuilt/layouts/VideoStreamingSection.tsx +30 -2
  53. package/src/Prebuilt/plugins/whiteboard/PusherCommunicationProvider.js +1 -1
  54. package/src/Prebuilt/plugins/whiteboard/README.md +1 -1
  55. package/src/Prebuilt/primitives/DialogContent.jsx +1 -1
  56. package/src/Prebuilt/provider/roomLayoutProvider/constants/index.ts +12 -1
  57. package/src/Prebuilt/provider/roomLayoutProvider/hooks/useRoomLayoutScreen.ts +5 -1
  58. package/dist/chunk-WREKGNP6.js.map +0 -7
  59. package/src/Prebuilt/components/hooks/useSetPinnedMessage.js +0 -38
  60. package/src/Prebuilt/services/tokenService.js +0 -49
  61. /package/dist/{HLSView-PWVM4OIP.js.map → HLSView-AIPLCDXY.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,34 @@ 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' };
28
37
 
29
38
  const formatTime = date => {
30
39
  if (!(date instanceof Date)) {
@@ -127,56 +136,176 @@ const getMessageType = ({ roles, receiver }) => {
127
136
  }
128
137
  return receiver ? 'private' : '';
129
138
  };
130
- const ChatActions = ({ onPin, showPinAction }) => {
139
+ const ChatActions = ({ onPin, showPinAction, message, peerId, sentByLocalPeer, isMobile, openSheet, setOpenSheet }) => {
140
+ const { elements } = useRoomLayoutConferencingScreen();
141
+ const { can_hide_message, can_block_user } = elements?.chat?.real_time_controls || {
142
+ can_hide_message: false,
143
+ can_block_user: false,
144
+ };
131
145
  const [open, setOpen] = useState(false);
132
- if (!showPinAction) {
133
- return null;
146
+ const blacklistedPeerIDs = useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_PEER_BLACKLIST)) || [];
147
+ const { blacklistItem: blacklistMessage } = useChatBlacklist(SESSION_STORE_KEY.CHAT_MESSAGE_BLACKLIST);
148
+
149
+ const blacklistedMessageIDs = useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_MESSAGE_BLACKLIST)) || [];
150
+ const { blacklistItem: blacklistPeer } = useChatBlacklist(SESSION_STORE_KEY.CHAT_PEER_BLACKLIST);
151
+ const { unpinBlacklistedMessages } = useSetPinnedMessages();
152
+
153
+ const pinnedMessages = useHMSStore(selectSessionStore(SESSION_STORE_KEY.PINNED_MESSAGES)) || [];
154
+
155
+ useEffect(() => {
156
+ if (!(blacklistedPeerIDs.length || blacklistedMessageIDs.length)) {
157
+ return;
158
+ }
159
+ const blacklistedMessageIDSet = new Set(blacklistedMessageIDs);
160
+ const blacklistedPeerIDSet = new Set(blacklistedPeerIDs);
161
+
162
+ unpinBlacklistedMessages(pinnedMessages, blacklistedPeerIDSet, blacklistedMessageIDSet);
163
+ }, [blacklistedMessageIDs, blacklistedPeerIDs, pinnedMessages, unpinBlacklistedMessages]);
164
+
165
+ const copyMessageContent = useCallback(() => {
166
+ try {
167
+ navigator?.clipboard.writeText(message.message);
168
+ ToastManager.addToast({
169
+ title: 'Message copied successfully',
170
+ });
171
+ } catch (e) {
172
+ console.log(e);
173
+ ToastManager.addToast({
174
+ title: 'Could not copy message',
175
+ });
176
+ }
177
+ }, [message]);
178
+
179
+ const options = {
180
+ pin: {
181
+ text: 'Pin message',
182
+ icon: <PinIcon style={iconStyle} />,
183
+ onClick: onPin,
184
+ show: showPinAction,
185
+ },
186
+ copy: {
187
+ text: 'Copy text',
188
+ icon: <CopyIcon style={iconStyle} />,
189
+ onClick: copyMessageContent,
190
+ show: true,
191
+ },
192
+ hide: {
193
+ text: 'Hide for everyone',
194
+ icon: <EyeCloseIcon style={iconStyle} />,
195
+ onClick: async () => blacklistMessage(blacklistedPeerIDs, message.id),
196
+ show: can_hide_message,
197
+ },
198
+ block: {
199
+ text: 'Block from chat',
200
+ icon: <CrossCircleIcon style={iconStyle} />,
201
+ onClick: async () => blacklistPeer(blacklistedMessageIDs, peerId),
202
+ color: '$alert_error_default',
203
+ show: can_block_user && !sentByLocalPeer,
204
+ },
205
+ };
206
+
207
+ if (isMobile) {
208
+ return (
209
+ <Sheet.Root open={openSheet} onOpenChange={setOpenSheet}>
210
+ <Sheet.Content css={{ bg: '$surface_default', pb: '$14' }} onClick={() => setOpenSheet(false)}>
211
+ <Sheet.Title
212
+ css={{
213
+ display: 'flex',
214
+ color: '$on_surface_high',
215
+ w: '100%',
216
+ justifyContent: 'space-between',
217
+ mt: '$8',
218
+ fontSize: '$md',
219
+ px: '$10',
220
+ pb: '$8',
221
+ borderBottom: '1px solid $border_bright',
222
+ alignItems: 'center',
223
+ }}
224
+ >
225
+ Message options
226
+ <Sheet.Close css={{ color: '$on_surface_high' }} onClick={() => setOpenSheet(false)}>
227
+ <CrossIcon />
228
+ </Sheet.Close>
229
+ </Sheet.Title>
230
+
231
+ {Object.keys(options).map(optionKey => {
232
+ const option = options[optionKey];
233
+ return option.show ? (
234
+ <MwebChatOption
235
+ key={optionKey}
236
+ text={option.text}
237
+ icon={option.icon}
238
+ onClick={option.onClick}
239
+ color={option?.color}
240
+ />
241
+ ) : null;
242
+ })}
243
+ </Sheet.Content>
244
+ </Sheet.Root>
245
+ );
134
246
  }
135
247
 
136
248
  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 />
142
- </Tooltip>
143
- </IconButton>
144
- </Dropdown.Trigger>
249
+ <Dropdown.Root open={open} onOpenChange={setOpen} css={{ '@md': { display: 'none' } }}>
250
+ <Flex
251
+ className="chat_actions"
252
+ css={{
253
+ background: '$surface_bright',
254
+ borderRadius: '$1',
255
+ p: '$2',
256
+ opacity: open ? 1 : 0,
257
+ '@md': { opacity: 1 },
258
+ }}
259
+ >
260
+ {options.pin.show ? (
261
+ <IconButton data-testid="pin_message_btn" onClick={options.pin.onClick}>
262
+ {options.pin.icon}
263
+ </IconButton>
264
+ ) : null}
265
+
266
+ {options.copy.show ? (
267
+ <IconButton onClick={options.copy.onClick} data-testid="copy_message_btn">
268
+ <CopyIcon style={iconStyle} />
269
+ </IconButton>
270
+ ) : null}
271
+
272
+ {options.block.show || options.hide.show ? (
273
+ <Dropdown.Trigger asChild>
274
+ <IconButton>
275
+ <Tooltip title="More options">
276
+ <VerticalMenuIcon style={iconStyle} />
277
+ </Tooltip>
278
+ </IconButton>
279
+ </Dropdown.Trigger>
280
+ ) : null}
281
+ </Flex>
145
282
  <Dropdown.Portal>
146
283
  <Dropdown.Content
147
284
  sideOffset={5}
148
285
  align="end"
149
286
  css={{ width: '$48', backgroundColor: '$surface_bright', py: '$0', border: '1px solid $border_bright' }}
150
287
  >
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 ? (
288
+ {options.hide.show ? (
289
+ <Dropdown.Item data-testid="hide_message_btn" onClick={options.hide.onClick}>
290
+ {options.hide.icon}
291
+ <Text variant="sm" css={{ ml: '$4', fontWeight: '$semiBold' }}>
292
+ {options.hide.text}
293
+ </Text>
294
+ </Dropdown.Item>
295
+ ) : null}
296
+
297
+ {options.block.show ? (
158
298
  <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
- }}
299
+ data-testid="block_peer_btn"
300
+ onClick={options.block.onClick}
301
+ css={{ color: options.block.color }}
173
302
  >
174
- <CopyIcon />
175
- <Text variant="sm" css={{ ml: '$4' }}>
176
- Copy Message
303
+ {options.block.icon}
304
+ <Text variant="sm" css={{ ml: '$4', color: 'inherit', fontWeight: '$semiBold' }}>
305
+ {options.block.text}
177
306
  </Text>
178
307
  </Dropdown.Item>
179
- ) : null} */}
308
+ ) : null}
180
309
  </Dropdown.Content>
181
310
  </Dropdown.Portal>
182
311
  </Dropdown.Root>
@@ -212,6 +341,7 @@ const ChatMessage = React.memo(
212
341
  roles: message.recipientRoles,
213
342
  receiver: message.recipientPeer,
214
343
  });
344
+ const [openSheet, setOpenSheet] = useState(false);
215
345
  // show pin action only if peer has remove others permission and the message is of broadcast type
216
346
  const showPinAction = permissions.removeOthers && !messageType && elements?.chat?.allow_pinning_messages;
217
347
 
@@ -231,7 +361,12 @@ const ChatMessage = React.memo(
231
361
  <Box
232
362
  ref={ref}
233
363
  as="div"
234
- css={{ mb: '$10', pr: '$10', mt: '$8', '&:hover .chat_actions': { opacity: 1 } }}
364
+ css={{
365
+ mb: '$10',
366
+ pr: '$10',
367
+ mt: '$8',
368
+ '&:hover .chat_actions': { opacity: 1 },
369
+ }}
235
370
  style={style}
236
371
  >
237
372
  <Flex
@@ -245,9 +380,17 @@ const ChatMessage = React.memo(
245
380
  px: messageType ? '$4' : '$2',
246
381
  py: messageType ? '$4' : 0,
247
382
  userSelect: 'none',
383
+ '@md': {
384
+ cursor: 'pointer',
385
+ },
248
386
  }}
249
387
  key={message.time}
250
388
  data-testid="chat_msg"
389
+ onClick={() => {
390
+ if (isMobile) {
391
+ setOpenSheet(true);
392
+ }
393
+ }}
251
394
  >
252
395
  <Text
253
396
  css={{
@@ -291,7 +434,17 @@ const ChatMessage = React.memo(
291
434
  receiver={message.recipientPeer}
292
435
  roles={message.recipientRoles}
293
436
  />
294
- {!isOverlay ? <ChatActions onPin={onPin} showPinAction={showPinAction} /> : null}
437
+
438
+ <ChatActions
439
+ onPin={onPin}
440
+ showPinAction={showPinAction}
441
+ message={message}
442
+ peerId={message.sender}
443
+ sentByLocalPeer={message.sender === localPeerId}
444
+ isMobile={isMobile}
445
+ openSheet={openSheet}
446
+ setOpenSheet={setOpenSheet}
447
+ />
295
448
  </Text>
296
449
  <Text
297
450
  variant="sm"
@@ -303,7 +456,10 @@ const ChatMessage = React.memo(
303
456
  userSelect: 'all',
304
457
  color: isOverlay ? '#FFF' : '$on_surface_high',
305
458
  }}
306
- onClick={e => e.stopPropagation()}
459
+ onClick={e => {
460
+ e.stopPropagation();
461
+ setOpenSheet(true);
462
+ }}
307
463
  >
308
464
  <AnnotisedMessage message={message.message} />
309
465
  </Text>
@@ -314,7 +470,9 @@ const ChatMessage = React.memo(
314
470
  );
315
471
  const ChatList = React.forwardRef(
316
472
  ({ width, height, setRowHeight, getRowHeight, messages, unreadCount = 0, scrollToBottom }, listRef) => {
317
- const { setPinnedMessage } = useSetPinnedMessage();
473
+ const { setPinnedMessages } = useSetPinnedMessages();
474
+ const pinnedMessages = useHMSStore(selectSessionStore(SESSION_STORE_KEY.PINNED_MESSAGES)) || [];
475
+ const localPeerName = useHMSStore(selectLocalPeerName);
318
476
  useLayoutEffect(() => {
319
477
  if (listRef.current && listRef.current.scrollToItem) {
320
478
  scrollToBottom(1);
@@ -343,7 +501,7 @@ const ChatList = React.forwardRef(
343
501
  unreadCount={unreadCount}
344
502
  isLast={index >= messages.length - 2}
345
503
  scrollToBottom={scrollToBottom}
346
- onPin={() => setPinnedMessage(messages[index])}
504
+ onPin={() => setPinnedMessages(pinnedMessages, messages[index], localPeerName)}
347
505
  />
348
506
  )}
349
507
  </VariableSizeList>
@@ -397,14 +555,27 @@ const VirtualizedChatMessages = React.forwardRef(({ messages, unreadCount = 0, s
397
555
  );
398
556
  });
399
557
 
400
- export const ChatBody = React.forwardRef(({ role, peerId, scrollToBottom }, listRef) => {
558
+ export const ChatBody = React.forwardRef(({ role, peerId, scrollToBottom, blacklistedPeerIDs }, listRef) => {
401
559
  const storeMessageSelector = role
402
560
  ? selectMessagesByRole(role)
403
561
  : peerId
404
562
  ? selectMessagesByPeerID(peerId)
405
563
  : selectHMSMessages;
406
- let messages = useHMSStore(storeMessageSelector);
407
- messages = useMemo(() => messages?.filter(message => message.type === 'chat') || [], [messages]);
564
+ let messages = useHMSStore(storeMessageSelector) || [];
565
+ const blacklistedMessageIDs = useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_MESSAGE_BLACKLIST)) || [];
566
+ const getFilteredMessages = () => {
567
+ const blacklistedMessageIDSet = new Set(blacklistedMessageIDs);
568
+ const blacklistedPeerIDSet = new Set(blacklistedPeerIDs);
569
+ return (
570
+ messages?.filter(
571
+ message =>
572
+ message.type === 'chat' &&
573
+ !blacklistedMessageIDSet.has(message.id) &&
574
+ !blacklistedPeerIDSet.has(message.sender),
575
+ ) || []
576
+ );
577
+ };
578
+
408
579
  const isMobile = useMedia(cssConfig.media.md);
409
580
  const { elements } = useRoomLayoutConferencingScreen();
410
581
  const unreadCount = useUnreadCount({ role, peerId });
@@ -440,7 +611,7 @@ export const ChatBody = React.forwardRef(({ role, peerId, scrollToBottom }, list
440
611
  return (
441
612
  <Fragment>
442
613
  <VirtualizedChatMessages
443
- messages={messages}
614
+ messages={getFilteredMessages()}
444
615
  scrollToBottom={scrollToBottom}
445
616
  unreadCount={unreadCount}
446
617
  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 { selectLocalPeerName, 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 localPeerName = useHMSStore(selectLocalPeerName);
85
90
  const isOverlayChat = elements?.chat?.is_overlay;
91
+ const can_disable_chat = !!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,48 @@ 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
+ {can_disable_chat ? (
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
+ hmsActions.sessionStore.set(SESSION_STORE_KEY.CHAT_STATE, {
149
+ enabled: false,
150
+ updatedBy: localPeerName,
151
+ });
152
+ }}
153
+ css={{
154
+ backgroundColor: '$surface_default',
155
+ display: 'flex',
156
+ alignItems: 'center',
157
+ gap: '$4',
158
+ borderRadius: '$1',
159
+ color: '$on_surface_high',
160
+ cursor: 'pointer',
161
+ '&:hover': { backgroundColor: '$surface_dim' },
162
+ }}
163
+ >
164
+ <PauseCircleIcon />
165
+ <Text variant="sm" css={{ fontWeight: '$semiBold' }}>
166
+ Pause Chat
167
+ </Text>
168
+ </Popover.Content>
169
+ </Popover.Portal>
170
+ </Popover.Root>
171
+ </Flex>
172
+ ) : null}
129
173
  <Flex align="center" css={{ gap: '$4', w: '100%' }}>
130
174
  <Flex
131
175
  align="center"
@@ -153,7 +197,7 @@ export const ChatFooter = ({
153
197
  '& ~ .send-msg': { color: '$on_surface_low' },
154
198
  '&::placeholder': { color: '$on_surface_medium' },
155
199
  }}
156
- placeholder="Send a message...."
200
+ placeholder={message_placeholder}
157
201
  ref={inputRef}
158
202
  required
159
203
  autoFocus={!isMobile}
@@ -195,6 +239,6 @@ export const ChatFooter = ({
195
239
  </BaseIconButton>
196
240
  </Flex>
197
241
  </Flex>
198
- </>
242
+ </Box>
199
243
  );
200
244
  };
@@ -0,0 +1,66 @@
1
+ import React, { useCallback } from 'react';
2
+ import { selectLocalPeerName, 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
+ const localPeerName = useHMSStore(selectLocalPeerName);
16
+
17
+ const unPauseChat = useCallback(
18
+ async () =>
19
+ await hmsActions.sessionStore.set(SESSION_STORE_KEY.CHAT_STATE, {
20
+ enabled: true,
21
+ updatedBy: localPeerName,
22
+ }),
23
+ [hmsActions, localPeerName],
24
+ );
25
+
26
+ return isChatEnabled ? null : (
27
+ <Flex
28
+ align="center"
29
+ justify="between"
30
+ css={{ borderRadius: '$1', bg: '$surface_default', p: '$4 $4 $4 $8', w: '100%' }}
31
+ >
32
+ <Box>
33
+ <Text variant="sm" css={{ fontWeight: '$semiBold', color: '$on_surface_high' }}>
34
+ Chat paused
35
+ </Text>
36
+ <Text
37
+ variant="xs"
38
+ css={{ color: '$on_surface_medium', maxWidth: '100%', overflow: 'hidden', textOverflow: 'ellipsis' }}
39
+ >
40
+ Chat has been paused by {chatStateUpdatedBy}
41
+ </Text>
42
+ </Box>
43
+ {can_disable_chat ? (
44
+ <Button css={{ fontWeight: '$semiBold', fontSize: '$sm', borderRadius: '$2' }} onClick={unPauseChat}>
45
+ Resume
46
+ </Button>
47
+ ) : (
48
+ <></>
49
+ )}
50
+ </Flex>
51
+ );
52
+ };
53
+
54
+ export const ChatBlocked = () => {
55
+ return (
56
+ <Flex
57
+ align="center"
58
+ justify="between"
59
+ css={{ borderRadius: '$1', bg: '$surface_default', p: '$4 $4 $4 $8', w: '100%' }}
60
+ >
61
+ <Text variant="sm" css={{ color: '$on_surface_medium', textAlign: 'center', w: '100%' }}>
62
+ You've been blocked from sending messages
63
+ </Text>
64
+ </Flex>
65
+ );
66
+ };
@@ -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: '$8',
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
+ };