- Adapted the exam to store all of its information to Zustand;
- Made it so, every time there is a change or every X seconds, it saves the session;
This commit is contained in:
@@ -22,9 +22,9 @@ export default function Level({exam, showSolutions = false, onFinish}: Props) {
|
||||
const [questionIndex, setQuestionIndex] = useState(0);
|
||||
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
|
||||
const [exerciseIndex, setExerciseIndex] = useState(0);
|
||||
const [userSolutions, setUserSolutions] = useState<UserSolution[]>(exam.exercises.map((x) => defaultUserSolutions(x, exam)));
|
||||
|
||||
const [hasExamEnded, setHasExamEnded] = useExamStore((state) => [state.hasExamEnded, state.setHasExamEnded]);
|
||||
const {userSolutions, setUserSolutions} = useExamStore((state) => state);
|
||||
const {hasExamEnded, setHasExamEnded} = useExamStore((state) => state);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentQuestionIndex(0);
|
||||
@@ -38,7 +38,7 @@ export default function Level({exam, showSolutions = false, onFinish}: Props) {
|
||||
|
||||
const nextExercise = (solution?: UserSolution) => {
|
||||
if (solution) {
|
||||
setUserSolutions((prev) => [...prev.filter((x) => x.exercise !== solution.exercise), solution]);
|
||||
setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), solution]);
|
||||
}
|
||||
setQuestionIndex((prev) => prev + currentQuestionIndex);
|
||||
|
||||
@@ -62,7 +62,7 @@ export default function Level({exam, showSolutions = false, onFinish}: Props) {
|
||||
|
||||
const previousExercise = (solution?: UserSolution) => {
|
||||
if (solution) {
|
||||
setUserSolutions((prev) => [...prev.filter((x) => x.exercise !== solution.exercise), solution]);
|
||||
setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), solution]);
|
||||
}
|
||||
|
||||
if (exerciseIndex > 0) {
|
||||
|
||||
@@ -7,7 +7,6 @@ import AudioPlayer from "@/components/Low/AudioPlayer";
|
||||
import Button from "@/components/Low/Button";
|
||||
import BlankQuestionsModal from "@/components/BlankQuestionsModal";
|
||||
import useExamStore from "@/stores/examStore";
|
||||
import {defaultUserSolutions} from "@/utils/exams";
|
||||
import {countExercises} from "@/utils/moduleUtils";
|
||||
|
||||
interface Props {
|
||||
@@ -22,23 +21,29 @@ const INSTRUCTIONS_AUDIO_SRC =
|
||||
export default function Listening({exam, showSolutions = false, onFinish}: Props) {
|
||||
const [questionIndex, setQuestionIndex] = useState(0);
|
||||
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
|
||||
const [exerciseIndex, setExerciseIndex] = useState(showSolutions ? 0 : -1);
|
||||
const [partIndex, setPartIndex] = useState(exam.variant === "partial" ? 0 : -1);
|
||||
const [timesListened, setTimesListened] = useState(0);
|
||||
const [userSolutions, setUserSolutions] = useState<UserSolution[]>(
|
||||
exam.parts.flatMap((x) => x.exercises).map((x) => defaultUserSolutions(x, exam)),
|
||||
);
|
||||
const [showBlankModal, setShowBlankModal] = useState(false);
|
||||
|
||||
const [hasExamEnded, setHasExamEnded] = useExamStore((state) => [state.hasExamEnded, state.setHasExamEnded]);
|
||||
const {userSolutions, setUserSolutions} = useExamStore((state) => state);
|
||||
const {hasExamEnded, setHasExamEnded} = useExamStore((state) => state);
|
||||
const {partIndex, setPartIndex} = useExamStore((state) => state);
|
||||
const {exerciseIndex, setExerciseIndex} = useExamStore((state) => state);
|
||||
|
||||
const scrollToTop = () => Array.from(document.getElementsByTagName("body")).forEach((body) => body.scrollTo(0, 0));
|
||||
|
||||
useEffect(() => {
|
||||
if (showSolutions) setExerciseIndex(-1);
|
||||
}, [setExerciseIndex, showSolutions]);
|
||||
|
||||
useEffect(() => {
|
||||
if (exam.variant !== "partial") setPartIndex(-1);
|
||||
}, [exam.variant, setPartIndex]);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasExamEnded && exerciseIndex === -1) {
|
||||
setExerciseIndex((prev) => prev + 1);
|
||||
setExerciseIndex(exerciseIndex + 1);
|
||||
}
|
||||
}, [hasExamEnded, exerciseIndex]);
|
||||
}, [hasExamEnded, exerciseIndex, setExerciseIndex]);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentQuestionIndex(0);
|
||||
@@ -56,17 +61,17 @@ export default function Listening({exam, showSolutions = false, onFinish}: Props
|
||||
const nextExercise = (solution?: UserSolution) => {
|
||||
scrollToTop();
|
||||
if (solution) {
|
||||
setUserSolutions((prev) => [...prev.filter((x) => x.exercise !== solution.exercise), solution]);
|
||||
setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), solution]);
|
||||
}
|
||||
setQuestionIndex((prev) => prev + currentQuestionIndex);
|
||||
|
||||
if (exerciseIndex + 1 < exam.parts[partIndex].exercises.length && !hasExamEnded) {
|
||||
setExerciseIndex((prev) => prev + 1);
|
||||
setExerciseIndex(exerciseIndex + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (partIndex + 1 < exam.parts.length && !hasExamEnded) {
|
||||
setPartIndex((prev) => prev + 1);
|
||||
setPartIndex(partIndex + 1);
|
||||
setExerciseIndex(showSolutions ? 0 : -1);
|
||||
return;
|
||||
}
|
||||
@@ -97,10 +102,10 @@ export default function Listening({exam, showSolutions = false, onFinish}: Props
|
||||
const previousExercise = (solution?: UserSolution) => {
|
||||
scrollToTop();
|
||||
if (solution) {
|
||||
setUserSolutions((prev) => [...prev.filter((x) => x.exercise !== solution.exercise), solution]);
|
||||
setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), solution]);
|
||||
}
|
||||
|
||||
setExerciseIndex((prev) => prev - 1);
|
||||
setExerciseIndex(exerciseIndex - 1);
|
||||
};
|
||||
|
||||
const getExercise = () => {
|
||||
@@ -175,12 +180,14 @@ export default function Listening({exam, showSolutions = false, onFinish}: Props
|
||||
|
||||
{/* Exercise renderer */}
|
||||
{exerciseIndex > -1 &&
|
||||
partIndex > -1 &&
|
||||
exerciseIndex < exam.parts[partIndex].exercises.length &&
|
||||
!showSolutions &&
|
||||
renderExercise(getExercise(), nextExercise, previousExercise, setCurrentQuestionIndex)}
|
||||
|
||||
{/* Solution renderer */}
|
||||
{exerciseIndex > -1 &&
|
||||
partIndex > -1 &&
|
||||
exerciseIndex < exam.parts[partIndex].exercises.length &&
|
||||
showSolutions &&
|
||||
renderSolution(exam.parts[partIndex].exercises[exerciseIndex], nextExercise, previousExercise, setCurrentQuestionIndex)}
|
||||
@@ -195,7 +202,7 @@ export default function Listening({exam, showSolutions = false, onFinish}: Props
|
||||
if (partIndex === 0) return setPartIndex(-1);
|
||||
|
||||
setExerciseIndex(exam.parts[partIndex - 1].exercises.length - 1);
|
||||
setPartIndex((prev) => prev - 1);
|
||||
setPartIndex(partIndex - 1);
|
||||
}}
|
||||
className="max-w-[200px] w-full">
|
||||
Back
|
||||
|
||||
@@ -83,18 +83,20 @@ function TextModal({isOpen, title, content, onClose}: {isOpen: boolean; title: s
|
||||
export default function Reading({exam, showSolutions = false, onFinish}: Props) {
|
||||
const [questionIndex, setQuestionIndex] = useState(0);
|
||||
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
|
||||
const [exerciseIndex, setExerciseIndex] = useState(showSolutions ? 0 : -1);
|
||||
const [partIndex, setPartIndex] = useState(0);
|
||||
const [showTextModal, setShowTextModal] = useState(false);
|
||||
const [userSolutions, setUserSolutions] = useState<UserSolution[]>(
|
||||
exam.parts.flatMap((x) => x.exercises).map((x) => defaultUserSolutions(x, exam)),
|
||||
);
|
||||
const [showBlankModal, setShowBlankModal] = useState(false);
|
||||
|
||||
const [hasExamEnded, setHasExamEnded] = useExamStore((state) => [state.hasExamEnded, state.setHasExamEnded]);
|
||||
const {userSolutions, setUserSolutions} = useExamStore((state) => state);
|
||||
const {hasExamEnded, setHasExamEnded} = useExamStore((state) => state);
|
||||
const {partIndex, setPartIndex} = useExamStore((state) => state);
|
||||
const {exerciseIndex, setExerciseIndex} = useExamStore((state) => state);
|
||||
|
||||
const scrollToTop = () => Array.from(document.getElementsByTagName("body")).forEach((body) => body.scrollTo(0, 0));
|
||||
|
||||
useEffect(() => {
|
||||
if (showSolutions) setExerciseIndex(-1);
|
||||
}, [setExerciseIndex, showSolutions]);
|
||||
|
||||
useEffect(() => {
|
||||
const listener = (e: KeyboardEvent) => {
|
||||
if (e.key === "F3" || ((e.ctrlKey || e.metaKey) && e.key === "f")) {
|
||||
@@ -115,9 +117,9 @@ export default function Reading({exam, showSolutions = false, onFinish}: Props)
|
||||
|
||||
useEffect(() => {
|
||||
if (hasExamEnded && exerciseIndex === -1) {
|
||||
setExerciseIndex((prev) => prev + 1);
|
||||
setExerciseIndex(exerciseIndex + 1);
|
||||
}
|
||||
}, [hasExamEnded, exerciseIndex]);
|
||||
}, [hasExamEnded, exerciseIndex, setExerciseIndex]);
|
||||
|
||||
const confirmFinishModule = (keepGoing?: boolean) => {
|
||||
if (!keepGoing) {
|
||||
@@ -131,17 +133,17 @@ export default function Reading({exam, showSolutions = false, onFinish}: Props)
|
||||
const nextExercise = (solution?: UserSolution) => {
|
||||
scrollToTop();
|
||||
if (solution) {
|
||||
setUserSolutions((prev) => [...prev.filter((x) => x.exercise !== solution.exercise), solution]);
|
||||
setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), solution]);
|
||||
}
|
||||
setQuestionIndex((prev) => prev + currentQuestionIndex);
|
||||
|
||||
if (exerciseIndex + 1 < exam.parts[partIndex].exercises.length && !hasExamEnded) {
|
||||
setExerciseIndex((prev) => prev + 1);
|
||||
setExerciseIndex(exerciseIndex + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (partIndex + 1 < exam.parts.length && !hasExamEnded) {
|
||||
setPartIndex((prev) => prev + 1);
|
||||
setPartIndex(partIndex + 1);
|
||||
setExerciseIndex(showSolutions ? 0 : -1);
|
||||
return;
|
||||
}
|
||||
@@ -172,10 +174,10 @@ export default function Reading({exam, showSolutions = false, onFinish}: Props)
|
||||
const previousExercise = (solution?: UserSolution) => {
|
||||
scrollToTop();
|
||||
if (solution) {
|
||||
setUserSolutions((prev) => [...prev.filter((x) => x.exercise !== solution.exercise), solution]);
|
||||
setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), solution]);
|
||||
}
|
||||
|
||||
setExerciseIndex((prev) => prev - 1);
|
||||
setExerciseIndex(exerciseIndex - 1);
|
||||
};
|
||||
|
||||
const getExercise = () => {
|
||||
@@ -256,7 +258,7 @@ export default function Reading({exam, showSolutions = false, onFinish}: Props)
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
setExerciseIndex(exam.parts[partIndex - 1].exercises.length - 1);
|
||||
setPartIndex((prev) => prev - 1);
|
||||
setPartIndex(partIndex - 1);
|
||||
}}
|
||||
className="max-w-[200px] w-full">
|
||||
Back
|
||||
|
||||
@@ -22,9 +22,10 @@ interface Props {
|
||||
export default function Speaking({exam, showSolutions = false, onFinish}: Props) {
|
||||
const [questionIndex, setQuestionIndex] = useState(0);
|
||||
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
|
||||
const [exerciseIndex, setExerciseIndex] = useState(0);
|
||||
const [userSolutions, setUserSolutions] = useState<UserSolution[]>(exam.exercises.map((x) => defaultUserSolutions(x, exam)));
|
||||
const [hasExamEnded, setHasExamEnded] = useExamStore((state) => [state.hasExamEnded, state.setHasExamEnded]);
|
||||
|
||||
const {userSolutions, setUserSolutions} = useExamStore((state) => state);
|
||||
const {hasExamEnded, setHasExamEnded} = useExamStore((state) => state);
|
||||
const {exerciseIndex, setExerciseIndex} = useExamStore((state) => state);
|
||||
|
||||
const scrollToTop = () => Array.from(document.getElementsByTagName("body")).forEach((body) => body.scrollTo(0, 0));
|
||||
|
||||
@@ -35,12 +36,12 @@ export default function Speaking({exam, showSolutions = false, onFinish}: Props)
|
||||
const nextExercise = (solution?: UserSolution) => {
|
||||
scrollToTop();
|
||||
if (solution) {
|
||||
setUserSolutions((prev) => [...prev.filter((x) => x.exercise !== solution.exercise), solution]);
|
||||
setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), solution]);
|
||||
}
|
||||
setQuestionIndex((prev) => prev + currentQuestionIndex);
|
||||
|
||||
if (exerciseIndex + 1 < exam.exercises.length) {
|
||||
setExerciseIndex((prev) => prev + 1);
|
||||
setExerciseIndex(exerciseIndex + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -60,11 +61,11 @@ export default function Speaking({exam, showSolutions = false, onFinish}: Props)
|
||||
const previousExercise = (solution?: UserSolution) => {
|
||||
scrollToTop();
|
||||
if (solution) {
|
||||
setUserSolutions((prev) => [...prev.filter((x) => x.exercise !== solution.exercise), solution]);
|
||||
setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), solution]);
|
||||
}
|
||||
|
||||
if (exerciseIndex > 0) {
|
||||
setExerciseIndex((prev) => prev - 1);
|
||||
setExerciseIndex(exerciseIndex - 1);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -19,21 +19,20 @@ interface Props {
|
||||
}
|
||||
|
||||
export default function Writing({exam, showSolutions = false, onFinish}: Props) {
|
||||
const [exerciseIndex, setExerciseIndex] = useState(0);
|
||||
const [userSolutions, setUserSolutions] = useState<UserSolution[]>(exam.exercises.map((x) => defaultUserSolutions(x, exam)));
|
||||
|
||||
const [hasExamEnded, setHasExamEnded] = useExamStore((state) => [state.hasExamEnded, state.setHasExamEnded]);
|
||||
const {userSolutions, setUserSolutions} = useExamStore((state) => state);
|
||||
const {hasExamEnded, setHasExamEnded} = useExamStore((state) => state);
|
||||
const {exerciseIndex, setExerciseIndex} = useExamStore((state) => state);
|
||||
|
||||
const scrollToTop = () => Array.from(document.getElementsByTagName("body")).forEach((body) => body.scrollTo(0, 0));
|
||||
|
||||
const nextExercise = (solution?: UserSolution) => {
|
||||
scrollToTop();
|
||||
if (solution) {
|
||||
setUserSolutions((prev) => [...prev.filter((x) => x.exercise !== solution.exercise), solution]);
|
||||
setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), solution]);
|
||||
}
|
||||
|
||||
if (exerciseIndex + 1 < exam.exercises.length) {
|
||||
setExerciseIndex((prev) => prev + 1);
|
||||
setExerciseIndex(exerciseIndex + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -53,11 +52,11 @@ export default function Writing({exam, showSolutions = false, onFinish}: Props)
|
||||
const previousExercise = (solution?: UserSolution) => {
|
||||
scrollToTop();
|
||||
if (solution) {
|
||||
setUserSolutions((prev) => [...prev.filter((x) => x.exercise !== solution.exercise), solution]);
|
||||
setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), solution]);
|
||||
}
|
||||
|
||||
if (exerciseIndex > 0) {
|
||||
setExerciseIndex((prev) => prev - 1);
|
||||
setExerciseIndex(exerciseIndex - 1);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user