Skip to content

feat(ios)!: replace web SPA with React Native app - #2

Merged
charlesponti merged 3 commits into
mainfrom
feat/ios-react-native-migration
Oct 13, 2026
Merged

charlesponti merged 3 commits into
mainfrom
feat/ios-react-native-migration

Conversation

@charlesponti

Copy link
Copy Markdown
Member

Replaces the Still web SPA with a bare React Native iOS app (io.ponti.still) backed by the StillCore Swift package.

New

  • Email-OTP sign-in through the public HominemAuth client (same mechanism as Thames), app-private Keychain session, no SSO
  • Seven screens (Overview, lists, groups, sign-in) with the store over authenticated requests
  • Hand-rolled TestFlight archive/deploy scripts plus docs/TESTFLIGHT.md runbook

Removed

  • Vite web app, Docker/nginx image, Railway config — the web is no longer supported

Validation (all green)

  • npx tsc --noEmit, jest 26/26, swift test 15/15, actionlint
  • Debug simulator build, Release archive + TestFlight export
  • Live simulator verification vs local API: OTP request/verify, session restore on relaunch, possessions POST/GET/DELETE round-trip, sign-out clears

Breaking change
The Still web app is removed. Rollback point: tag still-web-final (last web commit). Railway shutdown stays manual post-merge.

Ship Still as a bare React Native iOS app (io.ponti.still) with the StillCore Swift package: email-OTP sign-in through the public HominemAuth client with an app-private Keychain session, seven screens, and hand-rolled TestFlight archive/deploy scripts plus a runbook. The Vite web app, Docker/nginx image, and Railway config are removed.

BREAKING CHANGE: the Still web app is removed. Still ships as an iOS app only.
Copilot AI balanced review requested due to automatic review settings October 13, 2026 18:22
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-13T18:28:05.963219Z 464b526 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 464b526123

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

STILL_METRO_PORT = 8084;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Restrict the release target to iPhone

The Release configuration advertises support for iPad, but AppIcon.appiconset/Contents.json contains only iPhone and marketing icons, and the runbook explicitly describes the app as iPhone-only. App Store Connect therefore treats uploads as universal builds while required iPad icon sizes are absent, which can block TestFlight validation; use device family 1 or provide complete iPad assets and support.

Useful? React with 👍 / 👎.

Comment thread src/ui/ItemList.tsx
Comment on lines +82 to +85
<FlatList
data={rows}
keyExtractor={(i) => i.id}
scrollEnabled={false}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Use the item list as the vertical scroller

This FlatList is rendered inside the vertical ScrollView in App.tsx, and setting scrollEnabled={false} does not preserve FlatList virtualization in that arrangement. For inventories approaching the 5,000-item fetch limit, the screen can create thousands of row views and stall or exhaust memory; make a FlatList/SectionList the top-level scroller rather than nesting it in a same-axis ScrollView.

Useful? React with 👍 / 👎.

