- state/companion.ts: one store for the chosen companion (also in localStorage so splash/login show it).
BrandMark (the orange «π» tile) now shows the companion's symbol (Σ Δ π ω) in its colour; screen-header
mascots, loaders and empty/error states default to it; the assistant is «از {name} بپرس» with its face,
colour and wording; the shop, lesson and quiz «ask» buttons name it; the active tab takes its tint.
- Traits, all client-side and light: Σ sigma keeps a running tally of correct answers in the quiz header;
Δ delta shows a flame «×N» chip and an extra buzz for answers in a row; π pi gives a short solving tip
after a wrong answer; ω omega throws gold confetti at the end of every island whatever the score.
Each companion's «correct» sound is pitched differently; confetti uses its colours. Characters screen
lists each trait.
- android/README.md: what MainActivity.kt has that the installed APK lacks (vibrate bridge, media gesture,
in-app gateway + paysInApp), what to check on a phone after building, signing owner, Bazaar policy note.
Checked in headless Chrome with Sigma, Delta and Omega selected (home, assistant, shop, characters, quiz).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6.8 KiB
اپِ اندرویدِ تدریسینو (پوستهی WebView + پرداختِ بازار)
همان سایتِ math.tadrisino2.ir داخلِ یک WebView. تنها کدِ بومی پرداختِ درونبرنامهایِ
کافهبازار است (کتابخانهی Poolakey)، چون بازار برای اپی که در آن منتشر میشود پرداختِ
محصولِ دیجیتال را فقط از راهِ خودش میپذیرد. باقیِ اپ همان فرانتِ وب است و با دیپلویِ
فرانت بهروز میشود، بیآنکه نسخهی تازهی اپ لازم باشد.
ساخت و امضا با همتیمی است (MH-W90، m.hassanabadi) با روشِ امضای خودش (
my-release-key.keystore+ propertyهای Gradle)؛ تصمیمِ مالک این است که همان روش بماند. پیش از دست زدن به امضا از مالک بپرس و هرگز keystore را commit نکن.applicationIdواقعیcom.tadrisino.math.appاست (از APKِ ساختهشده با aapt2). هیچ خریدِ واقعیِ بازار هنوز آزموده نشده.
نسخهی بعدیِ APK — چه چیزی در کد هست و در اپِ نصبشده نیست
اینها در MainActivity.kt نوشته شدهاند (۱۴۰۵-۰۷، 468f5b2 و 737414f) ولی هیچکدام build نشدهاند؛ اپی که الان روی
گوشیهاست بدونِ آنهاست. فرانت با هر دو نسخه کار میکند (اگر پل نبود، راهِ قدیم را میرود).
| تغییر | چرا | بعد از build روی گوشی بسنج |
|---|---|---|
TadrisinoAndroid.vibrate(patternJson) با Vibratorِ گوشی |
navigator.vibrate در WebView همیشه کار نمیکند؛ مالک گفت اپ لرزش ندارد |
انتخابِ گزینهی آزمون، جوابِ درست/غلط، پایانِ جزیره و صفحهی «روزِ پیاپی» باید بلرزند؛ با خاموش کردنِ «صداها» در پروفایل نه |
mediaPlaybackRequiresUserGesture = false |
صداهای رابط بعد از پاسخِ سرور پخش میشوند، نه در لحظهی لمس | صدای جوابِ درست/غلط و پایانِ تمرین |
isPaymentPage + paysInApp() |
درگاهِ زرینپال داخلِ خودِ اپ باز میشود. اپِ فعلی آن را به مرورگرِ گوشی میدهد که Referer نمیفرستد و زرینپال بیReferer «دسترسی از این دامنه مجاز نمیباشد» میدهد؛ فعلاً فرانت از راهِ pay.html (با http://) دورش میزند |
«پرداخت» با کارتِ بانکی: صفحهی زرینپال و بانک داخلِ اپ، و بعد از پرداخت برگشت به اپ با پیامِ «پرداخت موفق بود» (PaymentResult). دکمهی برگشت وسطِ صفحهی بانک چه میکند را هم ببین |
پیشنهادهایی که هنوز نوشته نشدهاند:
- «برای خروج دوباره بزن»: دکمهی برگشتِ گوشی حالا قدمبهقدم در خودِ وب عقب میرود (
src/nav/Navigator.tsx) و از خانه یکراست بیرون میرود. اگر تأییدِ خروج خواستید، درhandleOnBackPressedوقتی!webView.canGoBack()یک Toast بدهید و فقط برگشتِ دوم در دو ثانیهfinish()کند. در وب شدنی نیست (کروم ورودیِ history بیلمس را رد میکند). versionCode/versionNameرا برای هر نسخه بالا ببرید؛ هنوز ۱ و 1.0.0 است.
قانونِ بازار: داخلِ اپِ بازار حالا پرداخت با درگاه هم پیشنهاد میشود (خواستِ مالک). بازار پرداختِ محتوای دیجیتال بیرون از پرداختِ درونبرنامهای را محدود میکند؛ پیش از انتشار در بازار این را با بازار یا مالک روشن کنید.
ساخت روی مکِ مالک: SDK در ~/Library/Android/sdk، JDK 17 (/opt/homebrew/opt/openjdk@17)، و چون Google Maven از
ایران بیVPN ۴۰۴ میدهد، ~/.gradle/init.d/google-mirror.gradle آینهی Aliyun را اضافه میکند؛ ساختِ اول ~۹ دقیقه.
پیش از build — سه چیز از پیشخانِ بازار (سنیور / حسابِ شرکت)
| چه | کجا گذاشته میشود | وضعیت |
|---|---|---|
| اسمِ پکیج | applicationId در app/build.gradle.kts |
com.tadrisino.math.app (APKِ ساختهشده)؛ ثبتش در پیشخان تأیید نشده |
| کلیدِ RSAِ عمومیِ اپ | BAZAAR_RSA_KEY در app/build.gradle.kts |
ندارد؛ بدونِ آن خرید شروع نمیشود |
| SKUِ هر بسته (محصولِ مصرفشدنی) | پنلِ ادمینِ بکاند: /admin/subscription/plan/ ← «Bazaar sku» |
هیچکدام تعریف نشده |
قیمتِ هر محصول در پیشخانِ بازار باید با قیمتِ همان بسته در ادمین یکی باشد؛ سرور مبلغ را از بازار نمیخواند.
build
- Android Studio ← Open ← همین پوشهی
android/. Studio خودش Gradle wrapper را میسازد. Build ← Generate Signed App Bundle / APK، با کلیدِ امضای شرکت. کلیدِ امضا را گم نکنید: بدونِ همان کلید هیچ نسخهی بعدی روی بازار بهروزرسانی نمیشود.- نصب روی گوشیای که کافهبازار دارد و با حسابِ تست وارد شده.
جریانِ خرید (برای عیبیابی)
فرانت POST /api/subscription/purchase {planId, paymentMethod:"bazaar"} → orderId, bazaarSku
فرانت TadrisinoAndroid.bazaarPurchase(sku, orderId)
اپ Poolakey.purchaseProduct(sku, payload=orderId) → purchaseToken
اپ رویدادِ «tadrisino:bazaar» {orderId, purchaseToken}
فرانت POST /api/payment/bazaar/verify {orderId, purchaseToken} ← سرور از بازار میپرسد
فرانت TadrisinoAndroid.bazaarConsume(token) ← تا همان بسته دوباره خریدنی باشد
اگر پول رفت ولی تأییدِ سرور نرسید، کاربر دوباره «پرداخت» میزند: فرانت همان سفارش را میفرستد
و اپ توکنِ ماندهٔ همان سفارش را از getPurchasedProducts پیدا میکند.
قرارداد سمتِ وب: src/lib/bazaar.ts. سمتِ سرور: payment/bazaar.py و docs/operations.mdِ بکاند.