replaced account data singleton with a session model

This commit is contained in:
2026-09-27 14:49:38 +02:00
parent 79b33ab248
commit 5e65c4671a
60 changed files with 1019 additions and 679 deletions
+15 -9
View File
@@ -13,10 +13,11 @@ import '../../../api/marianumconnect/marianumconnect_endpoint.dart';
import '../../../api/marianumconnect/queries/telemetry_heartbeat/telemetry_device_id.dart';
import '../../../background/widget_background_task.dart';
import '../../../extensions/date_time.dart';
import '../../../model/account_data.dart';
import '../../../model/endpoint_data.dart';
import '../../../push/push_registration_store.dart';
import '../../../push/push_registration_type.dart';
import '../../../session/session.dart';
import '../../../session/session_manager.dart';
import '../../../state/app/modules/account/bloc/account_bloc.dart';
import '../../../state/app/modules/settings/bloc/settings_cubit.dart';
import '../../../utils/clipboard_helper.dart';
@@ -44,7 +45,8 @@ class _DiagnosticsPageState extends State<DiagnosticsPage> {
Future<List<_Section>> _collect() async {
final settings = context.read<SettingsCubit>().val();
final accountStatus = context.read<AccountBloc>().state.status;
final account = AccountData();
final session = SessionManager().current;
final nextcloud = session?.nextcloud;
const tokens = MarianumConnectTokenStorage();
final push = PushRegistrationStore();
@@ -101,13 +103,17 @@ class _DiagnosticsPageState extends State<DiagnosticsPage> {
title: 'Sitzung',
rows: await rows({
'Status': () async => accountStatus.name,
'Benutzer': () async =>
account.isPopulated() ? account.getUsername() : null,
'Demo-Modus': () async => account.isDemo,
'Login Flow v2': () async => account.usesLoginFlow,
'App-Passwort (Dateien)': () async => account.hasAppPassword(),
'App-Passwort (Talk)': () async => account.hasAppPasswordTalk(),
'Session-Epoche': () async => account.sessionEpoch,
'Benutzer': () async => switch (session) {
CredentialSession(:final username) => username,
null => null,
},
'Demo-Modus': () async => session?.isDemo ?? false,
'Login Flow v2': () async => nextcloud?.usesLoginFlow ?? false,
'App-Passwort (Dateien)': () async =>
nextcloud?.hasAppPassword ?? false,
'App-Passwort (Talk)': () async =>
nextcloud?.hasAppPasswordTalk ?? false,
'Session-Epoche': () async => SessionManager().sessionEpoch,
'MC-Token-ID': tokens.readTokenId,
'MC-Token läuft ab': tokens.readExpiresAt,
}),
@@ -4,10 +4,10 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../../api/marianumcloud/cloud_users/cloud_users_actions.dart';
import '../../../../api/marianumconnect/queries/auth_logout/auth_logout.dart';
import '../../../../model/account_data.dart';
import '../../../../push/push_registration.dart';
import '../../../../routing/app_routes.dart';
import '../../../../session/session_lifecycle.dart';
import '../../../../session/session_manager.dart';
import '../../../../state/app/modules/account/bloc/account_bloc.dart';
import '../../../../state/app/modules/account/bloc/account_state.dart';
import '../../../../widget/app_progress_indicator.dart';
@@ -84,13 +84,17 @@ class _AccountSectionState extends State<AccountSection> {
setState(() => _avatarBusy = false);
if (!ok) return;
invalidateAvatarCache(id: AccountData().getUsername(), isGroup: false);
invalidateAvatarCache(
id: SessionManager().requireNextcloud().username,
isGroup: false,
);
setState(() => _avatarVersion++);
}
@override
Widget build(BuildContext context) {
final username = AccountData().getUsername();
final nextcloud = SessionManager().requireNextcloud();
final username = nextcloud.username;
final displayName = _displayName;
final theme = Theme.of(context);
@@ -175,7 +179,7 @@ class _AccountSectionState extends State<AccountSection> {
// Nur für Login-Flow-Konten (2FA) sichtbar — Passwort-Konten heilen
// sich still über das App-Passwort-Minting und sollen von dem ganzen
// Flow-Mechanismus nichts mitbekommen.
if (!AccountData().isDemo && AccountData().usesLoginFlow)
if (!SessionManager().isDemo && nextcloud.usesLoginFlow)
AsyncListTile(
leading: const Icon(Icons.cloud_sync_outlined),
title: const Text('Nextcloud neu verbinden'),
@@ -203,7 +207,7 @@ class _AccountSectionState extends State<AccountSection> {
Future<void> _showLogoutDialog(BuildContext context) async {
// Flip AccountBloc state only after the dialog fully closes: doing it from
// inside removeData (the previous approach) raced AsyncDialogAction's
// inside the sign-out (the previous approach) raced AsyncDialogAction's
// pop(true) against the listener's popUntil(isFirst) and could leave the
// navigator in an inconsistent state.
final confirmed = await showDialog<bool>(
@@ -219,14 +223,8 @@ class _AccountSectionState extends State<AccountSection> {
context.read<AccountBloc>().setStatus(AccountStatus.loggedOut);
}
// Ordered teardown: unregister push at Nextcloud + proxy and revoke the app
// password (while Nextcloud credentials are still available), THEN revoke the
// MC bearer token, and finally wipe local credentials. Each step is
// best-effort so an offline logout still reaches a clean local state.
Future<void> _performLogout() async {
await PushRegistration().logoutCleanup();
await AuthLogout().run();
await AccountData().removeData();
await SessionLifecycle.signOut();
_cachedDisplayName = null;
}
}