@100mslive/roomkit-react 0.1.13-alpha.0 → 0.1.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. package/dist/{HLSView-AIPLCDXY.js → HLSView-IENE4HRP.js} +2 -2
  2. package/dist/Prebuilt/components/Notifications/ChatNotifications.d.ts +2 -0
  3. package/dist/{chunk-5DCII2TP.js → chunk-U4AB6X6M.js} +945 -911
  4. package/dist/chunk-U4AB6X6M.js.map +7 -0
  5. package/dist/index.cjs.js +1450 -1400
  6. package/dist/index.cjs.js.map +4 -4
  7. package/dist/index.js +1 -1
  8. package/dist/meta.cjs.json +227 -148
  9. package/dist/meta.esbuild.json +232 -153
  10. package/package.json +6 -6
  11. package/src/Prebuilt/components/AuthToken.jsx +12 -2
  12. package/src/Prebuilt/components/Chat/Chat.jsx +5 -7
  13. package/src/Prebuilt/components/Chat/ChatBody.jsx +53 -34
  14. package/src/Prebuilt/components/Chat/ChatFooter.tsx +13 -7
  15. package/src/Prebuilt/components/Chat/ChatStates.jsx +8 -6
  16. package/src/Prebuilt/components/Chat/Navigation.tsx +1 -1
  17. package/src/Prebuilt/components/Chat/PinnedMessage.tsx +15 -17
  18. package/src/Prebuilt/components/Notifications/ChatNotifications.tsx +34 -0
  19. package/src/Prebuilt/components/Notifications/Notifications.tsx +2 -0
  20. package/src/Prebuilt/components/Polls/Voting/QuestionCard.jsx +6 -4
  21. package/src/Prebuilt/components/Polls/Voting/TimedVoting.jsx +3 -2
  22. package/src/Prebuilt/components/Polls/common/VoteCount.jsx +6 -4
  23. package/src/Prebuilt/components/Preview/PreviewJoin.tsx +1 -1
  24. package/src/Prebuilt/components/Toast/ToastConfig.jsx +0 -19
  25. package/src/Prebuilt/components/VirtualBackground/VBPicker.tsx +46 -35
  26. package/src/Prebuilt/components/hooks/useSetPinnedMessages.ts +1 -2
  27. package/src/Prebuilt/components/hooks/useUserPreferences.jsx +1 -0
  28. package/src/Prebuilt/layouts/VideoStreamingSection.tsx +10 -30
  29. package/dist/chunk-5DCII2TP.js.map +0 -7
  30. /package/dist/{HLSView-AIPLCDXY.js.map → HLSView-IENE4HRP.js.map} +0 -0
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "prebuilt",
11
11
  "roomkit"
12
12
  ],
13
- "version": "0.1.13-alpha.0",
13
+ "version": "0.1.13",
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.22-alpha.0",
80
- "@100mslive/hms-virtual-background": "1.11.22-alpha.0",
81
- "@100mslive/react-icons": "0.8.22-alpha.0",
82
- "@100mslive/react-sdk": "0.8.22-alpha.0",
79
+ "@100mslive/hls-player": "0.1.22",
80
+ "@100mslive/hms-virtual-background": "1.11.22",
81
+ "@100mslive/react-icons": "0.8.22",
82
+ "@100mslive/react-sdk": "0.8.22",
83
83
  "@100mslive/types-prebuilt": "0.12.4",
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": "b12ee3cc718d94936eb45de704bd29f823a5cad9"
118
+ "gitHead": "543b9152024ee7e4554701f1387b1af32906b58a"
119
119
  }
@@ -1,9 +1,12 @@
1
1
  import React, { useEffect, useState } from 'react';
2
+ import { useSessionStorage } from 'react-use';
3
+ import { v4 } from 'uuid';
2
4
  import { useHMSActions } from '@100mslive/react-sdk';
3
5
  import { styled } from '../../Theme';
4
6
  import { useHMSPrebuiltContext } from '../AppContext';
5
7
  import { ErrorDialog } from '../primitives/DialogContent';
6
8
  import { useSetAppDataByKey } from './AppData/useUISettings';
