iOS and Android companion app for Build, the Interactor project management platform. Built with React Native + Expo.
A native companion for the highest-value PM actions that happen on the go:
| Tab | What you can do |
|---|---|
| Home | Dashboard — project health at a glance |
| Tasks | View and update your assigned tasks |
| Approvals | Approve or reject deliverables and phases |
| Inbox | Notification feed, mark-read |
| Me | Your account, sign out |
Push notifications + live SSE updates keep every screen current without manual refresh.
- Node.js 22+
- npm 10+
- Expo CLI (
npm install -g expo-cli) - iOS: Xcode 15+ (for simulator) or the Expo Go app on a physical device
- Android: Android Studio with an emulator, or the Expo Go app on a physical device
# 1. Install dependencies
npm install
# 2. Copy env (defaults point to production API — change for local dev)
cp .env.example .env.local
# 3. Start the dev server
npm startExpo 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.
Edit .env.local:
EXPO_PUBLIC_API_URL=http://<your-machine-ip>:4025
EXPO_PUBLIC_ACCOUNT_SERVER_URL=https://auth.interactor.com
Use your machine's LAN IP (not localhost) — the device/emulator can't reach localhost on your laptop.
npm run typecheck # TypeScript check (no emit)
npm run lint # ESLint
npm run ios # Open iOS simulator directly
npm run android # Open Android emulator directlyapp/
├── _layout.tsx # Root layout: QueryClientProvider, AuthGuard, push + SSE wiring
├── (auth)/
│ └── login.tsx # Login screen
└── (tabs)/
├── _layout.tsx # Bottom tab bar
├── index.tsx # Home / Dashboard
├── tasks.tsx # My Tasks
├── approvals.tsx # Approvals
├── inbox.tsx # Inbox
└── me.tsx # Me / sign out
src/
├── lib/
│ ├── api-client.ts # Typed fetch wrapper (Bearer pm_mobile_* token)
│ ├── config.ts # EXPO_PUBLIC_* env vars
│ ├── push.ts # Push notification registration
│ ├── queries.ts # TanStack Query hooks
│ └── sse.ts # SSE foreground hook (notifications:<userId>)
└── store/
└── auth.ts # Zustand auth store + SecureStore persistence
- User enters email + password on the login screen
- Credentials are sent to the Interactor account server (
https://auth.interactor.com) → returns a short-lived user JWT - The JWT is exchanged with the Build API (
POST /api/v1/me/mobile-sessions) for a long-livedpm_mobile_*device token - The token is stored in the device Keychain (iOS) / Keystore (Android) via
expo-secure-store - All subsequent API calls use
Authorization: Bearer pm_mobile_*
Sign out revokes the session on the server and wipes the local token.
This app uses EAS Build for cloud builds.
| Profile | Distribution | Use for |
|---|---|---|
development |
Internal | Dev client builds |
preview |
Internal | QA / internal testing (sideload APK, TestFlight internal) |
production |
App Store / Play Store | Public release |
# Trigger a preview build (iOS + Android)
npx eas-cli build --platform all --profile preview
# Trigger a production build
npx eas-cli build --platform all --profile productionCI triggers a preview build automatically on every pull request.
- Install EAS CLI:
npm install -g eas-cli - Log in:
eas login(usepeter@interactor.com) - iOS credentials:
eas credentials --platform ios(requires a paid Apple Developer account) - Android credentials are managed automatically by EAS
| Variable | Default | Description |
|---|---|---|
EXPO_PUBLIC_API_URL |
https://build.interactor.com |
Build API base URL |
EXPO_PUBLIC_ACCOUNT_SERVER_URL |
https://auth.interactor.com |
Interactor auth server |
CI/EAS also requires EXPO_ACCESS_TOKEN (set in GitHub Actions secrets, never committed).
Every change requires a GitHub issue, a dedicated branch, and a PR — no direct pushes to main. See CLAUDE.md for the full gate.
AGPL-3.0 — see LICENSE.