Files
math-quiz/app/(game)/variable-parentheses/play/VariablePlayClient.tsx
T

186 lines
5.7 KiB
TypeScript

"use client";
import { useEffect, useState } from "react";
import { flushSync } from "react-dom";
import StageIndicator from "@/components/game/StageIndicator";
import VariableChoiceAnswerForm from "@/components/game/VariableChoiceAnswerForm";
import VariableFeedbackPanel from "@/components/game/VariableFeedbackPanel";
import VariableScoreSummary from "@/components/game/VariableScoreSummary";
import KatexRenderer from "@/components/math/KatexRenderer";
import {
useVariableGameStore,
type VariableProblemCount,
} from "@/store/variableGameStore";
interface VariablePlayClientProps {
problemCount: VariableProblemCount;
}
export default function VariablePlayClient({
problemCount,
}: VariablePlayClientProps) {
const session = useVariableGameStore((state) => state.session);
const selectedProblemCount = useVariableGameStore(
(state) => state.selectedProblemCount,
);
const setProblemCount = useVariableGameStore(
(state) => state.setProblemCount,
);
const initSession = useVariableGameStore((state) => state.initSession);
const submitAnswer = useVariableGameStore((state) => state.submitAnswer);
const nextProblem = useVariableGameStore((state) => state.nextProblem);
const resetSession = useVariableGameStore((state) => state.resetSession);
const [selectedChoiceIds, setSelectedChoiceIds] = useState<
[string | null, string | null]
>([null, null]);
const [activeTermIndex, setActiveTermIndex] = useState<0 | 1>(0);
const sessionProblemCount = session?.problems.length ?? null;
const currentProblem = session?.problems[session.currentIndex] ?? null;
const currentResult = session?.results[session.currentIndex] ?? null;
const isComplete = Boolean(
session && session.currentIndex >= session.problems.length,
);
const sessionNeedsInit = sessionProblemCount !== problemCount;
const correctCount =
session?.results.filter((result) => result?.correct).length ?? 0;
const totalCount = session?.problems.length ?? 0;
useEffect(() => {
if (selectedProblemCount !== problemCount) {
setProblemCount(problemCount);
}
if (sessionNeedsInit) {
initSession(problemCount);
}
}, [
initSession,
problemCount,
selectedProblemCount,
sessionProblemCount,
sessionNeedsInit,
setProblemCount,
]);
useEffect(() => {
setSelectedChoiceIds([null, null]);
setActiveTermIndex(0);
}, [currentProblem?.key]);
function handleSelectChoice(termIndex: 0 | 1, choiceId: string) {
setSelectedChoiceIds((current) => {
const next: [string | null, string | null] = [...current];
next[termIndex] = choiceId;
return next;
});
if (termIndex === 0) {
setActiveTermIndex(1);
}
}
function handleSubmit() {
if (currentResult || !currentProblem) return;
if (selectedChoiceIds[0] === null || selectedChoiceIds[1] === null) return;
submitAnswer({ selectedChoiceIds });
}
function handleNextProblem() {
flushSync(() => {
nextProblem();
});
}
if (!session || sessionNeedsInit) {
return (
<main className="flex min-h-screen items-center bg-slate-50 px-6 py-12 text-slate-950">
<section className="mx-auto w-full max-w-3xl">
<p className="text-lg font-semibold text-slate-700">
.
</p>
</section>
</main>
);
}
if (isComplete) {
return (
<VariableScoreSummary
correctCount={correctCount}
totalCount={totalCount}
problems={session.problems}
results={session.results}
onRestart={resetSession}
/>
);
}
if (!currentProblem) {
return (
<main className="flex min-h-screen items-center bg-slate-50 px-6 py-12 text-slate-950">
<section className="mx-auto w-full max-w-3xl">
<p className="text-lg font-semibold text-slate-700">
.
</p>
</section>
</main>
);
}
const submitted = currentResult !== null;
const isLastProblem = session.currentIndex === session.problems.length - 1;
return (
<main className="min-h-screen bg-slate-50 px-6 py-10 text-slate-950">
<section className="mx-auto flex w-full max-w-3xl flex-col gap-8">
<div className="flex flex-wrap items-center justify-between gap-4">
<div className="space-y-2">
<p className="text-sm font-semibold text-emerald-700">
</p>
<h1 className="text-3xl font-bold tracking-normal sm:text-4xl">
</h1>
</div>
<StageIndicator
currentIndex={session.currentIndex}
results={session.results}
/>
</div>
<div className="rounded-md bg-white px-6 pt-6 pb-2 shadow-sm ring-1 ring-slate-200 sm:p-8">
<p className="mb-4 text-sm font-semibold text-slate-500">
.
</p>
<div className="text-3xl font-semibold text-slate-950 sm:text-4xl">
<KatexRenderer latex={currentProblem.latexBefore} displayMode />
</div>
</div>
{!submitted ? (
<VariableChoiceAnswerForm
choices={currentProblem.choices}
selectedChoiceIds={selectedChoiceIds}
activeTermIndex={activeTermIndex}
onSelectChoice={handleSelectChoice}
onSubmit={handleSubmit}
/>
) : null}
{currentResult ? (
<VariableFeedbackPanel
result={currentResult}
problem={currentProblem}
isLastProblem={isLastProblem}
onNext={handleNextProblem}
/>
) : null}
</section>
</main>
);
}