- Install expo-auth-session + expo-web-browser
- Add 'bincio-rec' URL scheme to app.json for deep-link redirect
- auth.ts: generate PKCE verifier/challenge, open bincio.org/oauth2/authorize
in browser, exchange auth code for RS256 id_token, store in AsyncStorage
- SettingsScreen: remove handle/password fields, single 'Sign in with bincio'
button that opens the browser flow
Replace default MapLibre puck with a custom CircleLayer child:
accent-coloured fill, 2.5px white stroke, map-pitch-aligned.
Dot colour updates live when the palette changes in Settings.
Request foreground location permission on RecordingScreen mount so the
map can find the user immediately. Camera now always has trackUserLocation
set — 'default' when idle/paused (follows position, no rotation) and
'course' when recording (follows direction of travel).
- ThemeContext: dynamic palette (Default/Giro/Tour/Vuelta), font size
(small/medium/large), bold labels — all persisted to AsyncStorage
- Settings: three top tabs; Interface tab has palette picker + font
size pills + bold labels toggle; App tab has km notifications;
Sync tab has bincio instance login + autarchive placeholder
- RecordingScreen: stat labels now use theme accent colour and scale
with fontSize; font weight follows boldLabels setting
- All accent/accentDim usages migrated from static colors to useTheme()
New auth.ts service: login() POSTs to /api/auth/token with handle +
password, stores instanceUrl/handle/apiToken in AsyncStorage, password
never persisted. logout() clears all credentials. loadAuthState()
returns stored credentials or null.
Settings screen now shows a login form (URL + handle + password) when
not connected, and a connected state card with Disconnect button when
logged in. km notifications toggle auto-saves without a separate Save
button.
Replace SVG TrackView with a real MapLibre map:
- OpenFreeMap liberty tiles (no API key)
- Camera follows user in course mode while recording
- GeoJSONSource + LineLayer renders track polyline updated live
- UserLocation dot shows current GPS position
- Sensors button overlaid with semi-transparent background
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS intent is silently dropped without
the matching manifest permission. Added it to app.json permissions.
Also replaced .catch() chain (which only triggers on thrown errors) with
try/catch blocks so the fallback to IGNORE_BATTERY_OPTIMIZATION_SETTINGS
actually fires. Added resetBatteryOptPrompt() helper to re-trigger the
prompt during testing.
One-time prompt on first launch (Android only) directing the user to
exclude bincio-rec from battery optimization. Uses the direct
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS system dialog with a fallback to
the general settings page for OEMs that block the direct intent.
Dismissal persisted in AsyncStorage so prompt never repeats.
- Foreground notification handler in App.tsx (iOS shows banners while active)
- requestNotificationPermissions() called on app mount
- GPS task tracks running distance per recording session (module-level state)
- Fires immediate notification at each km crossed, gated on kmNotifications setting
- Keep-awake now conditional: activates only when recording + toggle on
- Sensor button overlaid on map area navigates to SensorPairing modal
- Pause/resume now start/stop the GPS background task, not just store state
- TrackView renders lat/lon polyline via react-native-svg (no tile server)
- react-native-svg added as dependency
prebuild --clean overwrote CLAUDE.md with an Expo-generated stub.
Restored the original project plan and added ignorePaths in app.json
so Expo skips CLAUDE.md on future prebuild runs.
Sets up the full bincio-rec source tree: Zustand recording store with
haversine stats, background GPS via expo-task-manager, BLE scan/subscribe
for HR and power, GPX writer with Garmin extensions, SQLite recordings
list, multipart upload to bincio-activity, React Navigation stack with
bottom tabs, and build instructions in README.md.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>