Commit Graph

27 Commits

Author SHA1 Message Date
Davide Scaini ccfc60934a fix: icon centering — use Center gravity with signed offsets
Previous script used NorthWest gravity with fixed pixel offsets, placing
the BR pair in the upper-left quadrant. When Android clips to a circle
the letters appeared off-center.

New approach: -gravity Center with ±HALF offsets so each letter is
symmetrically placed around the canvas centre mathematically. No
subshells, no pixel guesswork. Regenerated all four icon assets.
2026-06-04 01:04:02 +02:00
Davide Scaini 0d03f34371 feat: offline map download via MapLibre OfflineManager
New src/services/offline.ts:
- downloadRegion(): createPack with bounds, zoom 6-16, progress callback
- listRegions() / deleteRegion(): pack management
- expandBounds(): adds 5km buffer around the visible area
- formatBytes(): human-readable size string

RecordingScreen:
- MapRef attached to Map component to read getBounds()
- '↓ Offline' overlay button (Liberty style + idle state only)
- Modal: name input → download → progress bar with % and MB counter
- Raster styles show no download button (not supported by OfflineManager)

Settings → App → Offline maps:
- Lists all downloaded regions with size and tile count
- Delete with confirm alert
- Placeholder text when no regions exist
2026-06-04 00:57:23 +02:00
Davide Scaini 8cc2b07b1f feat: multiple map tile styles switchable in Settings > Interface
New src/mapStyles.ts defines five styles from bincio_planner's sources:
- Liberty (OpenFreeMap vector, default)
- CyclOSM (cycling infrastructure raster)
- Topo (OpenTopoMap elevation raster)
- Satellite (Esri World Imagery raster)
- OSM (standard raster fallback)

Raster sources are wrapped in a StyleSpecification so MapLibre handles
them natively. Setting persisted via ThemeContext (AsyncStorage key
mapTileStyle). RecordingScreen and ActivityDetailScreen both read from
context so the style updates everywhere simultaneously.

