import { useState } from 'react'; import { motion } from 'motion/react'; import { Brain, Check, ChevronDown, Crown, Lock, PartyPopper, Play, Sparkles } from 'lucide-react'; import { api, type HomeNode, type HomeUnit } from '@/api'; import { useAppData } from '@/state/AppData'; import { findContinue, unitColor, unitProgress } from '@/state/curriculum'; import { onWhite } from '@/lib/color'; import { useNav } from '@/nav/Navigator'; import { useAsync } from '@/lib/useAsync'; import { EmptyState, ErrorState, LoadingState, ProgressBar, Screen, ScreenHeader, Sep } from '@/components/ui'; import { iconFor } from '@/components/NodeIcon'; import { toFa } from '@/lib/format'; import { MathText } from '@/lib/MathText'; import { useFirstVisit } from '@/lib/firstVisit'; import { SubjectBar } from '@/components/SubjectBar'; /** * تبِ «درس‌ها»: فصل ← جزیره ← درس. فصل‌ها اول بسته‌اند و بالای صفحه یک کارتِ «ادامه بده» هست. * بازبودنِ فصل/جزیره در متغیرِ ماژول نگه داشته می‌شود، چون با رفتن به درس و برگشتن صفحه از نو ساخته * می‌شود (کلیدِ مسیر در MainApp) و کاربر جایش را گم می‌کرد. با بستنِ اپ پاک می‌شود، عمداً. */ const remembered: { unit: string | null; node: string | null } = { unit: null, node: null }; export function LessonsScreen() { const { home, homeError, refreshHome, gradeTitle } = useAppData(); const nav = useNav(); const units = home?.units ?? []; const current = findContinue(home); const [openUnit, setOpenUnitState] = useState(remembered.unit); const [openNode, setOpenNodeState] = useState(remembered.node); const setOpenUnit = (id: string | null) => { remembered.unit = id; setOpenUnitState(id); }; const setOpenNode = (id: string | null) => { remembered.node = id; setOpenNodeState(id); }; const totalNodes = units.reduce((n, u) => n + u.nodes.length, 0); const doneNodes = units.reduce((n, u) => n + unitProgress(u).done, 0); return ( {gradeTitle && <>{gradeTitle}}اول بخون، بعد آزمون جزیره رو بده} color="#2FB36B" exact mascot={{ mood: 'happy' }} > {home && totalNodes > 0 && (
جزیره‌هایی که تموم کردی {toFa(doneNodes)} از {toFa(totalNodes)}
)}
{!home ? ( homeError ? void refreshHome()} /> : ) : units.length === 0 ? ( ) : (
{current && current.node.nextLessonId != null && ( )} {units.map((unit, i) => ( setOpenUnit(openUnit === unit.id ? null : unit.id)} openNode={openNode} onToggleNode={(id) => setOpenNode(openNode === id ? null : id)} /> ))}
)}
); } function UnitCard({ index, unit, color, currentNodeId, open, onToggle, openNode, onToggleNode, }: { index: number; unit: HomeUnit; color: string; currentNodeId: string | null; open: boolean; onToggle: () => void; openNode: string | null; onToggleNode: (id: string) => void; }) { const p = unitProgress(unit); const complete = p.total > 0 && p.done === p.total; const untouched = unit.nodes.every((n) => n.status === 'locked'); const Icon = iconFor(unit.nodes[0]?.icon, index); const nav = useNav(); const premium = unit.requiresSubscription; const intro = useFirstVisit(`unit-card:${unit.id}`); return ( {open && !premium && (
    {unit.nodes.map((node, ni) => ( onToggleNode(node.id)} /> ))}
)}
); } function NodeRow({ num, node, color, isCurrent, open, onToggle, }: { num: number; node: HomeNode; color: string; isCurrent: boolean; open: boolean; onToggle: () => void }) { const nav = useNav(); const locked = node.status === 'locked'; const done = node.status === 'completed'; const lessons = useAsync(() => (open && !locked ? api.nodeLessons(node.id) : Promise.resolve(null)), [node.id, open, locked]); const playable = new Set(node.lessonIds); const list = lessons.data?.lessons.filter((l) => playable.has(Number(l.id))) ?? []; const loaded = !lessons.loading && !lessons.error && lessons.data !== null && lessons.data !== undefined; return (
  • {open && !locked && (
    {lessons.error ? ( ) : !loaded ? (
    {[0, 1, 2].map((k) =>
    )}
    ) : ( <>
      {list.map((l, li) => (
    • ))}
    )}
    )}
  • ); }