Navigation rework, added prompt edit to components that were missing

This commit is contained in:
Carlos-Mesquita
2024-11-25 16:50:46 +00:00
parent e9b7bd14cc
commit 114da173be
105 changed files with 3761 additions and 3728 deletions

View File

@@ -0,0 +1,217 @@
import { ExerciseOnlyExam, ModuleExam, PartExam } from "@/interfaces/exam";
import { useState, useEffect } from "react";
import scrollToTop from "../utils/scrollToTop";
import { Module } from "@/interfaces";
import { answeredEveryQuestion } from "../utils/answeredEveryQuestion";
import useExamStore, { usePersistentExamStore } from "@/stores/exam";
import hasDivider from "../utils/hasDivider";
const MC_PER_PAGE = 2;
type UseExamNavigation = (props: {
exam: ModuleExam;
module: Module;
showBlankModal?: boolean;
setShowBlankModal?: React.Dispatch<React.SetStateAction<boolean>>;
showSolutions: boolean;
preview: boolean;
disableBetweenParts?: boolean;
}) => {
showPartDivider: boolean;
seenParts: Set<number>;
isBetweenParts: boolean;
nextExercise: (isBetweenParts?: boolean) => void;
previousExercise: (isBetweenParts?: boolean) => void;
setShowPartDivider: React.Dispatch<React.SetStateAction<boolean>>;
setSeenParts: React.Dispatch<React.SetStateAction<Set<number>>>;
setIsBetweenParts: React.Dispatch<React.SetStateAction<boolean>>;
};
const useExamNavigation: UseExamNavigation = ({
exam,
module,
setShowBlankModal,
showSolutions,
preview,
disableBetweenParts = false,
}) => {
const examState = useExamStore((state) => state);
const persistentExamState = usePersistentExamStore((state) => state);
const {
exerciseIndex, setExerciseIndex,
partIndex, setPartIndex,
questionIndex, setQuestionIndex,
userSolutions, setModuleIndex,
setBgColor,
dispatch,
} = !preview ? examState : persistentExamState;
const [isBetweenParts, setIsBetweenParts] = useState(partIndex !== 0 && exerciseIndex == 0 && !disableBetweenParts);
const isPartExam = ["reading", "listening", "level"].includes(exam.module);
const [seenParts, setSeenParts] = useState<Set<number>>(
new Set(showSolutions ?
(isPartExam ?
(exam as PartExam).parts.map((_, index) => index) :
(exam as ExerciseOnlyExam).exercises.map((_, index) => index)
) :
[]
)
);
const [showPartDivider, setShowPartDivider] = useState<boolean>(hasDivider(exam, 0));
useEffect(() => {
if (!showSolutions && hasDivider(exam, isPartExam ? partIndex : exerciseIndex) && !seenParts.has(partIndex)) {
setShowPartDivider(true);
setBgColor(`bg-ielts-${module}-light`);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [partIndex]);
const nextExercise = (keepGoing: boolean = false) => {
scrollToTop();
if (isPartExam) {
nextPartExam(keepGoing);
} else {
nextExerciseOnlyExam();
}
};
const previousExercise = () => {
scrollToTop();
if (isPartExam) {
previousPartExam();
} else {
previousExerciseOnlyExam();
}
};
const nextPartExam = (keepGoing: boolean) => {
const partExam = (exam as PartExam);
const reachedFinalExercise = exerciseIndex + 1 === partExam.parts[partIndex].exercises.length;
const currentExercise = partExam.parts[partIndex].exercises[exerciseIndex];
if (isBetweenParts) {
setIsBetweenParts(false);
return;
}
if (currentExercise.type === "multipleChoice" && questionIndex < currentExercise.questions.length - 1) {
setQuestionIndex(questionIndex + MC_PER_PAGE);
return;
}
if (!reachedFinalExercise) {
setExerciseIndex(exerciseIndex + 1);
setQuestionIndex(0);
return;
}
if (partIndex < partExam.parts.length - 1) {
if (!disableBetweenParts) setIsBetweenParts(true);
setPartIndex(partIndex + 1);
setExerciseIndex(0);
setQuestionIndex(0);
return;
}
if (!answeredEveryQuestion(exam as PartExam, userSolutions) && !keepGoing && setShowBlankModal && !showSolutions && !preview) {
setShowBlankModal(true);
return;
}
if (preview) {
setPartIndex(0);
setExerciseIndex(0);
setQuestionIndex(0);
}
if (!showSolutions) {
dispatch({ type: "FINALIZE_MODULE", payload: { updateTimers: true } });
} else {
dispatch({ type: "FINALIZE_MODULE_SOLUTIONS"});
}
}
const previousPartExam = () => {
if (partIndex !== 0) {
setPartIndex(partIndex - 1);
setExerciseIndex((exam as PartExam).parts[partIndex].exercises.length - 1);
if (isBetweenParts) setIsBetweenParts(false);
return;
}
setQuestionIndex(0);
if (exerciseIndex === 0 && !disableBetweenParts) {
setIsBetweenParts(true);
return;
}
if (exerciseIndex !== 0) {
setExerciseIndex(exerciseIndex - 1);
}
};
const nextExerciseOnlyExam = () => {
const exerciseOnlyExam = (exam as ExerciseOnlyExam);
const reachedFinalExercise = exerciseIndex + 1 === exerciseOnlyExam.exercises.length;
const currentExercise = exerciseOnlyExam.exercises[exerciseIndex];
if (currentExercise.type === "interactiveSpeaking" && questionIndex < currentExercise.prompts.length - 1) {
setQuestionIndex(questionIndex + 1)
return;
}
if (!reachedFinalExercise) {
setQuestionIndex(0);
setExerciseIndex(exerciseIndex + 1);
return;
}
if (preview) {
setPartIndex(0);
setExerciseIndex(0);
setQuestionIndex(0);
}
if (!showSolutions) {
dispatch({ type: "FINALIZE_MODULE", payload: { updateTimers: true } });
} else {
dispatch({ type: "FINALIZE_MODULE_SOLUTIONS"});
}
}
const previousExerciseOnlyExam = () => {
const currentExercise = (exam as ExerciseOnlyExam).exercises[exerciseIndex];
if (currentExercise.type === "interactiveSpeaking" && questionIndex !== 0) {
setQuestionIndex(questionIndex - 1);
return;
}
if (exerciseIndex !== 0) {
setExerciseIndex(exerciseIndex - 1);
return;
}
};
return {
seenParts,
showPartDivider,
nextExercise,
previousExercise,
setShowPartDivider,
setSeenParts,
isBetweenParts,
setIsBetweenParts,
};
}
export default useExamNavigation;