@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.
Files changed (37) hide show
  1. package/dist/{HLSView-MR7RYQZB.js → HLSView-XU2WQ4VV.js} +2 -2
  2. package/dist/Prebuilt/components/Chat/Chat.d.ts +2 -0
  3. package/dist/Prebuilt/components/Chat/ChatActions.d.ts +12 -0
  4. package/dist/Prebuilt/components/Chat/ChatBody.d.ts +8 -0
  5. package/dist/Prebuilt/components/Chat/ChatFooter.d.ts +2 -2
  6. package/dist/Prebuilt/components/Chat/ChatStates.d.ts +1 -1
  7. package/dist/Prebuilt/components/Chat/MwebChatOption.d.ts +1 -1
  8. package/dist/Prebuilt/components/Chat/PinnedMessage.d.ts +1 -3
  9. package/dist/Prebuilt/components/hooks/useChatBlacklist.d.ts +1 -0
  10. package/dist/Prebuilt/components/hooks/useSetPinnedMessages.d.ts +3 -10
  11. package/dist/Prebuilt/components/hooks/useUnreadPollQuizPresent.d.ts +5 -0
  12. package/dist/{chunk-WFHOR7AP.js → chunk-2FX6MFDM.js} +4535 -4495
  13. package/dist/chunk-2FX6MFDM.js.map +7 -0
  14. package/dist/index.cjs.js +5110 -5055
  15. package/dist/index.cjs.js.map +4 -4
  16. package/dist/index.js +1 -1
  17. package/dist/meta.cjs.json +341 -223
  18. package/dist/meta.esbuild.json +351 -233
  19. package/package.json +6 -6
  20. package/src/Prebuilt/components/Chat/Chat.tsx +108 -0
  21. package/src/Prebuilt/components/Chat/ChatActions.tsx +295 -0
  22. package/src/Prebuilt/components/Chat/ChatBody.tsx +444 -0
  23. package/src/Prebuilt/components/Chat/ChatFooter.tsx +9 -3
  24. package/src/Prebuilt/components/Chat/ChatStates.tsx +5 -0
  25. package/src/Prebuilt/components/Chat/MwebChatOption.tsx +1 -1
  26. package/src/Prebuilt/components/Chat/PinnedMessage.tsx +4 -2
  27. package/src/Prebuilt/components/Footer/PollsToggle.tsx +12 -3
  28. package/src/Prebuilt/components/MoreSettings/SplitComponents/MwebOptions.tsx +5 -1
  29. package/src/Prebuilt/components/Polls/common/MultipleChoiceOptions.jsx +1 -1
  30. package/src/Prebuilt/components/SidePaneTabs.tsx +33 -11
  31. package/src/Prebuilt/components/hooks/useChatBlacklist.ts +7 -1
  32. package/src/Prebuilt/components/hooks/useSetPinnedMessages.ts +19 -11
  33. package/src/Prebuilt/components/hooks/useUnreadPollQuizPresent.tsx +20 -0
  34. package/dist/chunk-WFHOR7AP.js.map +0 -7
  35. package/src/Prebuilt/components/Chat/Chat.jsx +0 -124
  36. package/src/Prebuilt/components/Chat/ChatBody.jsx +0 -726
  37. /package/dist/{HLSView-MR7RYQZB.js.map → HLSView-XU2WQ4VV.js.map} +0 -0
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "prebuilt",
11
11
  "roomkit"
12
12
  ],
13
- "version": "0.1.18-alpha.1",
13
+ "version": "0.1.18",
14
14
  "author": "100ms",
15
15
  "license": "MIT",
16
16
  "files": [
@@ -76,10 +76,10 @@
76
76
  "react": ">=17.0.2 <19.0.0"
77
77
  },
78
78
  "dependencies": {
79
- "@100mslive/hls-player": "0.1.27-alpha.1",
80
- "@100mslive/hms-virtual-background": "1.11.27-alpha.1",
81
- "@100mslive/react-icons": "0.8.27-alpha.1",
82
- "@100mslive/react-sdk": "0.8.27-alpha.1",
79
+ "@100mslive/hls-player": "0.1.27",
80
+ "@100mslive/hms-virtual-background": "1.11.27",
81
+ "@100mslive/react-icons": "0.8.27",
82
+ "@100mslive/react-sdk": "0.8.27",
83
83
  "@100mslive/types-prebuilt": "0.12.5",
84
84
  "@emoji-mart/data": "^1.0.6",
85
85
  "@emoji-mart/react": "^1.0.1",
@@ -115,5 +115,5 @@
115
115
  "uuid": "^8.3.2",
116
116
  "worker-timers": "^7.0.40"
117
117
  },
