## 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
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 solocal.propertiesis 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.jsonversion(currently0.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 masterlogo-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):
- Google Play developer account
- Privacy policy URL (product site / legal)
- 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/
- Canonical:
- Data safety form (JWT auth, chat content, analytics if Tianji runs in WebView)
- Screenshots for phone (+ tablet if targeting)
- Upload
.aabto 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).