Updated the Listening format to work

This commit is contained in:
Tiago Ribeiro
2023-09-29 13:56:11 +01:00
parent 2a68d37de8
commit 782976c14f
6 changed files with 57 additions and 23 deletions

View File

@@ -55,7 +55,7 @@ export default function Finish({user, scores, modules, isLoading, onViewResults}
const getTotalExercises = () => { const getTotalExercises = () => {
const exam = exams.find((x) => x.module === selectedModule)!; const exam = exams.find((x) => x.module === selectedModule)!;
if (exam.module === "reading") { if (exam.module === "reading" || exam.module === "listening") {
return exam.parts.flatMap((x) => x.exercises).length; return exam.parts.flatMap((x) => x.exercises).length;
} }

View File

@@ -17,8 +17,11 @@ interface Props {
export default function Listening({exam, showSolutions = false, onFinish}: Props) { export default function Listening({exam, showSolutions = false, onFinish}: Props) {
const [exerciseIndex, setExerciseIndex] = useState(showSolutions ? 0 : -1); const [exerciseIndex, setExerciseIndex] = useState(showSolutions ? 0 : -1);
const [partIndex, setPartIndex] = useState(0);
const [timesListened, setTimesListened] = useState(0); const [timesListened, setTimesListened] = useState(0);
const [userSolutions, setUserSolutions] = useState<UserSolution[]>(exam.exercises.map((x) => defaultUserSolutions(x, exam))); const [userSolutions, setUserSolutions] = useState<UserSolution[]>(
exam.parts.flatMap((x) => x.exercises).map((x) => defaultUserSolutions(x, exam)),
);
const [showBlankModal, setShowBlankModal] = useState(false); const [showBlankModal, setShowBlankModal] = useState(false);
const [hasExamEnded, setHasExamEnded] = useExamStore((state) => [state.hasExamEnded, state.setHasExamEnded]); const [hasExamEnded, setHasExamEnded] = useExamStore((state) => [state.hasExamEnded, state.setHasExamEnded]);
@@ -43,11 +46,17 @@ export default function Listening({exam, showSolutions = false, onFinish}: Props
setUserSolutions((prev) => [...prev.filter((x) => x.exercise !== solution.exercise), solution]); setUserSolutions((prev) => [...prev.filter((x) => x.exercise !== solution.exercise), solution]);
} }
if (exerciseIndex + 1 < exam.exercises.length && !hasExamEnded) { if (exerciseIndex + 1 < exam.parts[partIndex].exercises.length && !hasExamEnded) {
setExerciseIndex((prev) => prev + 1); setExerciseIndex((prev) => prev + 1);
return; return;
} }
if (partIndex + 1 < exam.parts.length && !hasExamEnded) {
setPartIndex((prev) => prev + 1);
setExerciseIndex(showSolutions ? 0 : -1);
return;
}
if ( if (
solution && solution &&
![...userSolutions.filter((x) => x.exercise !== solution?.exercise).map((x) => x.score.missing), solution?.score.missing].every( ![...userSolutions.filter((x) => x.exercise !== solution?.exercise).map((x) => x.score.missing), solution?.score.missing].every(
@@ -80,7 +89,7 @@ export default function Listening({exam, showSolutions = false, onFinish}: Props
}; };
const getExercise = () => { const getExercise = () => {
const exercise = exam.exercises[exerciseIndex]; const exercise = exam.parts[partIndex].exercises[exerciseIndex];
return { return {
...exercise, ...exercise,
userSolutions: userSolutions.find((x) => x.exercise === exercise.id)?.solutions || [], userSolutions: userSolutions.find((x) => x.exercise === exercise.id)?.solutions || [],
@@ -92,17 +101,17 @@ export default function Listening({exam, showSolutions = false, onFinish}: Props
<div className="flex flex-col w-full gap-2"> <div className="flex flex-col w-full gap-2">
<h4 className="text-xl font-semibold">Please listen to the following audio attentively.</h4> <h4 className="text-xl font-semibold">Please listen to the following audio attentively.</h4>
<span className="text-base"> <span className="text-base">
{exam.audio.repeatableTimes > 0 {exam.parts[partIndex].audio.repeatableTimes > 0
? `You will only be allowed to listen to the audio ${exam.audio.repeatableTimes - timesListened} time(s).` ? `You will only be allowed to listen to the audio ${exam.parts[partIndex].audio.repeatableTimes - timesListened} time(s).`
: "You may listen to the audio as many times as you would like."} : "You may listen to the audio as many times as you would like."}
</span> </span>
</div> </div>
<div className="rounded-xl flex flex-col gap-4 items-center w-full h-fit"> <div className="rounded-xl flex flex-col gap-4 items-center w-full h-fit">
<AudioPlayer <AudioPlayer
src={exam.audio.source} src={exam.parts[partIndex].audio.source}
color="listening" color="listening"
onEnd={() => setTimesListened((prev) => prev + 1)} onEnd={() => setTimesListened((prev) => prev + 1)}
disabled={timesListened === exam.audio.repeatableTimes} disabled={timesListened === exam.parts[partIndex].audio.repeatableTimes}
disablePause disablePause
/> />
</div> </div>
@@ -114,23 +123,46 @@ export default function Listening({exam, showSolutions = false, onFinish}: Props
<BlankQuestionsModal isOpen={showBlankModal} onClose={confirmFinishModule} /> <BlankQuestionsModal isOpen={showBlankModal} onClose={confirmFinishModule} />
<div className="flex flex-col h-full w-full gap-8 justify-between"> <div className="flex flex-col h-full w-full gap-8 justify-between">
<ModuleTitle <ModuleTitle
exerciseIndex={exerciseIndex + 1} exerciseIndex={
(exam.parts
.flatMap((x) => x.exercises)
.findIndex(
(x) => x.id === exam.parts[partIndex].exercises[exerciseIndex === -1 ? exerciseIndex + 1 : exerciseIndex]?.id,
) || 0) + (exerciseIndex === -1 ? 0 : 1)
}
minTimer={exam.minTimer} minTimer={exam.minTimer}
module="listening" module="listening"
totalExercises={exam.exercises.length} totalExercises={exam.parts.flatMap((x) => x.exercises).length}
disableTimer={showSolutions} disableTimer={showSolutions}
/> />
{renderAudioPlayer()} {renderAudioPlayer()}
{exerciseIndex > -1 && {exerciseIndex > -1 &&
exerciseIndex < exam.exercises.length && exerciseIndex < exam.parts[partIndex].exercises.length &&
!showSolutions && !showSolutions &&
renderExercise(getExercise(), nextExercise, previousExercise)} renderExercise(getExercise(), nextExercise, previousExercise)}
{exerciseIndex > -1 && {exerciseIndex > -1 &&
exerciseIndex < exam.exercises.length && exerciseIndex < exam.parts[partIndex].exercises.length &&
showSolutions && showSolutions &&
renderSolution(exam.exercises[exerciseIndex], nextExercise, previousExercise)} renderSolution(exam.parts[partIndex].exercises[exerciseIndex], nextExercise, previousExercise)}
</div> </div>
{exerciseIndex === -1 && partIndex > 0 && (
<div className="self-end flex justify-between w-full gap-8 absolute bottom-8 left-0 px-8">
<Button
color="purple"
variant="outline"
onClick={() => {
setExerciseIndex(exam.parts[partIndex - 1].exercises.length - 1);
setPartIndex((prev) => prev - 1);
}}
className="max-w-[200px] w-full">
Back
</Button>
<Button color="purple" onClick={() => nextExercise()} className="max-w-[200px] self-end w-full">
Next
</Button>
</div>
)}
{exerciseIndex === -1 && ( {exerciseIndex === -1 && (
<Button color="purple" onClick={() => nextExercise()} className="max-w-[200px] self-end w-full justify-self-end"> <Button color="purple" onClick={() => nextExercise()} className="max-w-[200px] self-end w-full justify-self-end">
Start now Start now

View File

@@ -18,12 +18,14 @@ export interface ReadingExam {
} }
export interface ListeningExam { export interface ListeningExam {
parts: {
audio: { audio: {
source: string; source: string;
repeatableTimes: number; // *The amount of times the user is allowed to repeat the audio, 0 for unlimited repeatableTimes: number; // *The amount of times the user is allowed to repeat the audio, 0 for unlimited
}; };
id: string;
exercises: Exercise[]; exercises: Exercise[];
}[];
id: string;
module: "listening"; module: "listening";
minTimer: number; minTimer: number;
isDiagnostic: boolean; isDiagnostic: boolean;

View File

@@ -45,8 +45,8 @@ export default function ExamList() {
router.push("/exercises"); router.push("/exercises");
}; };
const getTotalExercises = (exam: Exam, module: Module) => { const getTotalExercises = (exam: Exam) => {
if (exam.module === "reading") { if (exam.module === "reading" || exam.module === "listening") {
return exam.parts.flatMap((x) => x.exercises).length; return exam.parts.flatMap((x) => x.exercises).length;
} }
@@ -62,7 +62,7 @@ export default function ExamList() {
header: "Module", header: "Module",
cell: (info) => <span className={CLASSES[info.getValue()]}>{capitalize(info.getValue())}</span>, cell: (info) => <span className={CLASSES[info.getValue()]}>{capitalize(info.getValue())}</span>,
}), }),
columnHelper.accessor((x) => getTotalExercises(x, x.module), { columnHelper.accessor((x) => getTotalExercises(x), {
header: "Exercises", header: "Exercises",
cell: (info) => info.getValue(), cell: (info) => info.getValue(),
}), }),

View File

@@ -137,7 +137,7 @@ export default function Page() {
}; };
const updateExamWithUserSolutions = (exam: Exam): Exam => { const updateExamWithUserSolutions = (exam: Exam): Exam => {
if (exam.module === "reading") { if (exam.module === "reading" || exam.module === "listening") {
const parts = exam.parts.map((p) => const parts = exam.parts.map((p) =>
Object.assign(p, { Object.assign(p, {
exercises: p.exercises.map((x) => Object.assign(x, {userSolutions: userSolutions.find((y) => x.id === y.exercise)?.solutions})), exercises: p.exercises.map((x) => Object.assign(x, {userSolutions: userSolutions.find((y) => x.id === y.exercise)?.solutions})),

View File

@@ -141,7 +141,7 @@ export default function Page() {
}; };
const updateExamWithUserSolutions = (exam: Exam): Exam => { const updateExamWithUserSolutions = (exam: Exam): Exam => {
if (exam.module === "reading") { if (exam.module === "reading" || exam.module === "listening") {
const parts = exam.parts.map((p) => const parts = exam.parts.map((p) =>
Object.assign(p, { Object.assign(p, {
exercises: p.exercises.map((x) => Object.assign(x, {userSolutions: userSolutions.find((y) => x.id === y.exercise)?.solutions})), exercises: p.exercises.map((x) => Object.assign(x, {userSolutions: userSolutions.find((y) => x.id === y.exercise)?.solutions})),