Skip to content

Persist server URL and gate auto-connect#3

Open
RobertoVillegas wants to merge 2 commits intoR44VC0RP:mainfrom
RobertoVillegas:fix-auto-connect-saved-url
Open

Persist server URL and gate auto-connect#3
RobertoVillegas wants to merge 2 commits intoR44VC0RP:mainfrom
RobertoVillegas:fix-auto-connect-saved-url

Conversation

@RobertoVillegas
Copy link

@RobertoVillegas RobertoVillegas commented Dec 30, 2025

Summary

This PR fixes the blank-screen startup flow and makes auto-connect behavior predictable by persisting the server URL and only auto-connecting when a saved URL exists. It also aligns Expo dependency versions to the expected compatibility set, fixes an RN type issue, and keeps the repo on npm by using the npm lockfile.

What changed

  • Persist server URL in AsyncStorage after a successful connection and load it on boot.
  • Expose storage readiness + saved URL flags in the OpenCode provider and only auto-connect once those are ready.
  • Always redirect to /connect when not connected to avoid a blank UI during failed/long connects.
  • Pin Expo-compatible versions for react-native-screens and react-native-svg.
  • Replace NodeJS.Timeout with ReturnType for RN type safety.
  • Remove pnpm-lock.yaml and update package-lock.json with npm.

Why

  • Auto-connect was running even without a saved URL, and the app could render nothing while connecting.
  • Expo warned about dependency version mismatches that can cause runtime errors.
  • The NodeJS namespace isn’t available in the RN TS environment.
  • The repo already uses npm; keeping a pnpm lockfile adds confusion.

Impact

  • First launch shows the Connect screen immediately; auto-connect only happens after a successful, saved connection.
  • Reduces startup ambiguity and improves reliability across Expo.
  • Tooling stays consistent for contributors.

Testing

  • not run (manual: launch app, verify /connect shows before any saved URL; save URL and confirm auto-connect on next launch)

@coderabbitai
Copy link

coderabbitai bot commented Dec 30, 2025

📝 Walkthrough

Walkthrough

This PR integrates AsyncStorage to persist the OpenCode server URL locally, tracking storage readiness and saved URL state. The auto-connect logic now waits for storage initialization before attempting connection, routing is simplified by removing the intermediate connecting state, and dependencies are updated.

Changes

Cohort / File(s) Summary
Provider state & persistence
src/providers/OpenCodeProvider.tsx
Added AsyncStorage integration to persist and load the OpenCode server URL. Extended context interface with storageReady and hasSavedServerUrl booleans. On bootstrap, loads saved URL from storage and marks storage as ready. Changed pollingIntervalRef type from NodeJS.Timeout to ReturnType<typeof setInterval> for cross-platform compatibility.
App initialization & auto-connect
app/_layout.tsx
Expanded destructuring of useOpenCode() to include storageReady and hasSavedServerUrl. Auto-connect now only triggers when storage is ready and a saved server URL exists, in addition to connection not being previously attempted. Updated useEffect dependency array accordingly.
Routing logic
app/index.tsx
Removed handling for the connecting state. Component now routes based solely on connected state: redirects to /(tabs)/sessions if connected, otherwise to /connect. Eliminates the previous intermediate null render during the connecting phase.
Dependencies
package.json
Added @react-native-async-storage/async-storage@^2.1.0. Downgraded react-native-screens from ^4.19.0 to ~4.16.0 and react-native-svg from ^15.15.1 to 15.12.1.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant App as App (_layout.tsx)
    participant Provider as OpenCodeProvider
    participant Storage as AsyncStorage
    participant Router as Router (index.tsx)
    
    User->>App: Launch app
    App->>Provider: Initialize provider
    Provider->>Storage: Load saved server URL
    Storage-->>Provider: URL (or null)
    rect rgba(76, 175, 80, 0.1)
      Note over Provider: Set storageReady = true
    end
    Provider-->>App: Context updated<br/>(storageReady, hasSavedServerUrl)
    
    alt hasSavedServerUrl && not attempted
      rect rgba(33, 150, 243, 0.1)
        App->>Provider: Trigger auto-connect
        Provider->>Provider: Connect to server
      end
      Provider-->>App: connected = true
    else No saved URL
      App->>App: Skip auto-connect
    end
    
    App->>Router: Route based on connected state
    alt connected = true
      Router->>User: Navigate to /(tabs)/sessions
    else connected = false
      Router->>User: Navigate to /connect
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 Through AsyncStorage we hop and store,
URLs saved forevermore!
Ready states guide the way,
Connecting flows, simpler today,
Routes refined with careful keep,
Persistence glows—a smoother leap! ✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and concisely summarizes the main changes: persisting the server URL and gating auto-connect behavior to only trigger when conditions are met.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/providers/OpenCodeProvider.tsx (1)

135-135: Consider using a constant instead of a ref.

