@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.
- 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>
|