Comment on lines +70 to +71
guard address.contains("@"), address.contains("."), !state.busy else {
return update { $0.problem = .badEmail }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Ignore duplicate code requests while busy

When a second request reaches the main actor while the first OTP request is awaiting the network—for example, from a rapid button tap plus keyboard submission—the !state.busy failure is handled as .badEmail. The first request can then succeed without clearing that problem, leaving the code-entry screen showing an incorrect email error; return unchanged when busy and reserve .badEmail for address validation failures.

Useful? React with 👍 / 👎.

Copilot AI 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.

🟡 Changes recommended

Numeric JSON corruption, incomplete offline sign-out, and iOS release configuration defects must be resolved before approval.

6 open findings
What changed in this PR

Replaces the Still web SPA with a React Native iOS client backed by StillCore and the Hominem API.

Changes:

  • Adds native email-OTP authentication, API bridging, and React Native screens.
  • Adds Jest/Swift coverage and iOS project configuration.
  • Adds device/TestFlight tooling while removing web deployment infrastructure.
File Description
vite.config.ts Removes Vite configuration.
tsconfig.json Adopts React Native TypeScript settings.
StillCore/​Tests/​StillCoreTests/​StillAccountTests.swift Tests account and serialization behavior.
StillCore/​Sources/​StillCore/​StillAccountService.swift Implements Hominem authentication and requests.
StillCore/​Sources/​StillCore/​StillAccount.swift Manages native account state.
StillCore/​Package.swift Defines the StillCore package.
StillCore/​Package.resolved Pins HominemAuth.
src/​vite-env.d.ts Removes Vite environment types.
src/​ui/​theme.ts Defines native UI colors.
src/​ui/​SignIn.tsx Adds OTP sign-in screens.
src/​ui/​Sheet.tsx Adds native bottom sheets.
src/​ui/​Overview.tsx Adds overview cards and panels.
src/​ui/​ItemSheet.tsx Adds item editing UI.
src/​ui/​ItemList.tsx Adds native item lists.
src/​ui/​Groups.tsx Adds group grids and editing.
src/​style.css Removes web styling.
src/​route.ts Removes browser routing.
src/​platform/​NativeStill.ts Defines the native bridge API.
src/​main.tsx Removes the browser entry point.
src/​core/​useAccount.ts Connects React state to native auth.
src/​core/​types.ts Defines domain types.
src/​core/​store.ts Routes storage through authenticated requests.
src/​core/​schemas.ts Validates native account state.
src/​core/​preferences.ts Bridges native preferences.
src/​core/​mapping.ts Maps API/domain models.
src/​core/​list.ts Implements filtering and sorting.
src/​core/​groups.ts Implements group counts.
src/​core/​format.ts Provides display formatting.
src/​core/​derive.ts Derives overview statistics.
src/​core/​apiTypes.ts Defines Hominem API models.
src/​core/​api.ts Implements bridged API calls.
src/​components/​Sheet.tsx Removes the web sheet.
src/​components/​Overview.tsx Removes web overview components.
src/​components/​Menu.tsx Removes the web menu.
src/​components/​ItemSheet.tsx Removes the web item form.
src/​components/​ItemList.tsx Removes the web item list.
src/​components/​GroupSheet.tsx Removes the web group form.
src/​components/​Groups.tsx Removes web group components.
src/​auth.ts Removes browser authentication.
src/​App.tsx Removes the web application shell.
src/​api.ts Removes the fetch-based API client.
scripts/​ExportOptions-testflight.plist Configures App Store export.
scripts/​deploy-ios.sh Adds physical-device deployment.
scripts/​archive-testflight.sh Adds TestFlight archive/upload automation.
README.md Documents iOS development and shipping.
railway.json Removes Railway deployment.
package.json Replaces web dependencies and scripts.
nginx.conf Removes web server configuration.
metro.config.js Configures Metro.
jest.config.js Configures React Native tests.
ios/​Still/​StillBridge.swift Bridges Swift services to JavaScript.
ios/​Still/​StillBridge.m Exports bridge methods to React Native.
ios/​Still/​PrivacyInfo.xcprivacy Declares privacy metadata.
ios/​Still/​LaunchScreen.storyboard Adds the launch screen.
ios/​Still/​Info.plist Configures the iOS application.
ios/​Still/​Images.xcassets/​Contents.json Defines the asset catalog.
ios/​Still/​Images.xcassets/​AppIcon.appiconset/​Contents.json Defines app icons.
ios/​Still/​AppDelegate.swift Boots React Native and Metro.
ios/​Still.xcworkspace/​xcshareddata/​swiftpm/​Package.resolved Pins workspace Swift dependencies.
ios/​Still.xcworkspace/​contents.xcworkspacedata Defines the Xcode workspace.
ios/​Still.xcodeproj/​xcshareddata/​xcschemes/​Still.xcscheme Defines the shared Xcode scheme.
ios/​Still.xcodeproj/​project.pbxproj Defines the iOS target and build.
ios/​Podfile Configures React Native pods.
ios/​.xcode.env Configures Node for Xcode.
index.js Registers the React Native app.
index.html Removes the web HTML entry point.
docs/​TESTFLIGHT.md Adds the TestFlight runbook.
Dockerfile Removes containerized web builds.
babel.config.js Configures React Native transforms.
App.tsx Implements the native application shell.
app.json Defines React Native app metadata.
.watchmanconfig Configures Watchman.
.gitignore Adds native build exclusions.
.github/​workflows/​validate.yml Validates JavaScript and Swift.
.env.example Removes web API configuration.
.dockerignore Removes Docker exclusions.
__tests__/​store.test.tsx Tests store loading and mutations.
__tests__/​screens.test.tsx Tests native screen rendering.
__tests__/​preferences.test.ts Tests bridged preferences.
__tests__/​logic.test.ts Ports domain logic tests.
__tests__/​list.test.ts Tests filtering and sorting.
__tests__/​groups.test.ts Tests group counts.
__tests__/​api.test.ts Tests native API bridging.
__tests__/​account.test.tsx Tests account-state bridging.
Files not reviewed (1)
  • ios/Still.xcworkspace/contents.xcworkspacedata: Generated file

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +141 to +143
case let value as Bool: try container.encode(value)
case let value as Int: try container.encode(value)
case let value as Double: try container.encode(value)
STILL_METRO_PORT = 8084;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
Comment on lines +33 to +37
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
BuildableName = "StillTests.xctest"
BlueprintName = "StillTests"
Comment on lines +68 to +72
public func requestCode(email: String) async {
let address = email.trimmingCharacters(in: .whitespacesAndNewlines)
guard address.contains("@"), address.contains("."), !state.busy else {
return update { $0.problem = .badEmail }
}
} catch { throw Self.problem(error) }
}

public func signOut() async { try? await auth.signOut() }
Comment thread src/ui/ItemList.tsx
Comment on lines +82 to +86
<FlatList
data={rows}
keyExtractor={(i) => i.id}
scrollEnabled={false}
renderItem={({ item }) => <Row item={item} containers={containers} actions={actions} />}
@charlesponti
charlesponti merged commit cc262ea into main Oct 13, 2026
2 checks passed
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