3c7eb9ddca
- CallSignalingService/callSignalingProvider und VoiceChannelService/ voiceChannelProvider als einzige oeffentliche Schnittstellen - voip_manager.dart / livekit_call_manager.dart in ihre Module verschoben, Logik unangetastet (nur Klassen-Koepfe, @override, Imports, Provider-Namen) - Alle Konsumenten entkoppelt; Kompositions-Punkt matrix_client.dart als dokumentierte Ausnahme. analyze sauber, 29 Tests gruen, Windows-Start ok. Call-Klick-/Geraetetest steht aus (PROGRESS.md)
79 lines
2.9 KiB
Dart
79 lines
2.9 KiB
Dart
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<Client>((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<FlutterSecureStorage>((ref) {
|
||
return const FlutterSecureStorage();
|
||
});
|