Files
MathApp_frontend/src/state/companion.ts
AliReza ca96a91dee No diacritics or written ezafe ی in the UI; map without the speech banner, companions cheering along the path; familiar payment sheet with gateway logos and per-channel methods; account deletion moved under «حساب کاربری»
- Text: kasra/fatha/damma/shadda/sukun/hamza-above and «ه‌ی» ezafe removed from every user-visible string (comments untouched); tanwin kept. Stale copy fixed (question packs are no longer sold), awkward onboarding sentences rewritten, سؤال → سوال. plainPersian() in MathText cleans the live tutor stream.
- Home: the mascot + speech bubble header is gone; the selected companion hops beside the current island and sits at every third bend (CSS transform animations only).
- Checkout: order summary, payment-method rows with Zarinpal/Bazaar icons and radio, amount on the pay button, lock trust line, darker overlay. paymentMethods in config.js per channel (web: zarinpal, app: zarinpal+bazaar by default).
- Profile: «ورود با حساب دیگر» removed (it was logout); new «حساب کاربری» sheet shows phone/ID and holds «حذف حساب کاربری»; logout is a red row. Delete sheet tells Bazaar subscribers to cancel renewal in Bazaar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 16:55:17 +03:30

71 lines
3.5 KiB
TypeScript

import { useSyncExternalStore } from 'react';
import { KEYS, store } from '@/lib/storage';
import { SEYAR_INFO, seyarFromSlug, type SeyarChar } from '@/mascots/seyar';
/**
* همراهِ انتخابیِ کاربر، یک جا برای کلِ اپ: نشانِ سرِ خانه، ماسکوتِ سربرگ‌ها و لودرها، نام و چهره‌ی دستیار، و ویژگیِ
* کوچکِ هر همراه. پیش‌تر هر صفحه همراهِ خودش را ثابت نوشته بود (فروشگاه و دستیار «پی»، درس‌ها «سیگما»، …) و انتخابِ
* کاربر فقط در خانه و درس دیده می‌شد.
*
* در localStorage هم می‌ماند تا اسپلش و صفحه‌ی ورود پیش از رسیدنِ پاسخِ سرور همان همراه را نشان دهند؛ منبعِ حقیقت
* همچنان سرور است (AppData بعد از ورود از /characters می‌خواند و این را به‌روز می‌کند).
*/
export interface CompanionLook {
char: SeyarChar;
name: string;
symbol: string;
/** رنگِ اصلی (بدنه‌ی ماسکوت) و رنگِ روشن‌ترِ کنارش، برای نشان و ته‌رنگ. */
color: string;
light: string;
/** ویژگیِ این همراه؛ در صفحه‌ی همراه‌ها نوشته شده و در آزمون دیده می‌شود (QuizScreen). */
trait: { title: string; text: string };
/** گامِ صدای «درست» نسبت به سیگما (نیم‌پرده)؛ هر همراه صدای خودش را دارد. */
pitch: number;
}
export const COMPANIONS: Record<SeyarChar, CompanionLook> = {
sigma: {
char: 'sigma', name: SEYAR_INFO.sigma.name, symbol: 'Σ', color: '#4C5FD5', light: '#7A8CFF', pitch: 0,
trait: { title: 'جمع‌زن', text: 'تو آزمون جوابای درستت رو یکی‌یکی برات جمع می‌زنه.' },
},
delta: {
char: 'delta', name: SEYAR_INFO.delta.name, symbol: 'Δ', color: '#FF7A3D', light: '#FFB25E', pitch: 2,
trait: { title: 'شعله سرعت', text: 'جوابای درست پشت هم رو آتیشی می‌کنه و تشویقت می‌کنه.' },
},
pi: {
char: 'pi', name: SEYAR_INFO.pi.name, symbol: 'π', color: '#9B7BFF', light: '#C6B3FF', pitch: 4,
trait: { title: 'راهنمای مهربون', text: 'وقتی غلط می‌زنی یه نکته کوچیک می‌گه که دفعه بعد گیر نکنی.' },
},
tedi: {
char: 'tedi', name: SEYAR_INFO.tedi.name, symbol: 'ω', color: '#E8A400', light: '#FFC63D', pitch: 7,
trait: { title: 'جشن‌ساز', text: 'آخر هر جزیره، هر نمره‌ای بگیری، برات جشن طلایی می‌گیره.' },
},
};
let current: SeyarChar = seyarFromSlug(store.get(KEYS.companion)) ?? 'sigma';
const listeners = new Set<() => void>();
export const companionStore = {
get: () => current,
set(char: SeyarChar) {
if (char === current) return;
current = char;
store.set(KEYS.companion, char);
listeners.forEach((l) => l());
},
subscribe(listener: () => void) {
listeners.add(listener);
return () => listeners.delete(listener);
},
};
export function useCompanion(): CompanionLook {
const char = useSyncExternalStore(companionStore.subscribe, companionStore.get, companionStore.get);
return COMPANIONS[char];
}
/** «من سیگمام»، «من پی‌ام». */
export function iAm(name: string): string {
return name.endsWith('ی') ? `${name}‌ام` : `${name}م`;
}