Skip to content

feat: first-run setup, Bluetooth permission banner, Connect Garmin button - #2

Merged
rlfleming93 merged 2 commits into
mainfrom
feat/onboarding
Oct 19, 2026
Merged

rlfleming93 merged 2 commits into
mainfrom
feat/onboarding

Conversation

@rlfleming93

Copy link
Copy Markdown
Owner

What

  • First-run setup (/welcome): a fresh install with no profiles lands here. Steps: Bluetooth state → rider (FTP hint, kg/lb) → trainer (pick, live watts + cadence, name saved on Next) → heart rate (skippable, live bpm, Garmin pairing hint) → summary with Connect Garmin and "Start your first ride".
  • Bluetooth never blocks startup. BleManager tracks the noble adapter state and scans whenever it turns poweredOn (also later, after permission is granted). No more 30 s timeout exit and misleading "port 4000" alert. GET /api/ble + adapterState WS frame (replayed on connect). App-wide banner for unauthorized / poweredOff with a System Settings link.
  • Live device readings outside a session: throttled deviceSample WS frames (≤1/s per device).
  • Connect Garmin in the UI: POST/GET /api/garmin/login/:riderId runs the headed login in-process, installs Playwright Chromium first when missing, 409 on duplicate, 404 unknown rider. scripts/garmin-login.ts is a thin caller.

Evidence

  • pnpm check clean; vitest 634/634 (new manager test: unauthorized does not reject, later poweredOn scans once).
  • Sim server with an empty data dir: / → /welcome; full wizard created rider (115 lb → 52.2 kg), assigned + labelled trainer and strap; live deviceSample frames observed (trainer cadence, strap bpm) with no session.
  • Real BLE start from a terminal: server listens immediately, /api/ble = poweredOn; this Mac's terminal already has Bluetooth, so the denied path was checked by injecting an adapterState: unauthorized frame (banner + settings link render).
  • Garmin routes: 404 unknown rider; GET returns {hasLogin:false,state:"idle"}. A full headed login was not run in the smoke (it opens a real sign-in window).

@rlfleming93

rlfleming93 commented Oct 19, 2026 •

Copy link
Copy Markdown
Owner Author

Review gate: PASS

  • Head: 4d9f04508db8ddd880f2c31ba7e8aa241e14b9a7
  • Reviewer: opus / claude-opus-5-5 (claude)
  • Rounds used: 2/2
ID Severity Status Location Finding Proof
— — — — None —
MINOR findings (4)
  • F1 apps/web/src/components/GarminConnect.tsx:73 — Riders with a saved Garmin login cannot reconnect from the UI, and a failed re-login stays hidden
  • F2 apps/web/src/pages/WelcomePage.tsx:358 — Going Back to the trainer step resets a custom device name, and Next then overwrites it
  • F3 apps/server/src/devices/hub.ts:53 — A failed BLE scan start is swallowed with no log
  • F4 apps/web/src/pages/WelcomePage.tsx:359 — Back within one 2 s device poll prefills the advertised name and Next overwrites the saved label

@rlfleming93
rlfleming93 merged commit f4421e3 into main Oct 19, 2026
1 check passed
@rlfleming93

Copy link
Copy Markdown
Owner Author

Shipped 2026-10-19: #1 (ffd7a58), #2 (f4421e3) and docs #3 (c21f867) are on main, and the live Mac app runs main.

What changed for riders:

  • Ride screen: cadence dial (80–100 rpm band) and torque per pedal stroke in Nm.
  • Profiles own their trainer and strap. Session fills both in, with live status.
  • A trainer dropout pauses the ride and resumes it; no 0 W rows are recorded.
  • NP uses the standard 30 s method. FIT files carry NP, IF, TSS, total work and average cadence.
  • New installs open a setup page. A Bluetooth permission banner replaces the startup crash.
  • Connect Garmin is a button on each profile.

Behavior changes for existing data:

  • NP values for new rides read slightly lower than before. Example: 230 → 227 W on the gap fixture.
  • deviceStatus and adapterState are replayed to each new socket.

Open: one MINOR from the delta review (Back within 2 s of Next on a setup device step can reset a custom name).

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.

1 participant