Öffentliche Schnittstellen für call_signaling, voice_channel, call_ui, settings, verification, storage u.a. entworfen. Wichtigste Erkenntnis: "Calls" ist architektonisch zwei getrennte Module (Matrix-VoIP vs. LiveKit-SFU-Voice-Channels), keine gemeinsame Fassade. Korrigiert nebenbei einen falschen Ist-Analyse-Befund (rooms_provider-Import war fälschlich als toter Code eingestuft).
7.7 KiB
M2 – Modul-Schnitt (Entwurf)
Stand: 2026-07-03. Reine Schnittstellen-Planung, noch kein Umbau. Grundlage:
Ist-Analyse in PROGRESS.md (Eintrag „M2: Ist-Analyse der Architektur
geschrieben"). Leitprinzip (Bernd, CLAUDE.md): jedes Modul muss sich in
einem Jahr komplett neu schreiben lassen, ohne andere Module anzufassen.
Andere Module dürfen daher nur die hier genannte öffentliche Schnittstelle
importieren, nie interne Dateien.
Korrektur zur Ist-Analyse: der Import von rooms_provider.dart in
app_state.dart ist kein toter Altlast-Import, wie dort vermutet –
voiceParticipantsProvider (Zeile 160) nutzt roomListProvider daraus, um
bei jedem Sync neu zu bauen. Der Eintrag in PROGRESS.md wird beim nächsten
Durchgang korrigiert.
Wichtige Erkenntnis vor dem Schnitt: „Calls" ist eigentlich ZWEI Features
Beim genaueren Lesen von voip_manager.dart und livekit_call_manager.dart
zeigt sich: das sind keine zwei Hälften derselben Sache, sondern zwei
unabhängige Call-Mechanismen, die zufällig beide „Call" heißen:
PyramidVoipManager– natives Matrix-VoIP (m.call.*-Events über das matrix-SDK, direkte 1:1-WebRTC-Peer-Connection, kein SFU). Vermutlich für spontane 1:1-Anrufe gedacht.LiveKitCallManager– SFU-basierte, dauerhafte Voice-Channels (LiveKit- Server-Raum, Presence über Matrix-State-Events, Screensharing, Mehrpersonen). Das ist der Discord-artige Voice-Channel aus M3/M4.
Beide Module sollten getrennt bleiben, nicht zu einem „calls"-Modul
verschmolzen werden. Nur die UI (call_ui) darf beide kennen, falls sie
gemeinsam dargestellt werden müssen (z. B. „du bist in einem Voice-Channel,
jemand ruft dich per 1:1-VoIP an").
Modul-Liste mit öffentlicher Schnittstelle (Entwurf)
1. call_signaling (Matrix-1:1-VoIP)
Ersetzt: core/voip_manager.dart
Öffentliche Schnittstelle (Ausschnitt):
abstract class CallSignalingService {
Stream<CallSession?> get currentCallStream;
bool get isMicMuted;
bool get isCameraMuted;
Future<void> startCall(String roomId, {bool video});
Future<void> hangup();
Future<void> toggleMic();
Future<void> toggleCamera();
Future<void> toggleScreenSharing(BuildContext? context);
RTCVideoRenderer get localRenderer; // bleibt vorerst WebRTC-typisiert,
RTCVideoRenderer get remoteRenderer; // da UI direkt rendert
}
Interna (ICE-Server-Merge, MediaDevicesWrapper, Geräte-Prefs aus
SharedPreferences) bleiben privat im Modul. Geräte-Prefs sollten künftig
über die storage-Fassade laufen statt direktem SharedPreferences-Zugriff
(aktuell Zeile 260, 447).
2. voice_channel (LiveKit-SFU, Discord-artige Kanäle)
Ersetzt: core/livekit_call_manager.dart
Öffentliche Schnittstelle (Ausschnitt):
abstract class VoiceChannelService {
Stream<VoiceChannelState> get stateStream; // isActive, isMuted, participants…
Future<void> join({required String roomName, required String matrixRoomId, ...});
Future<void> leave();
Future<void> toggleMute();
Future<void> toggleDeafen();
Future<void> toggleCamera();
Future<void> toggleScreenShare();
Future<void> setPublishQuality(String key); // Streamer-Seite (M4)
Future<void> setSubscribeQuality(String key); // Empfänger-Seite (M4)
Future<void> setOutputVolume(double v);
}
Presence-Writing (_writePresence, Matrix-State-Event
io.pyramid.voice.presence) bleibt intern, aber nutzt eine injizierte
Client-Referenz statt globalem Singleton-Zugriff – macht das Modul für
Tests austauschbar.
3. call_ui
Ersetzt: features/call/voice_channel.dart, features/call/mini_call_widget.dart
Konsumiert ausschließlich CallSignalingService + VoiceChannelService über
Riverpod-Provider (callSignalingProvider, voiceChannelProvider). Keine
direkten Importe von core/voip_manager.dart/core/livekit_call_manager.dart
mehr – heute importieren chat_view.dart, matrix_client.dart und
settings_modal.dart diese Dateien direkt, das entfällt.
4. settings (aufgesplittet)
Kein eigenes fachliches Modul, sondern reine Aggregations-UI. Jede Sektion
wird eine eigene Datei/Klasse mit einer schmalen Widget build(...)-Fläche,
die ihrerseits nur die Provider ihres Fachmoduls konsumiert (z. B.
_VoiceSection nutzt künftig voiceChannelProvider/callSignalingProvider
statt der Manager direkt). Vorschlag für Dateien:
settings/profile_section.dart, settings/notifications_section.dart,
settings/appearance_section.dart, settings/sessions_section.dart,
settings/account_section.dart, settings/voice_section.dart,
settings/about_section.dart.
5. verification (neu, aus Settings herausgelöst)
Quelle: widgets/settings_modal.dart Zeile 2921–4545 (SAS-Dialog,
QR-Anzeige, Emoji-Vergleich, Recovery-Key-Flow).
abstract class VerificationService {
Future<void> startSasVerification(DeviceKeys device);
Future<void> confirmSas();
Future<void> declineSas();
Stream<VerificationStep> get stepStream;
}
Eigenständig, weil SAS/QR-Verifikation künftig auch außerhalb der Settings
gebraucht werden könnte (z. B. beim Login auf einem neuen Gerät –
bootstrap_dialog.dart macht heute schon etwas Ähnliches separat).
6. storage (Fassade, kein Rewrite)
Ausbau von: core/settings_prefs.dart
abstract class Prefs {
Future<bool> getBool(String key, {bool fallback});
Future<void> setBool(String key, bool value);
// … String, double, StringSet analog zu BoolPref/StringSetPref
}
Ziel ist NICHT, SharedPreferences zu ersetzen, sondern die bereits
vorhandene BoolPref/StringSetPref-Abstraktion zur Pflicht zu machen.
Die 12 identifizierten Direktzugriffsstellen (u. a. voip_manager.dart:260,
livekit_call_manager.dart:246, settings_modal.dart:1804/1860/1870) werden
schrittweise umgestellt – nicht in diesem Schritt, sondern sobald das
jeweilige Modul (z. B. call_signaling) ohnehin angefasst wird.
Krypto-/Access-Token-Speicherung (flutter_secure_storage,
matrix_client.dart) bleibt bewusst außen vor – hängt an der noch offenen
SQLCipher-Frage (M1) und wird dort separat behandelt, nicht hier.
7. auth
Dateien: features/auth/login_notifier.dart, login_page.dart,
server_page.dart, bootstrap_dialog.dart (Bootstrap-Teil, Verifikations-Teil
wandert ggf. zu verification, s. o.). Schnittstelle bleibt der bereits
vorhandene matrixClientProvider/login_notifier-Zustandsautomat – heute
schon relativ sauber gekapselt, kein akuter Handlungsbedarf.
8. push
Dateien: core/background_push.dart, core/fcm_push_service.dart,
core/notification_service.dart. Bereits über eigene Dateien getrennt
(nativ vs. Flutter-Engine, siehe Memory „Pyramid Push"). Einzige Lücke:
eigener sqflite-Zugriff in background_push.dart statt gemeinsamer
DB-Schicht – bewusst so gewollt (Background-Isolate-Zwang), daher kein
Refactoring-Kandidat, nur dokumentieren.
9. chat_timeline / rooms
Niedrigste Priorität für den Pilot-Umbau, da laut Ist-Analyse strukturell am wenigsten verwoben (Hauptproblem dort ist Dateigröße + Duplikate, nicht Modul-Kopplung). Wird nach dem Call-Pilot angegangen.
Reihenfolge-Vorschlag für die Umsetzung
- Pilot:
call_signaling+voice_channel+call_ui(Punkt „Call-Schicht entwirren" in ROADMAP.md) – kleinster klar abgegrenzter Schnitt, deckt gleich zwei ROADMAP-Punkte ab (Modul-Schnitt beweisen + Call-Entwirrung). settings_modal.dartaufsplitten (reine Datei-Teilung, geringes Risiko, keine Verhaltensänderung).verificationaus Settings herauslösen.storage-Fassade schrittweise erzwingen, immer im Zuge anderer Umbauten.chat_timeline/roomszuletzt.
Bernd-Entscheidung nötig: siehe Frage in PROGRESS.md – ob Punkt 1 (Call-Pilot) so wie hier vorgeschlagen als Nächstes umgesetzt werden soll.