Skip to content

fix(mobile): reader bug sweep — TOC + progress + selection + save + relaunch#247

Merged
mrviduus merged 2 commits into
mainfrom
fix/mobile-reader-bug-sweep
May 24, 2026
Merged

fix(mobile): reader bug sweep — TOC + progress + selection + save + relaunch#247
mrviduus merged 2 commits into
mainfrom
fix/mobile-reader-bug-sweep

Conversation

@mrviduus
Copy link
Copy Markdown
Owner

Summary

Five Android mobile reader bugs blocking Play Store launch, plus senior architecture refactor extracting pure logic to @textstack/shared so both catalog and user-book readers share the same formulas (book-progress, LWW merge, locator parsing).

Bugs fixed

  1. TOC sheet empty — chapter list rendered as 0-height; now shows loading/empty states with minHeight
  2. Save word stranded toolbarsetSelection(null) after success + race guard
  3. No close button on selection toolbar — added X icon + native WebView selection clear
  4. Progress bar showed chapter %, not book % — new shared computeBookProgress() + LocalProgress.bookPercent cache
  5. App relaunch on "random screens"ColdResetOnResume after 30 min background, route-aware (skips reader)

Architecture (new in @textstack/shared)

  • reader/bookProgress.ts — pure book-wide % calculation (word-count weighted + chapter-index fallback)
  • reader/progressPayload.ts — payload builder + parseScrollLocator (handles slug-with-colons via split-from-right)
  • reader/continueReading.ts — LWW merge picker for home ContinueReadingCard
  • lib/pathPrefix.ts — PII-safe pathname compression for analytics

Mobile hooks

  • useUserBookProgress (new) — sibling of useReaderProgress with identical contract
  • useFlushOnBackground (new) — Android OS-kill mitigation, reentry-safe, try/catch isolated
  • useReaderProgress (existing) refactored to use useFlushOnBackground

Telemetry

  • app_resumed_from_background analytics event with PII-safe bucket_minutes + path_prefix + was_in_protected_route + reset_to_home — for post-launch dashboard debugging of "random screens" reports

Tests

  • 144 unit tests across 7 files
  • 9 property-based tests (~2300 generated cases via fast-check)
  • Coverage: 100% statements / 90-100% branches on shared/reader + shared/lib. Uncovered branches are defensive dead code (e.g. parseInt after strict-digits regex)
  • E2E: apps/mobile/e2e/tests/reader-smoke.spec.ts extended with TOC regression guard
  • Hooks intentionally not unit-tested (would need RN testing-library setup; rationale in ADR-011)

Docs

  • ADR-011 (docs/01-architecture/adr/ADR-011-mobile-reader-progress-architecture.md) — documents Android-first platform priority, client-side book-progress strategy, sibling-hooks decision
  • ADR-007 cross-linked to ADR-011

Verification

apps/mobile  tsc --noEmit  → exit 0
packages/shared  tsc --noEmit  → exit 0
packages/shared  pnpm test  → 144 passed

Rollback plan

Single revert — git revert <merge-sha>. No DB migrations, no feature flags, no API changes. Local AsyncStorage entries (bookPercent) are additive and ignored by older builds.

Notes

  • Manual verification needed on Android: 5 bugs + ADR-011 reference list (manual checklist on physical device — Playwright can't drive WebView selection)
  • Found a real defensive bug during PBT extraction: Date.parse('garbage') returned NaN silently — now guarded via parseEpochMs() accepting both ISO string + epoch number
  • Inline split(':') locator parsers in both readers would break on chapter slugs containing : — centralised in shared parseScrollLocator() with round-trip property tests
  • Telemetry transport still TODO (event payload + analytics call wired, but track() only console.debug — Firebase/Sentry SDK wire is next slice)

🤖 Generated with Claude Code

mrviduus and others added 2 commits May 23, 2026 22:21
…elaunch

5 user-reported mobile reader bugs + senior architecture refactor.

Bugs fixed:
- TOC sheet rendered empty silently → loading/empty states + minHeight
- Save word didn't close toolbar → setSelection(null) + race guard
- SelectionActionBar had no close button → X + native selection clear
- Progress bar showed chapter % not book % → computeBookProgress shared utility
- App relaunch on 'random screens' → ColdResetOnResume after 30min, route-aware

Architecture (extracted to @textstack/shared):
- bookProgress.ts — pure book-wide % calculation
- progressPayload.ts — payload builder + parseScrollLocator (multi-colon defense)
- continueReading.ts — LWW merge picker for home card
- lib/pathPrefix.ts — PII-safe analytics path compression

Mobile hooks:
- useReaderProgress (existing) refactored
- useUserBookProgress (new) — sibling hook with identical contract
- useFlushOnBackground — Android OS-kill mitigation, reentry-safe

Telemetry:
- app_resumed_from_background event for post-launch debug
- bucket_minutes + path_prefix + protected_route flag

Tests:
- 144 unit (5 reader modules + pathPrefix) + 9 properties (~2300 cases)
- Behavioral coverage 100% on shared/reader + shared/lib
- E2E TOC regression guard in reader-smoke.spec.ts

Docs:
- ADR-011 mobile reader progress architecture
- ADR-007 cross-link to ADR-011

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@mrviduus mrviduus merged commit b89783c into main May 24, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant