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

9.3 KiB
Raw Permalink Blame History

تدریسینو — اپِ ریاضی (فرانت‌اند)

اپِ وبِ موبایل‌محورِ تدریسینو، آموزشِ ریاضی به سبکِ دولینگو برای پایه‌ی سوم تا دوازدهم: نقشه‌ای از جزیره‌ها برای هر کتاب، آزمون‌های کوتاه از مسئله‌های خودِ کتابِ درسی، دستیارِ هوشِ مصنوعی، همراه‌ها، زنجیره‌ی روزانه و اشتراک. همین مخزن پوسته‌ی اندرویدِ اپ را هم دارد (android/).

بک‌اند مخزنِ جداگانه‌ای است (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

راه‌اندازی

npm install
npm run dev          # http://localhost:5173

در حالتِ توسعه، Vite درخواست‌های /api را به بک‌اند پاس می‌دهد، پس CORS مطرح نیست. پیش‌فرض بک‌اندِ محلی است (python manage.py runserver روی پورتِ ۸۰۰۰). بدونِ بک‌اندِ محلی، فایلِ .env.development.local بسازید:

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/ش سرو می‌کند):

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 عوض شود، هشِ تازه‌اش باید در همان فایل بنشیند، وگرنه مرورگر آن را اجرا نمی‌کند.