Warning
Experimental. This is an exploratory prototype, not a shipped product. It is unreleased, has no builds in either app store, and carries no support or stability guarantees. APIs, structure, and UI change without notice, and the project may be abandoned at any point. Do not depend on it.
Native iOS + Android client for Fleetyards, built with Expo (React Native).
See docs/native-app-expo.md for the full implementation plan.
- Node 22+ (matches the
.nvmrcinfleetyards/fleetyards) - pnpm (never npm)
- Xcode 16+ for iOS development
- Android Studio with an emulator image for Android development
- EAS CLI for production builds:
pnpm dlx eas-cli --version
pnpm install
pnpm doctor # reports any SDK-incompatible dep versions and fixes themIf pnpm doctor flags version mismatches, run pnpm expo install --fix to auto-align packages with the installed Expo SDK.
pnpm start # Metro bundler with QR code for Expo Go / dev client
pnpm ios # iOS simulator
pnpm android # Android emulatorIn dev, the app hits EXPO_PUBLIC_API_URL if set, otherwise http://localhost:3000/api/v1. To target the Rails dev server from a physical device, use your LAN IP:
EXPO_PUBLIC_API_URL=http://192.168.1.x:3000/api/v1 pnpm startThe TypeScript client is generated from the same swagger/v1/schema.yaml the web frontend consumes.
pnpm api:generate # regenerates src/api/ from the schemaRun this after any backend schema change. Steps 1–2 happen in
fleetyards/fleetyards, step 3 here:
- Edit
app/api_components/**(never editswagger/schema.yamldirectly). ./bin/generate-schema- Back in this repo:
pnpm api:generate
The app uses Authorization Code + PKCE against the Rails Doorkeeper provider. A fleetyards-mobile Doorkeeper application must be seeded server-side:
# in fleetyards/fleetyards
bin/rails oauth:apps:create_nativeThat task prints the client_id (uid). Put it in .env.local:
OAUTH_CLIENT_ID=<uid>
(This task does not exist yet — added in step 2 of the exec plan.)
pnpm dlx eas-cli build --profile development --platform ios
pnpm dlx eas-cli build --profile production --platform allProduction builds are triggered by pushing a tag mobile-v0.1.0 (handled in CI).
.
├── app/ # Expo Router file-based routes
│ ├── _layout.tsx # Root layout (providers)
│ ├── (tabs)/ # Bottom-tab routes
│ ├── oauth/ # OAuth callback
│ └── index.tsx # Home
├── src/
│ ├── api/
│ │ ├── axiosClient.ts # Axios instance + auth interceptors (the Orval mutator)
│ │ └── generated/ # Orval-generated React Query hooks + types (committed)
│ ├── auth/ # AuthProvider, token storage
│ ├── components/ # Primitives and shared components
│ ├── config/ # Mobile-config (force-update)
│ ├── theme/ # Tokens ported from web SCSS
│ └── links/ # Universal-link routing
├── assets/ # Icons, splash, etc.
├── app.config.ts
├── eas.json
└── package.json
- TypeScript strict —
noUncheckedIndexedAccessis on. - No barrel files — import from leaf modules.
- Server state lives in React Query; client UI state in Zustand.
- No third-party UI kit — primitives in
src/components/primitives/. - Conventional commits —
feat(mobile): …,fix(mobile): …, etc.