Replace keyboard emojis with the app's own lucide icons (quiz reactions, +XP, welcome hand, party popper, streak flame)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -111,7 +111,7 @@ export function NoHeartsScreen({
|
|||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-black text-ink">{back ? 'یه قلب برگشت!' : 'قلبهات تموم شد'}</h1>
|
<h1 className="text-2xl font-black text-ink">{back ? 'یه قلب برگشت!' : 'قلبهات تموم شد'}</h1>
|
||||||
<p className="mt-1.5 text-sm leading-7 text-muted">
|
<p className="mt-1.5 text-sm leading-7 text-muted">
|
||||||
{back ? 'بریم ادامه بدیم؛ این بار با دقتِ بیشتر 💪' : 'یه کم استراحت کن؛ مغزت هم استراحت لازم داره.'}
|
{back ? 'بریم ادامه بدیم؛ این بار با دقتِ بیشتر' : 'یه کم استراحت کن؛ مغزت هم استراحت لازم داره.'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { Bell, ChevronLeft, Coins, Flame, Star } from 'lucide-react';
|
import { Bell, ChevronLeft, Coins, Flame, Hand, PartyPopper, Star } from 'lucide-react';
|
||||||
import type { HomeNode } from '@/api';
|
import type { HomeNode } from '@/api';
|
||||||
import { useAppData } from '@/state/AppData';
|
import { useAppData } from '@/state/AppData';
|
||||||
import { findContinue, unitColor, unitProgress } from '@/state/curriculum';
|
import { findContinue, unitColor, unitProgress } from '@/state/curriculum';
|
||||||
@@ -39,8 +39,8 @@ export function HomeScreen() {
|
|||||||
: !home.units.length
|
: !home.units.length
|
||||||
? 'هنوز درسی برای این پایه ثبت نشده.'
|
? 'هنوز درسی برای این پایه ثبت نشده.'
|
||||||
: cont
|
: cont
|
||||||
? <>سلام {name}! 👋 وقتشه جزیرهی <b>«<MathText text={cont.node.title} />»</b> رو شروع کنی؛ دکمهی <b>شروع</b> رو بزن.</>
|
? <>سلام {name}! <Hand size={15} className="mb-0.5 inline text-[#E8A400]" /> وقتشه جزیرهی <b>«<MathText text={cont.node.title} />»</b> رو شروع کنی؛ دکمهی <b>شروع</b> رو بزن.</>
|
||||||
: <>آفرین {name}! همهی جزیرههای این پایه رو تموم کردی 🎉</>;
|
: <>آفرین {name}! همهی جزیرههای این پایه رو تموم کردی <PartyPopper size={15} className="mb-0.5 inline text-[#E8A400]" /></>;
|
||||||
|
|
||||||
const stats = [
|
const stats = [
|
||||||
{ icon: Flame, value: summary?.streak.current ?? 0, label: 'روزِ پیاپی', color: '#FF7A3D', fill: true, to: 'streak' as const },
|
{ icon: Flame, value: summary?.streak.current ?? 0, label: 'روزِ پیاپی', color: '#FF7A3D', fill: true, to: 'streak' as const },
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { motion } from 'motion/react';
|
import { motion } from 'motion/react';
|
||||||
import { Brain, Check, ChevronDown, Lock, Play, Sparkles } from 'lucide-react';
|
import { Brain, Check, ChevronDown, Lock, PartyPopper, Play, Sparkles } from 'lucide-react';
|
||||||
import { api, type HomeNode, type HomeUnit } from '@/api';
|
import { api, type HomeNode, type HomeUnit } from '@/api';
|
||||||
import { useAppData } from '@/state/AppData';
|
import { useAppData } from '@/state/AppData';
|
||||||
import { findContinue, unitColor, unitProgress } from '@/state/curriculum';
|
import { findContinue, unitColor, unitProgress } from '@/state/curriculum';
|
||||||
@@ -132,7 +132,7 @@ function UnitCard({
|
|||||||
</span>
|
</span>
|
||||||
<span className="min-w-0 flex-1">
|
<span className="min-w-0 flex-1">
|
||||||
<span className="block text-[11px] font-bold" style={{ color }}>
|
<span className="block text-[11px] font-bold" style={{ color }}>
|
||||||
{complete ? 'تموم شد 🎉' : `${toFa(p.total)} جزیره`}
|
{complete ? <><PartyPopper size={12} className="mb-0.5 ml-1 inline" />تموم شد</> : `${toFa(p.total)} جزیره`}
|
||||||
</span>
|
</span>
|
||||||
<span className="block text-sm font-black leading-6 text-ink"><MathText text={unit.title} /></span>
|
<span className="block text-sm font-black leading-6 text-ink"><MathText text={unit.title} /></span>
|
||||||
<span className="mt-1 flex items-center gap-2">
|
<span className="mt-1 flex items-center gap-2">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { AnimatePresence, motion } from 'motion/react';
|
import { AnimatePresence, motion } from 'motion/react';
|
||||||
import { Brain, Check, ChevronLeft, Coins, Flag, Heart, Infinity as InfinityIcon, Star, X } from 'lucide-react';
|
import { Brain, Check, ChevronLeft, Coins, Compass, Flag, Heart, Infinity as InfinityIcon, Lightbulb, Ruler, Sparkles, Star, Target, ThumbsUp, X, Zap, type LucideIcon } from 'lucide-react';
|
||||||
import { api, ApiError, type AnswerResult, type Hearts, type Question, type ReportReason } from '@/api';
|
import { api, ApiError, type AnswerResult, type Hearts, type Question, type ReportReason } from '@/api';
|
||||||
import { useAppData } from '@/state/AppData';
|
import { useAppData } from '@/state/AppData';
|
||||||
import { findNode, findNodeOfLesson, nextLessonOf, unitColor } from '@/state/curriculum';
|
import { findNode, findNodeOfLesson, nextLessonOf, unitColor } from '@/state/curriculum';
|
||||||
@@ -291,7 +291,7 @@ export function QuizScreen({ lessonId, nodeId }: { lessonId?: number; nodeId?: s
|
|||||||
transition={{ type: 'spring', stiffness: 400, damping: 14 }}
|
transition={{ type: 'spring', stiffness: 400, damping: 14 }}
|
||||||
className="mr-2 inline-block rounded-full bg-[#FFF3C4] px-2 py-0.5 text-[11px] font-black text-[#8A5A00]"
|
className="mr-2 inline-block rounded-full bg-[#FFF3C4] px-2 py-0.5 text-[11px] font-black text-[#8A5A00]"
|
||||||
>
|
>
|
||||||
+{toFa(answer.xpEarned)} ⭐
|
+{toFa(answer.xpEarned)} <Star size={11} className="mb-0.5 inline" fill="#E8A400" color="#E8A400" />
|
||||||
</motion.span>
|
</motion.span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -430,7 +430,7 @@ function Result({
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-2xl font-black text-ink">{result.stars === 3 ? 'عالی بود!' : result.stars === 2 ? 'آفرین!' : 'تموم شد!'}</h2>
|
<h2 className="text-2xl font-black text-ink">{result.stars === 3 ? 'عالی بود!' : result.stars === 2 ? 'آفرین!' : 'تموم شد!'}</h2>
|
||||||
{nodeMode && <p className="mt-1 text-sm font-bold" style={{ color }}>این جزیره رو تموم کردی 🏝️</p>}
|
{nodeMode && <p className="mt-1 text-sm font-bold" style={{ color }}>این جزیره رو تموم کردی</p>}
|
||||||
<p className="mt-1 text-sm text-muted">{toFa(correct)} جوابِ درست از {toFa(total)} سوال</p>
|
<p className="mt-1 text-sm text-muted">{toFa(correct)} جوابِ درست از {toFa(total)} سوال</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full gap-3">
|
<div className="flex w-full gap-3">
|
||||||
@@ -520,20 +520,21 @@ function ReportSheet({ open, questionId, onClose }: { open: boolean; questionId:
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** شکلکِ کوچکِ بعد از جواب؛ هر سوال یکی از مجموعه، با یک تکانِ کوتاه. فقط ایموجی و transform. */
|
/** آیکنِ کوچکِ بعد از جواب (همان خانوادهی آیکنِ اپ، نه ایموجیِ کیبورد)؛ هر سوال یکی از مجموعه، با یک تکانِ کوتاه. */
|
||||||
const WIN = ['🧠', '💪', '🎯', '⚡', '🤩'];
|
const WIN: [LucideIcon, string][] = [[Brain, '#9B7BFF'], [Zap, '#E8A400'], [Target, '#ED6956'], [ThumbsUp, '#3E9E73'], [Sparkles, '#4C5FD5']];
|
||||||
const MISS = ['🤔', '💡', '📐'];
|
const MISS: [LucideIcon, string][] = [[Lightbulb, '#E8A400'], [Ruler, '#4C5FD5'], [Compass, '#9B7BFF']];
|
||||||
function Reaction({ correct, seed }: { correct: boolean; seed: number }) {
|
function Reaction({ correct, seed }: { correct: boolean; seed: number }) {
|
||||||
const set = correct ? WIN : MISS;
|
const set = correct ? WIN : MISS;
|
||||||
|
const [Icon, color] = set[seed % set.length];
|
||||||
return (
|
return (
|
||||||
<motion.span
|
<motion.span
|
||||||
aria-hidden
|
aria-hidden
|
||||||
className="mr-1.5 inline-block"
|
className="mr-1.5 inline-block align-middle"
|
||||||
initial={{ scale: 0.4, rotate: 0 }}
|
initial={{ scale: 0.4, rotate: 0 }}
|
||||||
animate={{ scale: 1, rotate: [0, -14, 14, -8, 8, 0] }}
|
animate={{ scale: 1, rotate: [0, -14, 14, -8, 8, 0] }}
|
||||||
transition={{ duration: 0.6, ease: 'easeOut' }}
|
transition={{ duration: 0.6, ease: 'easeOut' }}
|
||||||
>
|
>
|
||||||
{set[seed % set.length]}
|
<Icon size={18} color={color} strokeWidth={2.4} />
|
||||||
</motion.span>
|
</motion.span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ function StreakGoal({ current }: { current: number }) {
|
|||||||
return (
|
return (
|
||||||
<div className="rounded-3xl border border-line bg-[#FFF3E8] px-4 py-3">
|
<div className="rounded-3xl border border-line bg-[#FFF3E8] px-4 py-3">
|
||||||
<p className="text-sm font-black leading-7 text-ink">
|
<p className="text-sm font-black leading-7 text-ink">
|
||||||
{reached ? `🔥 ${toFa(current)} روزهای! ` : ''}
|
{reached ? <><Flame size={16} fill="#FF7A3D" className="mb-0.5 ml-1 inline text-orange" />{toFa(current)} روزهای! </> : null}
|
||||||
{next ? `${toFa(next - current)} روز دیگه میشه زنجیرهی ${toFa(next)} روزه.` : 'از همهی نقطهعطفها رد شدی، افسانهای!'}
|
{next ? `${toFa(next - current)} روز دیگه میشه زنجیرهی ${toFa(next)} روزه.` : 'از همهی نقطهعطفها رد شدی، افسانهای!'}
|
||||||
</p>
|
</p>
|
||||||
{next && (
|
{next && (
|
||||||
|
|||||||
Reference in New Issue
Block a user