Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions apps/mobile-expo/.claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"enabledPlugins": {
"expo@claude-plugins-official": true
}
}
43 changes: 43 additions & 0 deletions apps/mobile-expo/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files

# dependencies
node_modules/

# Expo
.expo/
dist/
web-build/
expo-env.d.ts

# Native
.kotlin/
*.orig.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision

# Metro
.metro-health-check*

# debug
npm-debug.*
yarn-debug.*
yarn-error.*

# macOS
.DS_Store
*.pem

# local env files
.env*.local

# typescript
*.tsbuildinfo

example

# generated native folders
/ios
/android
1 change: 1 addition & 0 deletions apps/mobile-expo/.vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "recommendations": ["expo.vscode-expo-tools"] }
7 changes: 7 additions & 0 deletions apps/mobile-expo/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"editor.codeActionsOnSave": {
"source.fixAll": "explicit",
"source.organizeImports": "explicit",
"source.sortMembers": "explicit"
}
}
41 changes: 41 additions & 0 deletions apps/mobile-expo/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
This is an Expo/React Native mobile application. Prioritize mobile-first patterns, performance, and cross-platform compatibility.

## Expo has changed — do not trust your training data

Expo ships breaking changes every SDK release. APIs you remember are likely renamed, moved, or removed. Before writing any code that touches an Expo, EAS, or React Native API:

1. Read the major version of the `expo` package in `package.json`.
2. Fetch the matching versioned docs: `https://docs.expo.dev/versions/v<major>.0.0/`
3. For anything else, fetch https://docs.expo.dev/llms.txt — an index of all Expo docs with corrections to common LLM misconceptions. Follow its links to the specific page you need; never answer from memory.

## Commands

Use `bunx` instead of `npx` if the project uses bun (`bun.lock` present).

```bash
npx expo install <package> # ALWAYS use instead of npm/yarn/pnpm/bun add — resolves SDK-compatible versions
npx expo start # start the dev server
npx expo lint # lint
npx tsc --noEmit # typecheck
npx expo-doctor # diagnose dependency and config issues
npx expo install --fix # fix incompatible package versions
```

Run lint and typecheck before declaring any task done.

## Navigation & Routing

- Use **Expo Router** for all navigation. Routes live in `src/app/` — every file there is a screen, `_layout.tsx` files define navigators. Keep non-route code (components, hooks, utils) outside `src/app/`.
- Import `Link`, `router`, and `useLocalSearchParams` from `expo-router`.
- Docs: https://docs.expo.dev/router/introduction.md

## Building with EAS

Use EAS to build, sign, and submit the app in the cloud (`eas build`, `eas submit`) and to ship over-the-air updates (`eas update`) — no local Xcode or Android Studio required. Run EAS CLI as `bunx eas-cli <command>` in Bun projects, or `npx eas-cli@latest <command>` otherwise; substitute that for bare `eas` in docs examples.
Docs: https://docs.expo.dev/eas/index.md

## Rules

- If `ios/` and `android/` directories do not exist, they are generated (Continuous Native Generation). Never create or edit them by hand — configure native behavior in `app.json` and config plugins.
- Expo Go only includes its bundled native modules. After adding a library with native code, the app needs a development build: `npx expo run:ios|android` locally, or `eas build --profile development`.
- Prefer recommended Expo modules over third-party libraries, and check your available skills before adding dependencies. Docs: https://docs.expo.dev/versions/latest/index.md
1 change: 1 addition & 0 deletions apps/mobile-expo/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
86 changes: 86 additions & 0 deletions apps/mobile-expo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Roost Expo prototype

An Expo (SDK 57, React Native 0.86) prototype of four core screens from the
SwiftUI app in `apps/ios`, built to compare feel and decide whether React
Native is worth adopting. It talks to the same `/api/mobile/v1` API with a
device token, so it runs against a real Roost server or the native UI fixture.

It is not part of the pnpm workspace and has its own `package-lock.json`.

## What is ported

| Screen | Ported | Left out |
| --- | --- | --- |
| Connect | Server + token validation, session/agents check, secure storage (Keychain via `expo-secure-store`) | Token replacement, help sheet (uses an alert) |
| Agents | Native large-title list, search bar, pull to refresh, 15s foreground refresh | Sections, reorder, rename/delete, activity badges, create |
| Chat | Revision polling (1s busy / 4s idle / 8s error), idempotent send + retry, stop, approvals, reply threads, swipe-to-reply with haptic, long-press menu, typing indicator, entrance animation, keyboard-aware inverted list | Attachments, image previews, inline dashboards, draft persistence, activity details, message flight animation, Codex style |
| Feed | All/Saved filter, time-of-day sections, images, save/dismiss (long press), reader in a native form sheet | Swipe actions, preferences, discuss, pagination |

## Native controls

On iOS the chrome and controls are system components, so they pick up
Liquid Glass and the system animations on iOS 26:

