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

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 (42) hide show
  1. package/dist/{HLSView-UBVLOPNY.js → HLSView-7X5BVAZE.js} +2 -2
  2. package/dist/Prebuilt/App.d.ts +1 -0
  3. package/dist/Prebuilt/common/constants.d.ts +1 -1
  4. package/dist/Prebuilt/components/Chat/EmptyChat.d.ts +2 -0
  5. package/dist/Prebuilt/components/Polls/Voting/{Leaderboard.d.ts → LeaderboardSummary.d.ts} +1 -1
  6. package/dist/Prebuilt/components/Polls/Voting/StatisticBox.d.ts +5 -0
  7. package/dist/Prebuilt/components/VirtualBackground/VBCollection.d.ts +3 -4
  8. package/dist/Prebuilt/components/VirtualBackground/VBHandler.d.ts +13 -0
  9. package/dist/Prebuilt/components/VirtualBackground/VBPicker.d.ts +1 -1
  10. package/dist/Prebuilt/components/VirtualBackground/constants.d.ts +0 -2
  11. package/dist/{chunk-VU2CQFCB.js → chunk-H3B4ARYV.js} +1396 -1294
  12. package/dist/chunk-H3B4ARYV.js.map +7 -0
  13. package/dist/index.cjs.js +1741 -1610
  14. package/dist/index.cjs.js.map +4 -4
  15. package/dist/index.js +1 -1
  16. package/dist/meta.cjs.json +227 -90
  17. package/dist/meta.esbuild.json +234 -97
  18. package/package.json +6 -6
  19. package/src/Prebuilt/App.tsx +2 -1
  20. package/src/Prebuilt/common/constants.ts +1 -1
  21. package/src/Prebuilt/components/Chat/ChatActions.tsx +3 -3
  22. package/src/Prebuilt/components/Chat/ChatBody.tsx +28 -46
  23. package/src/Prebuilt/components/Chat/EmptyChat.tsx +51 -0
  24. package/src/Prebuilt/components/Footer/PollsToggle.tsx +7 -1
  25. package/src/Prebuilt/components/Notifications/Notifications.tsx +0 -29
  26. package/src/Prebuilt/components/Polls/Polls.tsx +2 -2
  27. package/src/Prebuilt/components/Polls/Voting/LeaderboardEntry.tsx +2 -2
  28. package/src/Prebuilt/components/Polls/Voting/LeaderboardSummary.tsx +162 -0
  29. package/src/Prebuilt/components/Polls/Voting/PeerParticipationSummary.tsx +2 -9
  30. package/src/Prebuilt/components/Polls/Voting/StatisticBox.tsx +15 -0
  31. package/src/Prebuilt/components/Polls/Voting/Voting.jsx +1 -17
  32. package/src/Prebuilt/components/TileMenu/TileMenuContent.tsx +1 -1
  33. package/src/Prebuilt/components/VirtualBackground/VBCollection.tsx +5 -11
  34. package/src/Prebuilt/components/VirtualBackground/VBHandler.tsx +72 -0
  35. package/src/Prebuilt/components/VirtualBackground/VBPicker.tsx +44 -81
  36. package/src/Prebuilt/components/VirtualBackground/constants.ts +0 -4
  37. package/src/Prebuilt/layouts/SidePane.tsx +6 -1
  38. package/src/Theme/stitches.config.ts +2 -10
  39. package/dist/chunk-VU2CQFCB.js.map +0 -7
  40. package/src/Prebuilt/components/Polls/Voting/Leaderboard.tsx +0 -123
  41. package/src/Prebuilt/components/Polls/Voting/PollResultSummary.jsx +0 -125
  42. /package/dist/{HLSView-UBVLOPNY.js.map → HLSView-7X5BVAZE.js.map} +0 -0
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "prebuilt",
11
11
  "roomkit"
12
12
  ],
13
- "version": "0.1.19-alpha.1",
13
+ "version": "0.1.19-alpha.2",
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.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",
79
+ "@100mslive/hls-player": "0.1.28-alpha.2",
80
+ "@100mslive/hms-virtual-background": "1.11.28-alpha.2",
81
+ "@100mslive/react-icons": "0.8.28-alpha.2",
82
+ "@100mslive/react-sdk": "0.8.28-alpha.2",
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": "5f85c1840b3852eee50dbd94689eac2aa7a1f327"
118
+ "gitHead": "89886ce5263b9ac84ae04737f2c258933eb60280"
119
119
  }