storageKeyRef never changes after initialization. Since the value is static, it could be a module-level constant rather than a ref, which would be more idiomatic and slightly more efficient.

🔎 Proposed refactor

Move this outside the component as a constant:

+const STORAGE_KEY = 'opencode.serverUrl';
+
 export function OpenCodeProvider({ children, defaultServerUrl = 'http://10.0.10.59:9034' }: OpenCodeProviderProps) {
   // Connection state
   const [connected, setConnected] = useState(false);
   const [connecting, setConnecting] = useState(false);
   const [error, setError] = useState<string | null>(null);
   const [serverUrl, setServerUrl] = useState(defaultServerUrl);
   const [storageReady, setStorageReady] = useState(false);
   const [hasSavedServerUrl, setHasSavedServerUrl] = useState(false);
   
   const clientRef = useRef<OpenCodeClient | null>(null);
-  const storageKeyRef = useRef('opencode.serverUrl');

Then update usages of storageKeyRef.current to STORAGE_KEY on Lines 201, 241.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 49488da and ba5d3fe.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (4)
  • app/_layout.tsx
  • app/index.tsx
  • package.json
  • src/providers/OpenCodeProvider.tsx
🧰 Additional context used
🧬 Code graph analysis (3)
src/providers/OpenCodeProvider.tsx (1)
src/hooks/useOpenCode.ts (1)
  • OpenCodeClient (10-10)
app/index.tsx (1)
src/providers/OpenCodeProvider.tsx (1)
  • useOpenCode (638-644)
app/_layout.tsx (1)
src/providers/OpenCodeProvider.tsx (1)
  • useOpenCode (638-644)
🔇 Additional comments (12)
app/index.tsx (1)

5-11: LGTM! Cleaner routing logic.

The simplified approach now relies solely on the connected state, which aligns well with the new storage-aware bootstrap flow. The intermediate connecting state is appropriately handled in app/_layout.tsx, preventing blank screens during connection attempts.

src/providers/OpenCodeProvider.tsx (7)

3-3: LGTM! AsyncStorage import added.

The import is correctly added to support server URL persistence.


80-81: LGTM! Context interface extended with storage state.

The addition of storageReady and hasSavedServerUrl properly exposes storage initialization state to consumers, enabling conditional auto-connect logic.


131-132: LGTM! Storage state initialized.

Both boolean states start as false, which is appropriate before storage is checked.


161-161: LGTM! Correct type for React Native environment.

Changing from NodeJS.Timeout to ReturnType<typeof setInterval> properly handles the React Native environment where setInterval returns a number rather than a Node.js Timeout object.


201-202: LGTM! Server URL persisted on successful connection.

The URL is correctly persisted after the connection test succeeds, and hasSavedServerUrl is updated accordingly. Error handling is implicit through the try-catch block.


236-256: LGTM! Bootstrap logic properly initializes storage state.

The effect correctly:

  • Runs once on mount (empty deps)
  • Loads the saved URL from AsyncStorage
  • Updates both serverUrl and hasSavedServerUrl when found
  • Sets storageReady in the finally block regardless of success/failure
  • Silently handles errors, which is acceptable for storage access

607-608: LGTM! Storage state exposed in context.

The new state values are correctly included in the context value, making them available to consumers via useOpenCode().

app/_layout.tsx (2)

9-9: LGTM! Storage state integrated into auth flow.

The destructuring correctly includes storageReady and hasSavedServerUrl, which are now required to determine auto-connect behavior.


16-20: LGTM! Auto-connect properly gated by storage state.

The condition now ensures auto-connect only triggers when:

  1. Not already attempted (!autoConnectAttempted)
  2. Storage is initialized (storageReady)
  3. A saved URL exists (hasSavedServerUrl)

The dependency array is correctly updated to include the new state values, ensuring the effect re-evaluates when storage bootstrap completes.

package.json (2)

14-14: AsyncStorage version is compatible with Expo 54 and React Native 0.81.5.

Version ^2.1.0 meets the minimum requirement (RN >= 0.65) and is verified to work with Expo SDK 54. Proceed with the code change. Use npx expo install during setup to ensure native binaries are correctly aligned.


27-28: Clarify the downgrades—package-lock.json is out of sync.

The downgraded versions align with Expo 54 requirements: react-native-screens ~4.16.0 and react-native-svg 15.12.1 are the official bundled versions for Expo SDK 54, and both are compatible with React Navigation v7 in use here (which requires only react-native-screens >= 4.0.0).

However, package-lock.json contains the older versions (^4.19.0 and ^15.15.1), which is out of sync with package.json. Run npm install or npm ci to update the lock file and document the rationale (e.g., "standardizing on Expo 54 bundled versions" or "aligning with native build requirements").

@neilpoulin
Copy link

This is a great idea! I had similar issues when starting up the app on my machine and had made some quick changes to default the connection url to be my computer's IP address - i like this approach a lot better. Smart to persist it, and skip auto-connecting when no connection urls have been added yet.

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