Commit Graph

6 Commits

Author SHA1 Message Date
engineer
42fa36f3cd fix(ux): 10 UI/UX bugs from pre-release audit (toward no-bug launch gate)
Found via parallel screen audit; each confirmed in code:
- AuthGate: auto-prompt biometrics on lock (useEffect was imported but unused)
- CodeBlock: horizontal scroll for long code lines (were wrapped/mangled)
- DiffView: horizontal scroll instead of numberOfLines=1 truncation
- chat: biometric-cancel on send shows feedback instead of silently dropping msg
- chat: send failure restores input + attachments and alerts
- chat: removed dead /compact + /clear builtin commands (advertised, no-op)
- sessions: delete + rename failures alert instead of silent; rename guarded
  against double-submit
- sessions: onRefresh spinner no longer hangs forever if a refresh rejects
- add/edit connection: validate URL has http(s):// scheme before save/test

typecheck clean, 65/65 unit tests pass. Runtime UI behavior still needs on-device
verification per the pre-posting test gate (HANDOFF §0b).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 05:34:12 -07:00
engineer
6563f5ef53 fix(ui): correct mirrored chat empty-state + clipped IP placeholder; add readiness-check skill; privacy page nav/meta
- app/session/[id].tsx: chat empty-state rendered mirrored on Android (inverted FlatList) — now an untransformed overlay
- app/connection/add.tsx: shortened clipped host placeholder
- .agents/skills/readiness-check: production-readiness gate (Play+F-Droid published, app+site health)
- docs/privacy: back-nav + canonical/theme-color/description meta
Verified: tsc clean, CUA smoke green (run 26814702062)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 06:16:22 -07:00
Den
2b9b571d6e feat(privacy+dist): telemetry consent gate + app store distribution prep (#4)
* fix(security): fail closed on biometric init error

H-03: setting isAuthenticated: true on initialization failure was a
security bypass — any crash during biometric setup granted full access.
Fail closed instead; user sees auth prompt on next open.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(security): use Crypto.randomUUID for connection IDs

H-04: Math.random() is not cryptographically random. Connection IDs are
used as SecureStore key suffixes; switch to expo-crypto randomUUID for
a secure source.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(deps): pin expo-crypto to ~15.0.9

15.0.10 does not exist on npm; ~15.0.9 is the latest stable in the 15.x series compatible with Expo SDK 54.

* feat: add OpenCode Connect coming-soon waitlist card

Adds a discoverable 'OpenCode Connect — Coming Soon' card to the
add-connection quick-connect screen. Users can enter their email and
tap 'Join Waitlist' to send a pre-filled mailto. No backend required.

* fix(cua): detect actual screen dimensions and fix JSON parsing

- Get real screen size via `wm size` instead of hardcoding 1080x2400;
  emulator is 1080x1920 so y-coordinates were systematically off
- Extract first JSON object via regex when model returns multiple objects
- Use AZURE_OPENAI_MODEL env var for deployment name (defaults gpt-5.4)
- Add AZURE_DEV_AI_* path for Azure AI Foundry endpoints

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(security): SHA-pin upload-google-play and sanitize notification bodies

M-02: Pin r0adkll/upload-google-play to commit SHA e738b9d (v1.1.5)
to prevent supply-chain hijack via tag mutation.

M-03: Sanitize all push notification bodies — strip control chars,
truncate to 200 chars. Prevents server-supplied strings (error messages,
file paths from permission patterns, session titles) from leaking
unbounded text into the OS notification drawer.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(privacy): add telemetry consent gate for Sentry crash reporting

Sentry was always-on, violating F-Droid anti-feature policy and user
trust norms. Now gated behind explicit opt-in:

- First-launch consent modal (TelemetryConsentModal) shows once on
  fresh install; user can Allow or Decline.
- Consent state persisted in expo-secure-store (survives restarts).
- Settings > Privacy section: crash reporting toggle + privacy policy link.
- initSentry() called only after consent granted — not on app start.

Closes #3 (partial)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(config): add real icons and complete iOS/Android app.json config

- Add 1024×1024 app icon, 432×432 adaptive icon foreground, 200×200 splash
- iOS: push notification entitlement (aps-environment: production), speech/
  microphone/camera/photo usage descriptions for future features, disable
  ITSAppUsesNonExemptEncryption
- Android: adaptive icon with dark background (#0F172A), versionCode: 1
- expo-notifications plugin wired in app.json

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(dist): add iOS CI workflow, README rewrite, CONTRIBUTING, and LICENSE

- publish-app-store.yml: EAS Build + TestFlight submission; runs on tag/release/
  workflow_dispatch; bumps ios.buildNumber from github.run_number
- README: full rewrite — features, install badges, connection guide, contributing
- CONTRIBUTING.md: contribution guide for OSS contributors
- LICENSE: MIT

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs(dist): add store listings, strategy, privacy policy, F-Droid/IzzyOnDroid templates

- distribution/strategy.md: monetization strategy (free client + opencode Cloud)
- distribution/play-listing.md: Google Play store copy (name, description, tags)
- distribution/app-store-listing.md: App Store listing copy
- distribution/privacy-policy.{md,html}: GDPR-compliant privacy policy
- distribution/PLAY_CONSOLE_SETUP.md: Play Console setup runbook
- distribution/ios-enrollment-runbook.md: Apple Developer Program enrollment steps
- distribution/SIGNING-KEY-FINGERPRINTS.md: keystore fingerprint for reproducible builds
- distribution/fdroid-submission/: F-Droid metadata template
- distribution/izzyondroid-submission/: IzzyOnDroid submission template
- distribution/whatsnew/: Play Store release notes (en-US)
- distribution/whatsnew-ios/: TestFlight release notes
- distribution/play-graphics/: Play Store screenshot placeholders
- distribution/app-store-graphics/: App Store screenshot placeholders

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(telemetry): handle SecureStore failure + Android back button

- add .catch() on loadTelemetryConsent() so SecureStore rejection
  shows the consent modal instead of blocking startup forever
- add onRequestClose={onDecline} to Modal so Android back button
  records the decline rather than silently dismissing
- fix catch block in telemetry.ts to not clobber _resolved when
  SecureStore read fails mid-session

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ci): run gradlew clean to prevent stale modules.json duplicate

Sentry Gradle plugin writes modules.json to src/main/assets; cached
build intermediates contain an old copy → mergeReleaseAssets fails
with 'Duplicate resources'. Running clean before assembleRelease
clears the intermediate state.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ci): remove android build output cache causing duplicate modules.json

