@100mslive/roomkit-react 0.1.19-alpha.1 → 0.1.19-alpha.2
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/{HLSView-UBVLOPNY.js → HLSView-7X5BVAZE.js} +2 -2
- package/dist/Prebuilt/App.d.ts +1 -0
- package/dist/Prebuilt/common/constants.d.ts +1 -1
- package/dist/Prebuilt/components/Chat/EmptyChat.d.ts +2 -0
- package/dist/Prebuilt/components/Polls/Voting/{Leaderboard.d.ts → LeaderboardSummary.d.ts} +1 -1
- package/dist/Prebuilt/components/Polls/Voting/StatisticBox.d.ts +5 -0
- package/dist/Prebuilt/components/VirtualBackground/VBCollection.d.ts +3 -4
- package/dist/Prebuilt/components/VirtualBackground/VBHandler.d.ts +13 -0
- package/dist/Prebuilt/components/VirtualBackground/VBPicker.d.ts +1 -1
- package/dist/Prebuilt/components/VirtualBackground/constants.d.ts +0 -2
- package/dist/{chunk-VU2CQFCB.js → chunk-H3B4ARYV.js} +1396 -1294
- package/dist/chunk-H3B4ARYV.js.map +7 -0
- package/dist/index.cjs.js +1741 -1610
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.js +1 -1
- package/dist/meta.cjs.json +227 -90
- package/dist/meta.esbuild.json +234 -97
- package/package.json +6 -6
- package/src/Prebuilt/App.tsx +2 -1
- package/src/Prebuilt/common/constants.ts +1 -1
- package/src/Prebuilt/components/Chat/ChatActions.tsx +3 -3
- package/src/Prebuilt/components/Chat/ChatBody.tsx +28 -46
- package/src/Prebuilt/components/Chat/EmptyChat.tsx +51 -0
- package/src/Prebuilt/components/Footer/PollsToggle.tsx +7 -1
- package/src/Prebuilt/components/Notifications/Notifications.tsx +0 -29
- package/src/Prebuilt/components/Polls/Polls.tsx +2 -2
- package/src/Prebuilt/components/Polls/Voting/LeaderboardEntry.tsx +2 -2
- package/src/Prebuilt/components/Polls/Voting/LeaderboardSummary.tsx +162 -0
- package/src/Prebuilt/components/Polls/Voting/PeerParticipationSummary.tsx +2 -9
- package/src/Prebuilt/components/Polls/Voting/StatisticBox.tsx +15 -0
- package/src/Prebuilt/components/Polls/Voting/Voting.jsx +1 -17
- package/src/Prebuilt/components/TileMenu/TileMenuContent.tsx +1 -1
- package/src/Prebuilt/components/VirtualBackground/VBCollection.tsx +5 -11
- package/src/Prebuilt/components/VirtualBackground/VBHandler.tsx +72 -0
- package/src/Prebuilt/components/VirtualBackground/VBPicker.tsx +44 -81
- package/src/Prebuilt/components/VirtualBackground/constants.ts +0 -4
- package/src/Prebuilt/layouts/SidePane.tsx +6 -1
- package/src/Theme/stitches.config.ts +2 -10
- package/dist/chunk-VU2CQFCB.js.map +0 -7
- package/src/Prebuilt/components/Polls/Voting/Leaderboard.tsx +0 -123
- package/src/Prebuilt/components/Polls/Voting/PollResultSummary.jsx +0 -125
- /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.
|
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.
|
80
|
-
"@100mslive/hms-virtual-background": "1.11.28-alpha.
|
81
|
-
"@100mslive/react-icons": "0.8.28-alpha.
|
82
|
-
"@100mslive/react-sdk": "0.8.28-alpha.
|
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": "
|
118
|
+
"gitHead": "89886ce5263b9ac84ae04737f2c258933eb60280"
|
119
119
|
}
|
package/src/Prebuilt/App.tsx
CHANGED
@@ -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
|
-
|
219
|
+
className={DEFAULT_PORTAL_CONTAINER.slice(1)} // Skips the '.' in the selector
|
219
220
|
css={{
|
220
221
|
bg: '$background_dim',
|
221
222
|
size: '100%',
|
@@ -104,8 +104,8 @@ export const ChatActions = ({
|
|
104
104
|
}
|
105
105
|
> = {
|
106
106
|
reply: {
|
107
|
-
text: message.recipientRoles?.length ? 'Reply to
|
108
|
-
tooltipText: message.recipientRoles?.length ? 'Reply to
|
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
|
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
|
-
|
45
|
-
return rowHeights[index] + 16 || 72;
|
44
|
+
return rowHeights[index]?.size || 72;
|
46
45
|
}
|
47
46
|
|
48
|
-
const setRowHeight = (index: number, size: number) => {
|
49
|
-
|
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
|
-
|
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={
|
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
|
408
|
+
if (!listRef.current) {
|
402
409
|
return;
|
403
410
|
}
|
404
411
|
// @ts-ignore
|
405
412
|
const outerElement = listRef.current._outerRef;
|
406
|
-
|
407
|
-
|
408
|
-
scrollToBottom(1);
|
413
|
+
if (outerElement.clientHeight + outerElement.scrollTop + outerElement.offsetTop >= outerElement.scrollHeight) {
|
414
|
+
requestAnimationFrame(() => scrollToBottom(1));
|
409
415
|
}
|
410
|
-
},
|
416
|
+
}, selectUnreadHMSMessagesCount);
|
411
417
|
return unsubscribe;
|
412
418
|
}, [vanillaStore, listRef, scrollToBottom]);
|
413
419
|
|
414
|
-
if (filteredMessages.length === 0
|
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 {
|
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 <
|
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: '$
|
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={
|
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
|
-
<
|
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
|
-
|
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 && (
|
@@ -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
|
-
|
17
|
+
value: string | HMSVirtualBackgroundTypes;
|
19
18
|
}[];
|
20
19
|
title: string;
|
21
|
-
activeBackground:
|
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
|
-
|
36
|
-
|
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>
|