118
- "gitHead": "ce20a10ac4c2cea6ac8a7cc4ffc0226c45aa8975"
118
+ "gitHead": "bebaa35da830548dc943d08a9d3cfe2f472e23b1"
119
119
  }
@@ -0,0 +1,108 @@
1
+ import React, { MutableRefObject, useCallback, useRef } from 'react';
2
+ import { useMedia } from 'react-use';
3
+ import { VariableSizeList } from 'react-window';
4
+ import { selectSessionStore, selectUnreadHMSMessagesCount } from '@100mslive/hms-video-store';
5
+ import { selectHMSMessagesCount, useHMSActions, useHMSStore, useHMSVanillaStore } from '@100mslive/react-sdk';
6
+ import { ChevronDownIcon } from '@100mslive/react-icons';
7
+ import { Button } from '../../../Button';
8
+ import { Flex } from '../../../Layout';
9
+ import { config as cssConfig } from '../../../Theme';
10
+ import { ChatBody } from './ChatBody';
11
+ import { ChatFooter } from './ChatFooter';
12
+ import { ChatBlocked, ChatPaused } from './ChatStates';
13
+ import { PinnedMessage } from './PinnedMessage';
14
+ import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
15
+ import { SESSION_STORE_KEY } from '../../common/constants';
16
+
17
+ export const Chat = () => {
18
+ const { elements } = useRoomLayoutConferencingScreen();
19
+ const listRef = useRef<VariableSizeList | null>(null);
20
+ const hmsActions = useHMSActions();
21
+ const vanillaStore = useHMSVanillaStore();
22
+ const { enabled: isChatEnabled = true } = useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_STATE)) || {};
23
+ const isMobile = useMedia(cssConfig.media.md);
24
+ const scrollToBottom = useCallback(
25
+ (unreadCount = 0) => {
26
+ if (listRef.current && listRef.current.scrollToItem && unreadCount > 0) {
27
+ const messagesCount = vanillaStore.getState(selectHMSMessagesCount);
28
+ listRef.current?.scrollToItem(messagesCount, 'end');
29
+ requestAnimationFrame(() => {
30
+ listRef.current?.scrollToItem(messagesCount, 'end');
31
+ });
32
+ hmsActions.setMessageRead(true);
33
+ }
34
+ },
35
+ [hmsActions, vanillaStore],
36
+ );
37
+
38
+ return (
39
+ <Flex
40
+ direction="column"
41
+ css={{
42
+ size: '100%',
43
+ gap: '$4',
44
+ }}
45
+ >
46
+ {isMobile && elements?.chat?.is_overlay ? null : <PinnedMessage />}
47
+ <ChatBody ref={listRef} scrollToBottom={scrollToBottom} />
48
+ <ChatPaused />
49
+ <ChatBlocked />
50
+ {isMobile && elements?.chat?.is_overlay ? <PinnedMessage /> : null}
51
+ {isChatEnabled ? (
52
+ <ChatFooter onSend={scrollToBottom}>
53
+ <NewMessageIndicator scrollToBottom={scrollToBottom} listRef={listRef} />
54
+ </ChatFooter>
55
+ ) : null}
56
+ </Flex>
57
+ );
58
+ };
59
+
60
+ const NewMessageIndicator = ({
61
+ scrollToBottom,
62
+ listRef,
63
+ }: {
64
+ scrollToBottom: (count: number) => void;
65
+ listRef: MutableRefObject<VariableSizeList | null>;
66
+ }) => {
67
+ const unreadCount = useHMSStore(selectUnreadHMSMessagesCount);
68
+ if (!unreadCount || !listRef.current) {
69
+ return null;
70
+ }
71
+ // @ts-ignore
72
+ const outerElement = listRef.current._outerRef;
73
+ // @ts-ignore
74
+ if (outerElement.scrollHeight === listRef.current.state.scrollOffset + outerElement.offsetHeight) {
75
+ return null;
76
+ }
77
+ return (
78
+ <Flex
79
+ justify="center"
80
+ css={{
81
+ width: '100%',
82
+ left: 0,
83
+ bottom: '$28',
84
+ position: 'absolute',
85
+ }}
86
+ >
87
+ <Button
88
+ variant="standard"
89
+ onClick={() => {
90
+ scrollToBottom(unreadCount);
91
+ }}
92
+ icon
93
+ css={{
94
+ p: '$3 $4',
95
+ pl: '$6',
96
+ '& > svg': { ml: '$4' },
97
+ borderRadius: '$round',
98
+ fontSize: '$xs',
99
+ fontWeight: '$semiBold',
100
+ c: '$on_secondary_high',
101
+ }}
102
+ >
103
+ New {unreadCount === 1 ? 'message' : 'messages'}
104
+ <ChevronDownIcon height={16} width={16} />
105
+ </Button>
106
+ </Flex>
107
+ );
108
+ };
@@ -0,0 +1,295 @@
1
+ import React, { useCallback, useState } from 'react';
2
+ import { HMSMessage, selectLocalPeerName, selectPermissions, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
3
+ import {
4
+ CopyIcon,
5
+ CrossCircleIcon,
6
+ CrossIcon,
7
+ EyeCloseIcon,
8
+ PeopleRemoveIcon,
9
+ PinIcon,
10
+ ReplyIcon,
11
+ VerticalMenuIcon,
12
+ } from '@100mslive/react-icons';
13
+ import { Dropdown } from '../../../Dropdown';
14
+ import { IconButton } from '../../../IconButton';
15
+ import { Flex } from '../../../Layout';
16
+ import { Sheet } from '../../../Sheet';
17
+ import { Text } from '../../../Text';
18
+ import { Tooltip } from '../../../Tooltip';
19
+ // @ts-ignore
20
+ import { ToastManager } from '../Toast/ToastManager';
21
+ import { MwebChatOption } from './MwebChatOption';
22
+ import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
23
+ import { useChatBlacklist } from '../hooks/useChatBlacklist';
24
+ import { useSetPinnedMessages } from '../hooks/useSetPinnedMessages';
25
+ import { SESSION_STORE_KEY } from '../../common/constants';
26
+
27
+ const iconStyle = { height: '1.125rem', width: '1.125rem' };
28
+ const tooltipBoxCSS = {
29
+ fontSize: '$xs',
30
+ backgroundColor: '$surface_default',
31
+ p: '$1 $5',
32
+ fontWeight: '$regular',
33
+ borderRadius: '$3',
34
+ };
35
+
36
+ export const ChatActions = ({
37
+ showPinAction,
38
+ onReply,
39
+ showReply,
40
+ message,
41
+ sentByLocalPeer,
42
+ isMobile,
43
+ openSheet,
44
+ setOpenSheet,
45
+ }: {
46
+ showPinAction: boolean;
47
+ onReply: () => void;
48
+ showReply: boolean;
49
+ message: HMSMessage;
50
+ sentByLocalPeer: boolean;
51
+ isMobile: boolean;
52
+ openSheet: boolean;
53
+ setOpenSheet: (value: boolean) => void;
54
+ }) => {
55
+ const { elements } = useRoomLayoutConferencingScreen();
56
+ const { can_hide_message, can_block_user } = elements?.chat?.real_time_controls || {
57
+ can_hide_message: false,
58
+ can_block_user: false,
59
+ };
60
+ const [open, setOpen] = useState(false);
61
+ const actions = useHMSActions();
62
+ const canRemoveOthers = useHMSStore(selectPermissions)?.removeOthers;
63
+ const { blacklistItem: blacklistPeer } = useChatBlacklist(SESSION_STORE_KEY.CHAT_PEER_BLACKLIST);
64
+ const localPeerName = useHMSStore(selectLocalPeerName);
65
+ const { setPinnedMessages, unpinBlacklistedMessages } = useSetPinnedMessages();
66
+
67
+ const { blacklistItem: blacklistMessage, blacklistedIDs: blacklistedMessageIDs } = useChatBlacklist(
68
+ SESSION_STORE_KEY.CHAT_MESSAGE_BLACKLIST,
69
+ );
70
+
71
+ const updatePinnedMessages = useCallback(
72
+ (messageID = '') => {
73
+ const blacklistedMessageIDSet = new Set([...(blacklistedMessageIDs || []), messageID]);
74
+ unpinBlacklistedMessages(blacklistedMessageIDSet);
75
+ },
76
+ [blacklistedMessageIDs, unpinBlacklistedMessages],
77
+ );
78
+
79
+ const copyMessageContent = useCallback(() => {
80
+ try {
81
+ navigator?.clipboard.writeText(message.message);
82
+ ToastManager.addToast({
83
+ title: 'Message copied successfully',
84
+ });
85
+ } catch (e) {
86
+ console.log(e);
87
+ ToastManager.addToast({
88
+ title: 'Could not copy message',
89
+ });
90
+ }
91
+ }, [message]);
92
+
93
+ const options: Record<
94
+ string,
95
+ {
96
+ text: string;
97
+ tooltipText?: string;
98
+ icon: React.ReactNode;
99
+ onClick: () => void | Promise<void>;
100
+ show: boolean;
101
+ color?: string;
102
+ }
103
+ > = {
104
+ reply: {
105
+ text: message.recipientRoles?.length ? 'Reply to Group' : 'Reply Privately',
106
+ tooltipText: message.recipientRoles?.length ? 'Reply to Group' : 'Reply Privately',
107
+ icon: <ReplyIcon style={iconStyle} />,
108
+ onClick: onReply,
109
+ show: showReply,
110
+ },
111
+ pin: {
112
+ text: 'Pin message',
113
+ tooltipText: 'Pin',
114
+ icon: <PinIcon style={iconStyle} />,
115
+ onClick: () => setPinnedMessages(message, localPeerName || ''),
116
+ show: showPinAction,
117
+ },
118
+ copy: {
119
+ text: 'Copy text',
120
+ tooltipText: 'Copy',
121
+ icon: <CopyIcon style={iconStyle} />,
122
+ onClick: copyMessageContent,
123
+ show: true,
124
+ },
125
+ hide: {
126
+ text: message.recipientPeer ? 'Hide for both' : 'Hide for everyone',
127
+ icon: <EyeCloseIcon style={iconStyle} />,
128
+ onClick: async () => {
129
+ blacklistMessage(message.id);
130
+ updatePinnedMessages(message.id);
131
+ },
132
+ show: !!can_hide_message,
133
+ },
134
+ block: {
135
+ text: 'Block from chat',
136
+ icon: <CrossCircleIcon style={iconStyle} />,
137
+ onClick: async () => {
138
+ if (message.senderUserId) {
139
+ blacklistPeer(message.senderUserId);
140
+ }
141
+ },
142
+ color: '$alert_error_default',
143
+ show: !!can_block_user && !sentByLocalPeer,
144
+ },
145
+ remove: {
146
+ text: 'Remove Partipant',
147
+ icon: <PeopleRemoveIcon style={iconStyle} />,
148
+ color: '$alert_error_default',
149
+ show: !!canRemoveOthers && !sentByLocalPeer,
150
+ onClick: async () => {
151
+ if (!message.sender) {
152
+ return;
153
+ }
154
+ try {
155
+ await actions.removePeer(message.sender, '');
156
+ } catch (error) {
157
+ ToastManager.addToast({ title: (error as Error).message, variant: 'error' });
158
+ }
159
+ },
160
+ },
161
+ };
162
+
163
+ if (isMobile) {
164
+ return (
165
+ <Sheet.Root open={openSheet} onOpenChange={setOpenSheet}>
166
+ <Sheet.Content css={{ bg: '$surface_default', pb: '$14' }} onClick={() => setOpenSheet(false)}>
167
+ <Sheet.Title
168
+ css={{
169
+ display: 'flex',
170
+ color: '$on_surface_high',
171
+ w: '100%',
172
+ justifyContent: 'space-between',
173
+ mt: '$8',
174
+ fontSize: '$md',
175
+ px: '$10',
176
+ pb: '$8',
177
+ borderBottom: '1px solid $border_bright',
178
+ alignItems: 'center',
179
+ }}
180
+ >
181
+ Message options
182
+ <Sheet.Close css={{ color: '$on_surface_high' }} onClick={() => setOpenSheet(false)}>
183
+ <CrossIcon />
184
+ </Sheet.Close>
185
+ </Sheet.Title>
186
+
187
+ {Object.keys(options).map(optionKey => {
188
+ const option = options[optionKey];
189
+ return option.show ? (
190
+ <MwebChatOption
191
+ key={optionKey}
192
+ text={option.text}
193
+ icon={option.icon}
194
+ onClick={option.onClick}
195
+ color={option?.color}
196
+ />
197
+ ) : null;
198
+ })}
199
+ </Sheet.Content>
200
+ </Sheet.Root>
201
+ );
202
+ }
203
+
204
+ return (
205
+ <Dropdown.Root open={open} onOpenChange={setOpen} css={{ '@md': { display: 'none' } }}>
206
+ <Flex
207
+ className="chat_actions"
208
+ css={{
209
+ background: '$surface_bright',
210
+ borderRadius: '$1',
211
+ p: '$2',
212
+ opacity: open ? 1 : 0,
213
+ position: 'absolute',
214
+ right: 0,
215
+ zIndex: 1,
216
+ '@md': { opacity: 1 },
217
+ }}
218
+ >
219
+ {options.reply.show ? (
220
+ <Tooltip boxCss={tooltipBoxCSS} title={options.reply.tooltipText}>
221
+ <IconButton data-testid="reply_message_btn" onClick={options.reply.onClick}>
222
+ {options.reply.icon}
223
+ </IconButton>
224
+ </Tooltip>
225
+ ) : null}
226
+ {options.pin.show ? (
227
+ <Tooltip boxCss={tooltipBoxCSS} title={options.pin.tooltipText}>
228
+ <IconButton data-testid="pin_message_btn" onClick={options.pin.onClick}>
229
+ {options.pin.icon}
230
+ </IconButton>
231
+ </Tooltip>
232
+ ) : null}
233
+
234
+ {options.copy.show ? (
235
+ <Tooltip boxCss={tooltipBoxCSS} title={options.copy.tooltipText}>
236
+ <IconButton onClick={options.copy.onClick} data-testid="copy_message_btn">
237
+ <CopyIcon style={iconStyle} />
238
+ </IconButton>
239
+ </Tooltip>
240
+ ) : null}
241
+
242
+ {options.block.show || options.hide.show || options.remove.show ? (
243
+ <Tooltip boxCss={tooltipBoxCSS} title="More actions">
244
+ <Dropdown.Trigger asChild>
245
+ <IconButton>
246
+ <VerticalMenuIcon style={iconStyle} />
247
+ </IconButton>
248
+ </Dropdown.Trigger>
249
+ </Tooltip>
250
+ ) : null}
251
+ </Flex>
252
+ <Dropdown.Portal>
253
+ <Dropdown.Content
254
+ sideOffset={5}
255
+ align="end"
256
+ css={{ width: '$48', backgroundColor: '$surface_bright', py: '$0', border: '1px solid $border_bright' }}
257
+ >
258
+ {options.hide.show ? (
259
+ <Dropdown.Item data-testid="hide_message_btn" onClick={options.hide.onClick}>
260
+ {options.hide.icon}
261
+ <Text variant="sm" css={{ ml: '$4', fontWeight: '$semiBold' }}>
262
+ {options.hide.text}
263
+ </Text>
264
+ </Dropdown.Item>
265
+ ) : null}
266
+
267
+ {options.block.show ? (
268
+ <Dropdown.Item
269
+ data-testid="block_peer_btn"
270
+ onClick={options.block.onClick}
271
+ css={{ color: options.block.color }}
272
+ >
273
+ {options.block.icon}
274
+ <Text variant="sm" css={{ ml: '$4', color: 'inherit', fontWeight: '$semiBold' }}>
275
+ {options.block.text}
276
+ </Text>
277
+ </Dropdown.Item>
278
+ ) : null}
279
+ {options.remove.show ? (
280
+ <Dropdown.Item
281
+ data-testid="remove_peer_btn"
282
+ onClick={options.remove.onClick}
283
+ css={{ color: options.remove.color }}
284
+ >
285
+ {options.remove.icon}
286
+ <Text variant="sm" css={{ ml: '$4', color: 'inherit', fontWeight: '$semiBold' }}>
287
+ {options.remove.text}
288
+ </Text>
289
+ </Dropdown.Item>
290
+ ) : null}
291
+ </Dropdown.Content>
292
+ </Dropdown.Portal>
293
+ </Dropdown.Root>
294
+ );
295
+ };