@100mslive/roomkit-react 0.1.12-alpha.0 → 0.1.13-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 (63) hide show
  1. package/dist/{HLSView-J2MIS3H2.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/Theme/stitches.config.d.ts +226 -226
  18. package/dist/{chunk-OYSYEA6R.js → chunk-5DCII2TP.js} +2248 -1269
  19. package/dist/chunk-5DCII2TP.js.map +7 -0
  20. package/dist/index.cjs.js +2943 -1902
  21. package/dist/index.cjs.js.map +4 -4
  22. package/dist/index.js +1 -1
  23. package/dist/meta.cjs.json +819 -313
  24. package/dist/meta.esbuild.json +826 -320
  25. package/package.json +8 -8
  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 +4 -4
  32. package/src/Prebuilt/components/Chat/Chat.jsx +51 -73
  33. package/src/Prebuilt/components/Chat/ChatBody.jsx +219 -48
  34. package/src/Prebuilt/components/Chat/ChatFooter.tsx +50 -6
  35. package/src/Prebuilt/components/Chat/ChatStates.jsx +66 -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 +118 -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/Preview/PreviewJoin.tsx +6 -8
  42. package/src/Prebuilt/components/RoleChangeRequest/RoleChangeRequestModal.tsx +4 -1
  43. package/src/Prebuilt/components/Settings/DeviceSettings.jsx +1 -1
  44. package/src/Prebuilt/components/SidePaneTabs.tsx +3 -2
  45. package/src/Prebuilt/components/Toast/ToastConfig.jsx +20 -0
  46. package/src/Prebuilt/components/VirtualBackground/VBCollection.tsx +4 -4
  47. package/src/Prebuilt/components/VirtualBackground/{VBPicker.jsx → VBPicker.tsx} +27 -18
  48. package/src/Prebuilt/components/VirtualBackground/constants.ts +0 -8
  49. package/src/Prebuilt/components/hooks/useAutoStartStreaming.tsx +10 -1
  50. package/src/Prebuilt/components/hooks/useChatBlacklist.ts +21 -0
  51. package/src/Prebuilt/components/hooks/useSetPinnedMessages.ts +76 -0
  52. package/src/Prebuilt/layouts/SidePane.tsx +1 -1
  53. package/src/Prebuilt/layouts/VideoStreamingSection.tsx +30 -2
  54. package/src/Prebuilt/plugins/whiteboard/PusherCommunicationProvider.js +1 -1
  55. package/src/Prebuilt/plugins/whiteboard/README.md +1 -1
  56. package/src/Prebuilt/primitives/DialogContent.jsx +1 -1
  57. package/src/Prebuilt/provider/roomLayoutProvider/constants/index.ts +12 -1
  58. package/src/Prebuilt/provider/roomLayoutProvider/hooks/useRoomLayoutScreen.ts +5 -1
  59. package/src/Theme/stitches.config.ts +1 -2
  60. package/dist/chunk-OYSYEA6R.js.map +0 -7
  61. package/src/Prebuilt/components/hooks/useSetPinnedMessage.js +0 -38
  62. package/src/Prebuilt/services/tokenService.js +0 -49
  63. /package/dist/{HLSView-J2MIS3H2.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
+ };