Die Tagesauswahl liegt lokal pro Fahrer und wurde ungeprüft wiederhergestellt. Durch den alten Session-Fehler hatte 414 auf dem Gerät das Fahrzeug von 423 gespeichert — das erschien weiter, obwohl „Fahrzeug verwalten" (frisch vom Server) leer war. CarSelectBloc lädt beim Wiederherstellen die Fahrzeuge des Accounts vom Server und übernimmt die Auswahl nur, wenn das Fahrzeug dazugehört und aktiv ist; sonst wird sie verworfen und neu abgefragt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
125 lines
5.1 KiB
Dart
125 lines
5.1 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import 'package:hl_lieferservice/data/cache/attachment_cache.dart';
|
|
import 'package:hl_lieferservice/data/repository/cars_repository_impl.dart';
|
|
import 'package:hl_lieferservice/data/repository/tour_repository_impl.dart';
|
|
import 'package:hl_lieferservice/feature/authentication/bloc/auth_bloc.dart';
|
|
import 'package:hl_lieferservice/feature/authentication/bloc/auth_state.dart';
|
|
import 'package:hl_lieferservice/feature/car_selection/bloc/bloc.dart';
|
|
import 'package:hl_lieferservice/feature/car_selection/repository/car_selection_repository.dart';
|
|
import 'package:hl_lieferservice/feature/cars/bloc/cars_bloc.dart';
|
|
import 'package:hl_lieferservice/feature/cars/bloc/cars_state.dart';
|
|
import 'package:hl_lieferservice/feature/delivery/bloc/phase_bloc.dart';
|
|
import 'package:hl_lieferservice/feature/delivery/bloc/tour_bloc.dart';
|
|
import 'package:hl_lieferservice/feature/delivery/bloc/tour_date_cubit.dart';
|
|
import 'package:hl_lieferservice/feature/delivery/bloc/tour_state.dart';
|
|
import 'package:hl_lieferservice/feature/payment_methods/bloc/payment_methods_cubit.dart';
|
|
import 'package:hl_lieferservice/main.dart' show locator;
|
|
import 'package:hl_lieferservice/widget/operations/bloc/operation_bloc.dart';
|
|
import 'package:holzleitner_api/holzleitner_api.dart' show HolzleitnerApi;
|
|
|
|
/// Fahrerbezogene Blocs — pro Anmeldung frisch.
|
|
///
|
|
/// Tour, gewähltes Datum, Fahrzeuge, Fahrzeugauswahl und Phase gehören zum
|
|
/// angemeldeten Fahrer. Sie leben unterhalb des [AuthBloc] und werden bei
|
|
/// JEDER Anmeldung (neuer Fahrer oder derselbe nach Abmelden/Session-Ablauf)
|
|
/// komplett neu angelegt: Nichts vom vorherigen Fahrer bleibt im Speicher,
|
|
/// alles wird neu vom Server geholt. Ein bloßer Token-Refresh
|
|
/// (Authenticated → Authenticated, gleicher Fahrer) zählt nicht als neue
|
|
/// Anmeldung.
|
|
///
|
|
/// [child] ist die `MaterialApp`, damit auch gepushte Routen (Lieferdetails,
|
|
/// Fahrzeugverwaltung) die Session-Blocs sehen. Eine neue Sitzung baut sie
|
|
/// deshalb mit neu auf — der Navigator startet wieder bei Home.
|
|
class SessionScope extends StatefulWidget {
|
|
const SessionScope({
|
|
super.key,
|
|
required this.environmentId,
|
|
required this.child,
|
|
});
|
|
|
|
/// Aktive Server-Umgebung; die Fahrzeugauswahl wird je Umgebung gespeichert.
|
|
final String environmentId;
|
|
final Widget child;
|
|
|
|
@override
|
|
State<SessionScope> createState() => _SessionScopeState();
|
|
}
|
|
|
|
class _SessionScopeState extends State<SessionScope> {
|
|
/// Zählt Anmeldungen; Teil des Keys, damit jede Anmeldung einen neuen
|
|
/// Bloc-Baum erzeugt.
|
|
int _session = 0;
|
|
int? _personalnummer;
|
|
|
|
bool _startsNewSession(AuthState previous, AuthState current) {
|
|
if (current is! Authenticated) return false;
|
|
return previous is! Authenticated ||
|
|
previous.personalnummer != current.personalnummer;
|
|
}
|
|
|
|
void _onAuthChanged(BuildContext context, AuthState state) {
|
|
final authenticated = state as Authenticated;
|
|
setState(() {
|
|
_session++;
|
|
_personalnummer = authenticated.personalnummer;
|
|
});
|
|
// Firmenweite Stammdaten liegen eine Ebene höher; ohne Session endet
|
|
// deren Start-Load in 401 — daher nach jeder Anmeldung neu laden.
|
|
context.read<PaymentMethodsCubit>().load();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return BlocListener<AuthBloc, AuthState>(
|
|
listenWhen: _startsNewSession,
|
|
listener: _onAuthChanged,
|
|
child: MultiBlocProvider(
|
|
key: ValueKey('session-$_session-$_personalnummer'),
|
|
providers: [
|
|
BlocProvider(
|
|
// Account-Filter serverseitig aus dem JWT.
|
|
create: (context) => TourBloc(
|
|
tourRepository: TourRepositoryImpl(locator<HolzleitnerApi>()),
|
|
opBloc: context.read<OperationBloc>(),
|
|
attachmentCache: locator<AttachmentCache>(),
|
|
),
|
|
),
|
|
BlocProvider(
|
|
// Im Header gewähltes Tour-Datum (null = heute).
|
|
create: (context) => TourDateCubit(),
|
|
),
|
|
BlocProvider(
|
|
create: (context) => CarSelectBloc(
|
|
repository: CarSelectionRepository(
|
|
environmentId: widget.environmentId,
|
|
),
|
|
carsRepository: CarsRepositoryImpl(locator<HolzleitnerApi>()),
|
|
),
|
|
),
|
|
BlocProvider(
|
|
// Fahrzeuge des angemeldeten Accounts (aus dem JWT).
|
|
create: (context) => CarsBloc(
|
|
repository: CarsRepositoryImpl(locator<HolzleitnerApi>()),
|
|
opBloc: context.read<OperationBloc>(),
|
|
),
|
|
),
|
|
BlocProvider(
|
|
// Eintrittsphase hängt an der Fahrzeug-Anzahl (CarsBloc) und an
|
|
// der Tour-Version (Tour.syncedAt, siehe `TourStatePhaseToken`).
|
|
create: (context) => PhaseBloc(
|
|
carCountResolver: () {
|
|
final carsState = context.read<CarsBloc>().state;
|
|
return carsState is CarsLoaded ? carsState.cars.length : null;
|
|
},
|
|
tourTokenResolver: () =>
|
|
context.read<TourBloc>().state.phaseToken,
|
|
),
|
|
),
|
|
],
|
|
child: widget.child,
|
|
),
|
|
);
|
|
}
|
|
}
|