@100mslive/roomkit-react 0.1.18 → 0.1.19-alpha.1

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "prebuilt",
11
11
  "roomkit"
12
12
  ],
13
- "version": "0.1.18",
13
+ "version": "0.1.19-alpha.1",
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",
80
- "@100mslive/hms-virtual-background": "1.11.27",
81
- "@100mslive/react-icons": "0.8.27",
82
- "@100mslive/react-sdk": "0.8.27",
79
+ "@100mslive/hls-player": "0.1.28-alpha.1",
80
+ "@100mslive/hms-virtual-background": "1.11.28-alpha.1",
81
+ "@100mslive/react-icons": "0.8.28-alpha.1",
82
+ "@100mslive/react-sdk": "0.8.28-alpha.1",
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": "bebaa35da830548dc943d08a9d3cfe2f472e23b1"
118
+ "gitHead": "5f85c1840b3852eee50dbd94689eac2aa7a1f327"
119
119
  }
@@ -121,15 +121,15 @@ export enum INTERACTION_TYPE {
121
121
  export enum QUESTION_TYPE_TITLE {
122
122
  'single-choice' = 'Single Choice',
123
123
  'multiple-choice' = 'Multiple Choice',
124
- 'short-answer' = 'Short Answer',
125
- 'long-answer' = 'Long Answer',
124
+ // 'short-answer' = 'Short Answer',
125
+ // 'long-answer' = 'Long Answer',
126
126
  }
127
127
 
128
128
  export enum QUESTION_TYPE {
129
129
  SINGLE_CHOICE = 'single-choice',
130
130
  MULTIPLE_CHOICE = 'multiple-choice',
131
- SHORT_ANSWER = 'short-answer',
132
- LONG_ANSWER = 'long-answer',
131
+ // SHORT_ANSWER = 'short-answer',
132
+ // LONG_ANSWER = 'long-answer',
133
133
  }
134
134
 
135
135
  export const ROLE_CHANGE_DECLINED = 'role_change_declined';
@@ -20,7 +20,7 @@ import { Tooltip } from '../../../Tooltip';
20
20
  import { ToastManager } from '../Toast/ToastManager';
21
21
  import { MwebChatOption } from './MwebChatOption';
22
22
  import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
23
- import { useChatBlacklist } from '../hooks/useChatBlacklist';
23
+ import { useChatBlacklist, useIsPeerBlacklisted } from '../hooks/useChatBlacklist';
24
24
  import { useSetPinnedMessages } from '../hooks/useSetPinnedMessages';
25
25
  import { SESSION_STORE_KEY } from '../../common/constants';
26
26
 
@@ -68,6 +68,8 @@ export const ChatActions = ({
68
68
  SESSION_STORE_KEY.CHAT_MESSAGE_BLACKLIST,
69
69
  );
70
70
 
71
+ const isSenderBlocked = useIsPeerBlacklisted({ peerCustomerUserId: message.senderUserId });
72
+
71
73
  const updatePinnedMessages = useCallback(
72
74
  (messageID = '') => {
73
75
  const blacklistedMessageIDSet = new Set([...(blacklistedMessageIDs || []), messageID]);
@@ -140,7 +142,7 @@ export const ChatActions = ({
140
142
  }
141
143
  },
142
144
  color: '$alert_error_default',
143
- show: !!can_block_user && !sentByLocalPeer,
145
+ show: !!can_block_user && !sentByLocalPeer && !isSenderBlocked,
144
146
  },
145
147
  remove: {
146
148
  text: 'Remove Partipant',
@@ -14,7 +14,7 @@ import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvid
14
14
  import { useChatDraftMessage } from '../AppData/useChatState';
15
15
  // @ts-ignore
16
16
  import { useSetSubscribedChatSelector, useSubscribeChatSelector } from '../AppData/useUISettings';
17
- import { useIsLocalPeerBlacklisted } from '../hooks/useChatBlacklist';
17
+ import { useIsPeerBlacklisted } from '../hooks/useChatBlacklist';
18
18
  // @ts-ignore
19
19
  import { useEmojiPickerStyles } from './useEmojiPickerStyles';
20
20
  import { useDefaultChatSelection } from '../../common/hooks';
@@ -86,7 +86,7 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo
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();
89
+ const isLocalPeerBlacklisted = useIsPeerBlacklisted({ local: true });
90
90
 
91
91
  useEffect(() => {
92
92
  if (!selectedPeer.id && !selectedRole && !['Everyone', ''].includes(defaultSelection)) {
@@ -4,7 +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
+ import { useIsPeerBlacklisted } from '../hooks/useChatBlacklist';
8
8
  import { SESSION_STORE_KEY } from '../../common/constants';
9
9
 
10
10
  export const ChatPaused = () => {
@@ -55,7 +55,7 @@ export const ChatPaused = () => {
55
55
  };
56
56
 
57
57
  export const ChatBlocked = () => {
58
- const isLocalPeerBlacklisted = useIsLocalPeerBlacklisted();
58
+ const isLocalPeerBlacklisted = useIsPeerBlacklisted({ local: true });
59
59
  if (!isLocalPeerBlacklisted) {
60
60
  return null;
61
61
  }
@@ -262,9 +262,9 @@ export const isValidQuestion = ({ text, type, options, weight, isQuiz = false })
262
262
  return false;
263
263
  }
264
264
 
265
- if (![QUESTION_TYPE.SINGLE_CHOICE, QUESTION_TYPE.MULTIPLE_CHOICE].includes(type)) {
266
- return true;
267
- }
265
+ // if (![QUESTION_TYPE.SINGLE_CHOICE, QUESTION_TYPE.MULTIPLE_CHOICE].includes(type)) {
266
+ // return true;
267
+ // }
268
268
 
269
269
  const everyOptionHasText = options.length > 0 && options.every(option => option && isValidTextInput(option.text, 1));
270
270
  const hasCorrectAnswer = options.some(option => option.isCorrectAnswer);
@@ -78,7 +78,15 @@ export const Leaderboard = ({ pollID }: { pollID: string }) => {
78
78
  <Text variant="xs" css={{ color: '$on_surface_medium' }}>
79
79
  Based on score and time taken to cast the correct answer
80
80
  </Text>
81
- <Box css={{ mt: '$8', gap: '$4', overflowY: 'auto', flex: '1 1 0', mr: '-$6', pr: '$6' }}>
81
+ <Box
82
+ css={{
83
+ mt: '$8',
84
+ overflowY: 'auto',
85
+ flex: '1 1 0',
86
+ mr: '-$6',
87
+ pr: '$6',
88
+ }}
89
+ >
82
90
  {pollLeaderboard?.entries &&
83
91
  pollLeaderboard.entries.map(question => (
84
92
  <LeaderboardEntry
@@ -21,7 +21,7 @@ export const LeaderboardEntry = ({
21
21
  maxPossibleScore: number;
22
22
  }) => {
23
23
  return (
24
- <Flex align="center" justify="between">
24
+ <Flex align="center" justify="between" css={{ my: '$4' }}>
25
25
  <Flex align="center" css={{ gap: '$6' }}>
26
26
  <Flex
27
27
  align="center"
@@ -44,17 +44,17 @@ export const LeaderboardEntry = ({
44
44
  {userName}
45
45
  </Text>
46
46
 
47
- <Text variant="sm">
48
- {score}/{maxPossibleScore} points
47
+ <Text variant="sm" css={{ mt: '$2' }}>
48
+ {score} / {maxPossibleScore} points
49
49
  </Text>
50
50
  </Box>
51
51
  </Flex>
52
- <Flex align="center" css={{ gap: '$6', color: '$on_surface_medium' }}>
53
- {position === 1 ? <TrophyFilledIcon /> : null}
52
+ <Flex align="center" css={{ gap: '$4', color: '$on_surface_medium' }}>
53
+ {position === 1 ? <TrophyFilledIcon height={18} width={18} /> : null}
54
54
  <CheckCircleIcon height={16} width={16} />
55
55
  {questionCount ? (
56
56
  <Text variant="xs">
57
- {correctResponses}/{questionCount}
57
+ {correctResponses} / {questionCount}
58
58
  </Text>
59
59
  ) : null}
60
60
  </Flex>
@@ -2,22 +2,22 @@
2
2
  import React, { useCallback, useMemo, useState } from 'react';
3
3
  import { selectLocalPeer, selectLocalPeerRoleName, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
4
4
  import { CheckCircleIcon, ChevronLeftIcon, ChevronRightIcon, CrossCircleIcon } from '@100mslive/react-icons';
5
- import { Box, Button, Flex, IconButton, Input, styled, Text } from '../../../../';
5
+ import { Box, Button, Flex, IconButton, Text } from '../../../../';
6
6
  import { checkCorrectAnswer } from '../../../common/utils';
7
7
  import { MultipleChoiceOptions } from '../common/MultipleChoiceOptions';
8
8
  import { SingleChoiceOptions } from '../common/SingleChoiceOptions';
9
9
  import { QUESTION_TYPE } from '../../../common/constants';
10
10
 
11
- const TextArea = styled('textarea', {
12
- backgroundColor: '$surface_brighter',
13
- border: '1px solid $border_bright',
14
- borderRadius: '$1',
15
- mb: '$md',
16
- color: '$on_surface_high',
17
- resize: 'none',
18
- p: '$2',
19
- w: '100%',
20
- });
11
+ // const TextArea = styled('textarea', {
12
+ // backgroundColor: '$surface_brighter',
13
+ // border: '1px solid $border_bright',
14
+ // borderRadius: '$1',
15
+ // mb: '$md',
16
+ // color: '$on_surface_high',
17
+ // resize: 'none',
18
+ // p: '$2',
19
+ // w: '100%',
20
+ // });
21
21
 
22
22
  export const QuestionCard = ({
23
23
  pollID,
@@ -67,23 +67,21 @@ export const QuestionCard = ({
67
67
  setCurrentIndex(curr => Math.max(1, curr - 1));
68
68
  };
69
69
 
70
- const [textAnswer, setTextAnswer] = useState('');
70
+ // const [textAnswer, setTextAnswer] = useState('');
71
71
  const [singleOptionAnswer, setSingleOptionAnswer] = useState();
72
72
  const [multipleOptionAnswer, setMultipleOptionAnswer] = useState(new Set());
73
73
 
74
- const stringAnswerExpected = [QUESTION_TYPE.LONG_ANSWER, QUESTION_TYPE.SHORT_ANSWER].includes(type);
74
+ // const stringAnswerExpected = [QUESTION_TYPE.LONG_ANSWER, QUESTION_TYPE.SHORT_ANSWER].includes(type);
75
75
 
76
76
  const respondedToQuiz = isQuiz && localPeerResponse && !localPeerResponse.skipped;
77
77
 
78
78
  const isValidVote = useMemo(() => {
79
- if (stringAnswerExpected) {
80
- return textAnswer.length > 0;
81
- } else if (type === QUESTION_TYPE.SINGLE_CHOICE) {
79
+ if (type === QUESTION_TYPE.SINGLE_CHOICE) {
82
80
  return singleOptionAnswer !== undefined;
83
81
  } else if (type === QUESTION_TYPE.MULTIPLE_CHOICE) {
84
82
  return multipleOptionAnswer.size > 0;
85
83
  }
86
- }, [textAnswer, singleOptionAnswer, multipleOptionAnswer, type, stringAnswerExpected]);
84
+ }, [singleOptionAnswer, multipleOptionAnswer, type]);
87
85
 
88
86
  const handleVote = useCallback(async () => {
89
87
  if (!isValidVote) {
@@ -92,12 +90,11 @@ export const QuestionCard = ({
92
90
  await actions.interactivityCenter.addResponsesToPoll(pollID, [
93
91
  {
94
92
  questionIndex: index,
95
- text: textAnswer,
96
93
  option: singleOptionAnswer,
97
94
  options: Array.from(multipleOptionAnswer),
98
95
  },
99
96
  ]);
100
- }, [actions, index, pollID, isValidVote, textAnswer, singleOptionAnswer, multipleOptionAnswer]);
97
+ }, [actions, index, pollID, isValidVote, singleOptionAnswer, multipleOptionAnswer]);
101
98
 
102
99
  const handleSkip = useCallback(async () => {
103
100
  await actions.interactivityCenter.addResponsesToPoll(pollID, [
@@ -181,7 +178,7 @@ export const QuestionCard = ({
181
178
  <Text css={{ color: '$on_surface_high' }}>{text}</Text>
182
179
  </Box>
183
180
 
184
- {type === QUESTION_TYPE.SHORT_ANSWER ? (
181
+ {/* {type === QUESTION_TYPE.SHORT_ANSWER ? (
185
182
  <Input
186
183
  disabled={!canRespond}
187
184
  placeholder="Enter your answer"
@@ -194,15 +191,15 @@ export const QuestionCard = ({
194
191
  cursor: localPeerResponse ? 'not-allowed' : 'text',
195
192
  }}
196
193
  />
197
- ) : null}
194
+ ) : null} */}
198
195
 
199
- {type === QUESTION_TYPE.LONG_ANSWER ? (
196
+ {/* {type === QUESTION_TYPE.LONG_ANSWER ? (
200
197
  <TextArea
201
198
  disabled={!canRespond}
202
199
  placeholder="Enter your answer"
203
200
  onChange={e => setTextAnswer(e.target.value)}
204
201
  />
205
- ) : null}
202
+ ) : null} */}
206
203
 
207
204
  {type === QUESTION_TYPE.SINGLE_CHOICE ? (
208
205
  <SingleChoiceOptions
@@ -1,6 +1,6 @@
1
1
  import React, { useEffect, useRef, useState } from 'react';
2
2
  import { HMSVirtualBackgroundTypes } from '@100mslive/hms-virtual-background';
3
- import { VirtualBackground, VirtualBackgroundMedia } from '@100mslive/types-prebuilt/elements/virtual_background';
3
+ import { VirtualBackgroundMedia } from '@100mslive/types-prebuilt/elements/virtual_background';
4
4
  import {
5
5
  HMSRoomState,
6
6
  selectIsLargeRoom,
@@ -28,7 +28,7 @@ import { defaultMedia, vbPlugin } from './constants';
28
28
  const iconDims = { height: '40px', width: '40px' };
29
29
  const MAX_RETRIES = 2;
30
30
 
31
- export const VBPicker = ({ background_media = [] }: VirtualBackground = {}) => {
31
+ export const VBPicker = ({ backgroundMedia = [] }: { backgroundMedia: VirtualBackgroundMedia[] }) => {
32
32
  const toggleVB = useSidepaneToggle(SIDE_PANE_OPTIONS.VB);
33
33
  const hmsActions = useHMSActions();
34
34
  const role = useHMSStore(selectLocalPeerRole);
@@ -46,7 +46,9 @@ export const VBPicker = ({ background_media = [] }: VirtualBackground = {}) => {
46
46
  const roomState = useHMSStore(selectRoomState);
47
47
  const isLargeRoom = useHMSStore(selectIsLargeRoom);
48
48
  const addedPluginToVideoTrack = useRef(false);
49
- const mediaList = [...background_media.map((media: VirtualBackgroundMedia) => media?.url), ...defaultMedia];
49
+ const mediaList = backgroundMedia.length
50
+ ? backgroundMedia.map((media: VirtualBackgroundMedia) => media?.url)
51
+ : defaultMedia;
50
52
 
51
53
  const inPreview = roomState === HMSRoomState.Preview;
52
54
  // Hidden in preview as the effect will be visible in the preview tile. Needed inside the room because the peer might not be on-screen
@@ -22,8 +22,8 @@ export const useChatBlacklist = (
22
22
  return { blacklistItem, blacklistedIDs };
23
23
  };
24
24
 
25
- export const useIsLocalPeerBlacklisted = () => {
25
+ export const useIsPeerBlacklisted = ({ local = false, peerCustomerUserId = '' }) => {
26
26
  const localPeer = useHMSStore(selectLocalPeer);
27
27
  const blacklistedPeerIDs = useHMSStore(selectSessionStore(SESSION_STORE_KEY.CHAT_PEER_BLACKLIST)) || [];
28
- return blacklistedPeerIDs?.includes(localPeer?.customerUserId);
28
+ return blacklistedPeerIDs?.includes(local ? localPeer?.customerUserId : peerCustomerUserId);
29
29
  };
@@ -12,7 +12,10 @@ import { Box, Flex } from '../../Layout';
12
12
  import { config as cssConfig } from '../../Theme';
13
13
  // @ts-ignore: No implicit Any
14
14
  import { useSidepaneReset } from '../components/AppData/useSidepane';
15
- import { useRoomLayoutConferencingScreen } from '../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
15
+ import {
16
+ useRoomLayoutConferencingScreen,
17
+ useRoomLayoutPreviewScreen,
18
+ } from '../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
16
19
  import { translateAcross } from '../../utils';
17
20
  import { APP_DATA, SIDE_PANE_OPTIONS } from '../common/constants';
18
21
 
@@ -28,6 +31,7 @@ const SidePane = ({
28
31
  const activeScreensharePeerId = useHMSStore(selectAppData(APP_DATA.activeScreensharePeerId));
29
32
  const trackId = useHMSStore(selectVideoTrackByPeerID(activeScreensharePeerId))?.id;
30
33
  const { elements } = useRoomLayoutConferencingScreen();
34
+ const { elements: preview_elements } = useRoomLayoutPreviewScreen();
31
35
  const resetSidePane = useSidepaneReset();
32
36
  let ViewComponent;
33
37
  if (sidepane === SIDE_PANE_OPTIONS.POLLS) {
@@ -37,7 +41,7 @@ const SidePane = ({
37
41
  ViewComponent = <SidePaneTabs hideControls={hideControls} active={sidepane} />;
38
42
  }
39
43
  if (sidepane === SIDE_PANE_OPTIONS.VB) {
40
- ViewComponent = <VBPicker {...elements.virtual_background} />;
44
+ ViewComponent = <VBPicker backgroundMedia={preview_elements?.virtual_background?.background_media || []} />;
41
45
  }
42
46
 
43
47
  useEffect(() => {
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import type { Layout } from '@100mslive/types-prebuilt';
3
- import merge from 'lodash.merge';
3
+ import { isArray, mergeWith } from 'lodash';
4
4
  // @ts-ignore: fix types
5
5
  import { useAuthToken } from '../../components/AppData/useUISettings';
6
6
  import { useFetchRoomLayout, useFetchRoomLayoutResponse } from './hooks/useFetchRoomLayout';
@@ -18,6 +18,17 @@ export const RoomLayoutContext = React.createContext<
18
18
  | undefined
19
19
  >(undefined);
20
20
 
21
+ // The default merge in lodash merges the values of current layout and the changes.
22
+ // This behaviour makes changes in array based values inconsistent since a union happens.
23
+ // The customizer uses the new value provided if one of the values is an array
24
+ function customizer(objValue: any, srcValue: any) {
25
+ if (isArray(objValue) || isArray(srcValue)) {
26
+ return srcValue;
27
+ }
28
+ // default merge behaviour is followed
29
+ return undefined;
30
+ }
31
+
21
32
  export const RoomLayoutProvider: React.FC<React.PropsWithChildren<RoomLayoutProviderProps>> = ({
22
33
  children,
23
34
  roomLayoutEndpoint,
@@ -25,7 +36,7 @@ export const RoomLayoutProvider: React.FC<React.PropsWithChildren<RoomLayoutProv
25
36
  }) => {
26
37
  const authToken: string = useAuthToken();
27
38
  const { layout, updateRoomLayoutForRole } = useFetchRoomLayout({ authToken, endpoint: roomLayoutEndpoint });
28
- const mergedLayout = authToken && layout ? merge(layout, overrideLayout) : layout;
39
+ const mergedLayout = authToken && layout ? mergeWith(layout, overrideLayout, customizer) : layout;
29
40
  return (
30
41
  <RoomLayoutContext.Provider value={{ layout: mergedLayout, updateRoomLayoutForRole }}>
31
42
  {children}