- Text: kasra/fatha/damma/shadda/sukun/hamza-above and «هی» ezafe removed from every user-visible string (comments untouched); tanwin kept. Stale copy fixed (question packs are no longer sold), awkward onboarding sentences rewritten, سؤال → سوال. plainPersian() in MathText cleans the live tutor stream.
- Home: the mascot + speech bubble header is gone; the selected companion hops beside the current island and sits at every third bend (CSS transform animations only).
- Checkout: order summary, payment-method rows with Zarinpal/Bazaar icons and radio, amount on the pay button, lock trust line, darker overlay. paymentMethods in config.js per channel (web: zarinpal, app: zarinpal+bazaar by default).
- Profile: «ورود با حساب دیگر» removed (it was logout); new «حساب کاربری» sheet shows phone/ID and holds «حذف حساب کاربری»; logout is a red row. Delete sheet tells Bazaar subscribers to cancel renewal in Bazaar.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first second was a blank page, then a splash with the selected companion, then the
home screen. Now index.html carries the splash (icon from the Android launcher, name,
white to match the native window background) and SplashScreen draws the same markup, so
the hand-over from HTML to React shows no change. Not run on a device.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Shop: subscription perks shown once (each card repeated them, «۳ سوال در روز» twice), plans as selectable
rows with one big buy button, prices as «۷۹۸ هزار تومان», a single clear status card for subscribers.
Checkout inside the Bazaar app offers «کارتِ بانکی» (default) or «کافهبازار» for packs with a SKU;
«بهزودی در بازار» is gone. The gateway opens in the phone browser; status refreshes on return.
- Back button: every back-able level (open sheet, pushed screen, non-home tab, shop list) owns exactly one
history entry created right after the tap; back closes the sheet, then the screen, then returns to Home,
then leaves the app. (A re-pushed trap is skipped by Chrome's history intervention.)
- Sound: AudioContext unlocked on the first touch (it was created after a network wait, which WebView/Safari
keep muted); softer notes, option tick, finish fanfare, streak whoosh. Haptics through the new
TadrisinoAndroid.vibrate bridge when the APK has it, navigator.vibrate otherwise.
- Streak: animated flame with lit / at-risk (grey) / frozen (ice) states on Home and the streak screen,
a companion reminder when the streak is at risk or frozen, and a full-screen «روزِ پیاپی» celebration
after the first lesson of the day (needs backend 9024c6b: streak.activeToday / lost).
- Lessons: intro/example text with inline «۱. … ۲. …» renders as a numbered list (2,250 of 4,469 texts).
- Quiz: duplicate React key on the answer bubble fixed.
- android/MainActivity.kt: vibrate bridge and mediaPlaybackRequiresUserGesture=false. Not built; needs the
next APK from the signing owner.
Checked in headless Chrome against the local backend (shop as a subscriber with the Bazaar bridge stubbed,
back sequences, quiz → streak celebration, frozen state). Not checked on a real phone or in the APK.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Navigator stage: the next screen mounts hidden under the current one and is revealed only when its
first data has arrived (ScreenGate held by useAsync/useGateHold), at most 450 ms; a thin top bar shows
after 120 ms of waiting. Push slides from the left, back from the right, tabs only fade.
- Session cache (stale-while-revalidate) for screen GETs: revisited screens draw instantly.
- Home map and stats are fetched during the splash, so the first screen arrives complete.
- Entrance pop-ins (mascot, islands, lesson cards) run on first view only, not on every revisit.
- Shop status card shows placeholders instead of a wrong «نداری» while loading.
- Re-tapping the open tab scrolls to top instead of reloading; sheets close with a swipe down.
- Quiz: «۱ از ۴» instead of «۱/۴» (read as a fraction), varied praise, «N تا پشتِ هم درست»,
growth-mindset copy after a weak round, zero-reward tiles hidden.
- Streak: a concrete next step when the streak is 0 or today is still empty.
Checked in headless Chrome against the local backend (frame captures at 150 ms and 700 ms latency,
rapid tab tapping, full quiz run). Not checked on a real phone or iOS Safari.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Store opens with two choices (subscription / question packs); 3- and 6-month cards
show per-month price and saving.
- Units that need a subscription show a crown badge and open the store; a 402 from
the quiz goes there too. «ادامه بده» skips locked units.
- Profile: subscription card with an animated days-left bar (planDurationDays).
- BrandMark (SVG) on splash, home header and footer; copyright footer; floating math
glyphs on splash. CSS transform/opacity only; honours reduced motion.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- muted text #9e9ec0 -> #727299 (contrast 2.6 -> 4.5 on white); tab labels 11px
- map: chapter titles wrap to two lines instead of being cut off
- reduced-motion respected (MotionConfig + CSS), focus-visible ring,
no pull-to-refresh, short vibration on correct/wrong answers
- src/lib/bazaar.ts: bridge to the Android WebView wrapper; checkout uses
Bazaar IAP only inside that app, Zarinpal everywhere else (unchanged)
- android/: WebView wrapper with Poolakey; written but never built
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Home is now the island path of every unit with one clear Start button on the
current island; the six-card dashboard is gone.
- Start opens /nodes/{id}/quiz (4 questions) instead of one-question lessons, so
an island takes one round instead of ~11. Lessons stay one tap away in the sheet.
- Tutor answers render the backend's validated diagram (plane, number line,
fraction) as React SVG; function expressions go through a small parser, no eval.
- Tedi, the mobile-preview mascot, joins as the fourth companion with all six moods
and an onboarding slide.
- Light polish: 3D buttons, header gradients, warmer page background.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The whole app lived in one 3,071-line App.tsx on a Figma scaffold (60 unused
packages, 45 unused UI components, no type-check). The live site could not
reach the backend, Persian-keyboard digits were erased from the phone field,
guests were signed out after an hour, "mark read" hit a method the server
does not accept (405), and iOS Safari blocked the payment window.
What changed:
- src/ split into api/ (one typed client: timeouts, GET retry, single-flight
refresh, SSE), screens/ (one file each), components/, state/, nav/, lib/.
- The API address is configuration only: public/config.js at runtime, then
VITE_API_BASE_URL at build time, then same-origin /api. No domain in code.
- Responsive shell instead of a fixed 390x844 phone frame; self-hosted
Vazirmatn instead of Google Fonts; strict TypeScript, `npm run build`
type-checks first.
- Everything on the handoff list: tutor answers keep their line breaks, have
thumbs up/down, follow-ups, "from the book" badge, retry, history that
survives tab switches and reloads, "why was I wrong" from a wrong answer
with questionId; question reports; hearts with maxHearts/unlimited and a
no-hearts screen with the refill time; result screen and a visible error
when lesson completion fails; subscription status, badge, discount code
before the gateway and the payment-return message; guest refresh token;
6-digit OTP with resend timer; age saved as a Jalali birthYear; PATCH
read-all; leaderboard refetched on open with "you" highlighted; selected
companion shown across the app; logout that revokes the refresh token.
- New screens for endpoints that had none: profile, streak calendar,
support tickets and bug reports, parents, invite-a-friend (with ?ref=
tracking), guest-to-phone upgrade.
- Browser/phone back button walks the screen stack.
Checked with a headless-Chrome walkthrough against a local backend at
390x844 and 360x740 (fresh guest: onboarding, setup, a full lesson, map,
island sheet, every tab and sub-screen, one real tutor question, reload,
logout): no console errors, no failed API calls. Real SMS login and a real
Zarinpal payment were not exercised.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>