@100mslive/roomkit-react 0.2.2-alpha.4 → 0.2.2-alpha.6
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/{HLSView-DPM6YJWZ.js → HLSView-U346BIZQ.js} +2 -2
- package/dist/Prebuilt/components/Chat/ChatActions.d.ts +1 -1
- package/dist/Prebuilt/components/Polls/common/utils.d.ts +1 -0
- package/dist/{chunk-S5NNUR2M.js → chunk-DY3FWEXJ.js} +317 -263
- package/dist/{chunk-S5NNUR2M.js.map → chunk-DY3FWEXJ.js.map} +4 -4
- package/dist/index.cjs.js +554 -493
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.js +1 -1
- package/dist/meta.cjs.json +60 -26
- package/dist/meta.esbuild.json +66 -32
- package/package.json +6 -6
- package/src/Prebuilt/common/utils.js +13 -0
- package/src/Prebuilt/components/Chat/ChatActions.tsx +3 -3
- package/src/Prebuilt/components/Chat/ChatBody.tsx +9 -5
- package/src/Prebuilt/components/Polls/Voting/LeaderboardEntry.tsx +3 -2
- package/src/Prebuilt/components/Polls/Voting/PeerParticipationSummary.tsx +4 -3
- package/src/Prebuilt/components/Polls/Voting/QuestionCard.jsx +16 -21
- package/src/Prebuilt/components/Polls/Voting/StatisticBox.tsx +1 -1
- package/src/Prebuilt/components/Polls/Voting/TimedVoting.tsx +31 -20
- package/src/Prebuilt/components/Polls/Voting/Voting.tsx +7 -8
- package/src/Prebuilt/components/Polls/common/MultipleChoiceOptions.jsx +2 -2
- package/src/Prebuilt/components/Polls/common/SingleChoiceOptions.jsx +4 -5
- package/src/Prebuilt/components/Polls/common/utils.ts +16 -0
- /package/dist/{HLSView-DPM6YJWZ.js.map → HLSView-U346BIZQ.js.map} +0 -0
@@ -189,7 +189,7 @@ const ChatMessage = React.memo(
|
|
189
189
|
roles: message.recipientRoles,
|
190
190
|
receiver: message.recipientPeer,
|
191
191
|
});
|
192
|
-
const [openSheet,
|
192
|
+
const [openSheet, setOpenSheetBare] = useState(false);
|
193
193
|
const showPinAction = !!elements?.chat?.allow_pinning_messages;
|
194
194
|
const showReply = message.sender !== selectedPeer.id && message.sender !== localPeerId && isPrivateChatEnabled;
|
195
195
|
useLayoutEffect(() => {
|
@@ -198,6 +198,11 @@ const ChatMessage = React.memo(
|
|
198
198
|
}
|
199
199
|
}, [index, message.id]);
|
200
200
|
|
201
|
+
const setOpenSheet = (value: boolean, e?: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
202
|
+
e?.stopPropagation();
|
203
|
+
setOpenSheetBare(value);
|
204
|
+
};
|
205
|
+
|
201
206
|
return (
|
202
207
|
<Box
|
203
208
|
css={{
|
@@ -228,9 +233,9 @@ const ChatMessage = React.memo(
|
|
228
233
|
},
|
229
234
|
}}
|
230
235
|
data-testid="chat_msg"
|
231
|
-
onClick={
|
236
|
+
onClick={e => {
|
232
237
|
if (isMobile) {
|
233
|
-
setOpenSheet(true);
|
238
|
+
setOpenSheet(true, e);
|
234
239
|
}
|
235
240
|
}}
|
236
241
|
>
|
@@ -321,8 +326,7 @@ const ChatMessage = React.memo(
|
|
321
326
|
color: isOverlay ? '#FFF' : '$on_surface_high',
|
322
327
|
}}
|
323
328
|
onClick={e => {
|
324
|
-
e
|
325
|
-
setOpenSheet(true);
|
329
|
+
setOpenSheet(true, e);
|
326
330
|
}}
|
327
331
|
>
|
328
332
|
<AnnotisedMessage message={message.message} />
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
2
2
|
import { CheckCircleIcon, ClockIcon, TrophyFilledIcon } from '@100mslive/react-icons';
|
3
3
|
import { Box, Flex } from '../../../../Layout';
|
4
4
|
import { Text } from '../../../../Text';
|
5
|
+
import { getFormattedTime } from '../common/utils';
|
5
6
|
|
6
7
|
const positionColorMap: Record<number, string> = { 1: '#D69516', 2: '#3E3E3E', 3: '#583B0F' };
|
7
8
|
|
@@ -23,7 +24,7 @@ export const LeaderboardEntry = ({
|
|
23
24
|
duration: number;
|
24
25
|
}) => {
|
25
26
|
return (
|
26
|
-
<Flex align="center" justify="between" css={{ my: '$
|
27
|
+
<Flex align="center" justify="between" css={{ my: '$8' }}>
|
27
28
|
<Flex align="center" css={{ gap: '$6' }}>
|
28
29
|
<Flex
|
29
30
|
align="center"
|
@@ -66,7 +67,7 @@ export const LeaderboardEntry = ({
|
|
66
67
|
{duration ? (
|
67
68
|
<Flex align="center" css={{ gap: '$2', color: '$on_surface_medium' }}>
|
68
69
|
<ClockIcon height={16} width={16} />
|
69
|
-
<Text variant="xs">{(duration
|
70
|
+
<Text variant="xs">{getFormattedTime(duration)}</Text>
|
70
71
|
</Flex>
|
71
72
|
) : null}
|
72
73
|
</Flex>
|
@@ -4,6 +4,7 @@ import { Box } from '../../../../Layout';
|
|
4
4
|
import { Text } from '../../../../Text';
|
5
5
|
import { StatisticBox } from './StatisticBox';
|
6
6
|
import { useQuizSummary } from './useQuizSummary';
|
7
|
+
import { getFormattedTime } from '../common/utils';
|
7
8
|
|
8
9
|
export const PeerParticipationSummary = ({ quiz }: { quiz: HMSPoll }) => {
|
9
10
|
const localPeerId = useHMSStore(selectLocalPeerID);
|
@@ -29,7 +30,7 @@ export const PeerParticipationSummary = ({ quiz }: { quiz: HMSPoll }) => {
|
|
29
30
|
}/${summary.totalUsers})`,
|
30
31
|
},
|
31
32
|
// Time in ms
|
32
|
-
{ title: 'Avg. Time Taken', value:
|
33
|
+
{ title: 'Avg. Time Taken', value: getFormattedTime(summary.avgTime) },
|
33
34
|
{
|
34
35
|
title: 'Avg. Score',
|
35
36
|
value: Number.isInteger(summary.avgScore) ? summary.avgScore : summary.avgScore.toFixed(2),
|
@@ -37,9 +38,9 @@ export const PeerParticipationSummary = ({ quiz }: { quiz: HMSPoll }) => {
|
|
37
38
|
]
|
38
39
|
: [
|
39
40
|
{ title: 'Your rank', value: peerEntry?.position || '-' },
|
40
|
-
{ title: 'Points', value: peerEntry?.score },
|
41
|
+
{ title: 'Points', value: peerEntry?.score || 0 },
|
41
42
|
// Time in ms
|
42
|
-
{ title: 'Time Taken', value:
|
43
|
+
{ title: 'Time Taken', value: getFormattedTime(peerEntry?.duration) },
|
43
44
|
{
|
44
45
|
title: 'Correct Answers',
|
45
46
|
value: peerEntry?.totalResponses ? `${peerEntry?.correctResponses}/${peerEntry.totalResponses}` : '-',
|
@@ -73,11 +73,6 @@ export const QuestionCard = ({
|
|
73
73
|
},
|
74
74
|
]);
|
75
75
|
startTime.current = Date.now();
|
76
|
-
|
77
|
-
if (isQuiz && index !== totalQuestions) {
|
78
|
-
setSingleOptionAnswer(undefined);
|
79
|
-
setMultipleOptionAnswer(new Set());
|
80
|
-
}
|
81
76
|
}, [
|
82
77
|
isValidVote,
|
83
78
|
actions.interactivityCenter,
|
@@ -118,8 +113,8 @@ export const QuestionCard = ({
|
|
118
113
|
gap: '$4',
|
119
114
|
}}
|
120
115
|
>
|
121
|
-
{respondedToQuiz && isCorrectAnswer && pollEnded ? <CheckCircleIcon height={
|
122
|
-
{respondedToQuiz && !isCorrectAnswer && pollEnded ? <CrossCircleIcon height={
|
116
|
+
{respondedToQuiz && isCorrectAnswer && pollEnded ? <CheckCircleIcon height={16} width={16} /> : null}
|
117
|
+
{respondedToQuiz && !isCorrectAnswer && pollEnded ? <CrossCircleIcon height={16} width={16} /> : null}
|
123
118
|
QUESTION {index} OF {totalQuestions}: {type.toUpperCase()}
|
124
119
|
</Text>
|
125
120
|
</Flex>
|
@@ -136,7 +131,9 @@ export const QuestionCard = ({
|
|
136
131
|
</Box>
|
137
132
|
</Flex>
|
138
133
|
|
139
|
-
<Box
|
134
|
+
<Box
|
135
|
+
css={{ maxHeight: showOptions ? '$80' : '0', transition: 'max-height 0.3s ease', overflowY: 'auto', mb: '$4' }}
|
136
|
+
>
|
140
137
|
{type === QUESTION_TYPE.SINGLE_CHOICE ? (
|
141
138
|
<SingleChoiceOptions
|
142
139
|
key={index}
|
@@ -150,7 +147,6 @@ export const QuestionCard = ({
|
|
150
147
|
showVoteCount={showVoteCount}
|
151
148
|
localPeerResponse={localPeerResponse}
|
152
149
|
isStopped={pollState === 'stopped'}
|
153
|
-
answer={singleOptionAnswer}
|
154
150
|
/>
|
155
151
|
) : null}
|
156
152
|
|
@@ -169,19 +165,18 @@ export const QuestionCard = ({
|
|
169
165
|
isStopped={pollState === 'stopped'}
|
170
166
|
/>
|
171
167
|
) : null}
|
172
|
-
|
173
|
-
{isLive && (
|
174
|
-
<QuestionActions
|
175
|
-
isValidVote={isValidVote}
|
176
|
-
onVote={handleVote}
|
177
|
-
response={localPeerResponse}
|
178
|
-
isQuiz={isQuiz}
|
179
|
-
incrementIndex={() => {
|
180
|
-
setCurrentIndex(curr => Math.min(totalQuestions, curr + 1));
|
181
|
-
}}
|
182
|
-
/>
|
183
|
-
)}
|
184
168
|
</Box>
|
169
|
+
{isLive && (
|
170
|
+
<QuestionActions
|
171
|
+
isValidVote={isValidVote}
|
172
|
+
onVote={handleVote}
|
173
|
+
response={localPeerResponse}
|
174
|
+
isQuiz={isQuiz}
|
175
|
+
incrementIndex={() => {
|
176
|
+
setCurrentIndex(curr => Math.min(totalQuestions, curr + 1));
|
177
|
+
}}
|
178
|
+
/>
|
179
|
+
)}
|
185
180
|
</Box>
|
186
181
|
);
|
187
182
|
};
|
@@ -3,7 +3,7 @@ import { Box } from '../../../../Layout';
|
|
3
3
|
import { Text } from '../../../../Text';
|
4
4
|
|
5
5
|
export const StatisticBox = ({ title, value = 0 }: { title: string; value: string | number | undefined }) => {
|
6
|
-
if (!value) {
|
6
|
+
if (!value && !(typeof value === 'number')) {
|
7
7
|
return <></>;
|
8
8
|
}
|
9
9
|
return (
|
@@ -1,33 +1,44 @@
|
|
1
1
|
import React, { useState } from 'react';
|
2
|
-
import { HMSPoll } from '@100mslive/react-sdk';
|
2
|
+
import { HMSPoll, selectLocalPeerID, useHMSStore } from '@100mslive/react-sdk';
|
3
3
|
// @ts-ignore
|
4
4
|
import { QuestionCard } from './QuestionCard';
|
5
|
+
// @ts-ignore
|
6
|
+
import { getLastAttemptedIndex } from '../../../common/utils';
|
5
7
|
|
6
8
|
export const TimedView = ({ poll }: { poll: HMSPoll }) => {
|
7
|
-
|
8
|
-
const
|
9
|
+
const localPeerId = useHMSStore(selectLocalPeerID);
|
10
|
+
const lastAttemptedIndex = getLastAttemptedIndex(poll.questions, localPeerId, '');
|
11
|
+
const [currentIndex, setCurrentIndex] = useState(lastAttemptedIndex);
|
9
12
|
const activeQuestion = poll.questions?.find(question => question.index === currentIndex);
|
13
|
+
const attemptedAll = poll.questions?.length === lastAttemptedIndex - 1;
|
10
14
|
|
11
|
-
if (!activeQuestion) {
|
15
|
+
if (!activeQuestion || !poll.questions?.length) {
|
12
16
|
return null;
|
13
17
|
}
|
14
18
|
|
15
19
|
return (
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
20
|
+
<>
|
21
|
+
{poll.questions.map(question => {
|
22
|
+
return attemptedAll || activeQuestion.index === question.index ? (
|
23
|
+
<QuestionCard
|
24
|
+
key={question.index}
|
25
|
+
pollID={poll.id}
|
26
|
+
isQuiz={poll.type === 'quiz'}
|
27
|
+
startedBy={poll.startedBy}
|
28
|
+
pollState={poll.state}
|
29
|
+
index={question.index}
|
30
|
+
text={question.text}
|
31
|
+
type={question.type}
|
32
|
+
result={question?.result}
|
33
|
+
totalQuestions={poll.questions?.length || 0}
|
34
|
+
options={question.options}
|
35
|
+
responses={question.responses}
|
36
|
+
answer={question.answer}
|
37
|
+
setCurrentIndex={setCurrentIndex}
|
38
|
+
rolesThatCanViewResponses={poll.rolesThatCanViewResponses}
|
39
|
+
/>
|
40
|
+
) : null;
|
41
|
+
})}
|
42
|
+
</>
|
32
43
|
);
|
33
44
|
};
|
@@ -68,7 +68,7 @@ export const Voting = ({ id, toggleVoting }: { id: string; toggleVoting: () => v
|
|
68
68
|
</Box>
|
69
69
|
</Flex>
|
70
70
|
|
71
|
-
<Flex direction="column" css={{ p: '$8 $10', overflowY: 'auto' }}>
|
71
|
+
<Flex direction="column" css={{ p: '$8 $10', flex: '1 1 0', overflowY: 'auto' }}>
|
72
72
|
{poll.state === 'started' ? (
|
73
73
|
<Text css={{ color: '$on_surface_medium', fontWeight: '$semiBold' }}>
|
74
74
|
{pollCreatorName || 'Participant'} started a {poll.type}
|
@@ -76,22 +76,21 @@ export const Voting = ({ id, toggleVoting }: { id: string; toggleVoting: () => v
|
|
76
76
|
) : null}
|
77
77
|
|
78
78
|
{showSingleView ? <TimedView poll={poll} /> : <StandardView poll={poll} />}
|
79
|
-
|
79
|
+
</Flex>
|
80
|
+
<Flex
|
81
|
+
css={{ w: '100%', justifyContent: 'end', alignItems: 'center', p: '$8', borderTop: '1px solid $border_bright' }}
|
82
|
+
>
|
80
83
|
{poll.state === 'started' && canEndActivity && (
|
81
84
|
<Button
|
82
85
|
variant="danger"
|
83
|
-
css={{ fontWeight: '$semiBold', w: 'max-content'
|
86
|
+
css={{ fontWeight: '$semiBold', w: 'max-content' }}
|
84
87
|
onClick={() => actions.interactivityCenter.stopPoll(id)}
|
85
88
|
>
|
86
89
|
End {poll.type}
|
87
90
|
</Button>
|
88
91
|
)}
|
89
|
-
|
90
92
|
{canViewLeaderboard ? (
|
91
|
-
<Button
|
92
|
-
css={{ fontWeight: '$semiBold', w: 'max-content', ml: 'auto', mt: '$8' }}
|
93
|
-
onClick={() => setPollView(POLL_VIEWS.RESULTS)}
|
94
|
-
>
|
93
|
+
<Button css={{ fontWeight: '$semiBold', w: 'max-content' }} onClick={() => setPollView(POLL_VIEWS.RESULTS)}>
|
95
94
|
View Leaderboard
|
96
95
|
</Button>
|
97
96
|
) : null}
|
@@ -52,7 +52,7 @@ export const MultipleChoiceOptions = ({
|
|
52
52
|
|
53
53
|
{isStopped && correctOptionIndexes?.includes(option.index) ? (
|
54
54
|
<Flex css={{ color: '$on_surface_high' }}>
|
55
|
-
<CheckCircleIcon />
|
55
|
+
<CheckCircleIcon height={20} width={20} />
|
56
56
|
</Flex>
|
57
57
|
) : null}
|
58
58
|
|
@@ -83,7 +83,7 @@ export const MultipleChoiceOptionInputs = ({ isQuiz, options, selectAnswer, hand
|
|
83
83
|
<Flex direction="column" css={{ gap: '$md', w: '100%', mb: '$md' }}>
|
84
84
|
{options.map((option, index) => {
|
85
85
|
return (
|
86
|
-
<Flex align="center" key={index} css={{ w: '100%', gap: '$
|
86
|
+
<Flex align="center" key={index} css={{ w: '100%', gap: '$4' }}>
|
87
87
|
{isQuiz && (
|
88
88
|
<Checkbox.Root
|
89
89
|
onCheckedChange={checked => selectAnswer(checked, index)}
|
@@ -17,14 +17,13 @@ export const SingleChoiceOptions = ({
|
|
17
17
|
isStopped,
|
18
18
|
isQuiz,
|
19
19
|
localPeerResponse,
|
20
|
-
answer,
|
21
20
|
}) => {
|
22
21
|
return (
|
23
|
-
<RadioGroup.Root value={
|
22
|
+
<RadioGroup.Root value={localPeerResponse?.option} onValueChange={value => setAnswer(value)}>
|
24
23
|
<Flex direction="column" css={{ gap: '$md', w: '100%', mb: '$md' }}>
|
25
24
|
{options.map(option => {
|
26
25
|
return (
|
27
|
-
<Flex align="start" key={`${questionIndex}-${option.index}`} css={{ w: '100%', gap: '$
|
26
|
+
<Flex align="start" key={`${questionIndex}-${option.index}`} css={{ w: '100%', gap: '$4' }}>
|
28
27
|
{!isStopped || !isQuiz ? (
|
29
28
|
<RadioGroup.Item
|
30
29
|
css={{
|
@@ -59,7 +58,7 @@ export const SingleChoiceOptions = ({
|
|
59
58
|
|
60
59
|
{isStopped && correctOptionIndex === option.index && isQuiz ? (
|
61
60
|
<Flex css={{ color: '$on_surface_high' }}>
|
62
|
-
<CheckCircleIcon />
|
61
|
+
<CheckCircleIcon height={20} width={20} />
|
63
62
|
</Flex>
|
64
63
|
) : null}
|
65
64
|
|
@@ -95,7 +94,7 @@ export const SingleChoiceOptionInputs = ({ isQuiz, options, selectAnswer, handle
|
|
95
94
|
<Flex direction="column" css={{ gap: '$md', w: '100%', mb: '$md' }}>
|
96
95
|
{options.map((option, index) => {
|
97
96
|
return (
|
98
|
-
<Flex align="center" key={`option-${index}`} css={{ w: '100%', gap: '$
|
97
|
+
<Flex align="center" key={`option-${index}`} css={{ w: '100%', gap: '$4' }}>
|
99
98
|
{isQuiz && (
|
100
99
|
<RadioGroup.Item
|
101
100
|
css={{
|
@@ -0,0 +1,16 @@
|
|
1
|
+
export const getFormattedTime = (milliseconds: number | undefined) => {
|
2
|
+
if (!milliseconds) return '-';
|
3
|
+
|
4
|
+
const totalSeconds = milliseconds / 1000;
|
5
|
+
const minutes = Math.floor(totalSeconds / 60);
|
6
|
+
const seconds = totalSeconds % 60;
|
7
|
+
|
8
|
+
let formattedSeconds = '';
|
9
|
+
if (Number.isInteger(seconds) || minutes) {
|
10
|
+
formattedSeconds = seconds.toFixed(0);
|
11
|
+
} else {
|
12
|
+
formattedSeconds = seconds.toFixed(1);
|
13
|
+
}
|
14
|
+
|
15
|
+
return `${minutes ? `${minutes}m ` : ''}${formattedSeconds}s`;
|
16
|
+
};
|
File without changes
|