@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.
- package/dist/{HLSView-PWVM4OIP.js → HLSView-IENE4HRP.js} +2 -2
- package/dist/Prebuilt/App.d.ts +0 -2
- package/dist/Prebuilt/AppContext.d.ts +0 -2
- package/dist/Prebuilt/common/constants.d.ts +110 -0
- package/dist/Prebuilt/components/AppData/AppData.d.ts +2 -0
- package/dist/Prebuilt/components/Chat/MwebChatOption.d.ts +7 -0
- package/dist/Prebuilt/components/Chat/Navigation.d.ts +8 -0
- package/dist/Prebuilt/components/Chat/PinnedMessage.d.ts +4 -0
- package/dist/Prebuilt/components/Footer/Footer.d.ts +3 -2
- package/dist/Prebuilt/components/Notifications/ChatNotifications.d.ts +2 -0
- package/dist/Prebuilt/components/Preview/PreviewJoin.d.ts +2 -1
- package/dist/Prebuilt/components/VirtualBackground/VBCollection.d.ts +2 -1
- package/dist/Prebuilt/components/VirtualBackground/VBPicker.d.ts +3 -0
- package/dist/Prebuilt/components/VirtualBackground/constants.d.ts +0 -6
- package/dist/Prebuilt/components/hooks/useChatBlacklist.d.ts +4 -0
- package/dist/Prebuilt/components/hooks/useSetPinnedMessages.d.ts +16 -0
- package/dist/Prebuilt/provider/roomLayoutProvider/hooks/useRoomLayoutScreen.d.ts +3 -2
- package/dist/{chunk-WREKGNP6.js → chunk-U4AB6X6M.js} +2178 -1148
- package/dist/chunk-U4AB6X6M.js.map +7 -0
- package/dist/index.cjs.js +2746 -1638
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.js +1 -1
- package/dist/meta.cjs.json +803 -208
- package/dist/meta.esbuild.json +812 -217
- package/package.json +8 -7
- package/src/Prebuilt/App.tsx +3 -14
- package/src/Prebuilt/AppContext.tsx +0 -2
- package/src/Prebuilt/common/{constants.js → constants.ts} +22 -20
- package/src/Prebuilt/components/AppData/{AppData.jsx → AppData.tsx} +8 -22
- package/src/Prebuilt/components/AppData/useUISettings.js +0 -4
- package/src/Prebuilt/components/AuthToken.jsx +15 -5
- package/src/Prebuilt/components/Chat/Chat.jsx +49 -73
- package/src/Prebuilt/components/Chat/ChatBody.jsx +241 -51
- package/src/Prebuilt/components/Chat/ChatFooter.tsx +56 -6
- package/src/Prebuilt/components/Chat/ChatStates.jsx +68 -0
- package/src/Prebuilt/components/Chat/MwebChatOption.tsx +24 -0
- package/src/Prebuilt/components/Chat/Navigation.tsx +60 -0
- package/src/Prebuilt/components/Chat/PinnedMessage.tsx +116 -0
- package/src/Prebuilt/components/Footer/Footer.tsx +4 -7
- package/src/Prebuilt/components/Header/common.jsx +1 -1
- package/src/Prebuilt/components/Notifications/ChatNotifications.tsx +34 -0
- package/src/Prebuilt/components/Notifications/Notifications.tsx +2 -0
- package/src/Prebuilt/components/Polls/Voting/QuestionCard.jsx +6 -4
- package/src/Prebuilt/components/Polls/Voting/TimedVoting.jsx +3 -2
- package/src/Prebuilt/components/Polls/common/VoteCount.jsx +6 -4
- package/src/Prebuilt/components/Preview/PreviewJoin.tsx +7 -9
- package/src/Prebuilt/components/RoleChangeRequest/RoleChangeRequestModal.tsx +4 -1
- package/src/Prebuilt/components/Settings/DeviceSettings.jsx +1 -1
- package/src/Prebuilt/components/SidePaneTabs.tsx +3 -2
- package/src/Prebuilt/components/Toast/ToastConfig.jsx +1 -0
- package/src/Prebuilt/components/VirtualBackground/VBCollection.tsx +4 -4
- package/src/Prebuilt/components/VirtualBackground/{VBPicker.jsx → VBPicker.tsx} +64 -44
- package/src/Prebuilt/components/VirtualBackground/constants.ts +0 -8
- package/src/Prebuilt/components/hooks/useAutoStartStreaming.tsx +10 -1
- package/src/Prebuilt/components/hooks/useChatBlacklist.ts +21 -0
- package/src/Prebuilt/components/hooks/useSetPinnedMessages.ts +75 -0
- package/src/Prebuilt/components/hooks/useUserPreferences.jsx +1 -0
- package/src/Prebuilt/layouts/SidePane.tsx +1 -1
- package/src/Prebuilt/layouts/VideoStreamingSection.tsx +16 -8
- package/src/Prebuilt/plugins/whiteboard/PusherCommunicationProvider.js +1 -1
- package/src/Prebuilt/plugins/whiteboard/README.md +1 -1
- package/src/Prebuilt/primitives/DialogContent.jsx +1 -1
- package/src/Prebuilt/provider/roomLayoutProvider/constants/index.ts +12 -1
- package/src/Prebuilt/provider/roomLayoutProvider/hooks/useRoomLayoutScreen.ts +5 -1
- package/dist/chunk-WREKGNP6.js.map +0 -7
- package/src/Prebuilt/components/hooks/useSetPinnedMessage.js +0 -38
- package/src/Prebuilt/services/tokenService.js +0 -49
- /package/dist/{HLSView-PWVM4OIP.js.map → HLSView-IENE4HRP.js.map} +0 -0
@@ -1,4 +1,4 @@
|
|
1
|
-
import React, { Fragment, useCallback, useEffect, useLayoutEffect,
|
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 {
|
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
|
-
|
133
|
-
|
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
|
-
<
|
139
|
-
|
140
|
-
|
141
|
-
|
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
|
-
|
144
|
-
</
|
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
|
-
|
152
|
-
<
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
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="
|
160
|
-
onClick={
|
161
|
-
|
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
|
-
|
175
|
-
<Text variant="sm" css={{ ml: '$4' }}>
|
176
|
-
|
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={{
|
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:
|
245
|
-
|
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
|
-
|
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 =>
|
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 {
|
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={() =>
|
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
|
-
|
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={
|
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=
|
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
|
+
};
|