fix(onboarding): clarify opencode-serve requirement + fail connect fast (retention) (#107)

* fix(onboarding): clarify opencode-serve requirement and fail connect tests fast

New users bounce at ~0% 7-day retention because nothing tells them the app
needs a computer running `opencode serve` on the same network/Tailscale, and
a bad IP hangs for the full 30s request timeout before failing.

- Rewrite the no-connection empty state subtitle and add a "How to set up a
  server" link to the setup guide (app/(tabs)/index.tsx, src/lib/links.ts).
- Surface the opencode-serve prerequisite as a one-line notice at the top of
  the Quick Connect form, above the existing detailed help box
  (app/connection/add.tsx).
- Give the interactive connection test (testConnection) its own 12s timeout
  via an optional Client.global.health(timeoutMs) parameter, instead of
  reusing the general 30s REQUEST_TIMEOUT_MS used for real session traffic
  (src/lib/sdk.ts, src/stores/connections.ts).
- Mirror all new/changed strings in the zh-Hans catalog; catalog-parity test
  keeps them in sync.

* docs(distribution): add retention analysis motivating first-run fixes

Diagnoses ~0% D7 retention as product-shape (no path to value without a
self-hosted server, no demo mode, store copy sets no expectation). Ranks
fixes and isolates the two owner-only strategic calls (store-copy honesty,
hosted OpenCode Connect).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T12AhSnQVrSxNnvwfCx2z6

* fix(onboarding): drop connect-screen prerequisite notice (kept off-screen the submit button in E2E)

The added notice pushed connect-submit-button below the fold, breaking the
Maestro activation-positive flow (and the other flows sharing the connect
prelude). The empty state already sets the opencode-serve expectation one
screen earlier, so this notice was redundant. Empty-state guidance + guide
link and the fast-fail connect timeout are unaffected and retained.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T12AhSnQVrSxNnvwfCx2z6

---------

Co-authored-by: engineer <engineer@macbookpro.lan>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Den
2026-07-17 17:55:12 -07:00
committed by GitHub
parent 2285f80e81
commit d54a74d3f5
7 changed files with 125 additions and 12 deletions

View File

@@ -11,6 +11,12 @@ const CONNECTIONS_KEY = "opencode_connections"
const PASSWORDS_PREFIX = "opencode_password_"
const RECENT_DIRS_KEY = "opencode_recent_dirs"
const MAX_RECENT_DIRS = 10
// A bad IP (unreachable host, wrong port) otherwise hangs for the full 30s
// general request timeout before the user sees a "connection failed" error —
// a first-run bounce driver. The interactive connect flow can afford to fail
// faster since a real server responds to /global/health in well under a
// second; this does NOT affect the timeout used for real session traffic.
const CONNECTION_TEST_TIMEOUT_MS = 12_000
// Cached auth so we can create directory-scoped clients without async SecureStore lookups
interface ClientBase {
@@ -259,7 +265,7 @@ export const useConnections = create<ConnectionsState>((set, get) => ({
auth: buildAuth(connection.username, password),
})
await client.global.health()
await client.global.health(CONNECTION_TEST_TIMEOUT_MS)
track(AnalyticsEvent.ConnectionSucceeded, { source })
return { ok: true }
} catch (error) {