Files
soconnor 355b14faef Add local iOS release pipeline, fix shortcuts, and improve invoice UX.
Enable App Store builds without EAS, iOS 18 App Intents plugins, and signing
fixes for distribution export. Add mobile invoice PDF preview, compact line
items, and more reliable shortcut deep-link handling.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-23 01:08:20 -04:00

144 lines
4.8 KiB
Markdown

# beenvoice Mobile
Expo companion for [beenvoice](../beenvoice) — dashboard, time clock, invoices, clients, businesses, and settings. Shares the **same tRPC API** and **better-auth** sessions as the web app.
**Architecture (dense):** [docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md)
## Prerequisites
- [Bun](https://bun.sh) 1.3+
- beenvoice API running ([setup](../beenvoice/README.md))
- Xcode + iOS Simulator (or device) for native dev build
- **Not Expo Go** — widgets, SecureStore auth, and biometrics need `expo-dev-client`
## Setup
```bash
cd beenvoice-app
bun install
cp .env.example .env
```
`.env`:
```env
# Simulator
EXPO_PUBLIC_API_URL=http://localhost:3000
# Physical iPhone — Mac LAN IP
EXPO_PUBLIC_API_URL=http://192.168.1.42:3000
```
Omit `EXPO_PUBLIC_API_URL` in production builds to default to `https://beenvoice.soconnor.dev`.
Server must enable `@better-auth/expo` in `beenvoice/src/lib/auth.ts` with `beenvoice://` in `trustedOrigins`.
## Run
```bash
# Terminal 1 — API
cd ../beenvoice && bun run dev
# Terminal 2 — mobile (builds native app if needed)
cd beenvoice-app && bun run ios
```
Metro uses port **8082** (avoids other Expo projects on 8081).
Metro only (app already installed):
```bash
bun run start -- --clear
```
Open the **beenvoice** dev build on the simulator — not Expo Go.
### After native changes
Icon (`assets/beenvoice.icon`), widgets, or new native modules:
```bash
bunx expo prebuild --platform ios --clean
bun run ios
```
## Features
| Area | Details |
|------|---------|
| **Auth** | Sign in, register, forgot/reset password; official or self-hosted server |
| **Multi-account** | Bitwarden-style switcher; per-account session in SecureStore |
| **Dashboard** | Revenue, pending, overdue, running timer, recent invoices |
| **Timer** | Clock in/out, client + invoice + rate; optional description (default "Clock In"); iOS Live Activity |
| **Entities** | Clients and businesses — list, create, edit |
| **Invoices** | List, filter, create, edit, status updates |
| **Settings** | Profile, accounts, theme, per-account app lock (PIN + Face ID), sign out |
| **App lock** | Per-account; locks on background return |
## Auth & accounts (summary)
- **Guest** auth storage: `beenvoice:guest` until first successful login
- **Per account**: `beenvoice:auth:{host::userId}` in SecureStore
- After login, `finalizeAuthenticatedAccount()` migrates session keys before activating the account (avoids double login)
- **Server picker**: Official (`beenvoice.soconnor.dev`) or custom URL on auth screens
Full flow: [docs/ARCHITECTURE.md#multi-account-model](./docs/ARCHITECTURE.md#multi-account-model)
## Deep links & Shortcuts
| URL | Action |
|-----|--------|
| `beenvoice://reset-password?token=…` | Reset password |
| `beenvoice://timer` | Open time clock |
| `beenvoice://shortcuts/clock-in` | Clock in (last client) |
| `beenvoice://shortcuts/clock-in?title=…` | Clock in with title |
| `beenvoice://shortcuts/clock-out` | Clock out running timer |
**iOS Shortcuts / Siri** (requires native rebuild: `bunx expo prebuild --platform ios && bun run ios`):
- **Clock In** — starts the timer with your last client
- **Clock Out** — stops the running timer
- **Open Time Clock** — opens the timer tab
1. Install a fresh build on a physical iPhone (iOS 16+).
2. Open the app once while signed in (registers shortcuts with the system).
3. Shortcuts app → search **beenvoice** → add actions, or say “Hey Siri, clock in with beenvoice”.
4. Pick a client once on the Timer tab before the first clock-in shortcut.
**Test deep links:**
```bash
xcrun simctl openurl booted "beenvoice://shortcuts/clock-in"
xcrun simctl openurl booted "beenvoice://shortcuts/clock-out"
xcrun simctl openurl booted "beenvoice://timer"
```
## Project layout
```
app/
_layout.tsx # Providers, auth guard
(auth)/ # sign-in, register, password flows
(app)/ # tab shell + nested stacks
components/ # UI, forms, chrome, time clock
contexts/ # Auth, Accounts, AppLock, Theme
lib/ # tRPC, auth storage, config, theming
widgets/ # iOS Live Activity (TimeClockActivity)
```
## Troubleshooting
| Issue | Fix |
|-------|-----|
| `PlatformConstants` / runtime not ready | Stop other Metro on 8081/8082; rebuild with `prebuild --clean` |
| Expo Go | Use `bun run ios` dev build |
| API errors on device | `EXPO_PUBLIC_API_URL` = Mac LAN IP; server `BETTER_AUTH_URL` must match |
| Live Activity empty | Rebuild iOS; widget UI must live inside `"widget"` function |
| Login twice | Server + app versions with session migration (`lib/auth-storage.ts`) |
## Related
- [beenvoice README](../beenvoice/README.md)
- [beenvoice ARCHITECTURE](../beenvoice/docs/ARCHITECTURE.md)
- [Workspace root README](../README.md)