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 createState() => _SessionScopeState(); } class _SessionScopeState extends State { /// 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().load(); } @override Widget build(BuildContext context) { return BlocListener( 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()), opBloc: context.read(), attachmentCache: locator(), ), ), BlocProvider( // Im Header gewähltes Tour-Datum (null = heute). create: (context) => TourDateCubit(), ), BlocProvider( create: (context) => CarSelectBloc( repository: CarSelectionRepository( environmentId: widget.environmentId, ), carsRepository: CarsRepositoryImpl(locator()), ), ), BlocProvider( // Fahrzeuge des angemeldeten Accounts (aus dem JWT). create: (context) => CarsBloc( repository: CarsRepositoryImpl(locator()), opBloc: context.read(), ), ), 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().state; return carsState is CarsLoaded ? carsState.cars.length : null; }, tourTokenResolver: () => context.read().state.phaseToken, ), ), ], child: widget.child, ), ); } }