import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_vodozemac/flutter_vodozemac.dart' as vod; import 'package:matrix/encryption/utils/key_verification.dart'; import 'package:matrix/matrix.dart'; import 'package:pyramid/core/app_database.dart'; import 'package:pyramid/core/auth_log.dart'; import 'package:pyramid/core/soft_logout_guard.dart'; // Kompositions-Punkt: Einzige Stelle außerhalb des call_signaling-Moduls, // die die Implementierung (statt der Fassade) kennen darf – sie erzeugt sie. import 'package:pyramid/features/call_signaling/voip_manager.dart'; final matrixClientProvider = FutureProvider((ref) async { // Factory-Wahl, Verschlüsselung (SQLCipher), Migration und PRAGMAs stecken // komplett in der Storage-Fassade — siehe docs/SQLCIPHER_MIGRATION.md. final db = await AppDatabase.open(); final sdkDb = await MatrixSdkDatabase.init('pyramid', database: db); final client = Client( 'Pyramid', database: sdkDb, // Custom State-Events, die beim App-Start mit aus der DB geladen werden // müssen — sonst sind Space-Banner & Voice-Presence nach Neustart leer, // bis der jeweilige Raum vollständig nachgeladen wird. importantStateEvents: { 'io.pyramid.space.banner', 'io.pyramid.voice.presence', }, verificationMethods: { KeyVerificationMethod.numbers, KeyVerificationMethod.emoji, KeyVerificationMethod.qrShow, }, logLevel: kReleaseMode ? Level.warning : Level.verbose, // Ohne eigenen Handler eskaliert das SDK jeden fehlgeschlagenen // Token-Refresh (auch bloße Netzwerkfehler!) zu logout()+clear() — // Session und Krypto-Schlüssel wären weg. Siehe soft_logout_guard.dart. onSoftLogout: guardedSoftLogoutRefresh, nativeImplementations: NativeImplementationsIsolate( compute, vodozemacInit: () => vod.init(), ), ); // Persistiert Warnungen/Fehler des SDK sowie jeden Login-Status-Wechsel, // damit ein unerwarteter Logout (z. B. Soft-Logout vom Server) auch nach // einem App-Neustart noch nachvollziehbar ist (ROADMAP M1, Uta-Logout-Bug). Logs().onLog = (event) { if (event.level.index <= Level.warning.index) { unawaited(AuthLog.write('[SDK ${event.level.name}] ${event.toFormattedString()}')); } }; client.onLoginStateChanged.stream.listen((state) { unawaited(AuthLog.write('LoginState -> ${state.name}')); }); await client.init( waitForFirstSync: false, waitUntilLoadCompletedLoaded: false, ); // Re-use or create singleton instance try { PyramidVoipManager.instance.client; } catch (_) { PyramidVoipManager(client); } return client; }); final secureStorageProvider = Provider((ref) { return const FlutterSecureStorage(); });