Skip to content

feat: port Expo companion app + Phase 0 foundation from the monorepo - #1

Open
dchasepdx wants to merge 10 commits into
open-flight:mainfrom
dchasepdx:feat/mobile-phase-0-foundation
Open

feat: port Expo companion app + Phase 0 foundation from the monorepo#1
dchasepdx wants to merge 10 commits into
open-flight:mainfrom
dchasepdx:feat/mobile-phase-0-foundation

Conversation

@dchasepdx

@dchasepdx dchasepdx commented Aug 30, 2026

Copy link
Copy Markdown

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.md documents that constraint and why the SDK shouldn't be bumped casually.

Phase 0 foundation — the structural work that makes later feature phases possible:

  • Navigation shell — bottom tabs via expo-router (Live / Shots / Stats / Device); Live is the default view.
  • Socket service (services/socket.ts) — a singleton mirroring the web UI's socketService.ts, so there is one place mapping each server event to store state.
  • Session store (stores/useSessionStore.ts) — shared across tabs, holding shots and connection state.
  • Connection persistence (storage/connection.ts) — server URL persisted via AsyncStorage, remembered across launches.
  • Test infrastructurejest-expo + @testing-library/react-native, with 20 tests over the socket service's event→state transitions, the store, and connection storage.

ROADMAP.md lays 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 test20 tests, 3 suites, all passing. Verified from a clean checkout with no monorepo present: npm ci, then npx jest --ci and npx 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 dev in open-flight/openflight, no hardware needed), or the real server on a Pi. Scan the Metro QR with an SDK 54 Expo Go, enter http://<server-ip>:8080 on the connection screen, and use the Simulate action to push a fake shot end-to-end.

Notes for review

  • Expo SDK 54 is pinned deliberately — a newer Expo Go rejects the app outright. See AGENTS.md and the README's "Get an SDK 54 Expo Go" section before upgrading.
  • The duplicated Shot type is intentional, not an oversight; see the DRY note at the bottom of ROADMAP.md.
  • The package-lock.json accounts for most of the diff's line count.

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 johnpacino left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The overall structure looks good for an architectural POC. Before approval, please address these three foundation cleanups:

  1. 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.
  1. 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.

  1. 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
@dchasepdx

Copy link
Copy Markdown
Author

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.

  1. 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 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 johnpacino left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants