The selected companion everywhere, each with a small trait; next-APK notes

- 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>
This commit is contained in:
2026-10-10 18:51:19 +03:30
parent 737414f246
commit 8a61cb25b8
21 changed files with 256 additions and 69 deletions

View File

@@ -1,3 +1,4 @@
import { useCompanion } from '@/state/companion';
import { Brain, Play, Zap } from 'lucide-react';
import { api } from '@/api';
import { useAppData } from '@/state/AppData';
@@ -15,6 +16,7 @@ import { StepText } from '@/lib/StepText';
/** معرفیِ درس، پیش از تمرین. */
export function LessonScreen({ lessonId, nodeId }: { lessonId: number; nodeId?: string }) {
const { home, companion } = useAppData();
const companionName = useCompanion().name;
const nav = useNav();
const intro = useAsync(() => api.lesson.intro(lessonId), [lessonId], { key: `lesson-intro:${lessonId}` });
const ref = findNode(home, nodeId) ?? findNodeOfLesson(home, lessonId);
@@ -50,7 +52,7 @@ export function LessonScreen({ lessonId, nodeId }: { lessonId: number; nodeId?:
) : (
<>
<div className="flex items-start gap-3">
<SeyarMascot char="sigma" mood="idle" size={56} />
<SeyarMascot char={companion} mood="idle" size={56} />
<div className="flex-1 rounded-3xl rounded-tr-sm border border-line bg-white p-4 shadow-sm">
<StepText text={data.introText} color={color} className="text-[15px] leading-[1.95] text-body" />
{figs('intro')}
@@ -100,7 +102,7 @@ export function LessonScreen({ lessonId, nodeId }: { lessonId: number; nodeId?:
onClick={() => nav.push({ name: 'ask', lessonId })}
className="flex items-center justify-center gap-2 rounded-2xl bg-[#F5F1FF] py-3 text-sm font-bold text-[#7857E0]"
>
<Brain size={16} /> درباره‌ی این درس از پی بپرس
<Brain size={16} /> درباره‌ی این درس از {companionName} بپرس
</button>
</div>
</>