import { Exercise, ReadingExam, UserSolution } from "@/interfaces/exam"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import clsx from "clsx"; import { convertCamelCaseToReadable } from "@/utils/string"; import { renderExercise } from "@/components/Exercises"; import { renderSolution } from "@/components/Solutions"; import ModuleTitle from "@/components/Medium/ModuleTitle"; import BlankQuestionsModal from "@/components/QuestionsModal"; import { countExercises } from "@/utils/moduleUtils"; import PartDivider from "./Navigation/SectionDivider"; import ReadingPassage from "./components/ReadingPassage"; //import ReadingPassageModal from "./components/ReadingPassageModal"; import { calculateExerciseIndex } from "./utils/calculateExerciseIndex"; import useExamNavigation from "./Navigation/useExamNavigation"; import { ExamProps } from "./types"; import useExamStore, { usePersistentExamStore } from "@/stores/exam"; import useExamTimer from "@/hooks/useExamTimer"; import SectionNavbar from "./Navigation/SectionNavbar"; import ProgressButtons from "./components/ProgressButtons"; import PracticeModal from "@/components/PracticeModal"; const Reading: 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 [showBlankModal, setShowBlankModal] = useState(false); //const [showTextModal, setShowTextModal] = useState(false); const [isTextMinimized, setIsTextMinimzed] = useState(false); const examState = useExamStore((state) => state); const persistentExamState = usePersistentExamStore((state) => state); const { exerciseIndex, partIndex, questionIndex, userSolutions, flags, timeSpentCurrentModule, setBgColor, setUserSolutions, setTimeIsUp, dispatch, } = !preview ? examState : persistentExamState; const timer = useRef(exam.minTimer - timeSpentCurrentModule / 60); const { finalizeModule, timeIsUp } = flags; const { nextExercise, previousExercise, showPartDivider, setShowPartDivider, seenParts, setSeenParts, isBetweenParts, setIsBetweenParts, startNow } = useExamNavigation({ exam, module: "reading", showBlankModal, setShowBlankModal, showSolutions, preview, disableBetweenParts: showSolutions }); const hasPractice = useMemo(() => { if (partIndex > -1 && partIndex < exam.parts.length) { return exam.parts[partIndex].exercises.some(e => e.isPractice) } return false }, [partIndex, exam.parts]) 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]) useEffect(() => { const listener = (e: KeyboardEvent) => { if (e.key === "F3" || ((e.ctrlKey || e.metaKey) && e.key === "f")) { e.preventDefault(); } }; document.addEventListener("keydown", listener); return () => { document.removeEventListener("keydown", listener); }; }, []); 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: "reading", exam: exam.id }]); setSolutionWasUpdated(false); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [solutionWasUpdated]) const currentExercise = useMemo(() => { const exercise = exam.parts[partIndex].exercises[exerciseIndex]; return { ...exercise, userSolutions: userSolutions.find((x) => x.exercise === exercise.id)?.solutions || [], }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [partIndex, exerciseIndex]); const confirmFinishModule = (keepGoing?: boolean) => { if (!keepGoing) { setShowBlankModal(false); return; } else { nextExercise(true); setShowBlankModal(false); } }; const memoizedExerciseIndex = useMemo(() => calculateExerciseIndex(exam, partIndex, exerciseIndex, questionIndex) // eslint-disable-next-line react-hooks/exhaustive-deps , [partIndex, exerciseIndex, questionIndex] ); const handlePartDividerClick = () => { setShowPartDivider(false); setBgColor("bg-white"); setSeenParts((prev) => new Set(prev).add(partIndex)); } useEffect(() => { if (partIndex !== 0 && !showSolutions) { setIsBetweenParts(true); } }, [partIndex, setIsBetweenParts, showSolutions]) const progressButtons = useMemo(() => // Do not remove the ()=> in handle next nextExercise()} /> , [nextExercise, previousExercise]); return ( <> {showPartDivider ?
handlePartDividerClick()} />
: ( <>
{/* setShowTextModal(false)} />*/} {exam.parts.length > 1 && } x.exercises))} disableTimer={showSolutions || preview} label={convertCamelCaseToReadable(exam.parts[partIndex].exercises[exerciseIndex].type)} preview={preview} />
{!startNow && !showPartDivider && !showSolutions && !isBetweenParts && renderExercise(currentExercise, exam.id, registerSolution, preview, progressButtons, progressButtons)} {showSolutions && renderSolution(currentExercise, progressButtons, progressButtons)}
{/*exerciseIndex > -1 && partIndex > -1 && exerciseIndex < exam.parts[partIndex].exercises.length && ( )*/}
{((startNow || isBetweenParts) && !showPartDivider && !showSolutions) && nextExercise()} /> } )} ); } export default Reading;