@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.
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
+ };