import { Module } from "@/interfaces"; import { ExamState } from "../types"; import { SESSION_ACTIONS, SessionActions, sessionReducer } from "./session"; import { Exam, UserSolution } from "@/interfaces/exam"; import { updateExamWithUserSolutions } from "../utils"; import { defaultExamUserSolutions } from "@/utils/exams"; import { Assignment } from "@/interfaces/results"; import { Stat } from "@/interfaces/user"; import { convertToUserSolutions } from "@/utils/stats"; export type RootActions = { type: 'INIT_EXAM'; payload: { exams: Exam[], modules: Module[], assignment?: Assignment } } | { type: 'INIT_SOLUTIONS'; payload: { exams: Exam[], modules: Module[], stats: Stat[], timeSpent?: number, inactivity?: number } } | { type: 'UPDATE_TIMERS'; payload: { timeSpent: number; inactivity: number; timeSpentCurrentModule: number; } } | { type: 'FINALIZE_MODULE'; payload: { updateTimers: boolean } } | { type: 'FINALIZE_MODULE_SOLUTIONS' } | { type: 'UPDATE_EXAMS' } export type Action = RootActions | SessionActions; export const rootReducer = ( state: ExamState, action: Action ): Partial => { if (SESSION_ACTIONS.includes(action.type as any)) { return sessionReducer(action as SessionActions); } switch (action.type) { case 'INIT_EXAM': { const { exams, modules, assignment } = action.payload; let examAndSolutions = {} // A new exam is about to start, // fill the first module with defaultUserSolutions let defaultSolutions = exams.map(defaultExamUserSolutions).flat(); examAndSolutions = { userSolutions: defaultSolutions, exam: updateExamWithUserSolutions(exams[0], defaultSolutions) } if (assignment) { examAndSolutions = { ...examAndSolutions, assignment } } // now all the modules start at 0 since navigation // is now handled at the module page's and no re-renders // reset the initial render caused by the timers // no need to do all that weird chainning with -1 // on some modules and triggering next() to update final solution // with hasExamEnded flag return { moduleIndex: 0, partIndex: 0, exerciseIndex: 0, questionIndex: 0, exams: exams, selectedModules: modules, showSolutions: false, ...examAndSolutions } }; case 'INIT_SOLUTIONS': { const { exams, modules, stats, timeSpent, inactivity } = action.payload; let time = {} if (timeSpent) time = { timeSpent } if (inactivity) time = { ...time, inactivity } return { moduleIndex: -1, partIndex: 0, exerciseIndex: 0, questionIndex: 0, exams: exams, selectedModules: modules, showSolutions: true, userSolutions: convertToUserSolutions(stats), ...time } } case 'UPDATE_TIMERS': { // Just assigning the timers at once instead of two different calls const { timeSpent, inactivity, timeSpentCurrentModule } = action.payload; return { timeSpentCurrentModule, timeSpent, inactivity } }; case 'FINALIZE_MODULE': { const { updateTimers } = action.payload; // To finalize a module first flag the timers to be updated if (updateTimers) { return { flags: { ...state.flags, finalizeModule: true } } } else { // then check whether there are more modules in the exam, if there are // setup the next module if (state.moduleIndex === state.selectedModules.length - 1) { return { showSolutions: true, flags: { ...state.flags, finalizeModule: false, finalizeExam: true, } } } else if (state.moduleIndex < state.selectedModules.length - 1) { return { moduleIndex: state.moduleIndex + 1, partIndex: 0, exerciseIndex: 0, questionIndex: 0, exam: updateExamWithUserSolutions(state.exams[state.moduleIndex + 1], state.userSolutions), flags: { ...state.flags, finalizeModule: false, } } } } } case 'FINALIZE_MODULE_SOLUTIONS': { if (state.flags.reviewAll) { const notLastModule = state.moduleIndex < state.selectedModules.length - 1; const moduleIndex = notLastModule ? state.moduleIndex + 1 : -1; if (notLastModule) { return { questionIndex: 0, exerciseIndex: 0, partIndex: 0, exam: state.exams[moduleIndex], moduleIndex: moduleIndex } } else { return { questionIndex: 0, exerciseIndex: 0, partIndex: 0, moduleIndex: -1 } } } else { return { moduleIndex: -1 } } } case 'UPDATE_EXAMS': { const exams = state.exams.map((e) => updateExamWithUserSolutions(e, state.userSolutions)); const exam = updateExamWithUserSolutions(state.exam!, state.userSolutions); return { exams, exam } } default: return {}; } };