Ship Android app by wrapping the web build with Capacitor #20

Closed
opened 2026-07-26 04:51:45 -07:00 by westfarn · 1 comment
Owner

Goal

Publish an Android app for chat.aimloperations.com that runs the existing CRA build inside a Capacitor WebView, so we ship one codebase (llm-fe/) to web and Android.

Approach

Add Capacitor to the existing llm-fe/ npm root instead of creating a separate mobile repo. The CRA build/ output becomes the Capacitor webDir, and the native Android project is committed alongside it.

cd llm-fe
npm i @capacitor/core @capacitor/cli @capacitor/android
npx cap init "AI ML Chat" com.aimloperations.chat --web-dir=build
npm run build && npx cap add android && npx cap sync android

Scope

  • Add Capacitor deps + capacitor.config.ts (appId com.aimloperations.chat, webDir: build).
  • Commit the generated android/ project; add Gradle/build artifacts to .gitignore.
  • Add npm scripts, e.g. build:mobile (prod web build + cap sync) and android:open.
  • Point the app at the prod backend. .env.production bakes in https://chatbackend.aimloperations.com/api/ and wss://.../ws/chat_again/, which works from the WebView, but decide whether mobile needs its own env file (e.g. .env.mobile) so we can flip beta/prod without touching web deploys.
  • Verify auth end to end. src/axiosApi.js uses JWT plus a CSRF cookie via js-cookie; under Capacitor the page origin is https://localhost (or a custom scheme), so cookies are cross-site relative to the backend domain. Confirm CSRF/session cookies are sent, or move fully to header-based JWT for the native client. Backend CORS/CSRF trusted-origins will likely need the Capacitor origin added (chat_backend change).
  • Verify the chat WebSocket connects and reconnects across app background/foreground and network changes.
  • Routing check: React Router 6 with BrowserRouter needs deep links to resolve against the bundled index.html; confirm no 404s on reload, switch to HashRouter or Capacitor server config if needed.
  • Android hardware back button behavior (navigate back in history, exit at root) and safe-area/keyboard insets for the MUI layout.
  • App icons + splash screen (@capacitor/assets), app name, versionCode/versionName strategy.
  • Manual QA on a physical device and an emulator: login, password flows, chat streaming, markdown/code-block rendering, charts (recharts), dark/light theme.
  • Signing: generate an upload keystore, store it outside the repo (host secrets, not git), document the release build (./gradlew bundleRelease).
  • Play Store listing: developer account, privacy policy URL, data-safety form, screenshots, internal-testing track first.

Out of scope

Push notifications, offline caching, and native-only features. File as follow-ups once the wrapper ships.

Notes

The iOS equivalent is tracked separately. Do the shared Capacitor scaffolding (config, webDir, env, auth/CORS, routing fixes) here since it is a prerequisite for both, then the iOS ticket only adds the ios/ platform and Apple-specific work.

## Goal Publish an Android app for chat.aimloperations.com that runs the existing CRA build inside a Capacitor WebView, so we ship one codebase (`llm-fe/`) to web and Android. ## Approach Add Capacitor to the existing `llm-fe/` npm root instead of creating a separate mobile repo. The CRA `build/` output becomes the Capacitor `webDir`, and the native Android project is committed alongside it. ```bash cd llm-fe npm i @capacitor/core @capacitor/cli @capacitor/android npx cap init "AI ML Chat" com.aimloperations.chat --web-dir=build npm run build && npx cap add android && npx cap sync android ``` ## Scope - [ ] Add Capacitor deps + `capacitor.config.ts` (appId `com.aimloperations.chat`, `webDir: build`). - [ ] Commit the generated `android/` project; add Gradle/build artifacts to `.gitignore`. - [ ] Add npm scripts, e.g. `build:mobile` (prod web build + `cap sync`) and `android:open`. - [ ] Point the app at the prod backend. `.env.production` bakes in `https://chatbackend.aimloperations.com/api/` and `wss://.../ws/chat_again/`, which works from the WebView, but decide whether mobile needs its own env file (e.g. `.env.mobile`) so we can flip beta/prod without touching web deploys. - [ ] Verify auth end to end. `src/axiosApi.js` uses JWT plus a CSRF cookie via `js-cookie`; under Capacitor the page origin is `https://localhost` (or a custom scheme), so cookies are cross-site relative to the backend domain. Confirm CSRF/session cookies are sent, or move fully to header-based JWT for the native client. Backend CORS/CSRF trusted-origins will likely need the Capacitor origin added (`chat_backend` change). - [ ] Verify the chat WebSocket connects and reconnects across app background/foreground and network changes. - [ ] Routing check: React Router 6 with `BrowserRouter` needs deep links to resolve against the bundled `index.html`; confirm no 404s on reload, switch to `HashRouter` or Capacitor server config if needed. - [ ] Android hardware back button behavior (navigate back in history, exit at root) and safe-area/keyboard insets for the MUI layout. - [ ] App icons + splash screen (`@capacitor/assets`), app name, versionCode/versionName strategy. - [ ] Manual QA on a physical device and an emulator: login, password flows, chat streaming, markdown/code-block rendering, charts (`recharts`), dark/light theme. - [ ] Signing: generate an upload keystore, store it outside the repo (host secrets, not git), document the release build (`./gradlew bundleRelease`). - [ ] Play Store listing: developer account, privacy policy URL, data-safety form, screenshots, internal-testing track first. ## Out of scope Push notifications, offline caching, and native-only features. File as follow-ups once the wrapper ships. ## Notes The iOS equivalent is tracked separately. Do the shared Capacitor scaffolding (config, `webDir`, env, auth/CORS, routing fixes) here since it is a prerequisite for both, then the iOS ticket only adds the `ios/` platform and Apple-specific work.
westfarn added a new dependency 2026-07-26 04:55:51 -07:00
Author
Owner

Blockers filed and set as Gitea issue dependencies (Dependencies panel in the sidebar):

  • #22 auth from a Capacitor WebView origin (JWT-only, CORS/CSRF for capacitor://localhost)
  • #23 chat WebSocket resilience across background/resume and network switches
  • #24 routing from a file-served bundle (React Router 6 under Capacitor)

Gitea will block closing this issue until those three are closed. #21 (iOS) carries the same three plus a dependency on this issue for the shared scaffolding.

Blockers filed and set as Gitea issue dependencies (Dependencies panel in the sidebar): - #22 auth from a Capacitor WebView origin (JWT-only, CORS/CSRF for `capacitor://localhost`) - #23 chat WebSocket resilience across background/resume and network switches - #24 routing from a file-served bundle (React Router 6 under Capacitor) Gitea will block closing this issue until those three are closed. #21 (iOS) carries the same three plus a dependency on this issue for the shared scaffolding.
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Reference: ai_ml_operations/chat_web_app#20