forked from Tadrisino/MathApp_frontend
116 lines
5.5 KiB
TypeScript
116 lines
5.5 KiB
TypeScript
import { useCompanion } from '@/state/companion';
|
||
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';
|
||
import { StepText } from '@/lib/StepText';
|
||
import { FeedbackBar } from '@/components/FeedbackBar';
|
||
|
||
/** معرفیِ درس، پیش از تمرین. */
|
||
export function LessonScreen({ lessonId, nodeId }: { lessonId: number; nodeId?: string }) {
|
||
const { home, companion } = useAppData();
|
||
const companionName = useCompanion().name;
|
||
const nav = useNav();
|
||
const intro = useAsync(() => api.lesson.intro(lessonId), [lessonId], { key: `lesson-intro:${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={companion} mood="idle" size={56} />
|
||
<div className="flex-1 rounded-3xl rounded-tr-sm border border-line bg-white p-4 shadow-sm">
|
||
<StepText text={data.introText} color={color} className="text-[15px] leading-[1.95] text-body" />
|
||
{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>
|
||
<StepText text={data.example} color={color} className="text-[15px] leading-[1.95] text-ink" />
|
||
{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} display />{figs('step', i)}</span>
|
||
</li>
|
||
))}
|
||
</ol>
|
||
</section>
|
||
)}
|
||
|
||
<div className="mt-auto flex flex-col gap-3 pt-2">
|
||
<FeedbackBar kind="lesson" id={String(lessonId)} />
|
||
{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} /> درباره این درس از {companionName} بپرس
|
||
</button>
|
||
</div>
|
||
</>
|
||
)}
|
||
</ScreenBody>
|
||
</Screen>
|
||
);
|
||
}
|