- 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>
71 lines
3.5 KiB
TypeScript
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}م`;
|
|
}
|