import { Flame } from 'lucide-react'; import { api } from '@/api'; import { useAsync } from '@/lib/useAsync'; import { ErrorState, LoadingState, Screen, ScreenBody, ScreenHeader } from '@/components/ui'; import { toFa } from '@/lib/format'; import { useAppData } from '@/state/AppData'; import { StreakFlame, flameState } from '@/components/StreakFlame'; const WEEKS = 6; const WEEKDAYS = ['ش', 'ی', 'د', 'س', 'چ', 'پ', 'ج']; /** YYYY-MM-DD به وقتِ محلی (سرور روزها را همین‌طور می‌دهد). */ const isoDay = (d: Date) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`; /** شش هفته‌ی اخیر، هفته از شنبه شروع می‌شود. */ function recentWeeks(today = new Date()): Date[][] { const end = new Date(today.getFullYear(), today.getMonth(), today.getDate()); const offsetFromSaturday = (end.getDay() + 1) % 7; const start = new Date(end); start.setDate(end.getDate() - offsetFromSaturday - (WEEKS - 1) * 7); return Array.from({ length: WEEKS }, (_, w) => Array.from({ length: 7 }, (_, d) => { const day = new Date(start); day.setDate(start.getDate() + w * 7 + d); return day; }), ); } const dayOfMonth = (d: Date) => new Intl.DateTimeFormat('fa-IR-u-ca-persian', { day: 'numeric' }).format(d); export function StreakScreen() { const calendar = useAsync(() => api.streakCalendar(), [], { key: 'streak-calendar' }); const { summary } = useAppData(); const flame = flameState(summary?.streak); const active = new Set(calendar.data?.activeDays ?? []); const today = isoDay(new Date()); const weeks = recentWeeks(); return ( {calendar.loading ? ( ) : calendar.error || !calendar.data ? ( ) : ( <>
{[ { label: 'زنجیره‌ی فعلی', value: calendar.data.currentStreak }, { label: 'بیشترین زنجیره', value: calendar.data.longestStreak }, ].map((s, i) => (
{i === 0 ? : } {toFa(s.value)} {s.label} (روز)
))}
{WEEKDAYS.map((d) => {d})}
{weeks.map((week, w) => (
{week.map((day) => { const key = isoDay(day); const on = active.has(key); const future = key > today; return ( {on ? : dayOfMonth(day)} ); })}
))}
)}
); } /** نقطه‌عطف‌های زنجیره؛ فقط پیام و نوار، هیچ پاداشِ واقعی‌ای پشتش نیست (پاداش تصمیمِ محصولی است). */ const MILESTONES = [3, 7, 14, 30, 60, 100]; function StreakGoal({ current, doneToday, lost }: { current: number; doneToday: boolean; lost: number }) { const next = MILESTONES.find((m) => m > current); const reached = MILESTONES.filter((m) => m <= current).pop(); // زنجیره‌ی صفر: «۳ روز دیگه می‌شه زنجیره‌ی ۳ روزه» هدفی دور بود؛ قدمِ امروز کوچک و روشن گفته می‌شود. // زنجیره‌ی روشن و امروزِ خالی: یادآوریِ این‌که امروز چیزی مانده تا زنجیره نسوزد. const today = current === 0 && lost > 0 ? `زنجیره‌ی ${toFa(lost)} روزه‌ت یخ زد! امروز یه درس بزن تا دوباره آتیشش روشن بشه.` : current === 0 ? 'امروز فقط یه درس یا یه جزیره تموم کن تا زنجیره‌ت روشن بشه.' : !doneToday ? `امروز هنوز تمرین نکردی؛ یه درس بزن تا زنجیره‌ی ${toFa(current)} روزه‌ت نسوزه.` : null; return (
{today &&

{today}

}

{reached ? <>{toFa(current)} روزه‌ای! : null} {next ? `${toFa(next - current)} روز دیگه می‌شه زنجیره‌ی ${toFa(next)} روزه.` : 'از همه‌ی نقطه‌عطف‌ها رد شدی، افسانه‌ای!'}

{next && (
)}
); }