@100mslive/roomkit-react 0.3.10 → 0.3.11-alpha.0
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-PF44ZBXN.js → HLSView-HJ44JWJK.js} +18 -3
- package/dist/HLSView-HJ44JWJK.js.map +7 -0
- package/dist/{HLSView-6J5FD3IV.css → HLSView-IBWU4R7W.css} +3 -3
- package/dist/{HLSView-6J5FD3IV.css.map → HLSView-IBWU4R7W.css.map} +1 -1
- package/dist/Prebuilt/common/constants.d.ts +0 -2
- package/dist/Prebuilt/common/hooks.d.ts +8 -1
- package/dist/Prebuilt/components/MoreSettings/CaptionContent.d.ts +5 -0
- package/dist/Prebuilt/components/MoreSettings/CaptionModal.d.ts +4 -0
- package/dist/Prebuilt/components/Polls/Voting/StandardVoting.d.ts +4 -2
- package/dist/Prebuilt/components/Polls/Voting/TimedVoting.d.ts +4 -2
- package/dist/Prebuilt/layouts/WaitingView.d.ts +6 -0
- package/dist/{chunk-TUSCTU6T.js → chunk-WDZ4KRYM.js} +2095 -1805
- package/dist/chunk-WDZ4KRYM.js.map +7 -0
- package/dist/index.cjs.css +2 -2
- package/dist/index.cjs.css.map +1 -1
- package/dist/index.cjs.js +2752 -2446
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.css +2 -2
- package/dist/index.css.map +1 -1
- package/dist/index.js +1 -1
- package/dist/meta.cjs.json +292 -114
- package/dist/meta.esbuild.json +311 -132
- package/package.json +7 -7
- package/src/Prebuilt/common/constants.ts +0 -2
- package/src/Prebuilt/common/hooks.ts +34 -1
- package/src/Prebuilt/common/utils.js +11 -11
- package/src/Prebuilt/components/AppData/AppData.tsx +2 -4
- package/src/Prebuilt/components/AppData/useUISettings.js +0 -3
- package/src/Prebuilt/components/Chat/Chat.tsx +26 -6
- package/src/Prebuilt/components/Chat/ChatFooter.tsx +18 -2
- package/src/Prebuilt/components/Chat/ChatStates.tsx +1 -1
- package/src/Prebuilt/components/Footer/ChatToggle.tsx +5 -1
- package/src/Prebuilt/components/Footer/ParticipantList.tsx +4 -2
- package/src/Prebuilt/components/Footer/PollsToggle.tsx +1 -1
- package/src/Prebuilt/components/MoreSettings/CaptionContent.tsx +132 -0
- package/src/Prebuilt/components/MoreSettings/CaptionModal.tsx +37 -0
- package/src/Prebuilt/components/MoreSettings/SplitComponents/DesktopOptions.tsx +40 -3
- package/src/Prebuilt/components/MoreSettings/SplitComponents/MwebOptions.tsx +19 -19
- package/src/Prebuilt/components/Polls/CreatePollQuiz/PollsQuizMenu.tsx +2 -15
- package/src/Prebuilt/components/Polls/Voting/LeaderboardSummary.tsx +71 -66
- package/src/Prebuilt/components/Polls/Voting/QuestionCard.jsx +39 -40
- package/src/Prebuilt/components/Polls/Voting/StandardVoting.tsx +12 -6
- package/src/Prebuilt/components/Polls/Voting/TimedVoting.tsx +21 -10
- package/src/Prebuilt/components/Polls/Voting/Voting.tsx +44 -2
- package/src/Prebuilt/components/VideoLayouts/EqualProminence.tsx +13 -17
- package/src/Prebuilt/components/VideoLayouts/GridLayout.tsx +17 -0
- package/src/Prebuilt/layouts/HLSView.jsx +14 -11
- package/src/Prebuilt/layouts/VideoStreamingSection.tsx +43 -9
- package/src/Prebuilt/layouts/WaitingView.tsx +52 -0
- package/dist/HLSView-PF44ZBXN.js.map +0 -7
- package/dist/chunk-TUSCTU6T.js.map +0 -7
- package/src/Prebuilt/layouts/NonPublisherView.jsx +0 -51
- package/src/Prebuilt/layouts/WaitingView.jsx +0 -51
@@ -29,7 +29,7 @@ import {
|
|
29
29
|
SettingsIcon,
|
30
30
|
VirtualBackgroundIcon,
|
31
31
|
} from '@100mslive/react-icons';
|
32
|
-
import { Box, Loading, Tooltip } from '../../../..';
|
32
|
+
import { Box, Loading, Text, Tooltip } from '../../../..';
|
33
33
|
import { Sheet } from '../../../../Sheet';
|
34
34
|
// @ts-ignore: No implicit any
|
35
35
|
import IconButton from '../../../IconButton';
|
@@ -43,6 +43,7 @@ import SettingsModal from '../../Settings/SettingsModal';
|
|
43
43
|
import { ToastManager } from '../../Toast/ToastManager';
|
44
44
|
// @ts-ignore: No implicit any
|
45
45
|
import { ActionTile } from '../ActionTile';
|
46
|
+
import { CaptionModal } from '../CaptionModal';
|
46
47
|
// @ts-ignore: No implicit any
|
47
48
|
import { ChangeNameModal } from '../ChangeNameModal';
|
48
49
|
// @ts-ignore: No implicit any
|
@@ -73,6 +74,7 @@ const MODALS = {
|
|
73
74
|
BULK_ROLE_CHANGE: 'bulkRoleChange',
|
74
75
|
MUTE_ALL: 'muteAll',
|
75
76
|
EMBED_URL: 'embedUrl',
|
77
|
+
CAPTION: 'caption',
|
76
78
|
};
|
77
79
|
|
78
80
|
export const MwebOptions = ({
|
@@ -106,9 +108,9 @@ export const MwebOptions = ({
|
|
106
108
|
const isLocalVideoEnabled = useHMSStore(selectIsLocalVideoEnabled);
|
107
109
|
const { startRecording, isRecordingLoading } = useRecordingHandler();
|
108
110
|
|
109
|
-
const
|
111
|
+
const isTranscriptionEnabled = useHMSStore(selectIsTranscriptionEnabled);
|
110
112
|
|
111
|
-
const [isCaptionEnabled
|
113
|
+
const [isCaptionEnabled] = useSetIsCaptionEnabled();
|
112
114
|
useDropdownList({ open: openModals.size > 0 || openOptionsSheet || openSettingsSheet, name: 'MoreSettings' });
|
113
115
|
|
114
116
|
const updateState = (modalName: string, value: boolean) => {
|
@@ -193,21 +195,17 @@ export const MwebOptions = ({
|
|
193
195
|
<ActionTile.Title>{isHandRaised ? 'Lower' : 'Raise'} Hand</ActionTile.Title>
|
194
196
|
</ActionTile.Root>
|
195
197
|
) : null}
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
<ActionTile.Title>{isCaptionEnabled ? 'Hide Captions' : 'Captions Disabled'}</ActionTile.Title>
|
208
|
-
</ActionTile.Root>
|
209
|
-
) : null}
|
210
|
-
|
198
|
+
<ActionTile.Root
|
199
|
+
onClick={() => {
|
200
|
+
setOpenOptionsSheet(false);
|
201
|
+
updateState(MODALS.CAPTION, true);
|
202
|
+
}}
|
203
|
+
>
|
204
|
+
{isTranscriptionEnabled && isCaptionEnabled ? <ClosedCaptionIcon /> : <OpenCaptionIcon />}
|
205
|
+
<Text variant="sm" css={{ ml: '$4', color: '$on_surface_high', flexGrow: '1' }}>
|
206
|
+
Closed Caption
|
207
|
+
</Text>
|
208
|
+
</ActionTile.Root>
|
211
209
|
{isLocalVideoEnabled && !!elements?.virtual_background ? (
|
212
210
|
<ActionTile.Root
|
213
211
|
onClick={() => {
|
@@ -323,7 +321,9 @@ export const MwebOptions = ({
|
|
323
321
|
openParentSheet={() => setOpenOptionsSheet(true)}
|
324
322
|
/>
|
325
323
|
)}
|
326
|
-
|
324
|
+
{openModals.has(MODALS.CAPTION) && (
|
325
|
+
<CaptionModal onOpenChange={(value: boolean) => updateState(MODALS.CAPTION, value)} />
|
326
|
+
)}
|
327
327
|
{showEmojiCard && (
|
328
328
|
<Box
|
329
329
|
ref={emojiCardRef}
|
@@ -152,18 +152,6 @@ const AddMenu = () => {
|
|
152
152
|
Hide Vote Count
|
153
153
|
</Text>
|
154
154
|
</Flex>
|
155
|
-
{/* <Flex align="center" css={{ mt: '$10' }}>
|
156
|
-
<Switch onCheckedChange={value => setAnonymous(value)} css={{ mr: '$6' }} />
|
157
|
-
<Text variant="body2" css={{ c: '$on_surface_medium' }}>
|
158
|
-
Make Results Anonymous
|
159
|
-
</Text>
|
160
|
-
</Flex> */}
|
161
|
-
{/* <Timer
|
162
|
-
timer={timer}
|
163
|
-
setTimer={setTimer}
|
164
|
-
showTimerDropDown={showTimerDropDown}
|
165
|
-
setShowTimerDropDown={setShowTimerDropDown}
|
166
|
-
/> */}
|
167
155
|
|
168
156
|
<Button
|
169
157
|
variant="primary"
|
@@ -198,8 +186,8 @@ const PrevMenu = () => {
|
|
198
186
|
const sortedPolls = useMemo(
|
199
187
|
() =>
|
200
188
|
polls
|
201
|
-
?.sort((a, b) => (b
|
202
|
-
?.sort((a, b) => (b
|
189
|
+
?.sort((a, b) => (b?.createdAt?.getTime?.() || 0) - (a?.createdAt?.getTime?.() || 0))
|
190
|
+
?.sort((a, b) => (b?.state === 'started' ? 1 : 0) - (a?.state === 'started' ? 1 : 0)),
|
203
191
|
[polls],
|
204
192
|
);
|
205
193
|
const permissions = useHMSStore(selectPermissions);
|
@@ -208,7 +196,6 @@ const PrevMenu = () => {
|
|
208
196
|
const updatePolls = async () => {
|
209
197
|
await hmsActions.interactivityCenter.getPolls();
|
210
198
|
};
|
211
|
-
|
212
199
|
updatePolls();
|
213
200
|
}, [hmsActions.interactivityCenter]);
|
214
201
|
|
@@ -4,6 +4,8 @@ import { ChevronLeftIcon, ChevronRightIcon, CrossIcon } from '@100mslive/react-i
|
|
4
4
|
import { Box, Flex } from '../../../../Layout';
|
5
5
|
import { Loading } from '../../../../Loading';
|
6
6
|
import { Text } from '../../../../Text';
|
7
|
+
// @ts-ignore
|
8
|
+
import { Container } from '../../Streaming/Common';
|
7
9
|
import { LeaderboardEntry } from './LeaderboardEntry';
|
8
10
|
import { PeerParticipationSummary } from './PeerParticipationSummary';
|
9
11
|
// @ts-ignore
|
@@ -32,80 +34,83 @@ export const LeaderboardSummary = ({ pollID }: { pollID: string }) => {
|
|
32
34
|
const questionCount = quiz.questions?.length || 0;
|
33
35
|
|
34
36
|
return (
|
35
|
-
<
|
36
|
-
<Flex
|
37
|
-
<Flex align="center" css={{
|
37
|
+
<Container rounded>
|
38
|
+
<Flex direction="column" css={{ size: '100%', p: '$8' }}>
|
39
|
+
<Flex justify="between" align="center" css={{ pb: '$6', borderBottom: '1px solid $border_bright', mb: '$8' }}>
|
40
|
+
<Flex align="center" css={{ gap: '$4' }}>
|
41
|
+
<Flex
|
42
|
+
css={{ color: '$on_surface_medium', '&:hover': { color: '$on_surface_high', cursor: 'pointer' } }}
|
43
|
+
onClick={() => setPollView(POLL_VIEWS.VOTE)}
|
44
|
+
>
|
45
|
+
<ChevronLeftIcon />
|
46
|
+
</Flex>
|
47
|
+
<Text variant="lg" css={{ fontWeight: '$semiBold' }}>
|
48
|
+
{quiz.title}
|
49
|
+
</Text>
|
50
|
+
<StatusIndicator status={quiz.state} />
|
51
|
+
</Flex>
|
38
52
|
<Flex
|
39
53
|
css={{ color: '$on_surface_medium', '&:hover': { color: '$on_surface_high', cursor: 'pointer' } }}
|
40
|
-
onClick={
|
54
|
+
onClick={toggleSidepane}
|
41
55
|
>
|
42
|
-
<
|
56
|
+
<CrossIcon />
|
43
57
|
</Flex>
|
44
|
-
<Text variant="lg" css={{ fontWeight: '$semiBold' }}>
|
45
|
-
{quiz.title}
|
46
|
-
</Text>
|
47
|
-
<StatusIndicator status={quiz.state} />
|
48
58
|
</Flex>
|
49
|
-
<
|
50
|
-
|
51
|
-
onClick={toggleSidepane}
|
52
|
-
>
|
53
|
-
<CrossIcon />
|
54
|
-
</Flex>
|
55
|
-
</Flex>
|
56
|
-
|
57
|
-
{!viewAllEntries ? <PeerParticipationSummary quiz={quiz} /> : null}
|
59
|
+
<Box css={{ overflowY: 'auto', mr: '-$4', pr: '$4' }}>
|
60
|
+
{!viewAllEntries ? <PeerParticipationSummary quiz={quiz} /> : null}
|
58
61
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
css={{
|
67
|
-
mt: '$8',
|
68
|
-
overflowY: 'auto',
|
69
|
-
flex: viewAllEntries ? '1 1 0' : 'unset',
|
70
|
-
mr: viewAllEntries ? '-$6' : 'unset',
|
71
|
-
px: viewAllEntries ? '0' : '$4',
|
72
|
-
pr: viewAllEntries ? '$6' : '$4',
|
73
|
-
backgroundColor: viewAllEntries ? '' : '$surface_default',
|
74
|
-
borderRadius: '$1',
|
75
|
-
}}
|
76
|
-
>
|
77
|
-
{quizLeaderboard?.entries &&
|
78
|
-
quizLeaderboard.entries
|
79
|
-
.slice(0, viewAllEntries ? undefined : 5)
|
80
|
-
.map(question => (
|
81
|
-
<LeaderboardEntry
|
82
|
-
key={question.position}
|
83
|
-
position={question.position}
|
84
|
-
score={question.score}
|
85
|
-
questionCount={questionCount}
|
86
|
-
correctResponses={question.correctResponses}
|
87
|
-
userName={question.peer.username || ''}
|
88
|
-
maxPossibleScore={maxPossibleScore}
|
89
|
-
duration={question.duration}
|
90
|
-
/>
|
91
|
-
))}
|
92
|
-
{quizLeaderboard?.entries?.length > 5 && !viewAllEntries ? (
|
93
|
-
<Flex
|
94
|
-
align="center"
|
95
|
-
justify="end"
|
62
|
+
<Text variant="sm" css={{ fontWeight: '$semiBold', mt: '$4' }}>
|
63
|
+
Leaderboard
|
64
|
+
</Text>
|
65
|
+
<Text variant="xs" css={{ color: '$on_surface_medium' }}>
|
66
|
+
Based on score and time taken to cast the correct answer
|
67
|
+
</Text>
|
68
|
+
<Box
|
96
69
|
css={{
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
70
|
+
mt: '$8',
|
71
|
+
overflowY: 'auto',
|
72
|
+
flex: viewAllEntries ? '1 1 0' : 'unset',
|
73
|
+
mr: viewAllEntries ? '-$6' : 'unset',
|
74
|
+
px: viewAllEntries ? '0' : '$4',
|
75
|
+
pr: viewAllEntries ? '$6' : '$4',
|
76
|
+
backgroundColor: viewAllEntries ? '' : '$surface_default',
|
77
|
+
borderRadius: '$1',
|
102
78
|
}}
|
103
|
-
onClick={() => setViewAllEntries(true)}
|
104
79
|
>
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
80
|
+
{quizLeaderboard?.entries &&
|
81
|
+
quizLeaderboard.entries
|
82
|
+
.slice(0, viewAllEntries ? undefined : 5)
|
83
|
+
.map(question => (
|
84
|
+
<LeaderboardEntry
|
85
|
+
key={question.position}
|
86
|
+
position={question.position}
|
87
|
+
score={question.score}
|
88
|
+
questionCount={questionCount}
|
89
|
+
correctResponses={question.correctResponses}
|
90
|
+
userName={question.peer.username || ''}
|
91
|
+
maxPossibleScore={maxPossibleScore}
|
92
|
+
duration={question.duration}
|
93
|
+
/>
|
94
|
+
))}
|
95
|
+
{quizLeaderboard?.entries?.length > 5 && !viewAllEntries ? (
|
96
|
+
<Flex
|
97
|
+
align="center"
|
98
|
+
justify="end"
|
99
|
+
css={{
|
100
|
+
w: '100%',
|
101
|
+
borderTop: '1px solid $border_bright',
|
102
|
+
cursor: 'pointer',
|
103
|
+
color: '$on_surface_high',
|
104
|
+
p: '$6 $2',
|
105
|
+
}}
|
106
|
+
onClick={() => setViewAllEntries(true)}
|
107
|
+
>
|
108
|
+
<Text variant="sm">View All</Text> <ChevronRightIcon />
|
109
|
+
</Flex>
|
110
|
+
) : null}
|
111
|
+
</Box>
|
112
|
+
</Box>
|
113
|
+
</Flex>
|
114
|
+
</Container>
|
110
115
|
);
|
111
116
|
};
|
@@ -1,5 +1,5 @@
|
|
1
1
|
// @ts-check
|
2
|
-
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
2
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
3
3
|
import { match } from 'ts-pattern';
|
4
4
|
import { selectLocalPeer, selectLocalPeerRoleName, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
|
5
5
|
import { CheckCircleIcon, ChevronDownIcon, CrossCircleIcon } from '@100mslive/react-icons';
|
@@ -21,15 +21,12 @@ export const QuestionCard = ({
|
|
21
21
|
text,
|
22
22
|
options = [],
|
23
23
|
answer,
|
24
|
-
|
25
|
-
|
24
|
+
localPeerResponse,
|
25
|
+
updateSavedResponses,
|
26
26
|
rolesThatCanViewResponses,
|
27
27
|
}) => {
|
28
28
|
const actions = useHMSActions();
|
29
29
|
const localPeer = useHMSStore(selectLocalPeer);
|
30
|
-
const localPeerResponse = responses?.find(
|
31
|
-
response => response.peer?.peerid === localPeer?.id || response.peer?.userid === localPeer?.customerUserId,
|
32
|
-
);
|
33
30
|
|
34
31
|
const isLocalPeerCreator = localPeer?.id === startedBy;
|
35
32
|
const localPeerRoleName = useHMSStore(selectLocalPeerRoleName);
|
@@ -37,20 +34,26 @@ export const QuestionCard = ({
|
|
37
34
|
!rolesThatCanViewResponses ||
|
38
35
|
rolesThatCanViewResponses.length === 0 ||
|
39
36
|
rolesThatCanViewResponses.includes(localPeerRoleName || '');
|
37
|
+
const [localPeerChoice, setLocalPeerChoice] = useState(localPeerResponse);
|
38
|
+
|
39
|
+
useEffect(() => {
|
40
|
+
setLocalPeerChoice(localPeerResponse);
|
41
|
+
}, [localPeerResponse]);
|
42
|
+
|
40
43
|
const showVoteCount =
|
41
|
-
roleCanViewResponse && (
|
44
|
+
roleCanViewResponse && (localPeerChoice || (isLocalPeerCreator && pollState === 'stopped')) && !isQuiz;
|
42
45
|
|
43
46
|
const isLive = pollState === 'started';
|
44
47
|
const pollEnded = pollState === 'stopped';
|
45
|
-
const canRespond = isLive && !
|
48
|
+
const canRespond = isLive && !localPeerChoice;
|
46
49
|
const startTime = useRef(Date.now());
|
47
|
-
const isCorrectAnswer = checkCorrectAnswer(answer,
|
50
|
+
const isCorrectAnswer = checkCorrectAnswer(answer, localPeerChoice, type);
|
48
51
|
|
49
52
|
const [singleOptionAnswer, setSingleOptionAnswer] = useState();
|
50
53
|
const [multipleOptionAnswer, setMultipleOptionAnswer] = useState(new Set());
|
51
54
|
const [showOptions, setShowOptions] = useState(true);
|
52
55
|
|
53
|
-
const respondedToQuiz = isQuiz &&
|
56
|
+
const respondedToQuiz = isQuiz && localPeerChoice && !localPeerChoice.skipped;
|
54
57
|
|
55
58
|
const isValidVote = useMemo(() => {
|
56
59
|
if (type === QUESTION_TYPE.SINGLE_CHOICE) {
|
@@ -64,17 +67,28 @@ export const QuestionCard = ({
|
|
64
67
|
if (!isValidVote) {
|
65
68
|
return;
|
66
69
|
}
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
70
|
+
const submittedResponse = {
|
71
|
+
questionIndex: index,
|
72
|
+
option: singleOptionAnswer,
|
73
|
+
options: Array.from(multipleOptionAnswer),
|
74
|
+
duration: Date.now() - startTime.current,
|
75
|
+
};
|
76
|
+
await actions.interactivityCenter.addResponsesToPoll(pollID, [submittedResponse]);
|
77
|
+
updateSavedResponses(prev => {
|
78
|
+
const prevCopy = { ...prev };
|
79
|
+
prevCopy[index] = { option: singleOptionAnswer, options: Array.from(multipleOptionAnswer) };
|
80
|
+
return prevCopy;
|
81
|
+
});
|
76
82
|
startTime.current = Date.now();
|
77
|
-
}, [
|
83
|
+
}, [
|
84
|
+
isValidVote,
|
85
|
+
index,
|
86
|
+
singleOptionAnswer,
|
87
|
+
multipleOptionAnswer,
|
88
|
+
actions.interactivityCenter,
|
89
|
+
pollID,
|
90
|
+
updateSavedResponses,
|
91
|
+
]);
|
78
92
|
|
79
93
|
return (
|
80
94
|
<Box
|
@@ -147,7 +161,7 @@ export const QuestionCard = ({
|
|
147
161
|
setAnswer={setSingleOptionAnswer}
|
148
162
|
totalResponses={result?.totalResponses}
|
149
163
|
showVoteCount={showVoteCount}
|
150
|
-
localPeerResponse={
|
164
|
+
localPeerResponse={localPeerChoice}
|
151
165
|
isStopped={pollState === 'stopped'}
|
152
166
|
/>
|
153
167
|
) : null}
|
@@ -163,27 +177,19 @@ export const QuestionCard = ({
|
|
163
177
|
setSelectedOptions={setMultipleOptionAnswer}
|
164
178
|
totalResponses={result?.totalResponses}
|
165
179
|
showVoteCount={showVoteCount}
|
166
|
-
localPeerResponse={
|
180
|
+
localPeerResponse={localPeerChoice}
|
167
181
|
isStopped={pollState === 'stopped'}
|
168
182
|
/>
|
169
183
|
) : null}
|
170
184
|
</Box>
|
171
185
|
{isLive && (
|
172
|
-
<QuestionActions
|
173
|
-
isValidVote={isValidVote}
|
174
|
-
onVote={handleVote}
|
175
|
-
response={localPeerResponse}
|
176
|
-
isQuiz={isQuiz}
|
177
|
-
incrementIndex={() => {
|
178
|
-
setCurrentIndex(curr => Math.min(totalQuestions, curr + 1));
|
179
|
-
}}
|
180
|
-
/>
|
186
|
+
<QuestionActions isValidVote={isValidVote} onVote={handleVote} response={localPeerChoice} isQuiz={isQuiz} />
|
181
187
|
)}
|
182
188
|
</Box>
|
183
189
|
);
|
184
190
|
};
|
185
191
|
|
186
|
-
const QuestionActions = ({ isValidVote, response, isQuiz, onVote
|
192
|
+
const QuestionActions = ({ isValidVote, response, isQuiz, onVote }) => {
|
187
193
|
return (
|
188
194
|
<Flex align="center" justify="end" css={{ gap: '$4', w: '100%' }}>
|
189
195
|
{response ? (
|
@@ -193,14 +199,7 @@ const QuestionActions = ({ isValidVote, response, isQuiz, onVote, incrementIndex
|
|
193
199
|
{!isQuiz && !response.skipped ? 'Voted' : null}
|
194
200
|
</Text>
|
195
201
|
) : (
|
196
|
-
<Button
|
197
|
-
css={{ p: '$xs $10', fontWeight: '$semiBold' }}
|
198
|
-
disabled={!isValidVote}
|
199
|
-
onClick={() => {
|
200
|
-
onVote();
|
201
|
-
incrementIndex();
|
202
|
-
}}
|
203
|
-
>
|
202
|
+
<Button css={{ p: '$xs $10', fontWeight: '$semiBold' }} disabled={!isValidVote} onClick={onVote}>
|
204
203
|
{isQuiz ? 'Answer' : 'Vote'}
|
205
204
|
</Button>
|
206
205
|
)}
|
@@ -1,10 +1,18 @@
|
|
1
|
-
import React from 'react';
|
1
|
+
import React, { Dispatch, SetStateAction } from 'react';
|
2
2
|
import { HMSPoll } from '@100mslive/react-sdk';
|
3
3
|
import { PeerParticipationSummary } from './PeerParticipationSummary';
|
4
4
|
// @ts-ignore
|
5
5
|
import { QuestionCard } from './QuestionCard';
|
6
6
|
|
7
|
-
export const StandardView = ({
|
7
|
+
export const StandardView = ({
|
8
|
+
poll,
|
9
|
+
localPeerResponses,
|
10
|
+
updateSavedResponses,
|
11
|
+
}: {
|
12
|
+
poll: HMSPoll;
|
13
|
+
localPeerResponses: Record<number, number | number[] | undefined>;
|
14
|
+
updateSavedResponses: Dispatch<SetStateAction<Record<any, any>>>;
|
15
|
+
}) => {
|
8
16
|
if (!poll?.questions) {
|
9
17
|
return null;
|
10
18
|
}
|
@@ -28,11 +36,9 @@ export const StandardView = ({ poll }: { poll: HMSPoll }) => {
|
|
28
36
|
result={question.result}
|
29
37
|
totalQuestions={poll.questions?.length || 0}
|
30
38
|
options={question.options}
|
31
|
-
|
39
|
+
localPeerResponse={localPeerResponses?.[question.index]}
|
32
40
|
answer={question.answer}
|
33
|
-
|
34
|
-
return;
|
35
|
-
}}
|
41
|
+
updateSavedResponses={updateSavedResponses}
|
36
42
|
rolesThatCanViewResponses={poll.rolesThatCanViewResponses}
|
37
43
|
/>
|
38
44
|
))}
|
@@ -1,16 +1,27 @@
|
|
1
|
-
import React, { useState } from 'react';
|
2
|
-
import { HMSPoll
|
1
|
+
import React, { Dispatch, SetStateAction, useEffect, useState } from 'react';
|
2
|
+
import { HMSPoll } from '@100mslive/react-sdk';
|
3
3
|
// @ts-ignore
|
4
4
|
import { QuestionCard } from './QuestionCard';
|
5
5
|
// @ts-ignore
|
6
|
-
import {
|
6
|
+
import { getIndexToShow } from '../../../common/utils';
|
7
7
|
|
8
|
-
export const TimedView = ({
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
export const TimedView = ({
|
9
|
+
poll,
|
10
|
+
localPeerResponses,
|
11
|
+
updateSavedResponses,
|
12
|
+
}: {
|
13
|
+
poll: HMSPoll;
|
14
|
+
localPeerResponses?: Record<number, number | number[] | undefined>;
|
15
|
+
updateSavedResponses: Dispatch<SetStateAction<Record<any, any>>>;
|
16
|
+
}) => {
|
17
|
+
const [currentIndex, setCurrentIndex] = useState(getIndexToShow(localPeerResponses));
|
12
18
|
const activeQuestion = poll.questions?.find(question => question.index === currentIndex);
|
13
|
-
const attemptedAll = poll.questions?.length
|
19
|
+
const attemptedAll = (poll.questions?.length || 0) < currentIndex;
|
20
|
+
|
21
|
+
// Handles increments so only one question is shown at a time in quiz
|
22
|
+
useEffect(() => {
|
23
|
+
setCurrentIndex(getIndexToShow(localPeerResponses));
|
24
|
+
}, [localPeerResponses]);
|
14
25
|
|
15
26
|
if ((!activeQuestion && !attemptedAll) || !poll.questions?.length) {
|
16
27
|
return null;
|
@@ -32,10 +43,10 @@ export const TimedView = ({ poll }: { poll: HMSPoll }) => {
|
|
32
43
|
result={question?.result}
|
33
44
|
totalQuestions={poll.questions?.length || 0}
|
34
45
|
options={question.options}
|
35
|
-
|
46
|
+
localPeerResponse={localPeerResponses?.[question.index]}
|
36
47
|
answer={question.answer}
|
37
|
-
setCurrentIndex={setCurrentIndex}
|
38
48
|
rolesThatCanViewResponses={poll.rolesThatCanViewResponses}
|
49
|
+
updateSavedResponses={updateSavedResponses}
|
39
50
|
/>
|
40
51
|
) : null;
|
41
52
|
})}
|
@@ -1,5 +1,6 @@
|
|
1
|
-
import React from 'react';
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
2
2
|
import {
|
3
|
+
selectLocalPeerID,
|
3
4
|
selectPeerNameByID,
|
4
5
|
selectPermissions,
|
5
6
|
selectPollByID,
|
@@ -14,6 +15,8 @@ import { StandardView } from './StandardVoting';
|
|
14
15
|
import { TimedView } from './TimedVoting';
|
15
16
|
// @ts-ignore
|
16
17
|
import { usePollViewState } from '../../AppData/useUISettings';
|
18
|
+
// @ts-ignore
|
19
|
+
import { getPeerResponses } from '../../../common/utils';
|
17
20
|
import { StatusIndicator } from '../common/StatusIndicator';
|
18
21
|
import { POLL_VIEWS } from '../../../common/constants';
|
19
22
|
|
@@ -26,6 +29,41 @@ export const Voting = ({ id, toggleVoting }: { id: string; toggleVoting: () => v
|
|
26
29
|
const { setPollView } = usePollViewState();
|
27
30
|
// Sets view - linear or vertical, toggles timer indicator
|
28
31
|
const showSingleView = poll?.type === 'quiz' && poll.state === 'started';
|
32
|
+
const fetchedInitialResponses = useRef(false);
|
33
|
+
const [savedResponses, setSavedResponses] = useState<Record<any, any>>({});
|
34
|
+
const localPeerId = useHMSStore(selectLocalPeerID);
|
35
|
+
|
36
|
+
// To reset whenever a different poll is opened
|
37
|
+
useEffect(() => {
|
38
|
+
fetchedInitialResponses.current = false;
|
39
|
+
setSavedResponses({});
|
40
|
+
}, [id, setSavedResponses]);
|
41
|
+
|
42
|
+
useEffect(() => {
|
43
|
+
const getResponses = async () => {
|
44
|
+
if (poll && actions.interactivityCenter && !fetchedInitialResponses.current) {
|
45
|
+
await actions.interactivityCenter.getPollResponses(poll, true);
|
46
|
+
fetchedInitialResponses.current = true;
|
47
|
+
}
|
48
|
+
};
|
49
|
+
getResponses();
|
50
|
+
}, [poll, actions.interactivityCenter]);
|
51
|
+
|
52
|
+
useEffect(() => {
|
53
|
+
if (poll?.questions) {
|
54
|
+
const localPeerResponses = getPeerResponses(poll.questions, localPeerId);
|
55
|
+
// @ts-ignore
|
56
|
+
localPeerResponses?.forEach(response => {
|
57
|
+
if (response) {
|
58
|
+
setSavedResponses(prev => {
|
59
|
+
const prevCopy = { ...prev };
|
60
|
+
prevCopy[response[0]?.questionIndex] = { option: response[0]?.option, options: response[0]?.options };
|
61
|
+
return prevCopy;
|
62
|
+
});
|
63
|
+
}
|
64
|
+
});
|
65
|
+
}
|
66
|
+
}, [localPeerId, poll?.questions, id]);
|
29
67
|
|
30
68
|
if (!poll) {
|
31
69
|
return null;
|
@@ -74,7 +112,11 @@ export const Voting = ({ id, toggleVoting }: { id: string; toggleVoting: () => v
|
|
74
112
|
</Text>
|
75
113
|
) : null}
|
76
114
|
|
77
|
-
{showSingleView ?
|
115
|
+
{showSingleView ? (
|
116
|
+
<TimedView poll={poll} localPeerResponses={savedResponses} updateSavedResponses={setSavedResponses} />
|
117
|
+
) : (
|
118
|
+
<StandardView poll={poll} localPeerResponses={savedResponses} updateSavedResponses={setSavedResponses} />
|
119
|
+
)}
|
78
120
|
</Flex>
|
79
121
|
<Flex
|
80
122
|
css={{ w: '100%', justifyContent: 'end', alignItems: 'center', p: '$8', borderTop: '1px solid $border_bright' }}
|
@@ -1,8 +1,9 @@
|
|
1
|
-
import React, { useEffect,
|
1
|
+
import React, { useEffect, useState } from 'react';
|
2
2
|
import { useMedia } from 'react-use';
|
3
|
-
import {
|
3
|
+
import { PeopleAddIcon } from '@100mslive/react-icons';
|
4
4
|
import { Flex } from '../../../Layout';
|
5
5
|
import { config as cssConfig } from '../../../Theme';
|
6
|
+
import { WaitingView } from '../../layouts/WaitingView';
|
6
7
|
import { InsetTile } from '../InsetTile';
|
7
8
|
import { Pagination } from '../Pagination';
|
8
9
|
import { Grid } from './Grid';
|
@@ -13,26 +14,14 @@ import { usePagesWithTiles, useTileLayout } from '../hooks/useTileLayout';
|
|
13
14
|
import { UI_SETTINGS } from '../../common/constants';
|
14
15
|
|
15
16
|
export function EqualProminence({ isInsetEnabled = false, peers, onPageChange, onPageSize, edgeToEdge }: LayoutProps) {
|
16
|
-
const localPeer = useHMSStore(selectLocalPeer);
|
17
17
|
const isMobile = useMedia(cssConfig.media.md);
|
18
18
|
let maxTileCount = useUISettings(UI_SETTINGS.maxTileCount);
|
19
19
|
maxTileCount = isMobile ? Math.min(maxTileCount, 6) : maxTileCount;
|
20
|
-
|
20
|
+
const pageList = usePagesWithTiles({
|
21
21
|
peers,
|
22
22
|
maxTileCount,
|
23
23
|
});
|
24
|
-
|
25
|
-
const inputPeers = useMemo(() => {
|
26
|
-
if (pageList.length === 0) {
|
27
|
-
return localPeer ? [localPeer] : [];
|
28
|
-
}
|
29
|
-
return peers;
|
30
|
-
}, [pageList.length, peers, localPeer]);
|
31
|
-
// Pass local peer to main grid if no other peer has tiles
|
32
|
-
pageList = usePagesWithTiles({
|
33
|
-
peers: inputPeers,
|
34
|
-
maxTileCount,
|
35
|
-
});
|
24
|
+
|
36
25
|
const { ref, pagesWithTiles } = useTileLayout({
|
37
26
|
pageList,
|
38
27
|
maxTileCount,
|
@@ -60,7 +49,14 @@ export function EqualProminence({ isInsetEnabled = false, peers, onPageChange, o
|
|
60
49
|
numPages={pagesWithTiles.length}
|
61
50
|
/>
|
62
51
|
)}
|
63
|
-
{
|
52
|
+
{pageList.length === 0 ? (
|
53
|
+
<WaitingView
|
54
|
+
title="Waiting for Host to join"
|
55
|
+
subtitle="Sit back and relax till others join"
|
56
|
+
icon={<PeopleAddIcon width="56px" height="56px" style={{ color: 'white' }} />}
|
57
|
+
/>
|
58
|
+
) : null}
|
59
|
+
{isInsetEnabled && <InsetTile />}
|
64
60
|
</Flex>
|
65
61
|
);
|
66
62
|
}
|