From 8a61cb25b8d78a8f4b0a78a932c30b7bf9fee9ca Mon Sep 17 00:00:00 2001 From: AliReza Date: Sat, 10 Oct 2026 18:51:19 +0330 Subject: [PATCH] The selected companion everywhere, each with a small trait; next-APK notes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- android/README.md | 33 ++++++++++++-- src/components/BottomNav.tsx | 9 +++- src/components/BrandMark.tsx | 22 ++++++--- src/components/NoHeartsScreen.tsx | 4 +- src/components/ui.tsx | 18 +++++--- src/lib/sound.ts | 13 ++++-- src/lib/storage.ts | 1 + src/screens/AskScreen.tsx | 24 +++++----- src/screens/CharactersScreen.tsx | 24 +++++----- src/screens/InviteScreen.tsx | 2 +- src/screens/LeaderboardScreen.tsx | 4 +- src/screens/LessonScreen.tsx | 6 ++- src/screens/LessonsScreen.tsx | 2 +- src/screens/NotificationsScreen.tsx | 2 +- src/screens/QuizScreen.tsx | 60 ++++++++++++++++++++++--- src/screens/ReportScreen.tsx | 6 ++- src/screens/StreakScreen.tsx | 4 +- src/screens/SubscriptionScreen.tsx | 8 ++-- src/screens/SupportScreen.tsx | 2 +- src/state/AppData.tsx | 11 +++-- src/state/companion.ts | 70 +++++++++++++++++++++++++++++ 21 files changed, 256 insertions(+), 69 deletions(-) create mode 100644 src/state/companion.ts diff --git a/android/README.md b/android/README.md index 07fff84..4219572 100644 --- a/android/README.md +++ b/android/README.md @@ -5,15 +5,40 @@ محصولِ دیجیتال را فقط از راهِ خودش می‌پذیرد. باقیِ اپ همان فرانتِ وب است و با دیپلویِ فرانت به‌روز می‌شود، بی‌آن‌که نسخه‌ی تازه‌ی اپ لازم باشد. -> ⚠️ **این پروژه هنوز یک بار هم build نشده.** روی ماشینی نوشته شد که Android SDK نداشت -> (۲۰۲۶-۱۰-۰۳). APIِ Poolakey از README خودِ کتابخانه است. اولین کار: باز کردن در Android -> Studio، build، و یک خریدِ واقعی روی گوشی. +> **ساخت و امضا با هم‌تیمی است (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.app` حدس است؛ تأیید نشده | +| اسمِ پکیج | `applicationId` در `app/build.gradle.kts` | `com.tadrisino.math.app` (APKِ ساخته‌شده)؛ ثبتش در پیشخان تأیید نشده | | کلیدِ RSAِ عمومیِ اپ | `BAZAAR_RSA_KEY` در `app/build.gradle.kts` | ندارد؛ بدونِ آن خرید شروع نمی‌شود | | SKUِ هر بسته (محصولِ **مصرف‌شدنی**) | پنلِ ادمینِ بک‌اند: `/admin/subscription/plan/` ← «Bazaar sku» | هیچ‌کدام تعریف نشده | diff --git a/src/components/BottomNav.tsx b/src/components/BottomNav.tsx index 4956194..fafeb35 100644 --- a/src/components/BottomNav.tsx +++ b/src/components/BottomNav.tsx @@ -1,4 +1,6 @@ import { sounds } from '@/lib/sound'; +import { useCompanion } from '@/state/companion'; +import { onWhite } from '@/lib/color'; import { Brain, ShoppingBag, Home, BookOpen, UserRound, type LucideIcon } from 'lucide-react'; import type { TabName } from '@/nav/routes'; @@ -11,6 +13,8 @@ const ITEMS: { id: TabName; icon: LucideIcon; label: string }[] = [ ]; export function BottomNav({ active, onChange }: { active: TabName | null; onChange: (tab: TabName) => void }) { + // تبِ روشن به رنگِ همراهِ انتخابی (تیره‌شده تا روی سفید خوانا بماند؛ امگای طلایی بی‌آن کم‌رنگ بود). + const tint = onWhite(useCompanion().color); return (