Files
MathApp_frontend/README.md
AliReza f7009b11e8 Instant home on reopen: snapshot of profile/grades/map/stats on the device, refreshed behind the scenes
A signed-in user with a snapshot goes straight to home; the same boot requests run in the
background and replace it. Snapshot is cleared whenever tokens are cleared and on a new login,
ignored from another build or when older than 7 days, and dropped when the fresh profile is a
different user (reboot remounts MainApp via a generation key; AnimatePresence otherwise kept the
exiting one). Notifications/characters wait for the profile answer so an expired access token
costs the same requests as before. refreshHome applies only the latest answer. Home shows a
"saved data" strip when the map could not be refreshed.

Measured locally (old vs new build, same backend, Chrome throttling, median of 3, time to map):
Fast 3G 2.46s -> 1.80s (expired token 3.63s -> 1.80s); Slow 3G 8.52s -> 6.14s (expired 12.6s ->
6.13s); API requests per open unchanged (6 valid, 11 expired). 12/12 behaviour checks passed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 19:57:42 +03:30

126 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# تدریسینو — اپِ ریاضی (فرانت‌اند)
اپِ وبِ موبایل‌محورِ **تدریسینو**، آموزشِ ریاضی به سبکِ دولینگو برای پایه‌ی سوم تا دوازدهم: نقشه‌ای از جزیره‌ها برای هر
کتاب، آزمون‌های کوتاه از مسئله‌های خودِ کتابِ درسی، دستیارِ هوشِ مصنوعی، همراه‌ها، زنجیره‌ی روزانه و اشتراک. همین
مخزن پوسته‌ی اندرویدِ اپ را هم دارد ([`android/`](android/README.md)).
بک‌اند مخزنِ جداگانه‌ای است ([`Tadrisino/MathApp`](https://gitea.tadrisino2.ir/Tadrisino/MathApp)) و قراردادِ API
اسکیمای OpenAPIِ همان‌جاست. این اپ تنها کلاینتِ آن API است.
| | |
|---|---|
| **سایتِ زنده** | `https://math.tadrisino2.ir` · API: `https://mathapi.tadrisino2.ir` |
| **فناوری** | React 18 · TypeScript (strict) · Vite 6 · Tailwind 4 · Motion |
| **وابستگی‌ها** | عمداً کم: بی‌کتابخانه‌ی router و state؛ ناوبری و داده‌ی مشترک در خودِ کد |
| **اندروید** | پوسته‌ی WebView روی همین سایت، با پرداختِ درون‌برنامه‌ایِ کافه‌بازار (Poolakey)؛ بسته‌ی `com.tadrisino.math.app` |
---
## راه‌اندازی
```bash
npm install
npm run dev # http://localhost:5173
```
در حالتِ توسعه، Vite درخواست‌های `/api` را به بک‌اند پاس می‌دهد، پس CORS مطرح نیست. پیش‌فرض بک‌اندِ محلی است
(`python manage.py runserver` روی پورتِ ۸۰۰۰). بدونِ بک‌اندِ محلی، فایلِ `.env.development.local` بسازید:
```bash
VITE_DEV_PROXY_TARGET=https://mathapi.tadrisino2.ir
```
> ⚠️ در این حالت هر کاری در اپ بکنید روی دیتابیسِ واقعی نوشته می‌شود.
| دستور | کار |
|---|---|
| `npm run dev` | سرورِ توسعه |
| `npm run typecheck` | بررسیِ نوع‌ها (strict) |
| `npm run build` | type-check و بعد build در `dist/` |
| `npm run preview` | اجرای همان build |
---
## ساختار
```
src/
config.ts آدرسِ API (بخشِ پایین)
api/ تنها درِ ارتباط با سرور — هیچ جای دیگر fetch نمی‌زند
http.ts توکن، timeout، تکرارِ GET، refreshِ تک‌نسخه‌ای، SSE، Idempotency-Key
index.ts یک تابع برای هر اندپوینت
types.ts شکلِ پاسخ‌ها، از روی پاسخِ واقعیِ سرور
app/ App (جریانِ ورود) و MainApp (تب‌ها و صفحه‌ها)
nav/ ناوبریِ پشته‌ای، هماهنگ با دکمه‌ی back گوشی
state/ داده‌ی مشترک (پروفایل، نقشه، کتابِ انتخاب‌شده، همراه) و گفتگوی دستیار
screens/ یک فایل برای هر صفحه
components/ اجزای مشترک: مسیرِ جزیره‌ها، نوارِ کتاب‌ها، برگه‌ی پایین، بازخورد، …
mascots/ همراه‌ها (سیگما، دلتا، پی، امگا): SVG از کد، شش حالتِ چهره
diagram/ شکلِ درس و جوابِ دستیار: توصیفِ JSON از بک‌اند → SVG؛ همه‌ی مختصات این‌جا حساب می‌شود
lib/ اعدادِ فارسی، تاریخِ شمسی، MathText، پیامِ خطا، صدا، پلِ کافه‌بازار، روش‌های پرداخت
android/ پوسته‌ی اندروید (README خودش)
pay.html · src/pay.ts صفحه‌ی واسطِ پرداختِ زرین‌پال برای اپِ اندرویدِ فعلی
```
قاعده‌ها:
- **کلاینت را از حافظه ننویسید.** قرارداد را از اسکیمای بک‌اند بخوانید
(`python manage.py spectacular --file schema.yaml --format openapi`) و هر تغییرِ `types.ts` را با پاسخِ واقعیِ سرور بسنجید.
- **صفحه‌ها هرگز مستقیم سرور را صدا نمی‌زنند:** همیشه `screens/* → api/index.ts → api/http.ts`.
- **اعداد:** برای نمایش `toFa`، برای ورودی `digitsOnly` (کیبوردِ فارسی ارقامِ `۰-۹` می‌فرستد و `\d` در JS آن‌ها را نمی‌شناسد).
- **رنگ‌ها** در `src/styles/index.css` (`@theme`) تعریف شده‌اند: `bg-coral`، `text-ink`، …
- **خطا:** پیامِ کاربر همیشه از `friendlyError` می‌آید، نه متنِ خامِ سرور.
- **بازشدنِ فوری:** کاربرِ واردشده خانه را از عکسِ روی گوشی می‌بیند (`state/homeSnapshot.ts`: پروفایل، پایه‌ها، نقشه،
آمار) و همان درخواست‌های بوت پشتِ صحنه تازه‌اش می‌کنند. عکس با پاک شدنِ توکن و ورودِ تازه پاک می‌شود، با بیلدِ دیگر
یا کهنه‌تر از ۷ روز خوانده نمی‌شود، و درستی (قفل، اشتراک، قلب) همیشه با سرور است. داده‌ی تازه‌ای که در خانه دیده
می‌شود باید در `AppData` بماند تا در عکس هم برود.
- **متنِ فارسی** بی‌اعراب و بی «ی»ِ اضافه‌ی نوشته‌شده («پایه‌ی سوم») است (تصمیمِ شرکت)؛ تنوین («قبلاً») می‌ماند. متنِ تازه
را همین‌طور بنویسید. متنِ سرور را بک‌اند هنگامِ ساختنِ پاسخ پاک می‌کند و `plainPersian` در `MathText` همان کار را برای
پاسخِ زنده‌ی دستیار. `lib/plainMath.ts` هر LaTeX یا markdownی را که برسد ساده می‌کند و باید با `content/plain_math.py`ِ
بک‌اند هم‌رفتار بماند.
---
## آدرسِ بک‌اند و عوض کردنِ دامنه
دامنه هیچ‌جای کد نیامده. آدرسِ API به این ترتیب خوانده می‌شود (`src/config.ts`):
1. **`dist/config.js`** (از `public/config.js`) — در زمانِ اجرا؛ بعد از build هم روی سرور ویرایش‌شدنی است.
2. **`VITE_API_BASE_URL`** — در زمانِ build، از `.env.production`.
3. **`/api`** — هم‌دامنه، وقتی nginxِ دامنه‌ی فرانت مسیرِ `/api` را به بک‌اند پراکسی کند.
برای جابه‌جاییِ دامنه:
- **سریع، بدونِ build:** روی سرور در `dist/config.js` بنویسید `apiBaseUrl: "https://<دامنه‌ی تازه>/api"` و کشِ آروان را
برای همین فایل پاک کنید.
- **ماندگار:** خطِ `VITE_API_BASE_URL` در `.env.production` را عوض کنید و build بگیرید.
- **سمتِ بک‌اند** دامنه‌ی تازه‌ی فرانت باید مجاز باشد (`FRONTEND_URL`)، و در CSPِ nginx (`connect-src`) دامنه‌ی تازه‌ی API
بیاید. رویه‌ی کامل: `docs/operations.md`ِ بک‌اند، بخشِ «عوض کردنِ دامنه».
## روش‌های پرداخت
برگه‌ی پرداخت روش‌ها را از `paymentMethods` در `dist/config.js` می‌خواند (`src/lib/paymentMethods.ts`)، جدا برای مرورگر
(`web`) و داخلِ اپِ اندروید (`app`)؛ مقدارها `"zarinpal"` و `"bazaar"`. پیش‌فرض: مرورگر فقط زرین‌پال، اپ هر دو. نسخه‌ای که
فقط در کافه‌بازار است: `paymentMethods: { app: ["bazaar"] }`؛ build لازم نیست، فقط کشِ آروانِ این فایل را پاک کنید.
---
## انتشار
روی سرور (`~/mathapp_frontend_new`، که nginxِ `math.tadrisino2.ir` از `dist/`ش سرو می‌کند):
```bash
cd ~/mathapp_frontend_new && ./deploy.sh # fetch، npm ci، type-check، build در dist.next و جابه‌جاییِ بی‌وقفه
```
buildِ قبلی در `dist.prev` می‌ماند (برگشت: `rm -rf dist && mv dist.prev dist`) و `dist/config.js`ِ ویرایش‌شده روی سرور به
buildِ تازه منتقل می‌شود. سرور مشترکِ شرکت است: پیش از هر کار آن‌جا قواعدِ `CLAUDE.md`ِ بک‌اند (بخشِ «Working on the
servers») را بخوانید.
جای دیگر: `npm ci && npm run build` و سرو کردنِ `dist/`. اپ مسیرِ جدا (route) ندارد و همه‌چیز از `/` بار می‌شود؛ فقط
`index.html` و `config.js` نباید طولانی کش شوند (باقیِ فایل‌ها نامِ hashدار دارند). درگاهِ پرداخت کاربر را به
`/?orderId=…&status=…` برمی‌گرداند و لینکِ دعوت `/?ref=CODE` است؛ هر دو را خودِ اپ می‌خواند.
CSPِ سایت در nginx است (`ops/nginx/zareian-front-headers.conf` در مخزنِ بک‌اند). اگر اسکریپتِ درون‌خطیِ `index.html` عوض
شود، هشِ تازه‌اش باید در همان فایل بنشیند، وگرنه مرورگر آن را اجرا نمی‌کند.