9
+ import { UserPreferencesKeys } from './hooks/useUserPreferences';
7
10
  import { APP_DATA } from '../common/constants';
8
11
 
9
12
  /**
@@ -21,6 +24,13 @@ const AuthToken = React.memo(({ authTokenByRoomCodeEndpoint, defaultAuthToken })
21
24
  const [error, setError] = useState({ title: '', body: '' });
22
25
  let authToken = defaultAuthToken;
23
26
  const [, setAuthTokenInAppData] = useSetAppDataByKey(APP_DATA.authToken);
27
+ const [savedUserId, setSavedUserId] = useSessionStorage(UserPreferencesKeys.USER_ID);
28
+
29
+ useEffect(() => {
30
+ if (!savedUserId && !userId) {
31
+ setSavedUserId(v4());
32
+ }
33
+ }, [savedUserId, setSavedUserId, userId]);
24
34
 
25
35
  useEffect(() => {
26
36
  if (authToken) {
@@ -33,10 +43,10 @@ const AuthToken = React.memo(({ authTokenByRoomCodeEndpoint, defaultAuthToken })
33
43
  }
34
44
 
35
45
  hmsActions
36
- .getAuthTokenByRoomCode({ roomCode, userId }, { endpoint: authTokenByRoomCodeEndpoint })
46
+ .getAuthTokenByRoomCode({ roomCode, userId: userId || savedUserId }, { endpoint: authTokenByRoomCodeEndpoint })
37
47
  .then(token => setAuthTokenInAppData(token))
38
48
  .catch(error => setError(convertError(error)));
39
- }, [hmsActions, authToken, authTokenByRoomCodeEndpoint, setAuthTokenInAppData, roomCode, userId]);
49
+ }, [hmsActions, authToken, authTokenByRoomCodeEndpoint, setAuthTokenInAppData, roomCode, userId, savedUserId]);
40
50
 
41
51
  if (error.title) {
42
52
  return <ErrorDialog title={error.title}>{error.body}</ErrorDialog>;
@@ -1,6 +1,6 @@
1
1
  import React, { useCallback, useEffect, useRef, useState } from 'react';
2
2
  import { useMedia } from 'react-use';
3
- import { selectLocalPeerID, selectSessionStore } from '@100mslive/hms-video-store';
3
+ import { selectLocalPeer, selectSessionStore } from '@100mslive/hms-video-store';
4
4
  import {
5
5
  HMSNotificationTypes,
6
6
  selectHMSMessagesCount,
@@ -28,7 +28,7 @@ export const Chat = ({ screenType }) => {
28
28
  const [peerSelector, setPeerSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.PEER_ID);
29
29
  const [roleSelector, setRoleSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.ROLE);
30
30
  const peerName = useHMSStore(selectPeerNameByID(peerSelector));
31
- const localPeerId = useHMSStore(selectLocalPeerID);
31
+ const localPeer = useHMSStore(selectLocalPeer);
32
32
  const [chatOptions, setChatOptions] = useState({
33
33
  role: roleSelector || '',
34
34
  peerId: peerSelector && peerName ? peerSelector : '',
@@ -52,7 +52,7 @@ export const Chat = ({ screenType }) => {
52
52
  }, [notification, peerSelector, setPeerSelector]);
53
53
  const blacklistedPeerIDs = useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_PEER_BLACKLIST)) || [];
54
54
  const blacklistedPeerIDSet = new Set(blacklistedPeerIDs);
55
- const isLocalPeerBlacklisted = blacklistedPeerIDSet.has(localPeerId);
55
+ const isLocalPeerBlacklisted = blacklistedPeerIDSet.has(localPeer?.customerUserId);
56
56
  const storeMessageSelector = selectHMSMessagesCount;
57
57
  const { elements } = useRoomLayoutConferencingScreen();
58
58
  const { enabled: isChatEnabled = true } = useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_STATE)) || {};
@@ -88,9 +88,7 @@ export const Chat = ({ screenType }) => {
88
88
  >
89
89
  {isMobile && elements?.chat?.is_overlay ? null : (
90
90
  <>
91
- {elements?.chat?.allow_pinning_messages ? (
92
- <PinnedMessage clearPinnedMessage={index => removePinnedMessage(pinnedMessages, index)} />
93
- ) : null}
91
+ <PinnedMessage clearPinnedMessage={index => removePinnedMessage(pinnedMessages, index)} />
94
92
  </>
95
93
  )}
96
94
 
@@ -107,7 +105,7 @@ export const Chat = ({ screenType }) => {
107
105
 
108
106
  {isLocalPeerBlacklisted ? <ChatBlocked /> : null}
109
107
 
110
- {isMobile && elements?.chat?.is_overlay && elements?.chat?.allow_pinning_messages ? (
108
+ {isMobile && elements?.chat?.is_overlay ? (
111
109
  <PinnedMessage clearPinnedMessage={index => removePinnedMessage(pinnedMessages, index)} />
112
110
  ) : null}
113
111
 
@@ -34,6 +34,13 @@ import { useUnreadCount } from './useUnreadCount';
34
34
  import { SESSION_STORE_KEY } from '../../common/constants';
35
35
 
36
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
+ };
37
44
 
38
45
  const formatTime = date => {
39
46
  if (!(date instanceof Date)) {
@@ -136,7 +143,7 @@ const getMessageType = ({ roles, receiver }) => {
136
143
  }
137
144
  return receiver ? 'private' : '';
138
145
  };
139
- const ChatActions = ({ onPin, showPinAction, message, peerId, sentByLocalPeer, isMobile, openSheet, setOpenSheet }) => {
146
+ const ChatActions = ({ onPin, showPinAction, message, sentByLocalPeer, isMobile, openSheet, setOpenSheet }) => {
140
147
  const { elements } = useRoomLayoutConferencingScreen();
141
148
  const { can_hide_message, can_block_user } = elements?.chat?.real_time_controls || {
142
149
  can_hide_message: false,
@@ -144,23 +151,23 @@ const ChatActions = ({ onPin, showPinAction, message, peerId, sentByLocalPeer, i
144
151
  };
145
152
  const [open, setOpen] = useState(false);
146
153
  const blacklistedPeerIDs = useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_PEER_BLACKLIST)) || [];
147
- const { blacklistItem: blacklistMessage } = useChatBlacklist(SESSION_STORE_KEY.CHAT_MESSAGE_BLACKLIST);
154
+ const { blacklistItem: blacklistPeer } = useChatBlacklist(SESSION_STORE_KEY.CHAT_PEER_BLACKLIST);
148
155
 
149
156
  const blacklistedMessageIDs = useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_MESSAGE_BLACKLIST)) || [];
150
- const { blacklistItem: blacklistPeer } = useChatBlacklist(SESSION_STORE_KEY.CHAT_PEER_BLACKLIST);
157
+ const { blacklistItem: blacklistMessage } = useChatBlacklist(SESSION_STORE_KEY.CHAT_MESSAGE_BLACKLIST);
158
+
151
159
  const { unpinBlacklistedMessages } = useSetPinnedMessages();
152
160
 
153
161
  const pinnedMessages = useHMSStore(selectSessionStore(SESSION_STORE_KEY.PINNED_MESSAGES)) || [];
154
162
 
155
- useEffect(() => {
156
- if (!(blacklistedPeerIDs.length || blacklistedMessageIDs.length)) {
157
- return;
158
- }
159
- const blacklistedMessageIDSet = new Set(blacklistedMessageIDs);
160
- const blacklistedPeerIDSet = new Set(blacklistedPeerIDs);
161
-
162
- unpinBlacklistedMessages(pinnedMessages, blacklistedPeerIDSet, blacklistedMessageIDSet);
163
- }, [blacklistedMessageIDs, blacklistedPeerIDs, pinnedMessages, unpinBlacklistedMessages]);
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
+ );
164
171
 
165
172
  const copyMessageContent = useCallback(() => {
166
173
  try {
@@ -179,12 +186,14 @@ const ChatActions = ({ onPin, showPinAction, message, peerId, sentByLocalPeer, i
179
186
  const options = {
180
187
  pin: {
181
188
  text: 'Pin message',
189
+ tooltipText: 'Pin',
182
190
  icon: <PinIcon style={iconStyle} />,
183
191
  onClick: onPin,
184
192
  show: showPinAction,
185
193
  },
186
194
  copy: {
187
195
  text: 'Copy text',
196
+ tooltipText: 'Copy',
188
197
  icon: <CopyIcon style={iconStyle} />,
189
198
  onClick: copyMessageContent,
190
199
  show: true,
@@ -192,13 +201,19 @@ const ChatActions = ({ onPin, showPinAction, message, peerId, sentByLocalPeer, i
192
201
  hide: {
193
202
  text: 'Hide for everyone',
194
203
  icon: <EyeCloseIcon style={iconStyle} />,
195
- onClick: async () => blacklistMessage(blacklistedPeerIDs, message.id),
204
+ onClick: async () => {
205
+ blacklistMessage(blacklistedMessageIDs, message.id);
206
+ updatePinnedMessages({ messageID: message.id });
207
+ },
196
208
  show: can_hide_message,
197
209
  },
198
210
  block: {
199
211
  text: 'Block from chat',
200
212
  icon: <CrossCircleIcon style={iconStyle} />,
201
- onClick: async () => blacklistPeer(blacklistedMessageIDs, peerId),
213
+ onClick: async () => {
214
+ blacklistPeer(blacklistedPeerIDs, message?.senderUserId);
215
+ updatePinnedMessages({ peerID: message?.senderUserId });
216
+ },
202
217
  color: '$alert_error_default',
203
218
  show: can_block_user && !sentByLocalPeer,
204
219
  },
@@ -258,25 +273,29 @@ const ChatActions = ({ onPin, showPinAction, message, peerId, sentByLocalPeer, i
258
273
  }}
259
274
  >
260
275
  {options.pin.show ? (
261
- <IconButton data-testid="pin_message_btn" onClick={options.pin.onClick}>
262
- {options.pin.icon}
263
- </IconButton>
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>
264
281
  ) : null}
265
282
 
266
283
  {options.copy.show ? (
267
- <IconButton onClick={options.copy.onClick} data-testid="copy_message_btn">
268
- <CopyIcon style={iconStyle} />
269
- </IconButton>
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>
270
289
  ) : null}
271
290
 
272
291
  {options.block.show || options.hide.show ? (
273
- <Dropdown.Trigger asChild>
274
- <IconButton>
275
- <Tooltip title="More options">
292
+ <Tooltip boxCss={tooltipBoxCSS} title="More actions">
293
+ <Dropdown.Trigger asChild>
294
+ <IconButton>
276
295
  <VerticalMenuIcon style={iconStyle} />
277
- </Tooltip>
278
- </IconButton>
279
- </Dropdown.Trigger>
296
+ </IconButton>
297
+ </Dropdown.Trigger>
298
+ </Tooltip>
280
299
  ) : null}
281
300
  </Flex>
282
301
  <Dropdown.Portal>
@@ -344,7 +363,6 @@ const ChatMessage = React.memo(
344
363
  const [openSheet, setOpenSheet] = useState(false);
345
364
  // show pin action only if peer has remove others permission and the message is of broadcast type
346
365
  const showPinAction = permissions.removeOthers && !messageType && elements?.chat?.allow_pinning_messages;
347
-
348
366
  useEffect(() => {
349
367
  if (message.id && !message.read && inView) {
350
368
  hmsActions.setMessageRead(true, message.id);
@@ -362,9 +380,9 @@ const ChatMessage = React.memo(
362
380
  ref={ref}
363
381
  as="div"
364
382
  css={{
365
- mb: '$10',
383
+ mb: '$5',
366
384
  pr: '$10',
367
- mt: '$8',
385
+ mt: '$4',
368
386
  '&:hover .chat_actions': { opacity: 1 },
369
387
  }}
370
388
  style={style}
@@ -376,13 +394,15 @@ const ChatMessage = React.memo(
376
394
  flexWrap: 'wrap',
377
395
  // Theme independent color, token should not be used for transparent chat
378
396
  bg: messageType ? (isOverlay ? 'rgba(0, 0, 0, 0.64)' : '$surface_default') : undefined,
379
- r: messageType ? '$1' : undefined,
380
- px: messageType ? '$4' : '$2',
381
- py: messageType ? '$4' : 0,
397
+ r: '$1',
398
+ p: '$1 $2',
382
399
  userSelect: 'none',
383
400
  '@md': {
384
401
  cursor: 'pointer',
385
402
  },
403
+ '&:hover': {
404
+ background: 'linear-gradient(277deg, $surface_default 0%, $surface_dim 60.87%)',
405
+ },
386
406
  }}
387
407
  key={message.time}
388
408
  data-testid="chat_msg"
@@ -439,7 +459,6 @@ const ChatMessage = React.memo(
439
459
  onPin={onPin}
440
460
  showPinAction={showPinAction}
441
461
  message={message}
442
- peerId={message.sender}
443
462
  sentByLocalPeer={message.sender === localPeerId}
444
463
  isMobile={isMobile}
445
464
  openSheet={openSheet}
@@ -571,7 +590,7 @@ export const ChatBody = React.forwardRef(({ role, peerId, scrollToBottom, blackl
571
590
  message =>
572
591
  message.type === 'chat' &&
573
592
  !blacklistedMessageIDSet.has(message.id) &&
574
- !blacklistedPeerIDSet.has(message.sender),
593
+ !blacklistedPeerIDSet.has(message?.senderUserId),
575
594
  ) || []
576
595
  );
577
596
  };
@@ -2,7 +2,7 @@ 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 { selectLocalPeerName, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
5
+ import { selectLocalPeer, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
6
6
  import { EmojiIcon, PauseCircleIcon, SendIcon, VerticalMenuIcon } from '@100mslive/react-icons';
7
7
  import { Box, config as cssConfig, Flex, IconButton as BaseIconButton, Popover, styled, Text } from '../../..';
8
8
  import { IconButton } from '../../../IconButton';
@@ -86,9 +86,9 @@ export const ChatFooter = ({
86
86
  const isMobile = useMedia(cssConfig.media.md);
87
87
  const { elements } = useRoomLayoutConferencingScreen();
88
88
  const message_placeholder = elements?.chat?.message_placeholder || 'Send a message';
89
- const localPeerName = useHMSStore(selectLocalPeerName);
89
+ const localPeer = useHMSStore(selectLocalPeer);
90
90
  const isOverlayChat = elements?.chat?.is_overlay;
91
- const can_disable_chat = !!elements?.chat?.real_time_controls?.can_disable_chat;
91
+ const canDisableChat = !!elements?.chat?.real_time_controls?.can_disable_chat;
92
92
 
93
93
  const sendMessage = useCallback(async () => {
94
94
  const message = inputRef?.current?.value;
@@ -132,7 +132,7 @@ export const ChatFooter = ({
132
132
  {/* {screenType !== 'hls_live_streaming' ? (
133
133
  <ChatSelectorContainer onSelect={onSelect} role={role} peerId={peerId} selection={selection} />
134
134
  ) : null} */}
