Files
MathApp_frontend/src/screens/LessonScreen.tsx

111 lines
5.2 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Brain, Play, Zap } from 'lucide-react';
import { api } from '@/api';
import { useAppData } from '@/state/AppData';
import { findNode, findNodeOfLesson, unitColor } from '@/state/curriculum';
import { useNav } from '@/nav/Navigator';
import { useAsync } from '@/lib/useAsync';
import { SeyarMascot } from '@/mascots/SeyarMascot';
import { moodFromServer } from '@/mascots/seyar';
import { ErrorState, LoadingState, PrimaryButton, Screen, ScreenBody, ScreenHeader, Sep } from '@/components/ui';
import { toFa } from '@/lib/format';
import { MathText } from '@/lib/MathText';
import { Diagram } from '@/diagram';
/** معرفیِ درس، پیش از تمرین. */
export function LessonScreen({ lessonId, nodeId }: { lessonId: number; nodeId?: string }) {
const { home, companion } = useAppData();
const nav = useNav();
const intro = useAsync(() => api.lesson.intro(lessonId), [lessonId]);
const ref = findNode(home, nodeId) ?? findNodeOfLesson(home, lessonId);
const color = ref ? unitColor(ref.unitIndex) : '#ED6956';
const data = intro.data;
const figs = (where: 'intro' | 'example' | 'step', index?: number) =>
(data?.figures ?? [])
.filter((f) => f.where === where && (where !== 'step' || f.index === index))
.map((f, k) => <Diagram key={k} spec={f.diagram} />);
return (
<Screen>
<ScreenHeader
title={data ? <MathText text={data.title} /> : '…'}
color={color}
back
mascot={{ char: companion, mood: moodFromServer(data?.mascotAnimation), size: 72 }}
subtitle={
ref && (
<span className="inline-flex items-center gap-1.5 rounded-full bg-white/15 px-3 py-1">
{/* ترتیبِ lessonIds «درسِ بعدی اول» است، نه ترتیبِ کتاب؛ پس شماره‌ی درس از آن درنمی‌آید. */}
<Zap size={11} fill="white" /> <MathText text={ref.node.title} /><Sep />{toFa(ref.node.lessonIds.length)} درس
</span>
)
}
/>
<ScreenBody className="flex flex-col gap-4">
{intro.loading ? (
<LoadingState text="دارم درس رو میارم…" />
) : intro.error || !data ? (
<ErrorState error={intro.error} onRetry={intro.reload} />
) : (
<>
<div className="flex items-start gap-3">
<SeyarMascot char="sigma" mood="idle" size={56} />
<div className="flex-1 rounded-3xl rounded-tr-sm border border-line bg-white p-4 shadow-sm">
<p className="whitespace-pre-line text-sm leading-[1.95] text-body"><MathText text={data.introText} /></p>
{figs('intro')}
</div>
</div>
{data.kind === 'video' && data.videoUrl && (
<video src={data.videoUrl} controls playsInline preload="metadata" className="w-full rounded-3xl bg-black" />
)}
{data.example && (
<section className="rounded-3xl border border-line bg-white p-4">
<h2 className="mb-2 text-xs font-black text-muted">مثال</h2>
<p className="whitespace-pre-line text-sm leading-[1.95] text-ink"><MathText text={data.example} /></p>
{figs('example')}
</section>
)}
{data.steps.length > 0 && (
<section className="rounded-3xl border border-line bg-white p-4">
<h2 className="mb-3 text-xs font-black text-muted">قدم‌به‌قدم</h2>
<ol className="flex flex-col gap-2.5">
{data.steps.map((step, i) => (
<li key={i} className="flex gap-3 text-sm leading-7 text-ink">
<span className="flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full text-xs font-black text-white" style={{ backgroundColor: color }}>
{toFa(i + 1)}
</span>
<span className="flex-1 whitespace-pre-line"><MathText text={step} />{figs('step', i)}</span>
</li>
))}
</ol>
</section>
)}
<div className="mt-auto flex flex-col gap-3 pt-2">
{data.questionCount === 0 ? (
<div className="rounded-2xl border-2 border-dashed border-[#dedced] bg-white py-4 text-center text-sm font-bold text-muted">
تمرینِ این درس به‌زودی اضافه می‌شه
</div>
) : (
<PrimaryButton color={color} onClick={() => nav.replace({ name: 'quiz', lessonId, nodeId: ref?.node.id ?? nodeId })}>
<Play size={18} fill="white" /> شروعِ تمرین ({toFa(data.questionCount)} سوال)
</PrimaryButton>
)}
<button
type="button"
onClick={() => nav.push({ name: 'ask', lessonId })}
className="flex items-center justify-center gap-2 rounded-2xl bg-[#F5F1FF] py-3 text-sm font-bold text-[#7857E0]"
>
<Brain size={16} /> درباره‌ی این درس از پی بپرس
</button>
</div>
</>
)}
</ScreenBody>
</Screen>
);
}