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

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 (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 {