Final commit.
This commit is contained in:
@ -1,63 +1,95 @@
|
||||
import '../../../../model/tour.dart';
|
||||
import 'package:hl_lieferservice/domain/entity/tour_details.dart';
|
||||
|
||||
abstract class TourState {}
|
||||
/// Lifecycle-States des `TourBloc`.
|
||||
///
|
||||
/// Bewusst eine sealed-Hierarchie: das UI kann via `switch` exhaustiv
|
||||
/// alle Pfade abbilden und der Compiler meldet, wenn ein neuer Pfad
|
||||
/// dazukommt.
|
||||
sealed class TourState {
|
||||
const TourState();
|
||||
}
|
||||
|
||||
class TourInitial extends TourState {}
|
||||
/// App-Start, bevor irgendetwas geladen wurde.
|
||||
class TourInitial extends TourState {
|
||||
const TourInitial();
|
||||
}
|
||||
|
||||
class TourLoading extends TourState {}
|
||||
/// Initial-Load läuft. Wird nur emittiert, wenn vorher kein Tour-State da
|
||||
/// war — für Refresh siehe `TourLoaded.isRefreshing`.
|
||||
class TourLoading extends TourState {
|
||||
const TourLoading();
|
||||
}
|
||||
|
||||
class TourLoadingFailed extends TourState {}
|
||||
/// Initial-Load ist gescheitert. Refresh-Fehler hingegen werden in
|
||||
/// `TourLoaded.refreshError` getragen, damit die alte Tour sichtbar bleibt.
|
||||
class TourLoadFailed extends TourState {
|
||||
const TourLoadFailed({required this.message});
|
||||
|
||||
final String message;
|
||||
}
|
||||
|
||||
/// Erfolgreich geladen — beinhaltet das volle Tour-Aggregat sowie
|
||||
/// UI-relevante Zusatzflags rund um Reorder- und Refresh-Operationen.
|
||||
class TourLoaded extends TourState {
|
||||
Tour tour;
|
||||
Map<String, double>? distances;
|
||||
List<Payment> paymentOptions;
|
||||
Map<String, List<String>> sortingInformation;
|
||||
|
||||
/// Number of scan-related server requests currently in flight. Drives the
|
||||
/// inline indicator on the scanner widget. Using a counter (not bool) lets
|
||||
/// rapid-fire scans coexist without one prematurely clearing the indicator.
|
||||
int pendingScanRequests;
|
||||
|
||||
/// True während der Backend-Call zur Persistierung der Sortier-Reihenfolge
|
||||
/// läuft. Wird vom Bestätigungs-Button in der Sortier-Page für Spinner
|
||||
/// und Button-Disabled-State ausgewertet.
|
||||
bool isPersistingSorting;
|
||||
|
||||
/// Letzte Fehlermeldung des Sortier-Persist-Calls. Wird nach Anzeige
|
||||
/// durch das UI ge-leert (z. B. nach SnackBar).
|
||||
String? sortingPersistError;
|
||||
|
||||
TourLoaded({
|
||||
required this.tour,
|
||||
this.distances,
|
||||
required this.paymentOptions,
|
||||
required this.sortingInformation,
|
||||
this.pendingScanRequests = 0,
|
||||
this.isPersistingSorting = false,
|
||||
this.sortingPersistError,
|
||||
const TourLoaded({
|
||||
required this.details,
|
||||
this.isRefreshing = false,
|
||||
this.isPersistingReorder = false,
|
||||
this.refreshError,
|
||||
this.reorderError,
|
||||
});
|
||||
|
||||
final TourDetails details;
|
||||
|
||||
/// Hintergrund-Reload läuft (Pull-to-Refresh, Provider-Wakeup). UI darf
|
||||
/// die alte Daten weiter zeigen und nur einen schmalen Indikator
|
||||
/// einblenden.
|
||||
final bool isRefreshing;
|
||||
|
||||
/// `PUT /tours/{id}/delivery-order` läuft. Sortier-Page nutzt das für
|
||||
/// den Bestätigungs-Button.
|
||||
final bool isPersistingReorder;
|
||||
|
||||
/// Fehler eines Hintergrund-Reloads — bleibt für eine einzelne Snackbar
|
||||
/// hängen und wird beim nächsten Reload geleert.
|
||||
final String? refreshError;
|
||||
|
||||
/// Fehler des letzten Reorder-Persist-Versuchs.
|
||||
final String? reorderError;
|
||||
|
||||
TourLoaded copyWith({
|
||||
Tour? tour,
|
||||
Map<String, double>? distances,
|
||||
List<Payment>? paymentOptions,
|
||||
Map<String, List<String>>? sortingInformation,
|
||||
int? pendingScanRequests,
|
||||
bool? isPersistingSorting,
|
||||
String? sortingPersistError,
|
||||
bool clearSortingPersistError = false,
|
||||
TourDetails? details,
|
||||
bool? isRefreshing,
|
||||
bool? isPersistingReorder,
|
||||
Object? refreshError = _sentinel,
|
||||
Object? reorderError = _sentinel,
|
||||
}) {
|
||||
return TourLoaded(
|
||||
tour: tour ?? this.tour,
|
||||
distances: distances ?? this.distances,
|
||||
paymentOptions: paymentOptions ?? this.paymentOptions,
|
||||
sortingInformation: sortingInformation ?? this.sortingInformation,
|
||||
pendingScanRequests: pendingScanRequests ?? this.pendingScanRequests,
|
||||
isPersistingSorting: isPersistingSorting ?? this.isPersistingSorting,
|
||||
sortingPersistError: clearSortingPersistError
|
||||
? null
|
||||
: (sortingPersistError ?? this.sortingPersistError),
|
||||
details: details ?? this.details,
|
||||
isRefreshing: isRefreshing ?? this.isRefreshing,
|
||||
isPersistingReorder: isPersistingReorder ?? this.isPersistingReorder,
|
||||
refreshError: identical(refreshError, _sentinel)
|
||||
? this.refreshError
|
||||
: refreshError as String?,
|
||||
reorderError: identical(reorderError, _sentinel)
|
||||
? this.reorderError
|
||||
: reorderError as String?,
|
||||
);
|
||||
}
|
||||
|
||||
/// Spezialfall: Initial-Load ist erfolgreich, aber das Backend hat dem
|
||||
/// angemeldeten Fahrer keine Tour für heute zugewiesen (kein ERP-Sync,
|
||||
/// Urlaub, …). UI kann darauf einen freundlichen Hinweis statt einer
|
||||
/// leeren Liste anzeigen.
|
||||
bool get isEmpty => details.deliveries.isEmpty;
|
||||
}
|
||||
|
||||
const Object _sentinel = Object();
|
||||
|
||||
/// Erfolgs-Spezialform für „heute keine Tour zugewiesen". Wir behandeln das
|
||||
/// als eigenständigen State (statt als `TourLoaded` mit leeren Listen),
|
||||
/// damit das UI im Routing klar trennen kann zwischen „Tour vorhanden,
|
||||
/// gerade keine Lieferungen offen" und „gar keine Tour für heute".
|
||||
class TourEmpty extends TourState {
|
||||
const TourEmpty();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user