feat: port Expo companion app + Phase 0 foundation from the monorepo - #1
feat: port Expo companion app + Phase 0 foundation from the monorepo#1dchasepdx wants to merge 10 commits into
Conversation
Starting point for a phone companion app that consumes the existing Socket.IO shot stream from server.py, reusing the React/TypeScript stack already used by ui/src/.
Connects to the existing Socket.IO server, renders incoming shots (club / ball speed / carry) with a header row, and exposes a Simulate Shot control for testing against --mock backends.
The server URL field opened the keyboard with no way to close it except the return key. Wrap the screen in TouchableWithoutFeedback -> Keyboard.dismiss, make the shot ScrollView dismiss on drag and persist taps (so buttons still register), and set the URL field's return key to "done" with an explicit dismiss on submit.
…yer, tests
Restructure the mobile app so features are cheap to add (roadmap Phase 0):
- Migrate to expo-router with a bottom tab bar (Live / Shots / Stats / Device).
Live is fully wired; Shots/Stats/Device are placeholders for later phases.
- Extract a singleton socket service (services/socket.ts) mirroring the web
app's shape: one place mapping each server event to a store mutation, with
Socket.IO auto-reconnect and session re-sync on every (re)connect.
- Add a zustand session store (newest-first shots invariant) and AsyncStorage
connection persistence that remembers the last successfully-connected URL.
- Expand types.ts with the wire-contract payloads later phases consume.
- Stand up test infra (jest-expo + @testing-library/react-native); 19 tests
covering store transitions, storage fallbacks, and socket event wiring.
- Add mobile/ROADMAP.md documenting the phased parity plan.
The app moved out of the openflight monorepo into its own repo, so the docs no longer describe where it lives or how it is run. - README: drop `cd mobile` / `make mobile-dev` in favour of `npm install` and `npm start`; point the wire-contract and mock-server references at open-flight/openflight. - ROADMAP: same, plus repo-relative file paths in the Phase 0 table. - Add `.node-version` (v24), previously inherited from the monorepo root and referenced by the README.
johnpacino
left a comment
There was a problem hiding this comment.
The overall structure looks good for an architectural POC. Before approval, please address these three foundation cleanups:
- Make the Expo SDK 54 dependency graph clean
npx expo-doctor currently fails because expo-font is not declared directly and two incompatible versions are installed (14.0.12 and 57.0.1). The Testing Library dependency also pulls a renderer expecting React 19.2, while SDK 54 uses React 19.1.
Please:
- Add the SDK-54-compatible expo-font dependency using npx expo install expo-font.
- Use a React-19.1-compatible @testing-library/react-native release, such as 13.3.3.
- Regenerate package-lock.json.
- Verify that npm ci, npx expo-doctor, npx tsc --noEmit, and npm test -- --ci all pass.
- Remove the duplicate Expo plugin configuration
app.json declares the plugins property twice. Both currently contain expo-router, but duplicate JSON keys are ambiguous and easy to overlook during future configuration changes. Please consolidate these into one plugins entry.
- Correct the README setup claims
The README says the iOS App Store is currently capped at Expo SDK 54, but Expo now lists SDK 57 as current. Please update the physical-iPhone instructions to explain that SDK 54 requires an existing installation or a development build.
The README also says CI builds against .node-version, but this repository does not currently have a CI workflow. Please remove that claim for now; CI can be added in a follow-up foundation PR.
Addresses review feedback on the port PR. expo-doctor was failing 2 of 18 checks: - `expo-font` is an open-ended peer of `@expo/vector-icons` (`>=14.0.4`), so npm auto-installed 57.0.1 at the top level while `expo@54` nested its own 14.0.12. Declaring it explicitly at the SDK-54-pinned `~14.0.12` (via `expo install`) dedupes to one copy. - `@testing-library/react-native@14` pulls `test-renderer@1.2.0` -> `react-reconciler@0.33.0`, which peer-requires React >=19.2 against SDK 54's pinned 19.1.0; npm only resolved it via an ERESOLVE override. Moved to 13.3.3, which has no such chain. 13.3.3 peer-requires `react-test-renderer`, and npm resolves that to 19.2.8 (React ^19.2.8), reproducing the same conflict — so it is pinned to 19.1.0 to match React. Install is now ERESOLVE-clean. `expo install` also collapsed the duplicated `plugins` key in app.json into a single array, which was the second review point. README: drop the claim that CI builds against `.node-version` — this repo has no CI workflow yet. The physical-iPhone row keeps the SDK 54 App Store cap (still accurate: SDK 55+ was never approved by Apple), but no longer claims newer builds can't reach a physical device — `eas go` ships one via TestFlight. Verified: `npm ci`, `npx expo-doctor` (18/18), `npx tsc --noEmit`, and `npm test -- --ci` (20 tests) all pass. Also smoke-tested that RNTL 13.3.3 renders against the pinned react-test-renderer.
A reviewer read expo.dev/go listing SDK 57 as current and concluded the README's SDK 54 claim was stale. Those are different things — the SDK release train vs. what Apple approved — so link the doc that says it outright: "Expo Go on the Apple App Store stops at SDK 54, and SDK 55 and later are not available there." Also correct two things the previous wording got wrong: - "The App Store / Play Store only ever offer the latest build" is no longer true of the App Store, which is frozen at SDK 54 while the Play Store tracks the latest SDK. - The physical-iPhone escape hatch led with `eas go`, which needs a paid Developer Program membership. Expo recommends sign.expo.dev first — free Apple ID, though the certificate lapses after ~7 days. Both are now listed with their actual costs. Refs: https://docs.expo.dev/troubleshooting/expo-go-version-mismatch/ https://expo.dev/changelog/expo-go-and-app-store-may-2026 expo/expo#44036
|
Thanks for the review @johnpacino ! I addressed the issues with the dependency graph and the duplicate expo plugin. The third issue has some nuance to it.
The Expo SDK is on version 57, but the version of Expo Go you get in the ios app store is capped at 54. They haven't received an approval on the later versions I guess. Pinning the version to 54 will work until apple approves the newer versions of expo go |
johnpacino
left a comment
There was a problem hiding this comment.
Thanks for addressing the review. I verified the dependency cleanup, consolidated Expo plugin configuration, and README corrections. I also confirmed that Expo’s current documentation supports the SDK 54 App Store limitation—my original comment conflated the current Expo SDK with the iOS App Store version of Expo Go.
npm ci, Expo Doctor (18/18), TypeScript, all 20 tests, and the Expo web export pass. Approved as the mobile architecture POC. CI and the remaining production-hardening work can follow separately.
Summary
Ports the Expo (React Native) companion app into this repo, plus the Phase 0 foundation work that was built on top of it.
The app connects to the OpenFlight Flask/Socket.IO server over the LAN on port 8080. It has no build-time dependency on open-flight/openflight
What's in it
Scaffold — Expo SDK 54, pinned to match the Expo Go build available on the maintainer's device.
AGENTS.mddocuments that constraint and why the SDK shouldn't be bumped casually.Phase 0 foundation — the structural work that makes later feature phases possible:
services/socket.ts) — a singleton mirroring the web UI'ssocketService.ts, so there is one place mapping each server event to store state.stores/useSessionStore.ts) — shared across tabs, holding shots and connection state.storage/connection.ts) — server URL persisted via AsyncStorage, remembered across launches.jest-expo+@testing-library/react-native, with 20 tests over the socket service's event→state transitions, the store, and connection storage.ROADMAP.mdlays out where this is heading: a complete standalone interface, so a builder can run the Pi headless and skip the kiosk touchscreen entirely.Automated tests
npm test— 20 tests, 3 suites, all passing. Verified from a clean checkout with no monorepo present:npm ci, thennpx jest --ciandnpx tsc --noEmit, both clean.Coverage is on the pure, testable parts: socket event→state transitions, store reducers, and connection URL persistence. Screens are not yet component-tested — that lands with the Phase 1 features per the roadmap's "tests land with each feature" principle.
Manual testing
Run against the mock server (
make devin open-flight/openflight, no hardware needed), or the real server on a Pi. Scan the Metro QR with an SDK 54 Expo Go, enterhttp://<server-ip>:8080on the connection screen, and use the Simulate action to push a fake shot end-to-end.Notes for review
AGENTS.mdand the README's "Get an SDK 54 Expo Go" section before upgrading.Shottype is intentional, not an oversight; see the DRY note at the bottom ofROADMAP.md.package-lock.jsonaccounts for most of the diff's line count.