76 lines
2.5 KiB
TypeScript
76 lines
2.5 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { useNavigate, useParams } from 'react-router-dom';
|
|
import type { Card } from '@flashcard/shared';
|
|
import { cardsApi } from '../api/cards.js';
|
|
import { sessionsApi } from '../api/sessions.js';
|
|
import { useSession } from '../stores/sessionStore.js';
|
|
import { Flashcard } from '../components/Flashcard.js';
|
|
|
|
export function PracticePage() {
|
|
const { lessonId } = useParams();
|
|
const { session, current, done, showAnswer, reveal, answer, end } = useSession();
|
|
const navigate = useNavigate();
|
|
const [card, setCard] = useState<Card | null>(null);
|
|
|
|
useEffect(() => {
|
|
if (useSession.getState().session) return;
|
|
(async () => {
|
|
const active = await sessionsApi.active();
|
|
if (!active || String(active.lessonId) !== lessonId) return;
|
|
const nx = await sessionsApi.next(active.id);
|
|
useSession.setState({
|
|
session: active,
|
|
current: nx.done ? null : nx.item,
|
|
done: nx.done,
|
|
showAnswer: false,
|
|
shownAt: Date.now(),
|
|
});
|
|
})();
|
|
}, [lessonId]);
|
|
|
|
useEffect(() => {
|
|
if (session) return;
|
|
const t = setTimeout(async () => {
|
|
if (useSession.getState().session) return;
|
|
const active = await sessionsApi.active();
|
|
if (!active || String(active.lessonId) !== lessonId) {
|
|
navigate(`/practice/${lessonId}/setup`);
|
|
}
|
|
}, 50);
|
|
return () => clearTimeout(t);
|
|
}, [session, lessonId, navigate]);
|
|
|
|
useEffect(() => {
|
|
let cancel = false;
|
|
(async () => {
|
|
if (!current) { setCard(null); return; }
|
|
const c = await cardsApi.get(current.cardId);
|
|
if (!cancel) setCard(c);
|
|
})();
|
|
return () => { cancel = true; };
|
|
}, [current]);
|
|
|
|
useEffect(() => {
|
|
if (done && session) { (async () => { await end(); navigate(`/practice/${lessonId}/done`); })(); }
|
|
}, [done, session, end, navigate, lessonId]);
|
|
|
|
if (!current || !card) return <div className="p-6 text-center">Laden...</div>;
|
|
|
|
const isReverse = current.direction === 'backward';
|
|
return (
|
|
<div className="flex h-full flex-col">
|
|
<div className="mx-auto w-full max-w-2xl p-6">
|
|
<div className="mb-2 text-xs text-slate-500">{session?.cardsShown ?? 0} kaarten behandeld</div>
|
|
<Flashcard
|
|
question={isReverse ? card.answer : card.question}
|
|
answer={isReverse ? card.question : card.answer}
|
|
hint={card.hint}
|
|
showAnswer={showAnswer}
|
|
onReveal={reveal}
|
|
onAnswer={(r) => answer(r)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|