9.9 KiB
Tasks: Map View
Input: Reverse-engineered from existing feature/map/ module
Prerequisites: plan.md (required), spec.md (required)
Tests: Included — existing tests migrated; gap tasks added for missing coverage.
Organization: Tasks grouped by implementation phase. All existing work marked [x]; identified gaps marked [ ].
Status: Migrated — all [x] tasks reflect code that already exists in the codebase.
Format: [MAP-TXXX] [P?] [Story?] Description
- [P]: Can run in parallel (different files, no dependencies)
- [Story]: Which user story this task belongs to (e.g., US1, US2, US3, US4, US5)
- Include exact file paths in descriptions
Path Conventions
- KMP commonMain:
feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/ - KMP commonTest:
feature/map/src/commonTest/kotlin/org/meshtastic/feature/map/ - Android source:
feature/map/src/androidMain/kotlin/org/meshtastic/feature/map/ - Android tests:
feature/map/src/androidUnitTestGoogle/kotlin/org/meshtastic/feature/map/ - Core deps:
core/repository/,core/model/,core/ui/
Phase 1: Core ViewModel & Models
Purpose: Shared business logic for node data, waypoints, filters, traceroute resolution, and map layer models.
- MAP-T001 [P] [US1] Create
BaseMapViewModelinfeature/map/src/commonMain/.../BaseMapViewModel.kt— shared ViewModel exposingnodes,nodesWithPosition,myNodeInfo,ourNodeInfo,isConnectedflows fromNodeRepositoryandRadioController. Filter ignored nodes from thenodesflow. (FR-001, FR-002, FR-003) - MAP-T002 [P] [US2] Implement
LastHeardFilterenum inBaseMapViewModel.ktwith entriesAny(0s),OneHour(3600s),EightHours(28800s),OneDay(86400s),TwoDays(172800s). IncludefromSeconds()companion factory defaulting toAnyfor unknown values. WirelastHeardFilterandlastHeardTrackFilterstate flows withMapPrefspersistence. (FR-004, FR-005) - MAP-T003 [P] [US3] Implement waypoint data flow in
BaseMapViewModel—waypoints: StateFlow<Map<Int, DataPacket>>fromPacketRepository.getWaypoints(), filtering expired waypoints usingnowSeconds. ImplementdeleteWaypoint(id)andsendWaypoint(wpt, contactKey)methods usingsafeLaunchwithioDispatcher. (FR-006, FR-007, FR-008) - MAP-T004 [P] [US5] Implement map filter toggles in
BaseMapViewModel—showOnlyFavorites,showWaypointsOnMap,showPrecisionCircleOnMapasStateFlow<Boolean>backed byMapPrefs. Combine all intoMapFilterStatedata class viamapFilterStateFlow. (FR-005) - MAP-T005 [P] [US4] Implement
TracerouteNodeSelectiondata class andtracerouteNodeSelection()top-level function inBaseMapViewModel.kt— resolve overlay node nums to displayableNodeinstances, prioritizing snapshot positions over live positions. Include convenience extension forBaseMapViewModel. (FR-009) - MAP-T006 [P] [US1] Create
SharedMapViewModelinfeature/map/src/commonMain/.../SharedMapViewModel.kt— Koin-injectable@KoinViewModelextendingBaseMapViewModelwith pass-through constructor. (FR-001) - MAP-T007 [P] [US1,US5] Create
MapLayerItemdata class andLayerTypeenum infeature/map/src/commonMain/.../model/MapLayer.kt— support KML and GeoJSON layer types with UUID-based IDs, visibility toggle, network flag, and refresh state. (FR-012)
Dependencies: None — all tasks are independent.
Checkpoint: Core data layer complete. All flows, filters, and models ready for UI consumption.
Phase 2: UI Components
Purpose: Map controls overlay and reusable button composables.
- MAP-T008 [P] [US5] Create
MapButtoncomposable infeature/map/src/commonMain/.../component/MapButton.kt—FilledIconButtonwrapper acceptingImageVector,contentDescription,onClick, optionaliconTint. UsesIconButtonDefaults.filledIconButtonColors(). (NFR-001, NFR-005) - MAP-T009 [US5] Create
MapControlsOverlaycomposable infeature/map/src/commonMain/.../component/MapControlsOverlay.kt—HorizontalFloatingToolbar(M3 Expressive) containing compass button, filter button with dropdown slot, map type slot, layers slot, optional refresh button withCircularProgressIndicator, and location tracking toggle. (FR-010, FR-011, NFR-001) - MAP-T010 [US5] Implement
CompassButtonprivate composable withinMapControlsOverlay.kt— rotates icon by-bearingdegrees, usesStatusRedwhen north-aligned,primarywhen following phone bearing. (FR-010) - MAP-T011 [US1] Create
MapScreencomposable infeature/map/src/androidMain/.../MapScreen.kt—ScaffoldwithMainAppBarshowing connected node chip, delegating map content toLocalMapViewProvider.current?.MapView(). (FR-014, NFR-002) - MAP-T012 [P] [US1] Create
NodeMapViewModelinfeature/map/src/commonMain/.../node/NodeMapViewModel.kt— per-node map ViewModel withdestNumfromSavedStateHandle,nodeflow fromNodeRepository,positionLogsflow fromMeshLogRepositorywith time/coordinate deduplication. (FR-013)
Dependencies: Phase 1 (MAP-T001–MAP-T007) must complete first.
Checkpoint: All UI components and ViewModels implemented.
Phase 3: Navigation & DI
Purpose: Wire feature into app navigation graph and dependency injection.
- MAP-T013 [US1] Create
MapNavigation.mapGraph()infeature/map/src/commonMain/.../navigation/MapNavigation.kt— registerMapRoute.Mapentry using Navigation 3EntryProviderScope, resolve map screen viaLocalMapMainScreenProvider, navigate toNodesRoute.NodeDetailon node tap. (FR-014) - MAP-T014 [P] Create
FeatureMapModuleinfeature/map/src/commonMain/.../di/FeatureMapModule.kt— Koin@Modulewith@ComponentScan("org.meshtastic.feature.map")for automatic ViewModel discovery.
Dependencies: Phase 2 (MAP-T011, MAP-T012) must complete first.
Checkpoint: Feature fully wired into app navigation and DI.
Phase 4: Testing
Purpose: Unit tests for ViewModels, models, and business logic. Includes existing tests and identified gaps.
- MAP-T015 [P] [US1] Create
BaseMapViewModelTestinfeature/map/src/commonTest/.../BaseMapViewModelTest.kt— test initialization,myNodeInfoflow (starts null),nodesWithPositionflow (starts empty),isConnectedflow (tracksConnectionStatechanges), node repository integration. Uses Mokkery mocks forMapPrefsandPacketRepository,FakeNodeRepositoryandFakeRadioControllerfor fakes. (SC-001) - MAP-T016 [P] [US2] Create
LastHeardFilterTestinfeature/map/src/commonTest/.../LastHeardFilterTest.kt— testfromSeconds()with all known values (0, 3600, 28800, 86400, 172800), unknown values (9999, -1, Long.MAX_VALUE default toAny), andsecondsproperty round-trip. (SC-005) - MAP-T017 [P] [US4] Create
TracerouteNodeSelectionTestinfeature/map/src/commonTest/.../TracerouteNodeSelectionTest.kt— 8 test cases: null overlay returns all nodes, node lookup filters to valid positions, overlay with snapshot uses snapshot coordinates, snapshot node lookup, snapshot filters to overlay nodes, overlay without snapshot falls back to live nodes, empty overlay routes yield empty selection, getNodeOrFallback invocation verification. (SC-004) - MAP-T018 [P] [US5] Create
MapLayerTestinfeature/map/src/commonTest/.../model/MapLayerTest.kt— testMapLayerItemdefault values (auto-generated ID, null URI, visible=true, isNetwork=false, isRefreshing=false). (SC-008) - MAP-T019 [P] [US4] Create
TracerouteOverlayTestinfeature/map/src/commonTest/.../model/TracerouteOverlayTest.kt— test empty routes (relatedNodeNumsempty,hasRoutesfalse) and populated routes (relatedNodeNumsunion,hasRoutestrue). - MAP-T020 [P] [US5] Create
MapViewModelTestinfeature/map/src/androidUnitTestGoogle/.../MapViewModelTest.kt— Google-flavor tests:getTileProviderreturnsUrlTileProviderfor remote config,addNetworkMapLayerdetects GeoJSON by extension, KML default for other extensions,setWaypointIdupdates and clears value. Uses Robolectric. (SC-008) - MAP-T021 [P] [US5] Create
MBTilesProviderTestinfeature/map/src/androidUnitTestGoogle/.../MBTilesProviderTest.kt— test TMS y-coordinate translation (y_tms = (1 << zoom) - 1 - y_google), tile retrieval from SQLite database. Uses Robolectric withTemporaryFolder.
Dependencies: Phase 1–3 must complete first (tests exercise the full feature).
Checkpoint: All existing tests passing. Gaps identified below.
Phase 5: Gap Tasks (Not Yet Implemented) ⚠️
Purpose: Address identified coverage gaps in the existing implementation.
- MAP-T022 [US3] [GAP] Add unit tests for waypoint expiration filtering logic in
BaseMapViewModel— test that waypoints withexpire > nowSecondsare included,expire <= nowSecondsare excluded, andexpire == 0(never expires) are always included. File:feature/map/src/commonTest/.../BaseMapViewModelTest.kt. (SC-003) - [DEFERRED] MAP-T023 [US1,US5] [GAP] Add Compose UI tests for
MapControlsOverlayandMapButtoncomposables — verify compass rotation, filter button click, location tracking toggle icon switch, refresh spinner visibility. File:feature/map/src/commonTest/.../component/MapControlsOverlayTest.kt. (NFR-001) — Deferred: requires Compose UI test infrastructure.
Dependencies: Phase 4 testing infrastructure.
Checkpoint: Full test coverage achieved.
Summary
| Phase | Tasks | Status |
|---|---|---|
| Phase 1: Core ViewModel & Models | MAP-T001–MAP-T007 (7 tasks) | ✅ All complete |
| Phase 2: UI Components | MAP-T008–MAP-T012 (5 tasks) | ✅ All complete |
| Phase 3: Navigation & DI | MAP-T013–MAP-T014 (2 tasks) | ✅ All complete |
| Phase 4: Testing | MAP-T015–MAP-T021 (7 tasks) | ✅ All complete |
| Phase 5: Gap Tasks | MAP-T022–MAP-T023 (2 tasks) | ⚠️ Not started |
| Total | 23 tasks | 21/23 complete |