diff --git a/lib/feature/delivery/bloc/phase_bloc.dart b/lib/feature/delivery/bloc/phase_bloc.dart index f01de47..7f83d26 100644 --- a/lib/feature/delivery/bloc/phase_bloc.dart +++ b/lib/feature/delivery/bloc/phase_bloc.dart @@ -43,6 +43,13 @@ class PhaseBloc extends Bloc { /// Phasen an die aktuelle Tour-Version. final TourTokenResolver? tourTokenResolver; + /// Merkt sich pro Fahrzeug, unter welchem Tour-Token der In-Memory-Stand + /// geladen wurde. Ändert sich der Token (Tageswechsel im Kopf-Kalender, + /// neuer Sync), lädt der nächste [PhaseLoadForCar] den persistierten Stand + /// der **neuen** Tour nach, statt den alten Tag weiterzuzeigen. Der alte + /// Tag bleibt persistiert und wird bei Rückkehr wiederhergestellt. + final Map _loadedTokenByCar = {}; + PhaseBloc({ PhaseService? phaseService, this.carCountResolver, @@ -77,11 +84,18 @@ class PhaseBloc extends Bloc { Future _load(PhaseLoadForCar event, Emitter emit) async { final current = _ensureReady(); - // Wenn bereits geladen, nichts tun — der Stepper-Tap entscheidet aktiv. - if (current.phaseByCar.containsKey(event.carId)) return; + final token = _token(); + // Bereits für GENAU diese Tour-Version geladen → nichts tun (der + // Stepper-Tap entscheidet aktiv). Ein anderer Token (Tageswechsel, neuer + // Sync) lädt den persistierten Stand der neuen Tour nach: ein neuer Tag + // startet so wieder in der Eintrittsphase (Beladen/Ausliefern zurück), + // der vorherige Tag behält seinen Stand für die Rückkehr. + if (current.phaseByCar.containsKey(event.carId) && + _loadedTokenByCar[event.carId] == token) { + return; + } try { - final token = _token(); final persisted = await phaseService.load(event.carId, token); final persistedMax = await phaseService.loadMax(event.carId, token); final phase = persisted ?? _entryPhase(); @@ -102,6 +116,7 @@ class PhaseBloc extends Bloc { await phaseService.saveMax(event.carId, token, maxPhase); } + _loadedTokenByCar[event.carId] = token; add(PhaseLoaded( carId: event.carId, phase: phase, @@ -111,6 +126,7 @@ class PhaseBloc extends Bloc { debugPrint("PhaseBloc._load: $e $st"); // Fail-soft: ohne Persistenz weiter, damit der Flow nicht hängen bleibt. final fallback = _entryPhase(); + _loadedTokenByCar[event.carId] = token; add(PhaseLoaded( carId: event.carId, phase: fallback, diff --git a/lib/feature/delivery/bloc/tour_state.dart b/lib/feature/delivery/bloc/tour_state.dart index 3be006d..c870ff1 100644 --- a/lib/feature/delivery/bloc/tour_state.dart +++ b/lib/feature/delivery/bloc/tour_state.dart @@ -93,3 +93,22 @@ const Object _sentinel = Object(); class TourEmpty extends TourState { const TourEmpty(); } + +/// Token, der die aktuell geladene **Tour-Version** identifiziert (aus +/// `Tour.syncedAt`). Der `PhaseBloc` bindet die persistierten Phasen-Häkchen +/// daran: ein Tageswechsel im Kopf-Kalender oder ein neuer Sync ergibt einen +/// neuen Token und damit einen eigenen, getrennten Phasen-Stand. `null` = +/// keine Tour geladen (z. B. `TourEmpty`). +/// +/// **Einzige Quelle** für diese Ableitung — wird vom `tourTokenResolver` in +/// `app.dart` UND von `Home._ensurePhaseLoaded` genutzt, damit beide Seiten +/// garantiert denselben Token sehen und ein Tour-Wechsel zuverlässig als +/// solcher erkannt wird. +extension TourStatePhaseToken on TourState { + String? get phaseToken { + final s = this; + return s is TourLoaded + ? s.details.tour.syncedAt.millisecondsSinceEpoch.toString() + : null; + } +} diff --git a/lib/widget/app.dart b/lib/widget/app.dart index c27ce3e..a2db61a 100644 --- a/lib/widget/app.dart +++ b/lib/widget/app.dart @@ -104,14 +104,10 @@ class _DeliveryAppState extends State { // Tour-Version (Tour.syncedAt). Ein erneuter Sync/Seed // schreibt eine neue syncedAt → neuer Token → frische // Phasen, ohne dass alte lokale Häkchen hängen bleiben. - tourTokenResolver: () { - final tourState = context.read().state; - return tourState is TourLoaded - ? tourState.details.tour.syncedAt - .millisecondsSinceEpoch - .toString() - : null; - }, + // Ableitung zentral in `TourStatePhaseToken` (tour_state.dart), + // dieselbe Quelle wie `Home._ensurePhaseLoaded`. + tourTokenResolver: () => + context.read().state.phaseToken, ), ), BlocProvider( diff --git a/lib/widget/home/presentation/home.dart b/lib/widget/home/presentation/home.dart index b552b67..0161236 100644 --- a/lib/widget/home/presentation/home.dart +++ b/lib/widget/home/presentation/home.dart @@ -34,7 +34,12 @@ class Home extends StatefulWidget { } class _HomeState extends State { - String? _initializedCarId; + /// Key `carId|tourToken`, für den der [PhaseBloc] zuletzt zum Laden + /// angestoßen wurde. Ändert sich das Auto ODER die Tour-Version + /// (Tageswechsel im Kopf-Kalender, neuer Sync), wird erneut geladen — der + /// Bloc holt dann den persistierten Stand der **neuen** Tour, sodass ein + /// neuer Tag nicht die Beladen-/Ausliefern-Häkchen des alten Tags erbt. + String? _initializedPhaseKey; /// Merkt, für welches Auto die automatische Lieferungs-Zuordnung bereits /// Auto, für das zuletzt automatisch zugewiesen wurde (Ein-Auto-Teams). @@ -75,18 +80,22 @@ class _HomeState extends State { } /// Stellt sicher, dass für das aktuell gewählte Auto eine Phase im - /// [PhaseBloc] existiert. Wird im build() reaktiv aufgerufen, daher mit - /// `_initializedCarId` gegen mehrfache Loads gesichert. + /// [PhaseBloc] existiert — und zwar für die **aktuell geladene Tour**. + /// Wird im build() reaktiv aufgerufen, daher per [_initializedPhaseKey] + /// (`carId|tourToken`) gegen mehrfache Loads gesichert. Der Token kommt aus + /// [TourStatePhaseToken.phaseToken] (derselbe wie im `tourTokenResolver`); + /// `null` (keine Tour) zählt dabei als eigener Zustand. /// /// Wichtig: Wir feuern den Load erst, sobald sowohl Tour als auch Cars /// einen geladen-Zustand haben — sonst fragt der `PhaseBloc` den /// `carCountResolver` ab, bekommt `null` und entscheidet für Mehr-Auto- /// Teams fälschlich auf `sortieren` statt `auswaehlen`. void _ensurePhaseLoaded(String carId) { - if (_initializedCarId == carId) return; + final key = '$carId|${context.read().state.phaseToken}'; + if (_initializedPhaseKey == key) return; final carsState = context.read().state; if (carsState is! CarsLoaded) return; - _initializedCarId = carId; + _initializedPhaseKey = key; context.read().add(PhaseLoadForCar(carId: carId)); }