@100mslive/roomkit-react 0.1.14 → 0.1.15
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-662T7R7H.js → HLSView-MXBOUQBG.js} +128 -39
- package/dist/HLSView-MXBOUQBG.js.map +7 -0
- package/dist/Prebuilt/common/constants.d.ts +2 -1
- package/dist/Prebuilt/components/HMSVideo/HLSCaptionSelector.d.ts +5 -0
- package/dist/Prebuilt/components/Polls/Voting/Leaderboard.d.ts +4 -0
- package/dist/Prebuilt/components/Polls/Voting/LeaderboardEntry.d.ts +9 -0
- package/dist/Prebuilt/components/Polls/Voting/PeerParticipationSummary.d.ts +5 -0
- package/dist/{chunk-2B7YYNHQ.js → chunk-HEOH5H43.js} +6767 -1064
- package/dist/chunk-HEOH5H43.js.map +7 -0
- package/dist/index.cjs.js +7263 -1445
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.js +1 -1
- package/dist/meta.cjs.json +335 -50
- package/dist/meta.esbuild.json +351 -65
- package/package.json +6 -6
- package/src/Prebuilt/common/PeersSorter.ts +12 -3
- package/src/Prebuilt/common/constants.ts +1 -0
- package/src/Prebuilt/common/utils.js +34 -0
- package/src/Prebuilt/components/Chat/Chat.jsx +3 -4
- package/src/Prebuilt/components/HMSVideo/HLSCaptionSelector.tsx +13 -0
- package/src/Prebuilt/components/HMSVideo/HMSVideo.jsx +34 -2
- package/src/Prebuilt/components/Notifications/Notifications.tsx +33 -2
- package/src/Prebuilt/components/Polls/CreatePollQuiz/PollsQuizMenu.jsx +3 -9
- package/src/Prebuilt/components/Polls/CreateQuestions/CreateQuestions.jsx +21 -1
- package/src/Prebuilt/components/Polls/CreateQuestions/QuestionForm.jsx +34 -7
- package/src/Prebuilt/components/Polls/CreateQuestions/SavedQuestion.jsx +2 -2
- package/src/Prebuilt/components/Polls/Polls.tsx +3 -0
- package/src/Prebuilt/components/Polls/Voting/Leaderboard.tsx +115 -0
- package/src/Prebuilt/components/Polls/Voting/LeaderboardEntry.tsx +63 -0
- package/src/Prebuilt/components/Polls/Voting/PeerParticipationSummary.tsx +38 -0
- package/src/Prebuilt/components/Polls/Voting/QuestionCard.jsx +28 -11
- package/src/Prebuilt/components/Polls/Voting/StandardVoting.jsx +7 -1
- package/src/Prebuilt/components/Polls/Voting/Voting.jsx +31 -13
- package/src/Prebuilt/components/Polls/common/MultipleChoiceOptions.jsx +33 -21
- package/src/Prebuilt/components/Polls/common/SingleChoiceOptions.jsx +47 -35
- package/src/Prebuilt/components/Polls/common/StatusIndicator.jsx +2 -22
- package/src/Prebuilt/components/Polls/common/VoteCount.jsx +1 -15
- package/src/Prebuilt/components/VideoLayouts/EqualProminence.tsx +6 -5
- package/src/Prebuilt/components/VideoLayouts/GridLayout.tsx +25 -6
- package/src/Prebuilt/components/VideoLayouts/ScreenshareLayout.tsx +0 -1
- package/src/Prebuilt/layouts/HLSView.jsx +51 -3
- package/dist/HLSView-662T7R7H.js.map +0 -7
- package/dist/chunk-2B7YYNHQ.js.map +0 -7
@@ -0,0 +1,38 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { HMSPoll, selectLocalPeer, useHMSStore } from '@100mslive/react-sdk';
|
3
|
+
import { Box } from '../../../../Layout';
|
4
|
+
import { Text } from '../../../../Text';
|
5
|
+
// @ts-ignore
|
6
|
+
import { getPeerParticipationSummary } from '../../../common/utils';
|
7
|
+
|
8
|
+
export const PeerParticipationSummary = ({ poll }: { poll: HMSPoll }) => {
|
9
|
+
const localPeer = useHMSStore(selectLocalPeer);
|
10
|
+
const { totalResponses, correctResponses, score } = getPeerParticipationSummary(
|
11
|
+
poll,
|
12
|
+
localPeer?.id,
|
13
|
+
localPeer?.customerUserId,
|
14
|
+
);
|
15
|
+
|
16
|
+
const boxes = [
|
17
|
+
{ title: 'Points', value: score },
|
18
|
+
{ title: 'Correct Answers', value: `${correctResponses}/${totalResponses}` },
|
19
|
+
];
|
20
|
+
return (
|
21
|
+
<Box>
|
22
|
+
<Text css={{ fontWeight: '$semiBold', my: '$8' }}>Participation Summary</Text>
|
23
|
+
<Box css={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '$4' }}>
|
24
|
+
{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>
|
34
|
+
))}
|
35
|
+
</Box>
|
36
|
+
</Box>
|
37
|
+
);
|
38
|
+
};
|
@@ -1,7 +1,7 @@
|
|
1
1
|
// @ts-check
|
2
2
|
import React, { useCallback, useMemo, useState } from 'react';
|
3
3
|
import { selectLocalPeer, selectLocalPeerRoleName, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
|
4
|
-
import { ChevronLeftIcon, ChevronRightIcon } from '@100mslive/react-icons';
|
4
|
+
import { CheckCircleIcon, ChevronLeftIcon, ChevronRightIcon, CrossCircleIcon } from '@100mslive/react-icons';
|
5
5
|
import { Box, Button, Flex, IconButton, Input, styled, Text } from '../../../../';
|
6
6
|
import { checkCorrectAnswer } from '../../../common/utils';
|
7
7
|
import { MultipleChoiceOptions } from '../common/MultipleChoiceOptions';
|
@@ -48,7 +48,8 @@ export const QuestionCard = ({
|
|
48
48
|
!rolesThatCanViewResponses ||
|
49
49
|
rolesThatCanViewResponses.length === 0 ||
|
50
50
|
rolesThatCanViewResponses.includes(localPeerRoleName || '');
|
51
|
-
const showVoteCount =
|
51
|
+
const showVoteCount =
|
52
|
+
roleCanViewResponse && (localPeerResponse || (isLocalPeerCreator && pollState === 'stopped')) && !isQuiz;
|
52
53
|
|
53
54
|
const isLive = pollState === 'started';
|
54
55
|
const canRespond = isLive && !localPeerResponse;
|
@@ -72,6 +73,8 @@ export const QuestionCard = ({
|
|
72
73
|
|
73
74
|
const stringAnswerExpected = [QUESTION_TYPE.LONG_ANSWER, QUESTION_TYPE.SHORT_ANSWER].includes(type);
|
74
75
|
|
76
|
+
const respondedToQuiz = isQuiz && localPeerResponse && !localPeerResponse.skipped;
|
77
|
+
|
75
78
|
const isValidVote = useMemo(() => {
|
76
79
|
if (stringAnswerExpected) {
|
77
80
|
return textAnswer.length > 0;
|
@@ -113,14 +116,22 @@ export const QuestionCard = ({
|
|
113
116
|
borderRadius: '$1',
|
114
117
|
p: '$md',
|
115
118
|
mt: '$md',
|
116
|
-
border:
|
117
|
-
isQuiz && localPeerResponse && !localPeerResponse.skipped
|
118
|
-
? `1px solid ${isCorrectAnswer ? '$alert_success' : '$alert_error_default'}`
|
119
|
-
: 'none',
|
119
|
+
border: respondedToQuiz ? `1px solid ${isCorrectAnswer ? '$alert_success' : '$alert_error_default'}` : 'none',
|
120
120
|
}}
|
121
121
|
>
|
122
122
|
<Flex align="center" justify="between">
|
123
|
-
<Text
|
123
|
+
<Text
|
124
|
+
variant="caption"
|
125
|
+
css={{
|
126
|
+
color: respondedToQuiz ? (isCorrectAnswer ? '$alert_success' : '$alert_error_default') : '$on_surface_low',
|
127
|
+
fontWeight: '$semiBold',
|
128
|
+
display: 'flex',
|
129
|
+
alignItems: 'center',
|
130
|
+
gap: '$4',
|
131
|
+
}}
|
132
|
+
>
|
133
|
+
{respondedToQuiz && isCorrectAnswer ? <CheckCircleIcon height={20} width={20} /> : null}
|
134
|
+
{respondedToQuiz && !isCorrectAnswer ? <CrossCircleIcon height={20} width={20} /> : null}
|
124
135
|
QUESTION {index} OF {totalQuestions}: {type.toUpperCase()}
|
125
136
|
</Text>
|
126
137
|
|
@@ -196,6 +207,8 @@ export const QuestionCard = ({
|
|
196
207
|
setAnswer={setSingleOptionAnswer}
|
197
208
|
totalResponses={result?.totalResponses}
|
198
209
|
showVoteCount={showVoteCount}
|
210
|
+
localPeerResponse={localPeerResponse}
|
211
|
+
isStopped={pollState === 'stopped'}
|
199
212
|
/>
|
200
213
|
) : null}
|
201
214
|
|
@@ -211,6 +224,8 @@ export const QuestionCard = ({
|
|
211
224
|
setSelectedOptions={setMultipleOptionAnswer}
|
212
225
|
totalResponses={result?.totalResponses}
|
213
226
|
showVoteCount={showVoteCount}
|
227
|
+
localPeerResponse={localPeerResponse}
|
228
|
+
isStopped={pollState === 'stopped'}
|
214
229
|
/>
|
215
230
|
) : null}
|
216
231
|
|
@@ -221,14 +236,14 @@ export const QuestionCard = ({
|
|
221
236
|
onSkip={handleSkip}
|
222
237
|
onVote={handleVote}
|
223
238
|
response={localPeerResponse}
|
224
|
-
|
239
|
+
isQuiz={isQuiz}
|
225
240
|
/>
|
226
241
|
)}
|
227
242
|
</Box>
|
228
243
|
);
|
229
244
|
};
|
230
245
|
|
231
|
-
const QuestionActions = ({ isValidVote, skippable, response,
|
246
|
+
const QuestionActions = ({ isValidVote, skippable, response, isQuiz, onVote, onSkip }) => {
|
232
247
|
return (
|
233
248
|
<Flex align="center" justify="end" css={{ gap: '$4', w: '100%' }}>
|
234
249
|
{skippable && !response ? (
|
@@ -239,11 +254,13 @@ const QuestionActions = ({ isValidVote, skippable, response, stringAnswerExpecte
|
|
239
254
|
|
240
255
|
{response ? (
|
241
256
|
<Text css={{ fontWeight: '$semiBold', color: '$on_surface_medium' }}>
|
242
|
-
{response.skipped ? 'Skipped' :
|
257
|
+
{response.skipped ? 'Skipped' : null}
|
258
|
+
{isQuiz && !response.skipped ? 'Answered' : null}
|
259
|
+
{!isQuiz && !response.skipped ? 'Voted' : null}
|
243
260
|
</Text>
|
244
261
|
) : (
|
245
262
|
<Button css={{ p: '$xs $10', fontWeight: '$semiBold' }} disabled={!isValidVote} onClick={onVote}>
|
246
|
-
{
|
263
|
+
{isQuiz ? 'Answer' : 'Vote'}
|
247
264
|
</Button>
|
248
265
|
)}
|
249
266
|
</Flex>
|
@@ -1,5 +1,6 @@
|
|
1
1
|
// @ts-check
|
2
2
|
import React from 'react';
|
3
|
+
import { PeerParticipationSummary } from './PeerParticipationSummary';
|
3
4
|
import { QuestionCard } from './QuestionCard';
|
4
5
|
|
5
6
|
/**
|
@@ -11,12 +12,17 @@ export const StandardView = ({ poll }) => {
|
|
11
12
|
if (!poll?.questions) {
|
12
13
|
return null;
|
13
14
|
}
|
15
|
+
|
16
|
+
const isQuiz = poll.type === 'quiz';
|
17
|
+
const isStopped = poll.state === 'stopped';
|
18
|
+
|
14
19
|
return (
|
15
20
|
<>
|
21
|
+
{isQuiz && isStopped ? <PeerParticipationSummary poll={poll} /> : null}
|
16
22
|
{poll.questions?.map((question, index) => (
|
17
23
|
<QuestionCard
|
18
24
|
pollID={poll.id}
|
19
|
-
isQuiz={
|
25
|
+
isQuiz={isQuiz}
|
20
26
|
startedBy={poll.startedBy}
|
21
27
|
pollState={poll.state}
|
22
28
|
key={`${question.text}-${index}`}
|
@@ -3,6 +3,7 @@ import React from 'react';
|
|
3
3
|
import {
|
4
4
|
selectLocalPeerID,
|
5
5
|
selectPeerNameByID,
|
6
|
+
selectPermissions,
|
6
7
|
selectPollByID,
|
7
8
|
useHMSActions,
|
8
9
|
useHMSStore,
|
@@ -23,11 +24,18 @@ export const Voting = ({ id, toggleVoting }) => {
|
|
23
24
|
const pollCreatorName = useHMSStore(selectPeerNameByID(poll?.createdBy));
|
24
25
|
const isLocalPeerCreator = useHMSStore(selectLocalPeerID) === poll?.createdBy;
|
25
26
|
const { setPollView } = usePollViewState();
|
27
|
+
const permissions = useHMSStore(selectPermissions);
|
28
|
+
|
29
|
+
// const sharedLeaderboards = useHMSStore(selectSessionStore(SESSION_STORE_KEY.SHARED_LEADERBOARDS));
|
26
30
|
|
27
31
|
if (!poll) {
|
28
32
|
return null;
|
29
33
|
}
|
30
34
|
|
35
|
+
// const isLeaderboardShared = (sharedLeaderboards || []).includes(id);
|
36
|
+
const canViewLeaderboard =
|
37
|
+
poll.type === 'quiz' && poll.state === 'stopped' && !poll.anonymous && permissions?.pollWrite;
|
38
|
+
|
31
39
|
// Sets view - linear or vertical, toggles timer indicator
|
32
40
|
const isTimed = (poll.duration || 0) > 0;
|
33
41
|
const isLive = poll.state === 'started';
|
@@ -52,8 +60,8 @@ export const Voting = ({ id, toggleVoting }) => {
|
|
52
60
|
>
|
53
61
|
<ChevronLeftIcon />
|
54
62
|
</Flex>
|
55
|
-
<Text variant="h6">{poll
|
56
|
-
<StatusIndicator isLive={isLive}
|
63
|
+
<Text variant="h6">{poll.title}</Text>
|
64
|
+
<StatusIndicator isLive={isLive} />
|
57
65
|
<Box
|
58
66
|
css={{
|
59
67
|
marginLeft: 'auto',
|
@@ -72,18 +80,8 @@ export const Voting = ({ id, toggleVoting }) => {
|
|
72
80
|
{pollCreatorName || 'Participant'} started a {poll.type}
|
73
81
|
</Text>
|
74
82
|
</Box>
|
75
|
-
{poll.state === 'started' && isLocalPeerCreator && (
|
76
|
-
<Box css={{ flex: 'initial' }}>
|
77
|
-
<Button
|
78
|
-
variant="danger"
|
79
|
-
css={{ fontSize: '$sm', fontWeight: '$semiBold', p: '$3 $6' }}
|
80
|
-
onClick={() => actions.interactivityCenter.stopPoll(id)}
|
81
|
-
>
|
82
|
-
End {poll.type}
|
83
|
-
</Button>
|
84
|
-
</Box>
|
85
|
-
)}
|
86
83
|
</Flex>
|
84
|
+
|
87
85
|
{/* {poll.state === "stopped" && (
|
88
86
|
<PollResultSummary
|
89
87
|
pollResult={poll.result}
|
@@ -92,7 +90,27 @@ export const Voting = ({ id, toggleVoting }) => {
|
|
92
90
|
isAdmin={isLocalPeerCreator}
|
93
91
|
/>
|
94
92
|
)} */}
|
93
|
+
|
95
94
|
{isTimed ? <TimedView poll={poll} /> : <StandardView poll={poll} />}
|
95
|
+
|
96
|
+
{poll.state === 'started' && isLocalPeerCreator && (
|
97
|
+
<Button
|
98
|
+
variant="danger"
|
99
|
+
css={{ fontWeight: '$semiBold', w: 'max-content', ml: 'auto', mt: '$8' }}
|
100
|
+
onClick={() => actions.interactivityCenter.stopPoll(id)}
|
101
|
+
>
|
102
|
+
End {poll.type}
|
103
|
+
</Button>
|
104
|
+
)}
|
105
|
+
|
106
|
+
{canViewLeaderboard ? (
|
107
|
+
<Button
|
108
|
+
css={{ fontWeight: '$semiBold', w: 'max-content', ml: 'auto', mt: '$8' }}
|
109
|
+
onClick={() => setPollView(POLL_VIEWS.RESULTS)}
|
110
|
+
>
|
111
|
+
View Leaderboard
|
112
|
+
</Button>
|
113
|
+
) : null}
|
96
114
|
</Flex>
|
97
115
|
</Container>
|
98
116
|
);
|
@@ -1,6 +1,6 @@
|
|
1
1
|
// @ts-check
|
2
2
|
import React from 'react';
|
3
|
-
import { CheckIcon } from '@100mslive/react-icons';
|
3
|
+
import { CheckCircleIcon, CheckIcon } from '@100mslive/react-icons';
|
4
4
|
import { Checkbox, Flex, Label, Text } from '../../../../';
|
5
5
|
import { OptionInputWithDelete } from './OptionInputWithDelete';
|
6
6
|
import { VoteCount } from './VoteCount';
|
@@ -8,15 +8,17 @@ import { VoteProgress } from './VoteProgress';
|
|
8
8
|
|
9
9
|
export const MultipleChoiceOptions = ({
|
10
10
|
questionIndex,
|
11
|
-
isQuiz,
|
12
11
|
options,
|
13
|
-
correctOptionIndexes,
|
14
12
|
canRespond,
|
15
13
|
response,
|
16
14
|
totalResponses,
|
17
15
|
selectedOptions,
|
18
16
|
setSelectedOptions,
|
19
17
|
showVoteCount,
|
18
|
+
isQuiz,
|
19
|
+
correctOptionIndexes,
|
20
|
+
localPeerResponse,
|
21
|
+
isStopped,
|
20
22
|
}) => {
|
21
23
|
const handleCheckedChange = (checked, index) => {
|
22
24
|
const newSelected = new Set(selectedOptions);
|
@@ -31,35 +33,45 @@ export const MultipleChoiceOptions = ({
|
|
31
33
|
return (
|
32
34
|
<Flex direction="column" css={{ gap: '$md', w: '100%', mb: '$md' }}>
|
33
35
|
{options.map(option => {
|
34
|
-
const isCorrectAnswer = isQuiz && correctOptionIndexes?.includes(option.index);
|
35
|
-
|
36
36
|
return (
|
37
37
|
<Flex align="center" key={`${questionIndex}-${option.index}`} css={{ w: '100%', gap: '$9' }}>
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
<
|
49
|
-
|
50
|
-
|
38
|
+
{!isStopped || !isQuiz ? (
|
39
|
+
<Checkbox.Root
|
40
|
+
id={`${questionIndex}-${option.index}`}
|
41
|
+
disabled={!canRespond}
|
42
|
+
checked={response?.options?.includes(option.index)}
|
43
|
+
onCheckedChange={checked => handleCheckedChange(checked, option.index)}
|
44
|
+
css={{
|
45
|
+
cursor: canRespond ? 'pointer' : 'not-allowed',
|
46
|
+
}}
|
47
|
+
>
|
48
|
+
<Checkbox.Indicator>
|
49
|
+
<CheckIcon width={16} height={16} />
|
50
|
+
</Checkbox.Indicator>
|
51
|
+
</Checkbox.Root>
|
52
|
+
) : null}
|
53
|
+
|
54
|
+
{isStopped && correctOptionIndexes.includes(option.index) ? (
|
55
|
+
<Flex css={{ color: '$on_surface_high' }}>
|
56
|
+
<CheckCircleIcon />
|
57
|
+
</Flex>
|
58
|
+
) : null}
|
51
59
|
|
52
60
|
<Flex direction="column" css={{ flexGrow: '1' }}>
|
53
61
|
<Flex css={{ w: '100%' }}>
|
54
62
|
<Text css={{ display: 'flex', flexGrow: '1' }}>
|
55
63
|
<Label htmlFor={`${questionIndex}-${option.index}`}>{option.text}</Label>
|
56
64
|
</Text>
|
57
|
-
{showVoteCount &&
|
58
|
-
<VoteCount isQuiz={isQuiz} isCorrectAnswer={isCorrectAnswer} voteCount={option.voteCount} />
|
59
|
-
)}
|
65
|
+
{showVoteCount && <VoteCount voteCount={option.voteCount} />}
|
60
66
|
</Flex>
|
61
67
|
{showVoteCount && <VoteProgress option={option} totalResponses={totalResponses} />}
|
62
68
|
</Flex>
|
69
|
+
|
70
|
+
{isStopped && isQuiz && localPeerResponse?.options.includes(option.index) ? (
|
71
|
+
<Text variant="sm" css={{ color: '$on_surface_medium', maxWidth: 'max-content' }}>
|
72
|
+
Your Answer
|
73
|
+
</Text>
|
74
|
+
) : null}
|
63
75
|
</Flex>
|
64
76
|
);
|
65
77
|
})}
|
@@ -1,5 +1,6 @@
|
|
1
1
|
// @ts-check
|
2
2
|
import React from 'react';
|
3
|
+
import { CheckCircleIcon } from '@100mslive/react-icons';
|
3
4
|
import { Flex, Label, RadioGroup, Text } from '../../../../';
|
4
5
|
import { OptionInputWithDelete } from './OptionInputWithDelete';
|
5
6
|
import { VoteCount } from './VoteCount';
|
@@ -7,64 +8,75 @@ import { VoteProgress } from './VoteProgress';
|
|
7
8
|
|
8
9
|
export const SingleChoiceOptions = ({
|
9
10
|
questionIndex,
|
10
|
-
isQuiz,
|
11
11
|
options,
|
12
12
|
response,
|
13
13
|
canRespond,
|
14
|
-
correctOptionIndex,
|
15
14
|
setAnswer,
|
16
15
|
totalResponses,
|
17
16
|
showVoteCount,
|
17
|
+
correctOptionIndex,
|
18
|
+
isStopped,
|
19
|
+
isQuiz,
|
20
|
+
localPeerResponse,
|
18
21
|
}) => {
|
19
22
|
return (
|
20
23
|
<RadioGroup.Root value={response?.option} onValueChange={value => setAnswer(value)}>
|
21
24
|
<Flex direction="column" css={{ gap: '$md', w: '100%', mb: '$md' }}>
|
22
25
|
{options.map(option => {
|
23
|
-
const isCorrectAnswer = isQuiz && option.index === correctOptionIndex;
|
24
|
-
|
25
26
|
return (
|
26
|
-
<Flex align="
|
27
|
-
|
28
|
-
|
29
|
-
background: 'none',
|
30
|
-
h: '$9',
|
31
|
-
w: '$9',
|
32
|
-
border: '2px solid',
|
33
|
-
borderColor: '$on_surface_high',
|
34
|
-
display: 'flex',
|
35
|
-
justifyContent: 'center',
|
36
|
-
alignItems: 'center',
|
37
|
-
cursor: canRespond ? 'pointer' : 'not-allowed',
|
38
|
-
'&[data-state="checked"]': {
|
39
|
-
borderColor: '$primary_bright',
|
40
|
-
borderWidth: '2px',
|
41
|
-
},
|
42
|
-
}}
|
43
|
-
disabled={!canRespond}
|
44
|
-
value={option.index}
|
45
|
-
id={`${questionIndex}-${option.index}`}
|
46
|
-
>
|
47
|
-
<RadioGroup.Indicator
|
27
|
+
<Flex align="start" key={`${questionIndex}-${option.index}`} css={{ w: '100%', gap: '$5' }}>
|
28
|
+
{!isStopped || !isQuiz ? (
|
29
|
+
<RadioGroup.Item
|
48
30
|
css={{
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
31
|
+
background: 'none',
|
32
|
+
h: '$9',
|
33
|
+
w: '$9',
|
34
|
+
border: '2px solid',
|
35
|
+
borderColor: '$on_surface_high',
|
36
|
+
display: 'flex',
|
37
|
+
justifyContent: 'center',
|
38
|
+
alignItems: 'center',
|
39
|
+
cursor: canRespond ? 'pointer' : 'not-allowed',
|
40
|
+
'&[data-state="checked"]': {
|
41
|
+
borderColor: '$primary_bright',
|
42
|
+
borderWidth: '2px',
|
43
|
+
},
|
53
44
|
}}
|
54
|
-
|
55
|
-
|
45
|
+
disabled={!canRespond}
|
46
|
+
value={option.index}
|
47
|
+
id={`${questionIndex}-${option.index}`}
|
48
|
+
>
|
49
|
+
<RadioGroup.Indicator
|
50
|
+
css={{
|
51
|
+
h: '80%',
|
52
|
+
w: '80%',
|
53
|
+
background: '$primary_bright',
|
54
|
+
borderRadius: '$round',
|
55
|
+
}}
|
56
|
+
/>
|
57
|
+
</RadioGroup.Item>
|
58
|
+
) : null}
|
59
|
+
|
60
|
+
{isStopped && correctOptionIndex === option.index && isQuiz ? (
|
61
|
+
<Flex css={{ color: '$on_surface_high' }}>
|
62
|
+
<CheckCircleIcon />
|
63
|
+
</Flex>
|
64
|
+
) : null}
|
56
65
|
|
57
66
|
<Flex direction="column" css={{ flexGrow: '1' }}>
|
58
67
|
<Flex css={{ w: '100%' }}>
|
59
68
|
<Text css={{ display: 'flex', flexGrow: '1' }}>
|
60
69
|
<Label htmlFor={`${questionIndex}-${option.index}`}>{option.text}</Label>
|
61
70
|
</Text>
|
62
|
-
{showVoteCount &&
|
63
|
-
<VoteCount isQuiz={isQuiz} isCorrectAnswer={isCorrectAnswer} voteCount={option.voteCount} />
|
64
|
-
)}
|
71
|
+
{showVoteCount && <VoteCount voteCount={option.voteCount} />}
|
65
72
|
</Flex>
|
66
73
|
{showVoteCount && <VoteProgress option={option} totalResponses={totalResponses} />}
|
67
74
|
</Flex>
|
75
|
+
{isStopped && isQuiz && localPeerResponse?.option === option.index ? (
|
76
|
+
<Text variant="sm" css={{ color: '$on_surface_medium', maxWidth: 'max-content' }}>
|
77
|
+
Your Answer
|
78
|
+
</Text>
|
79
|
+
) : null}
|
68
80
|
</Flex>
|
69
81
|
);
|
70
82
|
})}
|
@@ -2,14 +2,14 @@
|
|
2
2
|
import React from 'react';
|
3
3
|
import { Flex, Text } from '../../../../';
|
4
4
|
|
5
|
-
export const StatusIndicator = ({ isLive
|
5
|
+
export const StatusIndicator = ({ isLive }) => {
|
6
6
|
return (
|
7
7
|
<Flex align="center">
|
8
8
|
<Flex
|
9
9
|
css={{
|
10
10
|
backgroundColor: isLive ? '$alert_error_default' : '$secondary_default',
|
11
11
|
p: '$2 $4',
|
12
|
-
borderRadius:
|
12
|
+
borderRadius: '$0',
|
13
13
|
}}
|
14
14
|
>
|
15
15
|
<Text
|
@@ -22,26 +22,6 @@ export const StatusIndicator = ({ isLive, shouldShowTimer }) => {
|
|
22
22
|
{isLive ? 'LIVE' : 'ENDED'}
|
23
23
|
</Text>
|
24
24
|
</Flex>
|
25
|
-
|
26
|
-
{shouldShowTimer ? (
|
27
|
-
<Flex
|
28
|
-
css={{
|
29
|
-
borderRadius: '0 $0 $0 0',
|
30
|
-
p: '$2 $4',
|
31
|
-
backgroundColor: '$background_default',
|
32
|
-
}}
|
33
|
-
>
|
34
|
-
<Text
|
35
|
-
variant="caption"
|
36
|
-
css={{
|
37
|
-
fontWeight: '$semiBold',
|
38
|
-
color: '$on_surface_high',
|
39
|
-
}}
|
40
|
-
>
|
41
|
-
0:32
|
42
|
-
</Text>
|
43
|
-
</Flex>
|
44
|
-
) : null}
|
45
25
|
</Flex>
|
46
26
|
);
|
47
27
|
};
|
@@ -2,23 +2,9 @@
|
|
2
2
|
import React from 'react';
|
3
3
|
import { Flex, Text } from '../../../../';
|
4
4
|
|
5
|
-
export const VoteCount = ({
|
5
|
+
export const VoteCount = ({ voteCount }) => {
|
6
6
|
return (
|
7
7
|
<Flex css={{ alignItems: 'center' }}>
|
8
|
-
{isQuiz && (
|
9
|
-
<Text
|
10
|
-
variant="xs"
|
11
|
-
css={{
|
12
|
-
p: '$2',
|
13
|
-
mr: '$2',
|
14
|
-
color: isCorrectAnswer ? '$alert_success' : '$alert_error_default',
|
15
|
-
borderRadius: '$1',
|
16
|
-
border: `1px solid ${isCorrectAnswer ? '$alert_success' : '$alert_error_default'}`,
|
17
|
-
}}
|
18
|
-
>
|
19
|
-
{isCorrectAnswer ? 'Correct' : 'Incorrect'}
|
20
|
-
</Text>
|
21
|
-
)}
|
22
8
|
{voteCount ? (
|
23
9
|
<Text variant="sm" css={{ color: '$on_surface_medium' }}>
|
24
10
|
{voteCount}
|
@@ -10,7 +10,6 @@ import { LayoutProps } from './interface';
|
|
10
10
|
// @ts-ignore: No implicit Any
|
11
11
|
import { useUISettings } from '../AppData/useUISettings';
|
12
12
|
import { usePagesWithTiles, useTileLayout } from '../hooks/useTileLayout';
|
13
|
-
// @ts-ignore: No implicit Any
|
14
13
|
import { UI_SETTINGS } from '../../common/constants';
|
15
14
|
|
16
15
|
export function EqualProminence({ isInsetEnabled = false, peers, onPageChange, onPageSize, edgeToEdge }: LayoutProps) {
|
@@ -23,10 +22,12 @@ export function EqualProminence({ isInsetEnabled = false, peers, onPageChange, o
|
|
23
22
|
maxTileCount,
|
24
23
|
});
|
25
24
|
// useMemo is needed to prevent recursion as new array is created for localPeer
|
26
|
-
const inputPeers = useMemo(
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
const inputPeers = useMemo(() => {
|
26
|
+
if (pageList.length === 0) {
|
27
|
+
return localPeer ? [localPeer] : [];
|
28
|
+
}
|
29
|
+
return peers;
|
30
|
+
}, [pageList.length, peers, localPeer]);
|
30
31
|
// Pass local peer to main grid if no other peer has tiles
|
31
32
|
pageList = usePagesWithTiles({
|
32
33
|
peers: inputPeers,
|
@@ -1,13 +1,20 @@
|
|
1
1
|
import React, { useEffect, useMemo, useState } from 'react';
|
2
2
|
import { GridVideoTileLayout } from '@100mslive/types-prebuilt/elements/video_tile_layout';
|
3
|
-
import {
|
3
|
+
import {
|
4
|
+
selectLocalPeerRoleName,
|
5
|
+
selectPeers,
|
6
|
+
selectPeerScreenSharing,
|
7
|
+
useHMSStore,
|
8
|
+
useHMSVanillaStore,
|
9
|
+
} from '@100mslive/react-sdk';
|
4
10
|
import { EqualProminence } from './EqualProminence';
|
5
11
|
import { RoleProminence } from './RoleProminence';
|
6
12
|
import { ScreenshareLayout } from './ScreenshareLayout';
|
7
13
|
// @ts-ignore: No implicit Any
|
8
|
-
import { usePinnedTrack } from '../AppData/useUISettings';
|
14
|
+
import { usePinnedTrack, useSetAppDataByKey } from '../AppData/useUISettings';
|
9
15
|
import { VideoTileContext } from '../hooks/useVideoTileLayout';
|
10
16
|
import PeersSorter from '../../common/PeersSorter';
|
17
|
+
import { APP_DATA } from '../../common/constants';
|
11
18
|
|
12
19
|
export type TileCustomisationProps = {
|
13
20
|
hide_participant_name_on_tile: boolean;
|
@@ -34,6 +41,8 @@ export const GridLayout = ({
|
|
34
41
|
const peerSharing = useHMSStore(selectPeerScreenSharing);
|
35
42
|
const pinnedTrack = usePinnedTrack();
|
36
43
|
const peers = useHMSStore(selectPeers);
|
44
|
+
const localPeerRole = useHMSStore(selectLocalPeerRoleName);
|
45
|
+
const [activeScreensharePeerId] = useSetAppDataByKey(APP_DATA.activeScreensharePeerId);
|
37
46
|
const isRoleProminence =
|
38
47
|
(prominentRoles.length &&
|
39
48
|
peers.some(
|
@@ -41,11 +50,20 @@ export const GridLayout = ({
|
|
41
50
|
)) ||
|
42
51
|
pinnedTrack;
|
43
52
|
const updatedPeers = useMemo(() => {
|
44
|
-
|
45
|
-
|
53
|
+
// remove screenshare peer from active speaker sorting
|
54
|
+
if (activeScreensharePeerId) {
|
55
|
+
return peers.filter(peer => peer.id !== activeScreensharePeerId);
|
56
|
+
}
|
57
|
+
if (isInsetEnabled) {
|
58
|
+
// if localPeer role is prominent role, it shows up in the center, so allow it in active speaker sorting
|
59
|
+
if (localPeerRole && prominentRoles.includes(localPeerRole)) {
|
60
|
+
return peers;
|
61
|
+
} else {
|
62
|
+
return peers.filter(peer => !peer.isLocal);
|
63
|
+
}
|
46
64
|
}
|
47
65
|
return peers;
|
48
|
-
}, [isInsetEnabled,
|
66
|
+
}, [isInsetEnabled, activeScreensharePeerId, localPeerRole, prominentRoles, peers]);
|
49
67
|
const vanillaStore = useHMSVanillaStore();
|
50
68
|
const [sortedPeers, setSortedPeers] = useState(updatedPeers);
|
51
69
|
const peersSorter = useMemo(() => new PeersSorter(vanillaStore), [vanillaStore]);
|
@@ -61,7 +79,8 @@ export const GridLayout = ({
|
|
61
79
|
};
|
62
80
|
|
63
81
|
useEffect(() => {
|
64
|
-
if (mainPage !== 0) {
|
82
|
+
if (mainPage !== 0 || pageSize === 0) {
|
83
|
+
setSortedPeers(updatedPeers);
|
65
84
|
return;
|
66
85
|
}
|
67
86
|
peersSorter.setPeersAndTilesPerPage({
|
@@ -10,7 +10,6 @@ import { LayoutProps } from './interface';
|
|
10
10
|
import { ProminenceLayout } from './ProminenceLayout';
|
11
11
|
// @ts-ignore: No implicit Any
|
12
12
|
import { useSetAppDataByKey } from '../AppData/useUISettings';
|
13
|
-
// @ts-ignore: No implicit Any
|
14
13
|
import { APP_DATA } from '../../common/constants';
|
15
14
|
|
16
15
|
export const ScreenshareLayout = ({ peers, onPageChange, onPageSize, edgeToEdge }: LayoutProps) => {
|