135
- {can_disable_chat ? (
135
+ {canDisableChat ? (
136
136
  <Flex align="center" justify="end" css={{ w: '100%', mb: '$4' }}>
137
137
  <Popover.Root>
138
138
  <Popover.Trigger asChild>
@@ -145,10 +145,16 @@ export const ChatFooter = ({
145
145
  align="end"
146
146
  side="top"
147
147
  onClick={() => {
148
- hmsActions.sessionStore.set(SESSION_STORE_KEY.CHAT_STATE, {
148
+ const chatState = {
149
149
  enabled: false,
150
- updatedBy: localPeerName,
151
- });
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);
152
158
  }}
153
159
  css={{
154
160
  backgroundColor: '$surface_default',
@@ -1,5 +1,5 @@
1
1
  import React, { useCallback } from 'react';
2
- import { selectLocalPeerName, selectSessionStore, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
2
+ import { selectLocalPeer, selectSessionStore, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
3
3
  import { Button } from '../../../Button';
4
4
  import { Box, Flex } from '../../../Layout';
5
5
  import { Text } from '../../../Text';
@@ -10,17 +10,19 @@ export const ChatPaused = () => {
10
10
  const hmsActions = useHMSActions();
11
11
  const { elements } = useRoomLayoutConferencingScreen();
12
12
  const { can_disable_chat } = elements?.chat.real_time_controls || false;
13
- const { enabled: isChatEnabled = true, updatedBy: chatStateUpdatedBy = '' } =
13
+ const { enabled: isChatEnabled = true, updatedBy: chatStateUpdatedBy } =
14
14
  useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_STATE)) || {};
15
- const localPeerName = useHMSStore(selectLocalPeerName);
15
+
16
+ const localPeer = useHMSStore(selectLocalPeer);
16
17
 
17
18
  const unPauseChat = useCallback(
18
19
  async () =>
19
20
  await hmsActions.sessionStore.set(SESSION_STORE_KEY.CHAT_STATE, {
20
21
  enabled: true,
21
- updatedBy: localPeerName,
22
+ updatedBy: { userName: localPeer.name, userId: localPeer?.customerUserId, peerId: localPeer.id },
23
+ updatedAt: Date.now(),
22
24
  }),
23
- [hmsActions, localPeerName],
25
+ [hmsActions, localPeer],
24
26
  );
25
27
 
26
28
  return isChatEnabled ? null : (
@@ -37,7 +39,7 @@ export const ChatPaused = () => {
37
39
  variant="xs"
38
40
  css={{ color: '$on_surface_medium', maxWidth: '100%', overflow: 'hidden', textOverflow: 'ellipsis' }}
39
41
  >
40
- Chat has been paused by {chatStateUpdatedBy}
42
+ Chat has been paused by {chatStateUpdatedBy?.peerId === localPeer.id ? 'you' : chatStateUpdatedBy?.userName}
41
43
  </Text>
42
44
  </Box>
43
45
  {can_disable_chat ? (
@@ -27,7 +27,7 @@ export const Navigation = ({
27
27
  <Box
28
28
  css={{
29
29
  borderLeft: '2px solid',
30
- height: '$8',
30
+ height: '$4',
31
31
  borderColor: i === index ? '$on_surface_high' : '$on_surface_low',
32
32
  }}
33
33
  />
@@ -1,7 +1,7 @@
1
1
  import React, { useEffect, useRef, useState } from 'react';
2
2
  import { useSwipeable } from 'react-swipeable';
3
3
  import { useMedia } from 'react-use';
4
- import { selectPermissions, selectSessionStore, useHMSStore } from '@100mslive/react-sdk';
4
+ import { selectSessionStore, useHMSStore } from '@100mslive/react-sdk';
5
5
  import { CrossIcon, PinIcon } from '@100mslive/react-icons';
6
6
  import { Box, Flex } from '../../../Layout';
7
7
  import { Text } from '../../../Text';
@@ -16,13 +16,12 @@ import { SESSION_STORE_KEY } from '../../common/constants';
16
16
  const PINNED_MESSAGE_LENGTH = 75;
17
17
 
18
18
  export const PinnedMessage = ({ clearPinnedMessage }: { clearPinnedMessage: (index: number) => void }) => {
19
- const permissions = useHMSStore(selectPermissions);
20
19
  const pinnedMessages = useHMSStore(selectSessionStore(SESSION_STORE_KEY.PINNED_MESSAGES)) || [];
21
20
  const [pinnedMessageIndex, setPinnedMessageIndex] = useState(0);
22
21
  const isMobile = useMedia(cssConfig.media.md);
23
22
 
24
23
  const [hideOverflow, setHideOverflow] = useState(false);
25
-
24
+ const canOverflow = pinnedMessages?.[pinnedMessageIndex]?.text?.length > PINNED_MESSAGE_LENGTH || false;
26
25
  const formattedPinnedMessage = hideOverflow
27
26
  ? `${pinnedMessages?.[pinnedMessageIndex]?.text.slice(0, PINNED_MESSAGE_LENGTH)}... `
28
27
  : pinnedMessages?.[pinnedMessageIndex]?.text;
@@ -94,24 +93,23 @@ export const PinnedMessage = ({ clearPinnedMessage }: { clearPinnedMessage: (ind
94
93
  >
95
94
  <Text variant="sm" css={{ color: '$on_surface_medium' }} {...swipeHandlers}>
96
95
  <AnnotisedMessage message={formattedPinnedMessage} />
97
- {hideOverflow ? (
98
- <span style={{ cursor: 'pointer' }} onClick={() => setHideOverflow(false)}>
99
- See more
96
+ {canOverflow ? (
97
+ <span style={{ cursor: 'pointer' }} onClick={() => setHideOverflow(prev => !prev)}>
98
+ $nbsp;{hideOverflow ? 'See more' : 'Collapse'}
100
99
  </span>
101
100
  ) : null}
102
101
  </Text>
103
102
  </Box>
104
- {permissions?.removeOthers && (
105
- <Flex
106
- onClick={() => {
107
- clearPinnedMessage(pinnedMessageIndex);
108
- setPinnedMessageIndex(Math.max(0, pinnedMessageIndex - 1));
109
- }}
110
- css={{ cursor: 'pointer', color: '$on_surface_medium', '&:hover': { color: '$on_surface_high' } }}
111
- >
112
- <CrossIcon />
113
- </Flex>
114
- )}
103
+
104
+ <Flex
105
+ onClick={() => {
106
+ clearPinnedMessage(pinnedMessageIndex);
107
+ setPinnedMessageIndex(Math.max(0, pinnedMessageIndex - 1));
108
+ }}
109
+ css={{ cursor: 'pointer', color: '$on_surface_medium', '&:hover': { color: '$on_surface_high' } }}
110
+ >
111
+ <CrossIcon />
112
+ </Flex>
115
113
  </Flex>
116
114
  </Flex>
117
115
  ) : null;
@@ -0,0 +1,34 @@
1
+ import React, { useEffect } from 'react';
2
+ import { v4 as uuid } from 'uuid';
3
+ import { selectLocalPeerID, selectSessionStore, useHMSStore } from '@100mslive/react-sdk';
4
+ import { ChatIcon, ChatUnreadIcon } from '@100mslive/react-icons';
5
+ // @ts-ignore
6
+ import { ToastManager } from '../Toast/ToastManager';
7
+ import { SESSION_STORE_KEY } from '../../common/constants';
8
+
9
+ const NOTIFICATION_TIME_DIFFERENCE = 5000;
10
+
11
+ export const ChatNotifications = () => {
12
+ const chatState = useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_STATE));
13
+ const localPeerId = useHMSStore(selectLocalPeerID);
14
+
15
+ useEffect(() => {
16
+ if (!chatState || chatState.updatedBy?.peerId === localPeerId) {
17
+ return;
18
+ }
19
+
20
+ const showToast = Date.now() - chatState.updatedAt < NOTIFICATION_TIME_DIFFERENCE;
21
+
22
+ if (!showToast) {
23
+ return;
24
+ }
25
+
26
+ const notification = {
27
+ id: uuid(),
28
+ icon: chatState.enabled ? <ChatUnreadIcon /> : <ChatIcon />,
29
+ title: `Chat ${chatState.enabled ? 'resumed' : 'paused'} by ${chatState.updatedBy?.userName}`,
30
+ };
31
+ ToastManager.addToast(notification);
32
+ }, [chatState]);
33
+ return <></>;
34
+ };
@@ -20,6 +20,7 @@ import { ToastBatcher } from '../Toast/ToastBatcher';
20
20
  // @ts-ignore: No implicit Any
21
21
  import { ToastManager } from '../Toast/ToastManager';
22
22
  import { AutoplayBlockedModal } from './AutoplayBlockedModal';
23
+ import { ChatNotifications } from './ChatNotifications';
23
24
  import { InitErrorModal } from './InitErrorModal';
24
25
  import { PeerNotifications } from './PeerNotifications';
25
26
  import { PermissionErrorModal } from './PermissionErrorModal';
@@ -186,6 +187,7 @@ export function Notifications() {
186
187
  <AutoplayBlockedModal />
187
188
  <PermissionErrorModal />
188
189
  <InitErrorModal />
190
+ <ChatNotifications />
189
191
  </>
190
192
  );
191
193
  }
@@ -1,6 +1,6 @@
1
1
  // @ts-check
2
2
  import React, { useCallback, useMemo, useState } from 'react';
3
- import { selectLocalPeerID, selectLocalPeerRoleName, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
3
+ import { selectLocalPeer, selectLocalPeerRoleName, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
4
4
  import { ChevronLeftIcon, ChevronRightIcon } from '@100mslive/react-icons';
5
5
  import { Box, Button, Flex, IconButton, Input, styled, Text } from '../../../../';
6
6
  import { checkCorrectAnswer } from '../../../common/utils';
@@ -38,9 +38,11 @@ export const QuestionCard = ({
38
38
  rolesThatCanViewResponses,
39
39
  }) => {
40
40
  const actions = useHMSActions();
41
- const localPeerID = useHMSStore(selectLocalPeerID);
42
- const localPeerResponse = responses?.find(response => response.peer?.peerid === localPeerID);
43
- const isLocalPeerCreator = localPeerID === startedBy;
41
+ const localPeer = useHMSStore(selectLocalPeer);
42
+ const localPeerResponse = responses?.find(
43
+ response => response.peer?.peerid === localPeer?.id || response.peer?.userid === localPeer?.customerUserId,
44
+ );
45
+ const isLocalPeerCreator = localPeer?.id === startedBy;
44
46
  const localPeerRoleName = useHMSStore(selectLocalPeerRoleName);
45
47
  const roleCanViewResponse =
46
48
  !rolesThatCanViewResponses ||
@@ -8,8 +8,9 @@ import { QuestionCard } from './QuestionCard';
8
8
  * @returns
9
9
  */
10
10
  export const TimedView = ({ poll }) => {
11
- const [currentIndex, setCurrentIndex] = useState(0);
12
- const activeQuestion = poll.questions?.[currentIndex];
11
+ // backend question index starts at 1
12
+ const [currentIndex, setCurrentIndex] = useState(1);
13
+ const activeQuestion = poll.questions?.find(question => question.index === currentIndex);
13
14
  if (!activeQuestion) {
14
15
  return null;
15
16
  }
@@ -19,10 +19,12 @@ export const VoteCount = ({ isQuiz, voteCount, isCorrectAnswer }) => {
19
19
  {isCorrectAnswer ? 'Correct' : 'Incorrect'}
20
20
  </Text>
21
21
  )}
22
- <Text variant="sm" css={{ color: '$on_surface_medium' }}>
23
- {voteCount}&nbsp;
24
- {voteCount === 1 ? 'vote' : 'votes'}
25
- </Text>
22
+ {voteCount ? (
23
+ <Text variant="sm" css={{ color: '$on_surface_medium' }}>
24
+ {voteCount}&nbsp;
25
+ {voteCount === 1 ? 'vote' : 'votes'}
26
+ </Text>
27
+ ) : null}
26
28
  </Flex>
27
29
  );
28
30
  };
@@ -186,7 +186,7 @@ const PreviewJoin = ({
186
186
  />
187
187
  </Box>
188
188
  </Container>
189
- <Box css={{ position: 'absolute', right: '0', top: 0, height: '100%' }}>
189
+ <Box css={{ position: 'absolute', right: '0', top: 0, height: '100%', overflow: 'hidden' }}>
190
190
  <SidePane screenType="default" />
191
191
  </Box>
192
192
  </Flex>
@@ -1,7 +1,6 @@
1
1
  import React, { useCallback } from 'react';
2
2
  import { selectPeerByID, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
3
3
  import {
4
- ChatIcon,
5
4
  ChatUnreadIcon,
6
5
  ConnectivityIcon,
7
6
  HandIcon,
@@ -146,24 +145,6 @@ export const ToastConfig = {
146
145
  };
147
146
  },
148
147
  },
149
- CHAT_PAUSED: {
150
- single: notification => {
151
- return {
152
- title: `Chat paused by ${notification.data?.name}`,
153
- icon: <ChatIcon />,
154
- action: <ChatAction />,
155
- };
156
- },
157
- },
158
- CHAT_RESUMED: {
159
- single: notification => {
160
- return {
161
- title: `Chat resumed by ${notification.data?.name}`,
162
- icon: <ChatUnreadIcon />,
163
- action: <ChatAction />,
164
- };
165
- },
166
- },
167
148
  RECONNECTED: {
168
149
  single: () => {
169
150
  return {