@@ -51,6 +51,7 @@ export type HMSPrebuiltOptions = {
51
51
  userName?: string;
52
52
  userId?: string;
53
53
  endpoints?: object;
54
+ effectsSDKKey?: string;
54
55
  };
55
56
 
56
57
  export type HMSPrebuiltProps = {
@@ -215,7 +216,7 @@ export const HMSPrebuilt = React.forwardRef<HMSPrebuiltRefType, HMSPrebuiltProps
215
216
  <Init />
216
217
  <DialogContainerProvider dialogContainerSelector={containerSelector}>
217
218
  <Box
218
- id={DEFAULT_PORTAL_CONTAINER.slice(1)} //Skips the #
219
+ className={DEFAULT_PORTAL_CONTAINER.slice(1)} // Skips the '.' in the selector
219
220
  css={{
220
221
  bg: '$background_dim',
221
222
  size: '100%',
@@ -134,4 +134,4 @@ export enum QUESTION_TYPE {
134
134
 
135
135
  export const ROLE_CHANGE_DECLINED = 'role_change_declined';
136
136
 
137
- export const DEFAULT_PORTAL_CONTAINER = '#prebuilt-container';
137
+ export const DEFAULT_PORTAL_CONTAINER = '.prebuilt-container';
@@ -104,8 +104,8 @@ export const ChatActions = ({
104
104
  }
105
105
  > = {
106
106
  reply: {
107
- text: message.recipientRoles?.length ? 'Reply to Group' : 'Reply Privately',
108
- tooltipText: message.recipientRoles?.length ? 'Reply to Group' : 'Reply Privately',
107
+ text: message.recipientRoles?.length ? 'Reply to group' : 'Reply privately',
108
+ tooltipText: message.recipientRoles?.length ? 'Reply to group' : 'Reply privately',
109
109
  icon: <ReplyIcon style={iconStyle} />,
110
110
  onClick: onReply,
111
111
  show: showReply,
@@ -145,7 +145,7 @@ export const ChatActions = ({
145
145
  show: !!can_block_user && !sentByLocalPeer && !isSenderBlocked,
146
146
  },
147
147
  remove: {
148
- text: 'Remove Partipant',
148
+ text: 'Remove participant',
149
149
  icon: <PeopleRemoveIcon style={iconStyle} />,
150
150
  color: '$alert_error_default',
151
151
  show: !!canRemoveOthers && !sentByLocalPeer,
@@ -1,4 +1,4 @@
1
- import React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';
1
+ import React, { Fragment, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
2
2
  import { useMedia } from 'react-use';
3
3
  import AutoSizer from 'react-virtualized-auto-sizer';
4
4
  import { VariableSizeList } from 'react-window';
@@ -7,11 +7,11 @@ import {
7
7
  HMSPeerID,
8
8
  HMSRoleName,
9
9
  selectHMSMessages,
10
- selectHMSMessagesCount,
11
10
  selectLocalPeerID,
12
11
  selectLocalPeerRoleName,
13
12
  selectPeerNameByID,
14
13
  selectSessionStore,
14
+ selectUnreadHMSMessagesCount,
15
15
  useHMSActions,
16
16
  useHMSStore,
17
17
  useHMSVanillaStore,
@@ -20,9 +20,8 @@ import { Box, Flex } from '../../../Layout';
20
20
  import { Text } from '../../../Text';
21
21
  import { config as cssConfig, styled } from '../../../Theme';
22
22
  import { Tooltip } from '../../../Tooltip';
23
- // @ts-ignore
24
- import emptyChat from '../../images/empty-chat.svg';
25
23
  import { ChatActions } from './ChatActions';
24
+ import { EmptyChat } from './EmptyChat';
26
25
  import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
27
26
  // @ts-ignore: No implicit Any
28
27
  import { useSetSubscribedChatSelector } from '../AppData/useUISettings';
@@ -38,15 +37,19 @@ const formatTime = (date: Date) => {
38
37
  return `${hours < 10 ? '0' : ''}${hours}:${minutes < 10 ? '0' : ''}${minutes} ${suffix}`;
39
38
  };
40
39
 
41
- const rowHeights: Record<number, number> = {};
40
+ const rowHeights: Record<number, { size: number; id: string }> = {};
41
+ let listInstance: VariableSizeList | null = null; //eslint-disable-line
42
42
  function getRowHeight(index: number) {
43
43
  // 72 will be default row height for any message length
44
- // 16 will add margin value as clientHeight don't include margin
45
- return rowHeights[index] + 16 || 72;
44
+ return rowHeights[index]?.size || 72;
46
45
  }
47
46
 
48
- const setRowHeight = (index: number, size: number) => {
49
- Object.assign(rowHeights, { [index]: size });
47
+ const setRowHeight = (index: number, id: string, size: number) => {
48
+ if (rowHeights[index]?.id === id && rowHeights[index]?.size) {
49
+ return;
50
+ }
51
+ listInstance?.resetAfterIndex(Math.max(index - 1, 0));
52
+ Object.assign(rowHeights, { [index]: { size, id } });
50
53
  };
51
54
 
52
55
  const MessageTypeContainer = ({ left, right }: { left?: string; right?: string }) => {
@@ -181,11 +184,11 @@ const ChatMessage = React.memo(
181
184
  showReply = true;
182
185
  }
183
186
 
184
- useEffect(() => {
187
+ useLayoutEffect(() => {
185
188
  if (rowRef.current) {
186
- setRowHeight(index, rowRef.current.clientHeight);
189
+ setRowHeight(index, message.id, rowRef.current.clientHeight);
187
190
  }
188
- }, [index]);
191
+ }, [index, message.id]);
189
192
 
190
193
  return (
191
194
  <Box
@@ -356,7 +359,13 @@ const VirtualizedChatMessages = React.forwardRef<
356
359
  >
357
360
  {({ height, width }: { height: number; width: number }) => (
358
361
  <VariableSizeList
359
- ref={listRef}
362
+ ref={node => {
363
+ if (node) {
364
+ // @ts-ignore
365
+ listRef.current = node;
366
+ listInstance = node;
367
+ }
368
+ }}
360
369
  itemCount={messages.length}
361
370
  itemSize={getRowHeight}
362
371
  itemData={messages}
@@ -391,52 +400,25 @@ export const ChatBody = React.forwardRef<VariableSizeList, { scrollToBottom: (co
391
400
  return messages?.filter(message => message.type === 'chat' && !blacklistedMessageIDSet.has(message.id)) || [];
392
401
  }, [blacklistedMessageIDs, messages]);
393
402
 
394
- const isMobile = useMedia(cssConfig.media.md);
395
- const { elements } = useRoomLayoutConferencingScreen();
396
403
  const vanillaStore = useHMSVanillaStore();
397
404
 
398
405
  useEffect(() => {
399
406
  const unsubscribe = vanillaStore.subscribe(() => {
400
407
  // @ts-ignore
401
- if (!listRef?.current) {
408
+ if (!listRef.current) {
402
409
  return;
403
410
  }
404
411
  // @ts-ignore
405
412
  const outerElement = listRef.current._outerRef;
406
- // @ts-ignore
407
- if (outerElement.scrollHeight - (listRef.current.state.scrollOffset + outerElement.offsetHeight) <= 10) {
408
- scrollToBottom(1);
413
+ if (outerElement.clientHeight + outerElement.scrollTop + outerElement.offsetTop >= outerElement.scrollHeight) {
414
+ requestAnimationFrame(() => scrollToBottom(1));
409
415
  }
410
- }, selectHMSMessagesCount);
416
+ }, selectUnreadHMSMessagesCount);
411
417
  return unsubscribe;
412
418
  }, [vanillaStore, listRef, scrollToBottom]);
413
419
 
414
- if (filteredMessages.length === 0 && !(isMobile && elements?.chat?.is_overlay)) {
415
- return (
416
- <Flex
417
- css={{
418
- width: '100%',
419
- flex: '1 1 0',
420
- textAlign: 'center',
421
- px: '$4',
422
- }}
423
- align="center"
424
- justify="center"
425
- >
426
- <Box>
427
- <img src={emptyChat} alt="Empty Chat" height={132} width={185} style={{ margin: '0 auto' }} />
428
- <Text variant="h5" css={{ mt: '$8', c: '$on_surface_high' }}>
429
- Start a conversation
430
- </Text>
431
- <Text
432
- variant="sm"
433
- css={{ mt: '$4', maxWidth: '80%', textAlign: 'center', mx: 'auto', c: '$on_surface_medium' }}
434
- >
435
- There are no messages here yet. Start a conversation by sending a message.
436
- </Text>
437
- </Box>
438
- </Flex>
439
- );
420
+ if (filteredMessages.length === 0) {
421
+ return <EmptyChat />;
440
422
  }
441
423
 
442
424
  return <VirtualizedChatMessages messages={filteredMessages} ref={listRef} scrollToBottom={scrollToBottom} />;
@@ -0,0 +1,51 @@
1
+ import React from 'react';
2
+ import { useMedia } from 'react-use';
3
+ import { Box, Flex } from '../../../Layout';
4
+ import { Text } from '../../../Text';
5
+ import { config as cssConfig } from '../../../Theme';
6
+ // @ts-ignore
7
+ import emptyChat from '../../images/empty-chat.svg';
8
+ import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
9
+ import { useIsPeerBlacklisted } from '../hooks/useChatBlacklist';
10
+
11
+ export const EmptyChat = () => {
12
+ const { elements } = useRoomLayoutConferencingScreen();
13
+ const isLocalPeerBlacklisted = useIsPeerBlacklisted({ local: true });
14
+ const isMobile = useMedia(cssConfig.media.md);
15
+ const canSendMessages =
16
+ elements.chat &&
17
+ (elements.chat.public_chat_enabled ||
18
+ elements.chat.private_chat_enabled ||
19
+ (elements.chat.roles_whitelist && elements.chat.roles_whitelist.length)) &&
20
+ !isLocalPeerBlacklisted;
21
+
22
+ if (isMobile && elements?.chat?.is_overlay) return <></>;
23
+
24
+ return (
25
+ <Flex
26
+ css={{
27
+ width: '100%',
28
+ flex: '1 1 0',
29
+ textAlign: 'center',
30
+ px: '$4',
31
+ }}
32
+ align="center"
33
+ justify="center"
34
+ >
35
+ <Box>
36
+ <img src={emptyChat} alt="Empty Chat" height={132} width={185} style={{ margin: '0 auto' }} />
37
+ <Text variant="h5" css={{ mt: '$8', c: '$on_surface_high' }}>
38
+ {canSendMessages ? 'Start a conversation' : 'No messages yet'}
39
+ </Text>
40
+ {canSendMessages ? (
41
+ <Text
42
+ variant="sm"
43
+ css={{ mt: '$4', maxWidth: '80%', textAlign: 'center', mx: 'auto', c: '$on_surface_medium' }}
44
+ >
45
+ There are no messages here yet. Start a conversation by sending a message.
46
+ </Text>
47
+ ) : null}
48
+ </Box>
49
+ </Flex>
50
+ );
51
+ };
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useEffect } from 'react';
2
2
  import { QuizActiveIcon, QuizIcon } from '@100mslive/react-icons';
3
3
  import { Tooltip } from '../../..';
4
4
  // @ts-ignore: No implicit Any
@@ -14,6 +14,12 @@ export const PollsToggle = () => {
14
14
  const togglePollView = usePollViewToggle();
15
15
  const { unreadPollQuiz, setUnreadPollQuiz } = useUnreadPollQuizPresent();
16
16
 
17
+ useEffect(() => {
18
+ if (unreadPollQuiz && isPollsOpen) {
19
+ setUnreadPollQuiz(false);
20
+ }
21
+ }, [isPollsOpen, unreadPollQuiz, setUnreadPollQuiz]);
22
+
17
23
  return (
18
24
  <Tooltip key="polls" title={`${isPollsOpen ? 'Close' : 'Open'} polls and quizzes`}>
19
25
  <IconButton
@@ -56,36 +56,7 @@ export function Notifications() {
56
56
  });
57
57
  }, []);
58
58
 
59
- /*
60
- const leaderboardResultsShared = useCallback(
61
- (stringifiedPollDetails: string) => {
62
- const pollDetails = JSON.parse(stringifiedPollDetails);
63
- if (pollDetails.startedBy !== localPeerID) {
64
- const pollStartedBy = pollDetails.initiatorName;
65
- ToastManager.addToast({
66
- title: `${pollStartedBy} shared leaderboard for the quiz`,
67
- action: (
68
- <Button
69
- onClick={() => togglePollView(pollDetails.id)}
70
- variant="standard"
71
- css={{
72
- backgroundColor: '$surface_bright',
73
- fontWeight: '$semiBold',
74
- color: '$on_surface_high',
75
- p: '$xs $md',
76
- }}
77
- >
78
- View
79
- </Button>
80
- ),
81
- });
82
- }
83
- },
84
- [localPeerID, togglePollView],
85
- ); */
86
-
87
59
  useCustomEvent({ type: ROLE_CHANGE_DECLINED, onEvent: handleRoleChangeDenied });
88
- // useCustomEvent({ type: 'POLL_LEADERBOARD_SHARED', onEvent: leaderboardResultsShared });
89
60
 
90
61
  useEffect(() => {
91
62
  if (!notification || isNotificationDisabled) {
@@ -3,7 +3,7 @@ import React from 'react';
3
3
  import { PollsQuizMenu } from './CreatePollQuiz/PollsQuizMenu';
4
4
  // @ts-ignore: No implicit Any
5
5
  import { CreateQuestions } from './CreateQuestions/CreateQuestions';
6
- import { Leaderboard } from './Voting/Leaderboard';
6
+ import { LeaderboardSummary } from './Voting/LeaderboardSummary';
7
7
  // @ts-ignore: No implicit Any
8
8
  import { Voting } from './Voting/Voting';
9
9
  // @ts-ignore: No implicit Any
@@ -24,7 +24,7 @@ export const Polls = () => {
24
24
  } else if (view === POLL_VIEWS.VOTE) {
25
25
  return <Voting toggleVoting={togglePollView} id={pollID} />;
26
26
  } else if (view === POLL_VIEWS.RESULTS) {
27
- return <Leaderboard pollID={pollID} />;
27
+ return <LeaderboardSummary pollID={pollID} />;
28
28
  } else {
29
29
  return null;
30
30
  }
@@ -44,13 +44,13 @@ export const LeaderboardEntry = ({
44
44
  {userName}
45
45
  </Text>
46
46
 
47
- <Text variant="sm" css={{ mt: '$2' }}>
47
+ <Text variant="sm" css={{ mt: '$1' }}>
48
48
  {score} / {maxPossibleScore} points
49
49
  </Text>
50
50
  </Box>
51
51
  </Flex>
52
52
  <Flex align="center" css={{ gap: '$4', color: '$on_surface_medium' }}>
53
- {position === 1 ? <TrophyFilledIcon height={18} width={18} /> : null}
53
+ {position === 1 ? <TrophyFilledIcon height={16} width={16} /> : null}
54
54
  <CheckCircleIcon height={16} width={16} />
55
55
  {questionCount ? (
56
56
  <Text variant="xs">
@@ -0,0 +1,162 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import {
3
+ HMSQuizLeaderboardResponse,
4
+ HMSQuizLeaderboardSummary,
5
+ selectPollByID,
6
+ useHMSActions,
7
+ useHMSStore,
8
+ } from '@100mslive/react-sdk';
9
+ import { ChevronLeftIcon, ChevronRightIcon, CrossIcon } from '@100mslive/react-icons';
10
+ import { Box, Flex } from '../../../../Layout';
11
+ import { Loading } from '../../../../Loading';
12
+ import { Text } from '../../../../Text';
13
+ import { LeaderboardEntry } from './LeaderboardEntry';
14
+ import { StatisticBox } from './StatisticBox';
15
+ // @ts-ignore
16
+ import { useSidepaneToggle } from '../../AppData/useSidepane';
17
+ // @ts-ignore
18
+ import { usePollViewState } from '../../AppData/useUISettings';
19
+ // @ts-ignore
20
+ import { StatusIndicator } from '../common/StatusIndicator';
21
+ import { POLL_VIEWS } from '../../../common/constants';
22
+
23
+ export const LeaderboardSummary = ({ pollID }: { pollID: string }) => {
24
+ const hmsActions = useHMSActions();
25
+ const quiz = useHMSStore(selectPollByID(pollID));
26
+ const [quizLeaderboard, setQuizLeaderboard] = useState<HMSQuizLeaderboardResponse | undefined>();
27
+ const [viewAllEntries, setViewAllEntries] = useState(false);
28
+ const summary: HMSQuizLeaderboardSummary = quizLeaderboard?.summary || {
29
+ totalUsers: 0,
30
+ votedUsers: 0,
31
+ avgScore: 0,
32
+ avgTime: 0,
33
+ correctAnswers: 0,
34
+ };
35
+
36
+ const { setPollView } = usePollViewState();
37
+ const toggleSidepane = useSidepaneToggle();
38
+
39
+ useEffect(() => {
40
+ const fetchLeaderboardData = async () => {
41
+ if (!quizLeaderboard && quiz) {
42
+ const leaderboardData = await hmsActions.interactivityCenter.fetchLeaderboard(quiz, 0, 50);
43
+ setQuizLeaderboard(leaderboardData);
44
+ }
45
+ };
46
+ fetchLeaderboardData();
47
+ }, [quiz, hmsActions.interactivityCenter, quizLeaderboard]);
48
+
49
+ if (!quiz || !quizLeaderboard)
50
+ return (
51
+ <Flex align="center" justify="center" css={{ size: '100%' }}>
52
+ <Loading />
53
+ </Flex>
54
+ );
55
+
56
+ const defaultCalculations = { maxPossibleScore: 0, totalResponses: 0 };
57
+ const { maxPossibleScore, totalResponses } =
58
+ quiz.questions?.reduce((accumulator, question) => {
59
+ accumulator.maxPossibleScore += question.weight || 0;
60
+ accumulator.totalResponses += question?.responses?.length || 0;
61
+ return accumulator;
62
+ }, defaultCalculations) || defaultCalculations;
63
+
64
+ const questionCount = quiz.questions?.length || 0;
65
+
66
+ return (
67
+ <Flex direction="column" css={{ size: '100%' }}>
68
+ <Flex justify="between" align="center" css={{ pb: '$6', borderBottom: '1px solid $border_bright', mb: '$8' }}>
69
+ <Flex align="center" css={{ gap: '$4' }}>
70
+ <Flex
71
+ css={{ color: '$on_surface_medium', '&:hover': { color: '$on_surface_high', cursor: 'pointer' } }}
72
+ onClick={() => setPollView(POLL_VIEWS.VOTE)}
73
+ >
74
+ <ChevronLeftIcon />
75
+ </Flex>
76
+ <Text variant="lg" css={{ fontWeight: '$semiBold' }}>
77
+ {quiz.title}
78
+ </Text>
79
+ <StatusIndicator isLive={false} />
80
+ </Flex>
81
+ <Flex
82
+ css={{ color: '$on_surface_medium', '&:hover': { color: '$on_surface_high', cursor: 'pointer' } }}
83
+ onClick={toggleSidepane}
84
+ >
85
+ <CrossIcon />
86
+ </Flex>
87
+ </Flex>
88
+
89
+ {!viewAllEntries ? (
90
+ <Box css={{ py: '$4' }}>
91
+ <Text variant="sm" css={{ fontWeight: '$semiBold' }}>
92
+ Participation Summary
93
+ </Text>
94
+
95
+ <Box css={{ my: '$4' }}>
96
+ <Flex css={{ w: '100%', gap: '$4' }}>
97
+ <StatisticBox
98
+ title="Voted"
99
+ value={`${summary?.totalUsers ? (100 * summary?.votedUsers) / summary?.totalUsers : 0}%`}
100
+ />
101
+ <StatisticBox title="Correct Answers" value={`${summary?.correctAnswers}/${totalResponses}`} />
102
+ </Flex>
103
+ <Flex css={{ w: '100%', gap: '$4', mt: '$4' }}>
104
+ {summary?.avgTime > 0 ? <StatisticBox title="Avg. Time" value={summary?.avgTime} /> : null}
105
+ <StatisticBox title="Avg. Score" value={summary?.avgScore} />
106
+ </Flex>
107
+ </Box>
108
+ </Box>
109
+ ) : null}
110
+
111
+ <Text variant="sm" css={{ fontWeight: '$semiBold' }}>
112
+ Leaderboard
113
+ </Text>
114
+ <Text variant="xs" css={{ color: '$on_surface_medium' }}>
115
+ Based on score and time taken to cast the correct answer
116
+ </Text>
117
+ <Box
118
+ css={{
119
+ mt: '$8',
120
+ overflowY: 'auto',
121
+ flex: viewAllEntries ? '1 1 0' : 'unset',
122
+ mr: viewAllEntries ? '-$6' : 'unset',
123
+ px: viewAllEntries ? '0' : '$4',
124
+ pr: viewAllEntries ? '$6' : '$4',
125
+ backgroundColor: viewAllEntries ? '' : '$surface_default',
126
+ borderRadius: '$1',
127
+ }}
128
+ >
129
+ {quizLeaderboard?.entries &&
130
+ quizLeaderboard.entries
131
+ .slice(0, viewAllEntries ? undefined : 5)
132
+ .map(question => (
133
+ <LeaderboardEntry
134
+ key={question.position}
135
+ position={question.position}
136
+ score={question.score}
137
+ questionCount={questionCount}
138
+ correctResponses={question.correctResponses}
139
+ userName={question.peer.username || ''}
140
+ maxPossibleScore={maxPossibleScore}
141
+ />
142
+ ))}
143
+ {quizLeaderboard?.entries?.length > 5 && !viewAllEntries ? (
144
+ <Flex
145
+ align="center"
146
+ justify="end"
147
+ css={{
148
+ w: '100%',
149
+ borderTop: '1px solid $border_bright',
150
+ cursor: 'pointer',
151
+ color: '$on_surface_high',
152
+ p: '$6 $2',
153
+ }}
154
+ onClick={() => setViewAllEntries(true)}
155
+ >
156
+ <Text variant="sm">View All</Text> <ChevronRightIcon />
157
+ </Flex>
158
+ ) : null}
159
+ </Box>
160
+ </Flex>
161
+ );
162
+ };
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import { HMSPoll, selectLocalPeer, useHMSStore } from '@100mslive/react-sdk';
3
3
  import { Box } from '../../../../Layout';
4
4
  import { Text } from '../../../../Text';
5
+ import { StatisticBox } from './StatisticBox';
5
6
  // @ts-ignore
6
7
  import { getPeerParticipationSummary } from '../../../common/utils';
7
8
 
@@ -22,15 +23,7 @@ export const PeerParticipationSummary = ({ poll }: { poll: HMSPoll }) => {
22
23
  <Text css={{ fontWeight: '$semiBold', my: '$8' }}>Participation Summary</Text>
23
24
  <Box css={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '$4' }}>
24
25
  {boxes.map(box => (
25
- <Box key={box.title} css={{ p: '$8', background: '$surface_default', borderRadius: '$1' }}>
26
- <Text
27
- variant="tiny"
28
- css={{ textTransform: 'uppercase', color: '$on_surface_medium', fontWeight: '$semiBold', my: '$4' }}
29
- >
30
- {box.title}
31
- </Text>
32
- <Text css={{ fontWeight: '$semiBold' }}>{box.value}</Text>
33
- </Box>
26
+ <StatisticBox key={box.title} title={box.title} value={box.value} />
34
27
  ))}
35
28
  </Box>
36
29
  </Box>
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ import { Box } from '../../../../Layout';
3
+ import { Text } from '../../../../Text';
4
+
5
+ export const StatisticBox = ({ title, value = 0 }: { title: string; value: string | number }) => (
6
+ <Box css={{ p: '$8', background: '$surface_default', borderRadius: '$1', w: '100%' }}>
7
+ <Text
8
+ variant="tiny"
9
+ css={{ textTransform: 'uppercase', color: '$on_surface_medium', fontWeight: '$semiBold', my: '$4' }}
10
+ >
11
+ {title}
12
+ </Text>
13
+ <Text css={{ fontWeight: '$semiBold' }}>{value}</Text>
14
+ </Box>
15
+ );
@@ -3,7 +3,6 @@ import React from 'react';
3
3
  import {
4
4
  selectLocalPeerID,
5
5
  selectPeerNameByID,
6
- selectPermissions,
7
6
  selectPollByID,
8
7
  useHMSActions,
9
8
  useHMSStore,
@@ -11,7 +10,6 @@ import {
11
10
  import { ChevronLeftIcon, CrossIcon } from '@100mslive/react-icons';
12
11
  import { Box, Button, Flex, Text } from '../../../../';
13
12
  import { Container } from '../../Streaming/Common';
14
- // import { PollResultSummary } from "./PollResultSummary";
15
13
  import { StandardView } from './StandardVoting';
16
14
  import { TimedView } from './TimedVoting';
17
15
  import { usePollViewState } from '../../AppData/useUISettings';
@@ -24,17 +22,12 @@ export const Voting = ({ id, toggleVoting }) => {
24
22
  const pollCreatorName = useHMSStore(selectPeerNameByID(poll?.createdBy));
25
23
  const isLocalPeerCreator = useHMSStore(selectLocalPeerID) === poll?.createdBy;
26
24
  const { setPollView } = usePollViewState();
27
- const permissions = useHMSStore(selectPermissions);
28
-
29
- // const sharedLeaderboards = useHMSStore(selectSessionStore(SESSION_STORE_KEY.SHARED_LEADERBOARDS));
30
25
 
31
26
  if (!poll) {
32
27
  return null;
33
28
  }
34
29
 
35
- // const isLeaderboardShared = (sharedLeaderboards || []).includes(id);
36
- const canViewLeaderboard =
37
- poll.type === 'quiz' && poll.state === 'stopped' && !poll.anonymous && permissions?.pollWrite;
30
+ const canViewLeaderboard = poll.type === 'quiz' && poll.state === 'stopped' && !poll.anonymous;
38
31
 
39
32
  // Sets view - linear or vertical, toggles timer indicator
40
33
  const isTimed = (poll.duration || 0) > 0;
@@ -82,15 +75,6 @@ export const Voting = ({ id, toggleVoting }) => {
82
75
  </Box>
83
76
  </Flex>
84
77
 
85
- {/* {poll.state === "stopped" && (
86
- <PollResultSummary
87
- pollResult={poll.result}
88
- questions={poll.questions}
89
- isQuiz={poll.type === "quiz"}
90
- isAdmin={isLocalPeerCreator}
91
- />
92
- )} */}
93
-
94
78
  {isTimed ? <TimedView poll={poll} /> : <StandardView poll={poll} />}
95
79
 
96
80
  {poll.state === 'started' && isLocalPeerCreator && (
@@ -1,7 +1,7 @@
1
1
  import React, { Fragment } from 'react';
2
2
  import { useMedia } from 'react-use';
3
- import { HMSException } from '@100mslive/hms-video';
4
3
  import {
4
+ HMSException,
5
5
  HMSSimulcastLayerDefinition,
6
6
  HMSTrackID,
7
7
  HMSVideoTrack,
@@ -7,7 +7,6 @@ import { VBOption } from './VBOption';
7
7
  export const VBCollection = ({
8
8
  options,
9
9
  title,
10
- activeBackgroundType = HMSVirtualBackgroundTypes.NONE,
11
10
  activeBackground = '',
12
11
  }: {
13
12
  options: {
@@ -15,11 +14,10 @@ export const VBCollection = ({
15
14
  icon?: React.JSX.Element;
16
15
  onClick?: () => Promise<void>;
17
16
  mediaURL?: string;
18
- type: string;
17
+ value: string | HMSVirtualBackgroundTypes;
19
18
  }[];
20
19
  title: string;
21
- activeBackground: HTMLImageElement | string;
22
- activeBackgroundType: HMSVirtualBackgroundTypes;
20
+ activeBackground: string;
23
21
  }) => {
24
22
  if (options.length === 0) {
25
23
  return null;
@@ -32,14 +30,10 @@ export const VBCollection = ({
32
30
  <Box css={{ py: '$4', display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: '$8' }}>
33
31
  {options.map((option, index) => (
34
32
  <VBOption.Root
35
- testid={option.type === HMSVirtualBackgroundTypes.IMAGE ? `virtual_bg_option-${index}` : option.type}
36
- key={option?.mediaURL || option?.title}
33
+ key={option.value}
34
+ testid={option.value === HMSVirtualBackgroundTypes.IMAGE ? `virtual_bg_option-${index}` : option.value}
37
35
  {...option}
38
- isActive={
39
- ([HMSVirtualBackgroundTypes.NONE, HMSVirtualBackgroundTypes.BLUR].includes(activeBackgroundType) &&
40
- option.type === activeBackgroundType) ||
41
- activeBackground === option?.mediaURL
42
- }
36
+ isActive={activeBackground === option.value}
43
37
  >
44
38
  <VBOption.Icon>{option?.icon}</VBOption.Icon>
45
39
  <VBOption.Title>{option?.title}</VBOption.Title>