fix: fahrerbezogene Daten bei jeder Anmeldung neu laden
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>
This commit is contained in:
@ -1,32 +1,21 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hl_lieferservice/bloc/app_bloc.dart';
|
||||
import 'package:hl_lieferservice/data/cache/attachment_cache.dart';
|
||||
import 'package:hl_lieferservice/data/network/keycloak_oidc_token_provider.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/authentication/bloc/auth_event.dart';
|
||||
import 'package:hl_lieferservice/feature/authentication/presentation/login_enforcer.dart';
|
||||
import 'package:hl_lieferservice/main.dart' show locator;
|
||||
import 'package:hl_lieferservice/feature/car_selection/bloc/bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/car_selection/presentation/car_selection_enforcer.dart';
|
||||
import 'package:hl_lieferservice/feature/car_selection/repository/car_selection_repository.dart';
|
||||
import 'package:hl_lieferservice/data/repository/cars_repository_impl.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/cars/presentation/car_management_page.dart';
|
||||
import 'package:hl_lieferservice/data/repository/payment_methods_repository_impl.dart';
|
||||
import 'package:hl_lieferservice/feature/payment_methods/bloc/payment_methods_cubit.dart';
|
||||
import 'package:holzleitner_api/holzleitner_api.dart' show HolzleitnerApi;
|
||||
import 'package:hl_lieferservice/data/repository/tour_repository_impl.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/feature_flags/feature_flags.dart';
|
||||
import 'package:hl_lieferservice/widget/home/bloc/navigation_bloc.dart';
|
||||
import 'package:hl_lieferservice/widget/operations/bloc/operation_bloc.dart';
|
||||
import 'package:hl_lieferservice/widget/operations/presentation/operation_view_enforcer.dart';
|
||||
import 'package:hl_lieferservice/widget/session_scope.dart';
|
||||
|
||||
import 'package:hl_lieferservice/bloc/app_states.dart';
|
||||
import 'home/presentation/home.dart';
|
||||
@ -67,62 +56,6 @@ class _DeliveryAppState extends State<DeliveryApp> {
|
||||
// und ggf. direkt einloggen.
|
||||
..add(const RestoreSessionRequested()),
|
||||
),
|
||||
BlocProvider(
|
||||
// Phase-C+D-2-Migration: produktive TourRepository-Impl
|
||||
// gegen das generierte Rust-Backend-API. Account-Filter
|
||||
// serverseitig aus dem JWT, deshalb braucht der Bloc
|
||||
// keinen AuthBloc-Bezug mehr.
|
||||
create: (context) => TourBloc(
|
||||
tourRepository: TourRepositoryImpl(locator<HolzleitnerApi>()),
|
||||
opBloc: context.read<OperationBloc>(),
|
||||
attachmentCache: locator<AttachmentCache>(),
|
||||
),
|
||||
),
|
||||
BlocProvider(
|
||||
// Hält das im Header gewählte Tour-Datum (null = heute).
|
||||
// Steuert, welchen Tag der TourBloc lädt.
|
||||
create: (context) => TourDateCubit(),
|
||||
),
|
||||
BlocProvider(
|
||||
create: (context) =>
|
||||
CarSelectBloc(
|
||||
repository: CarSelectionRepository(
|
||||
environmentId: state.active.id,
|
||||
),
|
||||
),
|
||||
),
|
||||
BlocProvider(
|
||||
// Phase-D-Migration: produktive CarsRepository-Impl
|
||||
// gegen das generierte Rust-Backend-API. Account-Filter
|
||||
// serverseitig aus dem JWT, deshalb braucht der Bloc
|
||||
// keinen AuthBloc-Bezug mehr.
|
||||
create: (context) => CarsBloc(
|
||||
repository: CarsRepositoryImpl(locator<HolzleitnerApi>()),
|
||||
opBloc: context.read<OperationBloc>(),
|
||||
),
|
||||
),
|
||||
BlocProvider(
|
||||
// PhaseBloc liest die Team-Fahrzeug-Anzahl jetzt direkt
|
||||
// aus dem CarsBloc — der ist die alleinige Quelle der
|
||||
// Fahrzeug-Stammdaten. Beim ersten Load eines Fahrzeugs
|
||||
// bestimmt das die Eintrittsphase (Auswählen vs. Sortieren).
|
||||
create: (context) => PhaseBloc(
|
||||
carCountResolver: () {
|
||||
final carsState = context.read<CarsBloc>().state;
|
||||
return carsState is CarsLoaded
|
||||
? carsState.cars.length
|
||||
: null;
|
||||
},
|
||||
// Bindet die persistierten Phasen-Häkchen an die aktuelle
|
||||
// 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.
|
||||
// Ableitung zentral in `TourStatePhaseToken` (tour_state.dart),
|
||||
// dieselbe Quelle wie `Home._ensurePhaseLoaded`.
|
||||
tourTokenResolver: () =>
|
||||
context.read<TourBloc>().state.phaseToken,
|
||||
),
|
||||
),
|
||||
BlocProvider(
|
||||
// Zahlungsmethoden sind firmenweite Stammdaten — wir laden
|
||||
// sie einmal beim App-Start und cachen sie im Cubit. Der
|
||||
@ -134,46 +67,42 @@ class _DeliveryAppState extends State<DeliveryApp> {
|
||||
)..load(),
|
||||
),
|
||||
],
|
||||
child: MaterialApp(
|
||||
title: 'Holzleitner Auslieferung',
|
||||
debugShowCheckedModeBanner: false,
|
||||
// Wrap the Navigator (not just the home route) so the loading
|
||||
// overlay covers every pushed route — DeliveryDetail, Cars,
|
||||
// dialogs, etc. — not only the initial home tree.
|
||||
builder: (context, child) =>
|
||||
OperationViewEnforcer(child: child ?? const SizedBox.shrink()),
|
||||
home: BlocBuilder<AppBloc, AppState>(
|
||||
builder: (context, state) {
|
||||
if (state is AppConfigLoading) {
|
||||
return Scaffold(
|
||||
body: Center(child: CircularProgressIndicator()),
|
||||
);
|
||||
}
|
||||
// Fahrerbezogene Blocs (Tour, Fahrzeuge, Auswahl, Phase) je
|
||||
// Anmeldung neu — siehe SessionScope.
|
||||
child: SessionScope(
|
||||
environmentId: state.active.id,
|
||||
child: MaterialApp(
|
||||
title: 'Holzleitner Auslieferung',
|
||||
debugShowCheckedModeBanner: false,
|
||||
// Wrap the Navigator (not just the home route) so the loading
|
||||
// overlay covers every pushed route — DeliveryDetail, Cars,
|
||||
// dialogs, etc. — not only the initial home tree.
|
||||
builder: (context, child) =>
|
||||
OperationViewEnforcer(child: child ?? const SizedBox.shrink()),
|
||||
home: BlocBuilder<AppBloc, AppState>(
|
||||
builder: (context, state) {
|
||||
if (state is AppConfigLoading) {
|
||||
return Scaffold(
|
||||
body: Center(child: CircularProgressIndicator()),
|
||||
);
|
||||
}
|
||||
|
||||
if (state is AppConfigLoadingFailed) {
|
||||
return Scaffold(body: Center(child: Text(state.message)));
|
||||
}
|
||||
if (state is AppConfigLoadingFailed) {
|
||||
return Scaffold(body: Center(child: Text(state.message)));
|
||||
}
|
||||
|
||||
if (state is AppConfigLoaded) {
|
||||
// Stammdaten nach JEDEM erfolgreichen (Re-)Login neu laden.
|
||||
// Der Cubit lädt zwar beim App-Start — aber ohne Session
|
||||
// endet das in 401 („Sitzung abgelaufen") und blieb bisher
|
||||
// für immer im Fehlerzustand hängen, auch nach dem Login.
|
||||
return BlocListener<AuthBloc, AuthState>(
|
||||
listenWhen: (prev, curr) =>
|
||||
curr is Authenticated && prev is! Authenticated,
|
||||
listener: (context, _) =>
|
||||
context.read<PaymentMethodsCubit>().load(),
|
||||
child: LoginEnforcer(
|
||||
if (state is AppConfigLoaded) {
|
||||
// Zahlungsmethoden lädt SessionScope nach jeder Anmeldung neu.
|
||||
return LoginEnforcer(
|
||||
child: CarSelectionEnforcer(child: Home()),
|
||||
),
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return Container();
|
||||
},
|
||||
return Container();
|
||||
},
|
||||
),
|
||||
routes: {"/cars": (context) => CarManagementPage()},
|
||||
),
|
||||
routes: {"/cars": (context) => CarManagementPage()},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user