@100mslive/roomkit-react 0.1.18-alpha.1 → 0.1.18
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/{HLSView-MR7RYQZB.js → HLSView-XU2WQ4VV.js} +2 -2
- package/dist/Prebuilt/components/Chat/Chat.d.ts +2 -0
- package/dist/Prebuilt/components/Chat/ChatActions.d.ts +12 -0
- package/dist/Prebuilt/components/Chat/ChatBody.d.ts +8 -0
- package/dist/Prebuilt/components/Chat/ChatFooter.d.ts +2 -2
- package/dist/Prebuilt/components/Chat/ChatStates.d.ts +1 -1
- package/dist/Prebuilt/components/Chat/MwebChatOption.d.ts +1 -1
- package/dist/Prebuilt/components/Chat/PinnedMessage.d.ts +1 -3
- package/dist/Prebuilt/components/hooks/useChatBlacklist.d.ts +1 -0
- package/dist/Prebuilt/components/hooks/useSetPinnedMessages.d.ts +3 -10
- package/dist/Prebuilt/components/hooks/useUnreadPollQuizPresent.d.ts +5 -0
- package/dist/{chunk-WFHOR7AP.js → chunk-2FX6MFDM.js} +4535 -4495
- package/dist/chunk-2FX6MFDM.js.map +7 -0
- package/dist/index.cjs.js +5110 -5055
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.js +1 -1
- package/dist/meta.cjs.json +341 -223
- package/dist/meta.esbuild.json +351 -233
- package/package.json +6 -6
- package/src/Prebuilt/components/Chat/Chat.tsx +108 -0
- package/src/Prebuilt/components/Chat/ChatActions.tsx +295 -0
- package/src/Prebuilt/components/Chat/ChatBody.tsx +444 -0
- package/src/Prebuilt/components/Chat/ChatFooter.tsx +9 -3
- package/src/Prebuilt/components/Chat/ChatStates.tsx +5 -0
- package/src/Prebuilt/components/Chat/MwebChatOption.tsx +1 -1
- package/src/Prebuilt/components/Chat/PinnedMessage.tsx +4 -2
- package/src/Prebuilt/components/Footer/PollsToggle.tsx +12 -3
- package/src/Prebuilt/components/MoreSettings/SplitComponents/MwebOptions.tsx +5 -1
- package/src/Prebuilt/components/Polls/common/MultipleChoiceOptions.jsx +1 -1
- package/src/Prebuilt/components/SidePaneTabs.tsx +33 -11
- package/src/Prebuilt/components/hooks/useChatBlacklist.ts +7 -1
- package/src/Prebuilt/components/hooks/useSetPinnedMessages.ts +19 -11
- package/src/Prebuilt/components/hooks/useUnreadPollQuizPresent.tsx +20 -0
- package/dist/chunk-WFHOR7AP.js.map +0 -7
- package/src/Prebuilt/components/Chat/Chat.jsx +0 -124
- package/src/Prebuilt/components/Chat/ChatBody.jsx +0 -726
- /package/dist/{HLSView-MR7RYQZB.js.map → HLSView-XU2WQ4VV.js.map} +0 -0
@@ -0,0 +1,444 @@
|
|
1
|
+
import React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
2
|
+
import { useMedia } from 'react-use';
|
3
|
+
import AutoSizer from 'react-virtualized-auto-sizer';
|
4
|
+
import { VariableSizeList } from 'react-window';
|
5
|
+
import {
|
6
|
+
HMSMessage,
|
7
|
+
HMSPeerID,
|
8
|
+
HMSRoleName,
|
9
|
+
selectHMSMessages,
|
10
|
+
selectHMSMessagesCount,
|
11
|
+
selectLocalPeerID,
|
12
|
+
selectLocalPeerRoleName,
|
13
|
+
selectPeerNameByID,
|
14
|
+
selectSessionStore,
|
15
|
+
useHMSActions,
|
16
|
+
useHMSStore,
|
17
|
+
useHMSVanillaStore,
|
18
|
+
} from '@100mslive/react-sdk';
|
19
|
+
import { Box, Flex } from '../../../Layout';
|
20
|
+
import { Text } from '../../../Text';
|
21
|
+
import { config as cssConfig, styled } from '../../../Theme';
|
22
|
+
import { Tooltip } from '../../../Tooltip';
|
23
|
+
// @ts-ignore
|
24
|
+
import emptyChat from '../../images/empty-chat.svg';
|
25
|
+
import { ChatActions } from './ChatActions';
|
26
|
+
import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
|
27
|
+
// @ts-ignore: No implicit Any
|
28
|
+
import { useSetSubscribedChatSelector } from '../AppData/useUISettings';
|
29
|
+
import { CHAT_SELECTOR, SESSION_STORE_KEY } from '../../common/constants';
|
30
|
+
|
31
|
+
const formatTime = (date: Date) => {
|
32
|
+
if (!(date instanceof Date)) {
|
33
|
+
return '';
|
34
|
+
}
|
35
|
+
const hours = date.getHours();
|
36
|
+
const minutes = date.getMinutes();
|
37
|
+
const suffix = hours > 11 ? 'PM' : 'AM';
|
38
|
+
return `${hours < 10 ? '0' : ''}${hours}:${minutes < 10 ? '0' : ''}${minutes} ${suffix}`;
|
39
|
+
};
|
40
|
+
|
41
|
+
const rowHeights: Record<number, number> = {};
|
42
|
+
function getRowHeight(index: number) {
|
43
|
+
// 72 will be default row height for any message length
|
44
|
+
// 16 will add margin value as clientHeight don't include margin
|
45
|
+
return rowHeights[index] + 16 || 72;
|
46
|
+
}
|
47
|
+
|
48
|
+
const setRowHeight = (index: number, size: number) => {
|
49
|
+
Object.assign(rowHeights, { [index]: size });
|
50
|
+
};
|
51
|
+
|
52
|
+
const MessageTypeContainer = ({ left, right }: { left?: string; right?: string }) => {
|
53
|
+
return (
|
54
|
+
<Flex
|
55
|
+
align="center"
|
56
|
+
css={{
|
57
|
+
ml: '$2',
|
58
|
+
mr: '$4',
|
59
|
+
gap: '$space$2',
|
60
|
+
}}
|
61
|
+
>
|
62
|
+
{left && (
|
63
|
+
<SenderName
|
64
|
+
variant="xs"
|
65
|
+
as="span"
|
66
|
+
css={{ color: '$on_surface_medium', textTransform: 'capitalize', fontWeight: '$regular' }}
|
67
|
+
>
|
68
|
+
{left}
|
69
|
+
</SenderName>
|
70
|
+
)}
|
71
|
+
{right && (
|
72
|
+
<SenderName
|
73
|
+
as="span"
|
74
|
+
variant="overline"
|
75
|
+
css={{
|
76
|
+
color: '$on_surface_medium',
|
77
|
+
fontWeight: '$regular',
|
78
|
+
}}
|
79
|
+
>
|
80
|
+
{right}
|
81
|
+
</SenderName>
|
82
|
+
)}
|
83
|
+
</Flex>
|
84
|
+
);
|
85
|
+
};
|
86
|
+
|
87
|
+
const MessageType = ({
|
88
|
+
roles,
|
89
|
+
hasCurrentUserSent,
|
90
|
+
receiver,
|
91
|
+
}: {
|
92
|
+
roles?: HMSRoleName[];
|
93
|
+
hasCurrentUserSent: boolean;
|
94
|
+
receiver?: HMSPeerID;
|
95
|
+
}) => {
|
96
|
+
const peerName = useHMSStore(selectPeerNameByID(receiver));
|
97
|
+
const localPeerRoleName = useHMSStore(selectLocalPeerRoleName);
|
98
|
+
if (receiver) {
|
99
|
+
return (
|
100
|
+
<MessageTypeContainer left={hasCurrentUserSent ? `${peerName ? `to ${peerName}` : ''}` : 'to You'} right="(DM)" />
|
101
|
+
);
|
102
|
+
}
|
103
|
+
|
104
|
+
if (roles && roles.length) {
|
105
|
+
return <MessageTypeContainer left={`to ${hasCurrentUserSent ? roles[0] : localPeerRoleName}`} right="(Group)" />;
|
106
|
+
}
|
107
|
+
return null;
|
108
|
+
};
|
109
|
+
|
110
|
+
const URL_REGEX =
|
111
|
+
/^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/;
|
112
|
+
|
113
|
+
const Link = styled('a', {
|
114
|
+
color: '$primary_default',
|
115
|
+
wordBreak: 'break-word',
|
116
|
+
'&:hover': {
|
117
|
+
textDecoration: 'underline',
|
118
|
+
},
|
119
|
+
});
|
120
|
+
|
121
|
+
export const AnnotisedMessage = ({ message }: { message: string }) => {
|
122
|
+
if (!message) {
|
123
|
+
return <Fragment />;
|
124
|
+
}
|
125
|
+
|
126
|
+
return (
|
127
|
+
<Fragment>
|
128
|
+
{message
|
129
|
+
.trim()
|
130
|
+
.split(/(\s)/)
|
131
|
+
.map(part =>
|
132
|
+
URL_REGEX.test(part) ? (
|
133
|
+
<Link href={part} key={part} target="_blank" rel="noopener noreferrer">
|
134
|
+
{part}
|
135
|
+
</Link>
|
136
|
+
) : (
|
137
|
+
part
|
138
|
+
),
|
139
|
+
)}
|
140
|
+
</Fragment>
|
141
|
+
);
|
142
|
+
};
|
143
|
+
|
144
|
+
const getMessageType = ({ roles, receiver }: { roles?: HMSRoleName[]; receiver?: HMSPeerID }) => {
|
145
|
+
if (roles && roles.length > 0) {
|
146
|
+
return 'role';
|
147
|
+
}
|
148
|
+
return receiver ? 'private' : '';
|
149
|
+
};
|
150
|
+
const SenderName = styled(Text, {
|
151
|
+
overflow: 'hidden',
|
152
|
+
textOverflow: 'ellipsis',
|
153
|
+
whiteSpace: 'nowrap',
|
154
|
+
maxWidth: '14ch',
|
155
|
+
minWidth: 0,
|
156
|
+
color: '$on_surface_high',
|
157
|
+
fontWeight: '$semiBold',
|
158
|
+
});
|
159
|
+
|
160
|
+
const ChatMessage = React.memo(
|
161
|
+
({ index, style = {}, message }: { message: HMSMessage; index: number; style: React.CSSProperties }) => {
|
162
|
+
const { elements } = useRoomLayoutConferencingScreen();
|
163
|
+
const rowRef = useRef<HTMLDivElement | null>(null);
|
164
|
+
const isMobile = useMedia(cssConfig.media.md);
|
165
|
+
const isPrivateChatEnabled = !!elements?.chat?.private_chat_enabled;
|
166
|
+
const roleWhiteList = elements?.chat?.roles_whitelist || [];
|
167
|
+
const isOverlay = elements?.chat?.is_overlay && isMobile;
|
168
|
+
const localPeerId = useHMSStore(selectLocalPeerID);
|
169
|
+
const [selectedRole, setRoleSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.ROLE);
|
170
|
+
const [selectedPeer, setPeerSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.PEER);
|
171
|
+
const messageType = getMessageType({
|
172
|
+
roles: message.recipientRoles,
|
173
|
+
receiver: message.recipientPeer,
|
174
|
+
});
|
175
|
+
const [openSheet, setOpenSheet] = useState(false);
|
176
|
+
const showPinAction = !!elements?.chat?.allow_pinning_messages;
|
177
|
+
let showReply = false;
|
178
|
+
if (message.recipientRoles && roleWhiteList.includes(message.recipientRoles[0])) {
|
179
|
+
showReply = true;
|
180
|
+
} else if (message.sender !== selectedPeer.id && message.sender !== localPeerId && isPrivateChatEnabled) {
|
181
|
+
showReply = true;
|
182
|
+
}
|
183
|
+
|
184
|
+
useEffect(() => {
|
185
|
+
if (rowRef.current) {
|
186
|
+
setRowHeight(index, rowRef.current.clientHeight);
|
187
|
+
}
|
188
|
+
}, [index]);
|
189
|
+
|
190
|
+
return (
|
191
|
+
<Box
|
192
|
+
css={{
|
193
|
+
mb: '$5',
|
194
|
+
pr: '$10',
|
195
|
+
mt: '$4',
|
196
|
+
'&:not(:hover} .chat_actions': { display: 'none' },
|
197
|
+
'&:hover .chat_actions': { display: 'flex', opacity: 1 },
|
198
|
+
}}
|
199
|
+
style={style}
|
200
|
+
>
|
201
|
+
<Flex
|
202
|
+
ref={rowRef}
|
203
|
+
align="center"
|
204
|
+
css={{
|
205
|
+
flexWrap: 'wrap',
|
206
|
+
position: 'relative',
|
207
|
+
// Theme independent color, token should not be used for transparent chat
|
208
|
+
bg:
|
209
|
+
messageType && !(selectedPeer.id || selectedRole)
|
210
|
+
? isOverlay
|
211
|
+
? 'rgba(0, 0, 0, 0.64)'
|
212
|
+
: '$surface_default'
|
213
|
+
: undefined,
|
214
|
+
r: '$1',
|
215
|
+
p: '$4',
|
216
|
+
userSelect: 'none',
|
217
|
+
'@md': {
|
218
|
+
cursor: 'pointer',
|
219
|
+
},
|
220
|
+
'&:hover': {
|
221
|
+
background: 'linear-gradient(277deg, $surface_default 0%, $surface_dim 60.87%)',
|
222
|
+
},
|
223
|
+
}}
|
224
|
+
data-testid="chat_msg"
|
225
|
+
onClick={() => {
|
226
|
+
if (isMobile) {
|
227
|
+
setOpenSheet(true);
|
228
|
+
}
|
229
|
+
}}
|
230
|
+
>
|
231
|
+
<Text
|
232
|
+
css={{
|
233
|
+
color: isOverlay ? '#FFF' : '$on_surface_high',
|
234
|
+
fontWeight: '$semiBold',
|
235
|
+
display: 'flex',
|
236
|
+
alignItems: 'center',
|
237
|
+
alignSelf: 'stretch',
|
238
|
+
width: '100%',
|
239
|
+
}}
|
240
|
+
as="div"
|
241
|
+
>
|
242
|
+
<Flex align="baseline">
|
243
|
+
{message.senderName === 'You' || !message.senderName ? (
|
244
|
+
<SenderName
|
245
|
+
as="span"
|
246
|
+
variant="sub2"
|
247
|
+
css={{ color: isOverlay ? '#FFF' : '$on_surface_high', fontWeight: '$semiBold' }}
|
248
|
+
>
|
249
|
+
{message.senderName || 'Anonymous'}
|
250
|
+
</SenderName>
|
251
|
+
) : (
|
252
|
+
<Tooltip title={message.senderName} side="top" align="start">
|
253
|
+
<SenderName
|
254
|
+
as="span"
|
255
|
+
variant="sub2"
|
256
|
+
css={{ color: isOverlay ? '#FFF' : '$on_surface_high', fontWeight: '$semiBold' }}
|
257
|
+
>
|
258
|
+
{message.senderName}
|
259
|
+
</SenderName>
|
260
|
+
</Tooltip>
|
261
|
+
)}
|
262
|
+
<MessageType
|
263
|
+
hasCurrentUserSent={message.sender === localPeerId}
|
264
|
+
receiver={message.recipientPeer}
|
265
|
+
roles={message.recipientRoles}
|
266
|
+
/>
|
267
|
+
</Flex>
|
268
|
+
|
269
|
+
{!isOverlay ? (
|
270
|
+
<Text
|
271
|
+
as="span"
|
272
|
+
variant="caption"
|
273
|
+
css={{
|
274
|
+
color: '$on_surface_medium',
|
275
|
+
flexShrink: 0,
|
276
|
+
position: 'absolute',
|
277
|
+
right: 0,
|
278
|
+
zIndex: 1,
|
279
|
+
mr: '$4',
|
280
|
+
p: '$2',
|
281
|
+
}}
|
282
|
+
>
|
283
|
+
{formatTime(message.time)}
|
284
|
+
</Text>
|
285
|
+
) : null}
|
286
|
+
<ChatActions
|
287
|
+
showPinAction={showPinAction}
|
288
|
+
message={message}
|
289
|
+
sentByLocalPeer={message.sender === localPeerId}
|
290
|
+
onReply={() => {
|
291
|
+
if (message.recipientRoles?.length) {
|
292
|
+
setRoleSelector(message.recipientRoles[0]);
|
293
|
+
setPeerSelector({});
|
294
|
+
} else {
|
295
|
+
setRoleSelector('');
|
296
|
+
setPeerSelector({ id: message.sender, name: message.senderName });
|
297
|
+
}
|
298
|
+
}}
|
299
|
+
showReply={showReply}
|
300
|
+
isMobile={isMobile}
|
301
|
+
openSheet={openSheet}
|
302
|
+
setOpenSheet={setOpenSheet}
|
303
|
+
/>
|
304
|
+
</Text>
|
305
|
+
<Text
|
306
|
+
variant="sm"
|
307
|
+
css={{
|
308
|
+
w: '100%',
|
309
|
+
mt: '$2',
|
310
|
+
wordBreak: 'break-word',
|
311
|
+
whiteSpace: 'pre-wrap',
|
312
|
+
userSelect: 'all',
|
313
|
+
color: isOverlay ? '#FFF' : '$on_surface_high',
|
314
|
+
}}
|
315
|
+
onClick={e => {
|
316
|
+
e.stopPropagation();
|
317
|
+
setOpenSheet(true);
|
318
|
+
}}
|
319
|
+
>
|
320
|
+
<AnnotisedMessage message={message.message} />
|
321
|
+
</Text>
|
322
|
+
</Flex>
|
323
|
+
</Box>
|
324
|
+
);
|
325
|
+
},
|
326
|
+
);
|
327
|
+
|
328
|
+
const MessageWrapper = React.memo(
|
329
|
+
({ index, style, data }: { index: number; style: React.CSSProperties; data: HMSMessage[] }) => {
|
330
|
+
return <ChatMessage style={style} index={index} key={data[index].id} message={data[index]} />;
|
331
|
+
},
|
332
|
+
);
|
333
|
+
|
334
|
+
const VirtualizedChatMessages = React.forwardRef<
|
335
|
+
VariableSizeList,
|
336
|
+
{ messages: HMSMessage[]; scrollToBottom: (count: number) => void }
|
337
|
+
>(({ messages, scrollToBottom }, listRef) => {
|
338
|
+
const hmsActions = useHMSActions();
|
339
|
+
const itemKey = useCallback((index: number, data: HMSMessage[]) => {
|
340
|
+
return data[index].id;
|
341
|
+
}, []);
|
342
|
+
useEffect(() => {
|
343
|
+
requestAnimationFrame(() => scrollToBottom(1));
|
344
|
+
}, [scrollToBottom]);
|
345
|
+
return (
|
346
|
+
<Box
|
347
|
+
css={{
|
348
|
+
mr: '-$10',
|
349
|
+
h: '100%',
|
350
|
+
}}
|
351
|
+
>
|
352
|
+
<AutoSizer
|
353
|
+
style={{
|
354
|
+
width: '90%',
|
355
|
+
}}
|
356
|
+
>
|
357
|
+
{({ height, width }: { height: number; width: number }) => (
|
358
|
+
<VariableSizeList
|
359
|
+
ref={listRef}
|
360
|
+
itemCount={messages.length}
|
361
|
+
itemSize={getRowHeight}
|
362
|
+
itemData={messages}
|
363
|
+
width={width}
|
364
|
+
height={height}
|
365
|
+
style={{
|
366
|
+
overflowX: 'hidden',
|
367
|
+
}}
|
368
|
+
itemKey={itemKey}
|
369
|
+
onItemsRendered={({ visibleStartIndex, visibleStopIndex }) => {
|
370
|
+
for (let i = visibleStartIndex; i <= visibleStopIndex; i++) {
|
371
|
+
if (!messages[i].read) {
|
372
|
+
hmsActions.setMessageRead(true, messages[i].id);
|
373
|
+
}
|
374
|
+
}
|
375
|
+
}}
|
376
|
+
>
|
377
|
+
{MessageWrapper}
|
378
|
+
</VariableSizeList>
|
379
|
+
)}
|
380
|
+
</AutoSizer>
|
381
|
+
</Box>
|
382
|
+
);
|
383
|
+
});
|
384
|
+
|
385
|
+
export const ChatBody = React.forwardRef<VariableSizeList, { scrollToBottom: (count: number) => void }>(
|
386
|
+
({ scrollToBottom }: { scrollToBottom: (count: number) => void }, listRef) => {
|
387
|
+
const messages = useHMSStore(selectHMSMessages);
|
388
|
+
const blacklistedMessageIDs = useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_MESSAGE_BLACKLIST));
|
389
|
+
const filteredMessages = useMemo(() => {
|
390
|
+
const blacklistedMessageIDSet = new Set(blacklistedMessageIDs || []);
|
391
|
+
return messages?.filter(message => message.type === 'chat' && !blacklistedMessageIDSet.has(message.id)) || [];
|
392
|
+
}, [blacklistedMessageIDs, messages]);
|
393
|
+
|
394
|
+
const isMobile = useMedia(cssConfig.media.md);
|
395
|
+
const { elements } = useRoomLayoutConferencingScreen();
|
396
|
+
const vanillaStore = useHMSVanillaStore();
|
397
|
+
|
398
|
+
useEffect(() => {
|
399
|
+
const unsubscribe = vanillaStore.subscribe(() => {
|
400
|
+
// @ts-ignore
|
401
|
+
if (!listRef?.current) {
|
402
|
+
return;
|
403
|
+
}
|
404
|
+
// @ts-ignore
|
405
|
+
const outerElement = listRef.current._outerRef;
|
406
|
+
// @ts-ignore
|
407
|
+
if (outerElement.scrollHeight - (listRef.current.state.scrollOffset + outerElement.offsetHeight) <= 10) {
|
408
|
+
scrollToBottom(1);
|
409
|
+
}
|
410
|
+
}, selectHMSMessagesCount);
|
411
|
+
return unsubscribe;
|
412
|
+
}, [vanillaStore, listRef, scrollToBottom]);
|
413
|
+
|
414
|
+
if (filteredMessages.length === 0 && !(isMobile && elements?.chat?.is_overlay)) {
|
415
|
+
return (
|
416
|
+
<Flex
|
417
|
+
css={{
|
418
|
+
width: '100%',
|
419
|
+
flex: '1 1 0',
|
420
|
+
textAlign: 'center',
|
421
|
+
px: '$4',
|
422
|
+
}}
|
423
|
+
align="center"
|
424
|
+
justify="center"
|
425
|
+
>
|
426
|
+
<Box>
|
427
|
+
<img src={emptyChat} alt="Empty Chat" height={132} width={185} style={{ margin: '0 auto' }} />
|
428
|
+
<Text variant="h5" css={{ mt: '$8', c: '$on_surface_high' }}>
|
429
|
+
Start a conversation
|
430
|
+
</Text>
|
431
|
+
<Text
|
432
|
+
variant="sm"
|
433
|
+
css={{ mt: '$4', maxWidth: '80%', textAlign: 'center', mx: 'auto', c: '$on_surface_medium' }}
|
434
|
+
>
|
435
|
+
There are no messages here yet. Start a conversation by sending a message.
|
436
|
+
</Text>
|
437
|
+
</Box>
|
438
|
+
</Flex>
|
439
|
+
);
|
440
|
+
}
|
441
|
+
|
442
|
+
return <VirtualizedChatMessages messages={filteredMessages} ref={listRef} scrollToBottom={scrollToBottom} />;
|
443
|
+
},
|
444
|
+
);
|
@@ -10,11 +10,11 @@ import { IconButton } from '../../../IconButton';
|
|
10
10
|
import { ToastManager } from '../Toast/ToastManager';
|
11
11
|
import { ChatSelectorContainer } from './ChatSelectorContainer';
|
12
12
|
import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
|
13
|
-
// import { ChatSelectorContainer } from './ChatSelectorContainer';
|
14
13
|
// @ts-ignore
|
15
14
|
import { useChatDraftMessage } from '../AppData/useChatState';
|
16
15
|
// @ts-ignore
|
17
16
|
import { useSetSubscribedChatSelector, useSubscribeChatSelector } from '../AppData/useUISettings';
|
17
|
+
import { useIsLocalPeerBlacklisted } from '../hooks/useChatBlacklist';
|
18
18
|
// @ts-ignore
|
19
19
|
import { useEmojiPickerStyles } from './useEmojiPickerStyles';
|
20
20
|
import { useDefaultChatSelection } from '../../common/hooks';
|
@@ -72,7 +72,7 @@ function EmojiPicker({ onSelect }: { onSelect: (emoji: any) => void }) {
|
|
72
72
|
);
|
73
73
|
}
|
74
74
|
|
75
|
-
export const ChatFooter = ({ onSend, children }: { onSend: () => void; children: ReactNode }) => {
|
75
|
+
export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => void; children: ReactNode }) => {
|
76
76
|
const hmsActions = useHMSActions();
|
77
77
|
const inputRef = useRef<HTMLTextAreaElement>(null);
|
78
78
|
const [draftMessage, setDraftMessage] = useChatDraftMessage();
|
@@ -86,6 +86,8 @@ export const ChatFooter = ({ onSend, children }: { onSend: () => void; children:
|
|
86
86
|
const [selectedRole, setRoleSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.ROLE);
|
87
87
|
const defaultSelection = useDefaultChatSelection();
|
88
88
|
const selection = selectedPeer.name || selectedRole || defaultSelection;
|
89
|
+
const isLocalPeerBlacklisted = useIsLocalPeerBlacklisted();
|
90
|
+
|
89
91
|
useEffect(() => {
|
90
92
|
if (!selectedPeer.id && !selectedRole && !['Everyone', ''].includes(defaultSelection)) {
|
91
93
|
setRoleSelector(defaultSelection);
|
@@ -108,7 +110,7 @@ export const ChatFooter = ({ onSend, children }: { onSend: () => void; children:
|
|
108
110
|
}
|
109
111
|
inputRef.current.value = '';
|
110
112
|
setTimeout(() => {
|
111
|
-
onSend();
|
113
|
+
onSend(1);
|
112
114
|
}, 0);
|
113
115
|
} catch (error) {
|
114
116
|
const err = error as HMSException;
|
@@ -132,6 +134,10 @@ export const ChatFooter = ({ onSend, children }: { onSend: () => void; children:
|
|
132
134
|
};
|
133
135
|
}, [setDraftMessage]);
|
134
136
|
|
137
|
+
if (isLocalPeerBlacklisted) {
|
138
|
+
return null;
|
139
|
+
}
|
140
|
+
|
135
141
|
return (
|
136
142
|
<Box>
|
137
143
|
<Flex>
|
@@ -4,6 +4,7 @@ import { Button } from '../../../Button';
|
|
4
4
|
import { Box, Flex } from '../../../Layout';
|
5
5
|
import { Text } from '../../../Text';
|
6
6
|
import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
|
7
|
+
import { useIsLocalPeerBlacklisted } from '../hooks/useChatBlacklist';
|
7
8
|
import { SESSION_STORE_KEY } from '../../common/constants';
|
8
9
|
|
9
10
|
export const ChatPaused = () => {
|
@@ -54,6 +55,10 @@ export const ChatPaused = () => {
|
|
54
55
|
};
|
55
56
|
|
56
57
|
export const ChatBlocked = () => {
|
58
|
+
const isLocalPeerBlacklisted = useIsLocalPeerBlacklisted();
|
59
|
+
if (!isLocalPeerBlacklisted) {
|
60
|
+
return null;
|
61
|
+
}
|
57
62
|
return (
|
58
63
|
<Flex
|
59
64
|
align="center"
|
@@ -11,7 +11,7 @@ export const MwebChatOption = ({
|
|
11
11
|
icon: any;
|
12
12
|
text: string;
|
13
13
|
onClick: () => void | Promise<void>;
|
14
|
-
color
|
14
|
+
color?: string;
|
15
15
|
}) => {
|
16
16
|
return (
|
17
17
|
<Flex align="center" css={{ w: '100%', color, cursor: 'pointer', gap: '$4', p: '$8' }} onClick={onClick}>
|
@@ -11,13 +11,15 @@ import { ArrowNavigation } from './ArrowNavigation';
|
|
11
11
|
import { AnnotisedMessage } from './ChatBody';
|
12
12
|
import { StickIndicator } from './StickIndicator';
|
13
13
|
import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
|
14
|
+
import { useSetPinnedMessages } from '../hooks/useSetPinnedMessages';
|
14
15
|
import { SESSION_STORE_KEY } from '../../common/constants';
|
15
16
|
|
16
17
|
const PINNED_MESSAGE_LENGTH = 75;
|
17
18
|
|
18
|
-
export const PinnedMessage = (
|
19
|
+
export const PinnedMessage = () => {
|
19
20
|
const pinnedMessages = useHMSStore(selectSessionStore(SESSION_STORE_KEY.PINNED_MESSAGES));
|
20
21
|
const [pinnedMessageIndex, setPinnedMessageIndex] = useState(0);
|
22
|
+
const { removePinnedMessage } = useSetPinnedMessages();
|
21
23
|
const isMobile = useMedia(cssConfig.media.md);
|
22
24
|
|
23
25
|
const { elements } = useRoomLayoutConferencingScreen();
|
@@ -117,7 +119,7 @@ export const PinnedMessage = ({ clearPinnedMessage }: { clearPinnedMessage: (ind
|
|
117
119
|
{canUnpinMessage ? (
|
118
120
|
<Flex
|
119
121
|
onClick={() => {
|
120
|
-
|
122
|
+
removePinnedMessage(pinnedMessageIndex);
|
121
123
|
setPinnedMessageIndex(Math.max(0, pinnedMessageIndex - 1));
|
122
124
|
}}
|
123
125
|
css={{
|
@@ -1,21 +1,30 @@
|
|
1
1
|
import React from 'react';
|
2
|
-
import { QuizIcon } from '@100mslive/react-icons';
|
2
|
+
import { QuizActiveIcon, QuizIcon } from '@100mslive/react-icons';
|
3
3
|
import { Tooltip } from '../../..';
|
4
4
|
// @ts-ignore: No implicit Any
|
5
5
|
import IconButton from '../../IconButton';
|
6
6
|
// @ts-ignore: No implicit Any
|
7
7
|
import { useIsSidepaneTypeOpen, usePollViewToggle } from '../AppData/useSidepane';
|
8
|
+
import { useUnreadPollQuizPresent } from '../hooks/useUnreadPollQuizPresent';
|
8
9
|
// @ts-ignore: No implicit Any
|
9
10
|
import { SIDE_PANE_OPTIONS } from '../../common/constants';
|
10
11
|
|
11
12
|
export const PollsToggle = () => {
|
12
13
|
const isPollsOpen = useIsSidepaneTypeOpen(SIDE_PANE_OPTIONS.POLLS);
|
13
14
|
const togglePollView = usePollViewToggle();
|
15
|
+
const { unreadPollQuiz, setUnreadPollQuiz } = useUnreadPollQuizPresent();
|
14
16
|
|
15
17
|
return (
|
16
18
|
<Tooltip key="polls" title={`${isPollsOpen ? 'Close' : 'Open'} polls and quizzes`}>
|
17
|
-
<IconButton
|
18
|
-
|
19
|
+
<IconButton
|
20
|
+
onClick={() => {
|
21
|
+
togglePollView();
|
22
|
+
setUnreadPollQuiz(false);
|
23
|
+
}}
|
24
|
+
active={!isPollsOpen}
|
25
|
+
data-testid="polls_btn"
|
26
|
+
>
|
27
|
+
{unreadPollQuiz ? <QuizActiveIcon /> : <QuizIcon />}
|
19
28
|
</IconButton>
|
20
29
|
</Tooltip>
|
21
30
|
);
|
@@ -18,6 +18,7 @@ import {
|
|
18
18
|
HandIcon,
|
19
19
|
HandRaiseSlashedIcon,
|
20
20
|
PeopleIcon,
|
21
|
+
QuizActiveIcon,
|
21
22
|
QuizIcon,
|
22
23
|
RecordIcon,
|
23
24
|
SettingsIcon,
|
@@ -48,6 +49,7 @@ import { useShowPolls } from '../../AppData/useUISettings';
|
|
48
49
|
import { useDropdownList } from '../../hooks/useDropdownList';
|
49
50
|
// @ts-ignore: No implicit any
|
50
51
|
import { useMyMetadata } from '../../hooks/useMetadata';
|
52
|
+
import { useUnreadPollQuizPresent } from '../../hooks/useUnreadPollQuizPresent';
|
51
53
|
// @ts-ignore: No implicit any
|
52
54
|
import { getFormattedCount } from '../../../common/utils';
|
53
55
|
// @ts-ignore: No implicit any
|
@@ -92,6 +94,7 @@ export const MwebOptions = ({
|
|
92
94
|
const { isBRBOn, toggleBRB, isHandRaised, toggleHandRaise } = useMyMetadata();
|
93
95
|
const { toggleAudio, toggleVideo } = useAVToggle();
|
94
96
|
const noAVPermissions = !(toggleAudio || toggleVideo);
|
97
|
+
const { unreadPollQuiz, setUnreadPollQuiz } = useUnreadPollQuizPresent();
|
95
98
|
// const isVideoOn = useHMSStore(selectIsLocalVideoEnabled);
|
96
99
|
|
97
100
|
useDropdownList({ open: openModals.size > 0 || openOptionsSheet || openSettingsSheet, name: 'MoreSettings' });
|
@@ -202,9 +205,10 @@ export const MwebOptions = ({
|
|
202
205
|
onClick={() => {
|
203
206
|
togglePollView();
|
204
207
|
setOpenOptionsSheet(false);
|
208
|
+
setUnreadPollQuiz(false);
|
205
209
|
}}
|
206
210
|
>
|
207
|
-
<QuizIcon />
|
211
|
+
{unreadPollQuiz ? <QuizActiveIcon /> : <QuizIcon />}
|
208
212
|
<ActionTile.Title>Polls and Quizzes</ActionTile.Title>
|
209
213
|
</ActionTile.Root>
|
210
214
|
)}
|
@@ -51,7 +51,7 @@ export const MultipleChoiceOptions = ({
|
|
51
51
|
</Checkbox.Root>
|
52
52
|
) : null}
|
53
53
|
|
54
|
-
{isStopped && correctOptionIndexes
|
54
|
+
{isStopped && correctOptionIndexes?.includes(option.index) ? (
|
55
55
|
<Flex css={{ color: '$on_surface_high' }}>
|
56
56
|
<CheckCircleIcon />
|
57
57
|
</Flex>
|
@@ -107,16 +107,37 @@ export const SidePaneTabs = React.memo<{
|
|
107
107
|
<>
|
108
108
|
{hideTabs ? (
|
109
109
|
<>
|
110
|
-
<
|
111
|
-
{
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
110
|
+
<Flex justify="between" css={{ w: '100%' }}>
|
111
|
+
<Text variant="sm" css={{ fontWeight: '$semiBold', p: '$4', c: '$on_surface_high', pr: '$12' }}>
|
112
|
+
{showChat ? (
|
113
|
+
chat_title
|
114
|
+
) : (
|
115
|
+
<span>
|
116
|
+
Participants
|
117
|
+
<ParticipantCount count={peerCount} />
|
118
|
+
</span>
|
119
|
+
)}
|
120
|
+
</Text>
|
121
|
+
<Flex>
|
122
|
+
{showChatSettings ? <ChatSettings /> : null}
|
123
|
+
{isOverlayChat && isChatOpen ? null : (
|
124
|
+
<IconButton
|
125
|
+
css={{ my: '$1', color: '$on_surface_medium', '&:hover': { color: '$on_surface_high' } }}
|
126
|
+
onClick={e => {
|
127
|
+
e.stopPropagation();
|
128
|
+
if (activeTab === SIDE_PANE_OPTIONS.CHAT) {
|
129
|
+
toggleChat();
|
130
|
+
} else {
|
131
|
+
toggleParticipants();
|
132
|
+
}
|
133
|
+
}}
|
134
|
+
data-testid="close_chat"
|
135
|
+
>
|
136
|
+
<CrossIcon />
|
137
|
+
</IconButton>
|
138
|
+
)}
|
139
|
+
</Flex>
|
140
|
+
</Flex>
|
120
141
|
{showChat ? <Chat /> : <ParticipantList offStageRoles={off_stage_roles} onActive={setActiveRole} />}
|
121
142
|
</>
|
122
143
|
) : (
|
@@ -148,7 +169,8 @@ export const SidePaneTabs = React.memo<{
|
|
148
169
|
color: activeTab !== SIDE_PANE_OPTIONS.PARTICIPANTS ? '$on_surface_low' : '$on_surface_high',
|
149
170
|
}}
|
150
171
|
>
|
151
|
-
Participants
|
172
|
+
Participants
|
173
|
+
<ParticipantCount count={peerCount} />
|
152
174
|
</Tabs.Trigger>
|
153
175
|
</Tabs.List>
|
154
176
|
{showChatSettings ? <ChatSettings /> : null}
|