Files
westfarn 9e1449f0db
Deploy Beta / unit-tests (push) Successful in 12s
Unit Tests / test (push) Successful in 11s
Deploy Beta / deploy-beta (push) Successful in 2m23s
Add public account-deletion page for Google Play (#103) (#110)
## Summary
- Closes [#103](#103).
- Adds public SPA route `/account-deletion/` (no auth) with Hesychia / `ai.hesychia.chat` branding, in-app deletion steps, soft-delete data retention copy, and a contact path (30-day SLA) for users who cannot sign in.
- Documents Play Console **Account deletion URL** in `ANDROID.md` and `android/README.md` (`https://hesychia.ai/account-deletion/`, legacy host, HashRouter form).

## Notes
- Retention copy matches backend soft-delete (v1): no claim of full erasure / hard purge yet.
- Play Console field still needs to be set out-of-band after deploy.

## Test plan
- [ ] Open `/account-deletion/` signed out — page loads (no redirect to sign-in)
- [ ] Confirm steps match Account → Delete my account + email confirm
- [ ] Confirm retention sections are accurate vs soft-delete
- [ ] `npm run test:ci -- --testPathPattern=AccountDeletionPage`
- [ ] After beta deploy, set Play Console Account deletion URL to the public HTTPS URLReviewed-on: #110
2026-08-04 10:53:48 -07:00

5.2 KiB

Android (Capacitor)

Ship the CRA web build as an Android app via Capacitor. One codebase: llm-fe/ → web + Android + iOS WebView.

Build & Play deploy steps: android/README.md. iOS sibling: IOS.md / ios/README.md.

Prerequisites for blockers already merged: JWT-only auth (#22), WebSocket lifecycle (#23), HashRouter (#24).

Prerequisites

  • Node.js 20 (matches CI; Capacitor 7.x)
  • JDK 17+ (OpenJDK fine)
  • Android Studio (Ladybug+) with Android SDK Platform 35, build-tools, and an emulator or device
  • Set ANDROID_HOME (or open the project once in Android Studio so local.properties is generated)

One-time / day-to-day

cd llm-fe
npm ci
npm run build:mobile   # loads .env.mobile → CRA build → cap sync android
npm run android:open   # opens android/ in Android Studio

Or sync only after an existing build/:

npm run android:sync

Environment

File Purpose
.env.mobile Endpoints baked into the mobile bundle (build:mobile)
.env.production Web prod deploys (build / build:prod)
.env.beta Web beta deploys

Default .env.mobile matches production (chatbackend.aimloperations.com). Point it at beta to flip the shell without touching web deploys. Optional gitignored override: .env.mobile.local.

RevenueCat IAP (#100)

Native billing uses @revenuecat/purchases-capacitor (Capacitor 7 → package 11.x). Web keeps Stripe Checkout / Customer Portal.

Set public SDK keys in .env.mobile (empty placeholders OK until store apps ship):

Variable Purpose
REACT_APP_REVENUECAT_APPLE_API_KEY iOS public SDK key
REACT_APP_REVENUECAT_GOOGLE_API_KEY Android public SDK key
REACT_APP_REVENUECAT_OFFERING_ID Optional offering pin (else current)

After install / key changes: npm run build:mobile (runs cap sync). More detail: MONETIZATION.md.

Versioning

In android/app/build.gradle:

  • versionName — user-visible string; keep aligned with package.json version (currently 0.1.0)
  • versionCode — integer; must increase for every Play Store upload

Bump both before each store release.

Icons / splash

Source art lives in llm-fe/assets/:

  • icon.png / splash.png / splash-dark.png — Capacitor inputs (Hesychia meander mark)
  • logo-source.png — cropped square master
  • logo-mark-transparent.png — circular mark with transparent corners (web / in-app)

In-app UI also imports src/llm-fe/assets/brand/hesychia-mark.png.

Regenerate Android densities after changing source art:

npm run assets:generate

Same command also regenerates iOS Assets.xcassets (see IOS.md).

Signing (upload keystore)

Never commit keystores. Store outside the repo (e.g. host secrets / password manager).

keytool -genkey -v -keystore ~/Documents/secrets/aiml-chat-upload.jks \
  -keyalg RSA -keysize 2048 -validity 10000 \
  -alias aiml-chat-upload

Create llm-fe/android/keystore.properties (gitignored):

storeFile=/absolute/path/to/aiml-chat-upload.jks
storePassword=...
keyAlias=aiml-chat-upload
keyPassword=...

Release App Bundle:

cd llm-fe/android
./gradlew bundleRelease
# → app/build/outputs/bundle/release/app-release.aab

Debug APK (no signing props needed):

./gradlew assembleDebug

Manual QA checklist

On emulator and physical device:

  • Login / logout / password reset flows
  • Chat streaming + markdown / code blocks
  • Charts (recharts)
  • Dark / light theme
  • Hardware back: navigates history, exits at root
  • Keyboard does not cover compose input; notch/safe-area OK
  • Background → resume keeps or reconnects WebSocket
  • Airplane mode → restore reconnects

Play Store (internal testing first)

Out of band ops (not automated here):

  1. Google Play developer account
  2. Privacy policy URL (product site / legal)
  3. Account deletion URL (Data safety / Account deletion) → public page:
    • Canonical: https://hesychia.ai/account-deletion/
    • Legacy host until DNS cutover: https://chat.aimloperations.com/account-deletion/
    • Capacitor HashRouter: /#/account-deletion/
  4. Data safety form (JWT auth, chat content, analytics if Tianji runs in WebView)
  5. Screenshots for phone (+ tablet if targeting)
  6. Upload .aab to internal testing track before production

In-app delete remains Account → Delete my account (DELETE /api/user/). The public URL above is for Play reviewers and users who never open the app (#103).

  • #20 — this Android wrap
  • #21 — iOS (reuses this scaffolding)
  • #22 / #23 / #24 — auth / WS / routing
  • #103 — Play public account-deletion URL