V3-06: live stats in the notification

RideNotificationController seam over flutter_local_notifications; RideNotificationCoordinator wires TripRepository.watchActiveTrip() to it and routes Pause/Resume actions back into RecordingEngine. Instantiated eagerly at app root rather than screen-owned, since a pocketed ride has no visible widget tree. Documents an unresolved risk: geolocator's own foreground-service notification cannot be suppressed, so two notifications may be visible until verified on a device.
This commit is contained in:
2026-08-17 15:04:34 -05:00
parent fbdb55258c
commit 5013b7002f
11 changed files with 471 additions and 13 deletions

View File

@@ -14,6 +14,8 @@ import '../config/config.dart';
import '../data/database.dart';
import '../data/trip_repository.dart';
import '../domain/models.dart';
import '../notification/ride_notification_controller.dart';
import '../notification/ride_notification_coordinator.dart';
import '../recording/geolocator_location_source.dart';
import '../recording/location_source.dart';
import '../recording/recording_engine.dart';
@@ -122,6 +124,25 @@ final wakelockControllerProvider = Provider<WakelockController>(
(ref) => PlusWakelockController(),
);
/// Overridden in tests with [FakeRideNotificationController].
final rideNotificationControllerProvider = Provider<RideNotificationController>(
(ref) => PlusRideNotificationController(),
);
/// V3-06: eager, not screen-owned -- watched once from app root ([main.dart]) purely to
/// force creation, so it keeps running (and the notification keeps updating, and
/// Pause/Resume keeps working) regardless of which screen is on top or whether the app is
/// backgrounded at all.
final rideNotificationCoordinatorProvider = Provider<RideNotificationCoordinator>((ref) {
final coordinator = RideNotificationCoordinator(
controller: ref.watch(rideNotificationControllerProvider),
engine: ref.watch(recordingEngineProvider),
tripStream: ref.watch(tripRepositoryProvider).watchActiveTrip(),
);
ref.onDispose(coordinator.dispose);
return coordinator;
});
/// Live path for the recording screen's map (V3-04). `.family` + `autoDispose` so the
/// stream tears down the moment the record screen stops watching it — no leftover
/// subscription ticking away once a ride ends or the map toggle goes off.