# 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):** ```dart abstract class CallSignalingService { Stream get currentCallStream; bool get isMicMuted; bool get isCameraMuted; Future startCall(String roomId, {bool video}); Future hangup(); Future toggleMic(); Future toggleCamera(); Future 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):** ```dart abstract class VoiceChannelService { Stream get stateStream; // isActive, isMuted, participants… Future join({required String roomName, required String matrixRoomId, ...}); Future leave(); Future toggleMute(); Future toggleDeafen(); Future toggleCamera(); Future toggleScreenShare(); Future setPublishQuality(String key); // Streamer-Seite (M4) Future setSubscribeQuality(String key); // Empfänger-Seite (M4) Future 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). ```dart abstract class VerificationService { Future startSasVerification(DeviceKeys device); Future confirmSas(); Future declineSas(); Stream 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` ```dart abstract class Prefs { Future getBool(String key, {bool fallback}); Future 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.