Parent
What to build
The Favorites tracer bullet: star a whole past ride and see it in a new Favorites tab. A Favorite is a durable time range [startMs, endMs] + optional name in a new native table in the telemetry DB (both platforms, @parity), never a reference to a derived session id (ADR 0029). Create the table with a native-minted stable UUID id, created_at, and updated_at, plus the range, name, and summary fields. Summary stats (mirroring history session summary fields: distance, moving duration, avg/max speed, battery used) are computed once at creation from raw samples in the range — minute buckets are too coarse — and denormalized onto the row (ADR 0005 style). Route preview derives on read from existing history range reads; no stored polyline.
Bridge API: list favorites, create favorite from range with optional name, delete favorite (unpin only — telemetry untouched in this slice). JS: favorites store in the history module; history screen title replaced by a PillSelector ↻ History | ⭐ Favorites; Favorites tab lists rows with stats + optional name (unnamed rows show date + stats like history rows); star action on an open history ride creates a full-Moving-Window favorite. Trim UI, pinning, detail sheet, media are follow-up slices.
Likely files
modules/vescape-core/ios/telemetry/TelemetryDao.swift - favorites table CRUD lives beside telemetry DAO
modules/vescape-core/ios/telemetry/TelemetryDatabase.swift - schema migration for the new table
modules/vescape-core/android/src/main/java/expo/modules/vescapecore/telemetry/TelemetryDao.kt - Android peer DAO
modules/vescape-core/android/src/main/java/expo/modules/vescapecore/telemetry/TelemetryRepository.kt - repository layer for summary calc + API
src/modules/history/store/historyStore.ts - pattern for bridge-backed history store; add sibling favoriteStore
src/modules/history/lib/sessions.ts - session grouping + summary field shapes to mirror
src/components/controls/PillSelector.tsx - the tab control for History | Favorites
src/modules/history/components/HistorySessionSheet.tsx - host for the star action
Implementation hints
- Follow the existing telemetry DAO + migration test patterns on both platforms (iOS has
TelemetryMigrationTests.swift in flight).
- Mint identity natively and stamp timestamps from the native clock. JS inputs cannot fabricate identity or timestamps.
- Mirror the
TelemetrySummary field set for denormalized stats rather than inventing new names.
- TS types crossing the bridge need
@parity lines to both native peers per AGENTS.md.
- New PillSelector usage on the history screen should get a showcase preview under
src/app/settings/components/ if a new visual state is introduced.
Acceptance criteria
Blocked by
None - can start immediately
Related
Parent
What to build
The Favorites tracer bullet: star a whole past ride and see it in a new Favorites tab. A Favorite is a durable time range
[startMs, endMs]+ optional name in a new native table in the telemetry DB (both platforms,@parity), never a reference to a derived session id (ADR 0029). Create the table with a native-minted stable UUIDid,created_at, andupdated_at, plus the range, name, and summary fields. Summary stats (mirroring history session summary fields: distance, moving duration, avg/max speed, battery used) are computed once at creation from raw samples in the range — minute buckets are too coarse — and denormalized onto the row (ADR 0005 style). Route preview derives on read from existing history range reads; no stored polyline.Bridge API: list favorites, create favorite from range with optional name, delete favorite (unpin only — telemetry untouched in this slice). JS: favorites store in the history module; history screen title replaced by a PillSelector
↻ History | ⭐ Favorites; Favorites tab lists rows with stats + optional name (unnamed rows show date + stats like history rows); star action on an open history ride creates a full-Moving-Window favorite. Trim UI, pinning, detail sheet, media are follow-up slices.Likely files
modules/vescape-core/ios/telemetry/TelemetryDao.swift- favorites table CRUD lives beside telemetry DAOmodules/vescape-core/ios/telemetry/TelemetryDatabase.swift- schema migration for the new tablemodules/vescape-core/android/src/main/java/expo/modules/vescapecore/telemetry/TelemetryDao.kt- Android peer DAOmodules/vescape-core/android/src/main/java/expo/modules/vescapecore/telemetry/TelemetryRepository.kt- repository layer for summary calc + APIsrc/modules/history/store/historyStore.ts- pattern for bridge-backed history store; add sibling favoriteStoresrc/modules/history/lib/sessions.ts- session grouping + summary field shapes to mirrorsrc/components/controls/PillSelector.tsx- the tab control for History | Favoritessrc/modules/history/components/HistorySessionSheet.tsx- host for the star actionImplementation hints
TelemetryMigrationTests.swiftin flight).TelemetrySummaryfield set for denormalized stats rather than inventing new names.@paritylines to both native peers per AGENTS.md.src/app/settings/components/if a new visual state is introduced.Acceptance criteria
@paritytagged, with DAO round-trip testscreated_atandupdated_at↻ History | ⭐ Favoritespill toggleBlocked by
None - can start immediately
Related