Caching android/app/build/intermediates and android/app/.cxx causes
two issues:
1. Stale modules.json in intermediates → Duplicate resources error
2. .cxx CMake artifacts reference absolute paths → ninja clean fails

Keeping only Gradle distribution cache (~/.gradle) which is safe.
Expo prebuild regenerates android sources fresh each run anyway.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 17:42:03 -07:00
Den
30be6636c2 feat(diagnostics): active connect-failure triage + Sentry + in-app share (#2)
* feat(diagnostics): active connect-failure triage + Sentry + in-app share

Replaces the opaque "Connection Failed" / "Network request failed" dead-end
with on-device diagnostics that classify *why* a connect attempt failed.

On failure (quick connect and edit-connection test), the app now:
- runs parallel probes: target /global/health, target root, and a public
  204 endpoint (internet reachability check)
- classifies the cause: malformed-url, no-internet, server-unreachable,
  health-failed, tls-error, timeout
- shows a plain-English summary + a "Share report" button that copies a
  full report (target URL, per-probe results w/ error.cause, device/app
  info, recent log ring-buffer) to the clipboard and opens the share sheet
- captures the same structured context to Sentry (auto-upload), gated on
  EXPO_PUBLIC_SENTRY_DSN so dev/CI builds work without secrets

New: src/lib/logbuffer.ts (ring buffer + logger), src/lib/diagnostics.ts
(regex URL parse — Hermes URL is incomplete — probe + report + share),
src/lib/sentry.ts (no-op-without-DSN wrapper, scrubs basic-auth from URLs).

Wired Sentry.wrap around RootLayout and initSentry() at module load.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* ci(sentry): wire Sentry DSN + source-map upload env into build; bump to 0.2.2

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 17:07:44 -07:00
Den
e9991cb61b fix(connect): fix tailnet 'Connection Failed' (pasted-URL double scheme) + surface real error (#1)
* fix(connect): surface real connection error instead of generic dialog

testConnection swallowed the actual fetch error and returned a bare
boolean, so every failure collapsed to the same "Connection Failed"
text. On-device this made tailnet/LAN connect failures impossible to
diagnose (DNS vs timeout vs 401 vs cleartext all looked identical).

- testConnection now returns { ok, error } with the real error message
- add.tsx and [id].tsx dialogs show the error + target URL, plus a
  Tailscale/MagicDNS hint
- IP field keyboard: decimal-pad -> url, so tailnet hostnames can be
  typed (not just pasted)

Verified backend is healthy and reachable over tailnet (health 200,
port 4096 open in packet filter, cleartext present in shipped v0.2.0
APK), so the failure is client-side and was previously unobservable.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(connect): normalize pasted IP/URL to avoid double scheme

Quick-connect blindly did `http://${ip}:${port}`. Pasting a full URL
(e.g. "http://100.108.64.76:4096", as the clipboard auto-paste offers)
produced "http://http://100.108.64.76:4096:4096" -> malformed URL ->
"Network request failed". This is the real tailnet connect failure:
typing a bare IP worked, pasting the displayed URL did not.

buildUrl now strips an existing http(s) scheme, drops any path, and
lifts a trailing :port out of the host field, so pasted full URLs,
host:port, and bare hosts all resolve to a single well-formed URL.

Reproduced and fixed on the Android emulator (paste full URL: fails
before, connects after).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 04:44:13 -07:00
Ubuntu
1843a7c37e Initial commit: OpenCode mobile app (extracted from opencode monorepo) 2026-05-17 19:52:54 +00:00