Tour, Datum, Fahrzeuge, Fahrzeugauswahl und Phase lebten bisher pro Server-Umgebung, nicht pro Anmeldung. Nach Abmelden von 423 und Anmelden von 414 lieferte der gecachte CarsBloc weiter die Fahrzeuge von 423. Neu: SessionScope unter dem AuthBloc legt diese Blocs bei jeder Anmeldung (neuer Fahrer oder Re-Login) komplett neu an; alles wird frisch vom Server geholt. Token-Refresh desselben Fahrers zählt nicht als neue Anmeldung. Zahlungsmethoden werden ebenfalls nach jeder Anmeldung neu geladen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
124 lines
5.1 KiB
Dart
124 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,
|
|
),
|
|
),
|
|
),
|
|
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,
|
|
),
|
|
);
|
|
}
|
|
}
|