Files
pyramid/docs/M2_MODULE_SCHNITT.md
T
Bernd Steckmeister f5b9ce9cd7 docs: M2-Modul-Schnitt definieren (docs/M2_MODULE_SCHNITT.md)
Ö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).
2026-07-03 10:27:35 +02:00

7.7 KiB
Raw Blame History

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 29214545 (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

  1. 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).
  2. settings_modal.dart aufsplitten (reine Datei-Teilung, geringes Risiko, keine Verhaltensänderung).
  3. verification aus Settings herauslösen.
  4. storage-Fassade schrittweise erzwingen, immer im Zuge anderer Umbauten.
  5. chat_timeline/rooms zuletzt.

Bernd-Entscheidung nötig: siehe Frage in PROGRESS.md ob Punkt 1 (Call-Pilot) so wie hier vorgeschlagen als Nächstes umgesetzt werden soll.