import { renderExercise } from "@/components/Exercises"; import ModuleTitle from "@/components/Medium/ModuleTitle"; import { renderSolution } from "@/components/Solutions"; import { Exercise, UserSolution, WritingExam } from "@/interfaces/exam"; import useExamStore, { usePersistentExamStore } from "@/stores/exam"; import { countExercises } from "@/utils/moduleUtils"; import PartDivider from "./Navigation/SectionDivider"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { ExamProps } from "./types"; import useExamTimer from "@/hooks/useExamTimer"; import useExamNavigation from "./Navigation/useExamNavigation"; import SectionNavbar from "./Navigation/SectionNavbar"; import ProgressButtons from "./components/ProgressButtons"; import PracticeModal from "@/components/PracticeModal"; const Writing: React.FC> = ({ exam, showSolutions = false, preview = false }) => { const updateTimers = useExamTimer(exam.module, preview || showSolutions); const userSolutionRef = useRef<(() => UserSolution) | null>(null); const [solutionWasUpdated, setSolutionWasUpdated] = useState(false); const examState = useExamStore((state) => state); const persistentExamState = usePersistentExamStore((state) => state); const { exerciseIndex, flags, setBgColor, setUserSolutions, setTimeIsUp, userSolutions, dispatch, navigation, timeSpentCurrentModule } = !preview ? examState : persistentExamState; const timer = useRef(exam.minTimer - timeSpentCurrentModule / 60); const hasPractice = useMemo(() => exam.exercises.some(e => e.isPractice), [exam.exercises]) const { finalizeModule, timeIsUp } = flags; const { nextDisabled } = navigation; useEffect(() => { if (finalizeModule || timeIsUp) { updateTimers(); if (timeIsUp) { setTimeIsUp(false); } dispatch({ type: "FINALIZE_MODULE", payload: { updateTimers: false } }) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [finalizeModule, timeIsUp]) const { nextExercise, previousExercise, showPartDivider, setShowPartDivider, seenParts, setSeenParts, } = useExamNavigation({ exam, module: "writing", showSolutions, preview }); const registerSolution = useCallback((updateSolution: () => UserSolution) => { userSolutionRef.current = updateSolution; setSolutionWasUpdated(true); }, []); useEffect(() => { if (solutionWasUpdated && userSolutionRef.current) { const solution = userSolutionRef.current(); setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), { ...solution, module: "writing", exam: exam.id }]); setSolutionWasUpdated(false); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [solutionWasUpdated]) const currentExercise = useMemo(() => { const exercise = exam.exercises[exerciseIndex]; return { ...exercise, userSolutions: userSolutions.find((x) => x.exercise === exercise.id)?.solutions || [], }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [exerciseIndex]); const handlePartDividerClick = () => { setShowPartDivider(false); setBgColor("bg-white"); setSeenParts((prev) => new Set(prev).add(exerciseIndex)); } const progressButtons = useMemo(() => // Do not remove the ()=> in handle next nextExercise()} nextDisabled={nextDisabled} /> , [nextExercise, previousExercise, nextDisabled]); return ( <> {(showPartDivider) ? : (
{exam.exercises.length > 1 && } {!showPartDivider && !showSolutions && renderExercise(currentExercise, exam.id, registerSolution, preview, progressButtons, progressButtons)} {showSolutions && renderSolution(currentExercise, progressButtons, progressButtons)}
) } ); } export default Writing;