@100mslive/roomkit-react 0.1.18-alpha.1 → 0.1.18
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.
- 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}
|