Commit Graph

11 Commits

Author SHA1 Message Date
Davide Scaini a5c2810568 fix(mobile): gate watch directory on instance URL; skip auto-scan if unconfigured
Settings: watch directory field is hidden behind a warning if no instance
URL is saved yet, making the dependency explicit before the user sets a
path.

Import: runAutoScan silently skips (no errors) when instance URL is
missing; manualScan shows a single clear message instead of one failure
per file.
2026-04-25 22:20:02 +02:00
Davide Scaini 42d829737c fix(mobile): auto-save watch directory on blur, not via Save button
The Save button is anchored to the Instance section near the top. Moving
the auto_import_path write to onBlur on the field itself means the value
is persisted as soon as the user leaves the input — no need to scroll up
and hit Save.
2026-04-25 22:12:59 +02:00
Davide Scaini e062ef5837 fix(mobile): watch-folder button visibility + Karoo file picker crash
- useFocusEffect re-reads auto_import_path from DB every time the Import
  tab is focused, so the scan button appears immediately after saving in
  Settings (was broken by conditional hook call which violated Rules of
  Hooks and never re-fired on setting changes)
- Nested inner try/catch isolates DocumentPicker.getDocumentAsync so
  ActivityNotFoundException (Karoo has no DocumentsUI) shows a friendly
  message instead of crashing the tab
- Settings watch-directory placeholder updated to /sdcard/FitFiles
2026-04-25 22:08:32 +02:00
Davide Scaini 2e520137ac fix: palette changes now propagate immediately via ThemeContext
Replace useSetting()-based useTheme() with a React context (ThemeProvider
+ useTheme/usePaletteControl). The context holds palette key in state so
pressing a palette button in Settings re-renders all screens instantly.
Persists to SQLite and reloads the stored value on mount.
2026-04-25 15:45:27 +02:00
Davide Scaini dfe5307ab4 feat: seasonal race palette (Giro/Tour/Vuelta) + mobile picker
- theme.ts: useTheme() hook with race calendar (May–Sep windows),
  auto-detects Giro/Tour/Vuelta by date; stores override in SQLite
- All screens (feed, import, activity, tab bar) now use accent/dim
  from useTheme() instead of hardcoded #60a5fa
- Settings: Palette section with Auto/Default/Giro/Tour/Vuelta buttons
  to override the auto-detected palette for testing
2026-04-25 15:41:20 +02:00
Davide Scaini 5330b7b489 app icon 2026-04-25 15:33:51 +02:00
Davide Scaini c7c7fe9395 feat: bidirectional sync — upload local activities to remote instance
- Server: POST /api/upload/bas accepts pre-extracted BAS JSON (activity + optional timeseries/geojson), writes files and triggers merge_all
- sync.ts: uploadLocalActivities reads unsynced local activities by original_path, POSTs to /api/upload/bas, marks synced_at on success
- Settings: Upload toggle (Off / Upload local activities) in Sync section with subLabel dividers for Download / Upload groups
- Feed: sync message includes uploaded count when activities are pushed
2026-04-24 22:26:13 +02:00
Davide Scaini a306682b52 feat: add sync mode setting — summaries only vs full data download 2026-04-24 21:52:25 +02:00
Davide Scaini c1e4ab5af7 feat: add reset synced data button to settings 2026-04-24 15:45:51 +02:00
Davide Scaini 44b2878b14 feat: Phase 0.5 — remote feed sync via Bearer token auth
Server (bincio/serve/server.py):
- Add _require_auth: accepts session cookie OR Authorization: Bearer token
- POST /api/auth/token: same as /api/auth/login but returns token in body
  (password used once, not stored; mobile stores only the session token)
- GET /api/feed: auth-gated; reads _merged/index.json for the user and
  returns the activities array as JSON

Mobile:
- db/sync.ts: syncFeed(db) fetches /api/feed, upserts each summary into
  local SQLite as origin='remote'; skips locally-imported activities
- db/queries.ts: add upsertRemoteActivity (INSERT ... ON CONFLICT DO UPDATE
  WHERE origin='remote' — never overwrites local imports); fix feed sort
  order to started_at DESC instead of insertion order
- settings.tsx: Connect section — password field (not persisted) + Connect
  button calls POST /api/auth/token and stores token; Disconnect clears it
- index.tsx: ↓ Sync button + pull-to-refresh both trigger syncFeed; cloud
  badge on remote activities; empty state updated
2026-04-24 12:07:49 +02:00
Davide Scaini b37df88fe1 feat: Phase 0 mobile app scaffold — Expo 55, SQLite, Feed/Import/Settings screens 2026-04-24 10:39:06 +02:00