|
| 1 | +# Build — Mobile |
| 2 | + |
| 3 | +iOS and Android companion app for [Build](https://build.interactor.com), the Interactor project management platform. Built with React Native + Expo. |
| 4 | + |
| 5 | +## What it does |
| 6 | + |
| 7 | +A native companion for the highest-value PM actions that happen on the go: |
| 8 | + |
| 9 | +| Tab | What you can do | |
| 10 | +|---|---| |
| 11 | +| **Home** | Dashboard — project health at a glance | |
| 12 | +| **Tasks** | View and update your assigned tasks | |
| 13 | +| **Approvals** | Approve or reject deliverables and phases | |
| 14 | +| **Inbox** | Notification feed, mark-read | |
| 15 | +| **Me** | Your account, sign out | |
| 16 | + |
| 17 | +Push notifications + live SSE updates keep every screen current without manual refresh. |
| 18 | + |
| 19 | +## Requirements |
| 20 | + |
| 21 | +- Node.js 22+ |
| 22 | +- npm 10+ |
| 23 | +- [Expo CLI](https://docs.expo.dev/get-started/installation/) (`npm install -g expo-cli`) |
| 24 | +- iOS: Xcode 15+ (for simulator) or the [Expo Go](https://expo.dev/go) app on a physical device |
| 25 | +- Android: Android Studio with an emulator, or the [Expo Go](https://expo.dev/go) app on a physical device |
| 26 | + |
| 27 | +## Running locally |
| 28 | + |
| 29 | +```bash |
| 30 | +# 1. Install dependencies |
| 31 | +npm install |
| 32 | + |
| 33 | +# 2. Copy env (defaults point to production API — change for local dev) |
| 34 | +cp .env.example .env.local |
| 35 | + |
| 36 | +# 3. Start the dev server |
| 37 | +npm start |
| 38 | +``` |
| 39 | + |
| 40 | +Expo will print a QR code. Scan it with the Expo Go app (iOS or Android) to open the app on your device, or press `i` for iOS simulator / `a` for Android emulator. |
| 41 | + |
| 42 | +### Local dev against a local Build API |
| 43 | + |
| 44 | +Edit `.env.local`: |
| 45 | + |
| 46 | +``` |
| 47 | +EXPO_PUBLIC_API_URL=http://<your-machine-ip>:4025 |
| 48 | +EXPO_PUBLIC_ACCOUNT_SERVER_URL=https://auth.interactor.com |
| 49 | +``` |
| 50 | + |
| 51 | +Use your machine's LAN IP (not `localhost`) — the device/emulator can't reach `localhost` on your laptop. |
| 52 | + |
| 53 | +## Other commands |
| 54 | + |
| 55 | +```bash |
| 56 | +npm run typecheck # TypeScript check (no emit) |
| 57 | +npm run lint # ESLint |
| 58 | +npm run ios # Open iOS simulator directly |
| 59 | +npm run android # Open Android emulator directly |
| 60 | +``` |
| 61 | + |
| 62 | +## Project structure |
| 63 | + |
| 64 | +``` |
| 65 | +app/ |
| 66 | +├── _layout.tsx # Root layout: QueryClientProvider, AuthGuard, push + SSE wiring |
| 67 | +├── (auth)/ |
| 68 | +│ └── login.tsx # Login screen |
| 69 | +└── (tabs)/ |
| 70 | + ├── _layout.tsx # Bottom tab bar |
| 71 | + ├── index.tsx # Home / Dashboard |
| 72 | + ├── tasks.tsx # My Tasks |
| 73 | + ├── approvals.tsx # Approvals |
| 74 | + ├── inbox.tsx # Inbox |
| 75 | + └── me.tsx # Me / sign out |
| 76 | +
|
| 77 | +src/ |
| 78 | +├── lib/ |
| 79 | +│ ├── api-client.ts # Typed fetch wrapper (Bearer pm_mobile_* token) |
| 80 | +│ ├── config.ts # EXPO_PUBLIC_* env vars |
| 81 | +│ ├── push.ts # Push notification registration |
| 82 | +│ ├── queries.ts # TanStack Query hooks |
| 83 | +│ └── sse.ts # SSE foreground hook (notifications:<userId>) |
| 84 | +└── store/ |
| 85 | + └── auth.ts # Zustand auth store + SecureStore persistence |
| 86 | +``` |
| 87 | + |
| 88 | +## Auth flow |
| 89 | + |
| 90 | +1. User enters email + password on the login screen |
| 91 | +2. Credentials are sent to the Interactor account server (`https://auth.interactor.com`) → returns a short-lived user JWT |
| 92 | +3. The JWT is exchanged with the Build API (`POST /api/v1/me/mobile-sessions`) for a long-lived `pm_mobile_*` device token |
| 93 | +4. The token is stored in the device Keychain (iOS) / Keystore (Android) via `expo-secure-store` |
| 94 | +5. All subsequent API calls use `Authorization: Bearer pm_mobile_*` |
| 95 | + |
| 96 | +Sign out revokes the session on the server and wipes the local token. |
| 97 | + |
| 98 | +## Builds (EAS) |
| 99 | + |
| 100 | +This app uses [EAS Build](https://docs.expo.dev/build/introduction/) for cloud builds. |
| 101 | + |
| 102 | +| Profile | Distribution | Use for | |
| 103 | +|---|---|---| |
| 104 | +| `development` | Internal | Dev client builds | |
| 105 | +| `preview` | Internal | QA / internal testing (sideload APK, TestFlight internal) | |
| 106 | +| `production` | App Store / Play Store | Public release | |
| 107 | + |
| 108 | +```bash |
| 109 | +# Trigger a preview build (iOS + Android) |
| 110 | +npx eas-cli build --platform all --profile preview |
| 111 | + |
| 112 | +# Trigger a production build |
| 113 | +npx eas-cli build --platform all --profile production |
| 114 | +``` |
| 115 | + |
| 116 | +CI triggers a preview build automatically on every pull request. |
| 117 | + |
| 118 | +### First-time EAS setup |
| 119 | + |
| 120 | +1. Install EAS CLI: `npm install -g eas-cli` |
| 121 | +2. Log in: `eas login` (use `peter@interactor.com`) |
| 122 | +3. iOS credentials: `eas credentials --platform ios` (requires a paid Apple Developer account) |
| 123 | +4. Android credentials are managed automatically by EAS |
| 124 | + |
| 125 | +## Environment variables |
| 126 | + |
| 127 | +| Variable | Default | Description | |
| 128 | +|---|---|---| |
| 129 | +| `EXPO_PUBLIC_API_URL` | `https://build.interactor.com` | Build API base URL | |
| 130 | +| `EXPO_PUBLIC_ACCOUNT_SERVER_URL` | `https://auth.interactor.com` | Interactor auth server | |
| 131 | + |
| 132 | +CI/EAS also requires `EXPO_ACCESS_TOKEN` (set in GitHub Actions secrets, never committed). |
| 133 | + |
| 134 | +## Contributing |
| 135 | + |
| 136 | +Every change requires a GitHub issue, a dedicated branch, and a PR — no direct pushes to `main`. See [CLAUDE.md](./CLAUDE.md) for the full gate. |
| 137 | + |
| 138 | +## License |
| 139 | + |
| 140 | +AGPL-3.0 — see [LICENSE](./LICENSE). |
0 commit comments