MAP_STRATEGY.md added (untracked) with full map roadmap.
2026-06-04 00:52:58 +02:00
Davide Scaini 7db54712fa feat: app icon, name fix, and icon generation script
- scripts/generate_icons.sh: ImageMagick script — dark #09090b bg,
  white B and red R (#ef4444) at the same 480pt size, NotoSans-Bold.
  Generates icon, adaptive foreground, monochrome, and splash variants.
- app.json: name changed to 'Bincio Rec'; adaptive icon backgroundColor
  updated to #09090b; removed redundant backgroundImage
2026-06-04 00:36:06 +02:00
Davide Scaini 7e78bcba2c docs: mention AGPL v3 license in README 2026-06-04 00:24:08 +02:00
Davide Scaini 38cf792d09 fix: edit modal respects Android safe area insets
Removed iOS-only presentationStyle='pageSheet'. Modal now applies
useSafeAreaInsets() padding so it doesn't overlap the status bar
or gesture navigation bar on Android.
2026-06-04 00:18:04 +02:00
Davide Scaini b3a0c33b9e Add AGPL v3 license 2026-06-04 00:17:22 +02:00
Davide Scaini dd4533efd2 feat: activity detail screen with map, stats, and inline editing
Tap any saved recording to open ActivityDetailScreen:
- Full-screen MapLibre map with track fitted to bounds (LngLatBounds padding)
- Stats panel: duration, distance, avg speed, elevation gain (computed
  from track points), avg HR/power/cadence, point count
- 'Edit' button in header opens a pageSheet modal with title TextInput,
  sport grid, and subtype pills — same controls as PostRecordingScreen
- updateRecording() added to db.ts; edits update header title and sport
  summary without navigating away

SavedRecordingsScreen: tapping a card in normal mode navigates to detail;
tapping in selection mode still toggles the checkbox.
2026-06-04 00:13:53 +02:00
Davide Scaini 41a2435cc2 feat: bulk operations in Saved tab (export, upload, delete, merge)
Select button in header enters selection mode. Cards show a checkbox and
become tappable. Header updates to show N selected + Cancel.

Bulk actions (bottom bar):
- Export: sequential Sharing.shareAsync for each selected file
- Upload: sequential upload with N/total progress text
- Delete: confirm then delete all selected at once
- Merge: parses each GPX file, combines track points sorted by time,
  prompts for title via Modal, saves as new recording

parseGpxFile() added to gpx.ts: reads file via expo-file-system,
extracts trkpt elements including hr/power/cad extensions via regex.
2026-06-04 00:08:06 +02:00
Davide Scaini cb74135c6c feat: activity type and subtype selection at save time
New src/sports.ts mirrors bincio_activity definitions exactly:
7 sports (cycling/running/hiking/walking/swimming/skiing/other),
10 subtypes grouped by sport, icons and labels matching format.ts.

PostRecordingScreen: sport grid with emoji pills, conditional subtype
row (only for sports that have subtypes), subtype deselectable by
tapping again. Defaults to cycling.

DB: sport + sub_sport columns added; ALTER TABLE migration for existing
installs (try/catch — column already exists is silently ignored).

SavedRecordingsScreen: sport emoji + label + subtype shown in each card.
2026-06-03 23:57:47 +02:00
Davide Scaini 116992b3be feat: configurable map orientation (Settings > App)
Three modes selectable from Settings > App > Map orientation:
- North up: map always points north (MapLibre 'default')
- Compass: rotates with device compass heading ('heading')
- Course up: rotates to direction of travel ('course')

Default is North up. Setting persisted in AsyncStorage via ThemeContext.
2026-06-03 23:45:05 +02:00
Davide Scaini 27e7f008f0 auth: replace password login with OIDC PKCE flow (Phase 5)
- 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
2026-06-03 16:12:15 +02:00
Davide Scaini 358f3f12c1 feat: themed user location dot on map
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.
2026-06-03 10:24:58 +02:00
Davide Scaini ec6a6facd1 fix: centre map on user location at startup
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).
2026-06-03 10:06:24 +02:00
Davide Scaini efc7af4a4a feat: ThemeContext + Settings tabs (Interface / App / Sync)
- 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()
2026-06-03 10:00:27 +02:00
Davide Scaini ea938e5644 design: align visual style with bincio_autarchive
- Add src/theme.ts with centralised color palette
- Backgrounds: #111 → #09090b, surfaces #1e1e1e → #18181b
- All cards get 1px #27272a borders (matches autarchive cards)
- Text: #fff/#888/#555 → #f4f4f5/#a1a1aa/#71717a
- Accent: #3b82f6 → #60a5fa (autarchive default palette)
- Tab icons: colored emoji → monochromatic Unicode (◉ ☰ ⚙)
- Action buttons use muted palette (#16a34a / #d97706 / #dc2626)
- Keep-awake toggle uses ◑/◌ symbols, border highlights accent on active
- Connect/Scan buttons match autarchive surface+border style
2026-06-03 09:50:05 +02:00
Davide Scaini 6e47ced264 feat: replace API token field with login flow
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.
2026-06-03 09:43:52 +02:00
Davide Scaini 9d82084fa1 feat: section 5 — MapLibre map with live track and camera follow
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
2026-06-03 09:32:49 +02:00
Davide Scaini 4e1c2ebef9 fix: battery optimization prompt — add missing manifest permission and fix fallback
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.
2026-06-03 09:18:03 +02:00
Davide Scaini 765efe288e feat: section 4 — Android battery optimization prompt
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.
2026-06-03 09:09:51 +02:00
Davide Scaini 5f12b2857d feat: section 3 — km milestone notifications
- 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
2026-06-03 09:03:55 +02:00
Davide Scaini 2378d31f0b feat: section 2 — BLE permissions, cadence parsing, sensor persistence
- requestBlePermissions() handles BLUETOOTH_SCAN + BLUETOOTH_CONNECT on Android 12+
- parseCscMeasurement() implements stateful CSC crank RPM with uint16 rollover
- savePairedDevice / loadPairedDevices / removePairedDevice via AsyncStorage
- SensorPairingScreen: saved sensors section, auto-reconnect on mount, Forget button
2026-06-03 09:00:21 +02:00
Davide Scaini 767c2d78aa feat: section 1 — keep-awake toggle, sensor button, GPS pause, track view
- 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
2026-06-03 00:13:35 +02:00
Davide Scaini 5fa8fa86f9 docs: add v1 completion plan to CLAUDE.md 2026-06-03 00:05:35 +02:00
Davide Scaini 879eaea3df fix: restore CLAUDE.md and protect it from expo prebuild --clean
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.
2026-06-03 00:02:50 +02:00
Davide Scaini 896b528a4c feat: scaffold Expo Prebuild project with all v1 screens and services
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>
2026-06-02 22:16:56 +02:00
Davide Scaini ee28cb0c30 Initial commit
Generated by create-expo-app 4.0.0.
2026-06-02 16:19:04 +02:00