- Tabs: `NativeTabs` (UITabBarController).
- Header buttons and menus: `Stack.Toolbar` (UIBarButtonItem + UIMenu).
- Buttons: `NativeButton` renders a SwiftUI `Button` with `.buttonStyle(.glass)`
or `.glassProminent` via `@expo/ui/swift-ui` (`native-button.ios.tsx`).
- Composer: `GlassView` from `expo-glass-effect` (UIGlassEffect).
- Message long-press: SwiftUI `.contextMenu` via `@expo/ui` (`message-menu.ios.tsx`).

Android and web use the plain React Native fallbacks in the same folders.

Themes and pixel characters are read straight from
`apps/ios/Roost/Resources/*.json`, so both apps share one source of truth.

## Run it

```sh
# Terminal 1: local fixture server on 127.0.0.1:4399 (repo root)
corepack pnpm install && corepack pnpm dev:mobile-fixture

# Terminal 2
cd apps/mobile-expo
npm install
npx expo run:ios # development build on the simulator (needs Xcode)
# or: npx expo start --web
```

Connect with server `http://127.0.0.1:4399` and token
`roost_mobile_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa` (43 `a`s, fixture only).

Expo Go is not enough: `react-native-keyboard-controller` needs a
development build. On web the mobile API rejects browser `Origin` headers,
so the web build needs a same-origin proxy that strips them.

Xcode 27 builds need the UIScene life cycle or the app is killed at launch.
`plugins/with-scene-lifecycle.js` switches the generated project to Expo's
`ExpoAppSceneDelegate` during prebuild, so leave it in `app.json`. CocoaPods
also needs a UTF-8 locale (`export LANG=en_US.UTF-8`) or `pod install` fails.

## Updates without store review

`expo-updates` is installed. A release build checks for a new JS bundle at
launch and switches to it on the next launch, on iOS and Android alike.

- Only JS and assets ship this way. Adding a native module, changing
permissions or native `app.json` settings, or bumping the Expo SDK needs a
new store build.
- `runtimeVersion` uses the `fingerprint` policy: it is a hash of the native
project, so an update only reaches binaries whose native code can run it.
- `eas.json` builds `preview` (internal install) and `production` binaries
on matching update channels, so an update can go to your phone first.
- There is no update server yet. `updates.url` is unset, which leaves updates
off in release builds and does nothing in development builds. To use EAS,
run `npx eas-cli init` and `npx eas-cli update:configure` (they add the
project id and `updates.url`), make a build per platform, then publish with
`npx eas-cli update --channel preview`. A self-hosted server that speaks the
Expo Updates protocol can take the place of EAS by pointing `updates.url` at it.

## Checks

```sh
npx tsc --noEmit
npx expo export --platform ios --platform android # JS bundles build
corepack pnpm lint # from repo root (Biome)
```
49 changes: 49 additions & 0 deletions apps/mobile-expo/app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"expo": {
"name": "Roost",
"slug": "roost-expo-prototype",
"version": "0.1.0",
"orientation": "portrait",
"icon": "./assets/images/icon.png",
"scheme": "roost-expo",
"userInterfaceStyle": "automatic",
"runtimeVersion": {
"policy": "fingerprint"
},
"updates": {
"checkAutomatically": "ON_LOAD",
"fallbackToCacheTimeout": 0
},
"ios": {
"bundleIdentifier": "dev.roost.expo-prototype",
"supportsTablet": true
},
"android": {
"package": "dev.roost.expoprototype"
},
"web": {
"output": "single",
"favicon": "./assets/images/favicon.png"
},
"plugins": [
"expo-router",
"expo-secure-store",
[
"expo-splash-screen",
{
"backgroundColor": "#FFFFFF",
"image": "./assets/images/splash-icon.png",
"imageWidth": 96,
"dark": {
"backgroundColor": "#20221E"
}
}
],
"./plugins/with-scene-lifecycle"
],
"experiments": {
"typedRoutes": true,
"reactCompiler": true
}
}
}
Binary file added apps/mobile-expo/assets/images/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/mobile-expo/assets/images/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/mobile-expo/assets/images/splash-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions apps/mobile-expo/eas.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"cli": {
"version": ">= 16.0.0",
"appVersionSource": "remote"
},
"build": {
"preview": {
"distribution": "internal",
"channel": "preview"
},
"production": {
"channel": "production",
"autoIncrement": true
}
},
"submit": {
"production": {}
}
}
10 changes: 10 additions & 0 deletions apps/mobile-expo/metro.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const path = require("node:path");
const { getDefaultConfig } = require("expo/metro-config");

const config = getDefaultConfig(__dirname);
// Themes.json and Characters.json are shared with the SwiftUI app.
config.watchFolders = [
...(config.watchFolders ?? []),
path.resolve(__dirname, "../ios/Roost/Resources"),
];
module.exports = config;
Loading
Loading