feat: Call-Robustheit + Standalone-Voice-Channel per Klick beitretbar (M3)
Mit zwei echten Test-Accounts (zwei GUI-Instanzen via PYRAMID_PROFILE_DIR) durchgeklickt: Beitreten, Teilnehmerliste, bidirektionaler Screenshare, Live-Qualitaetswechsel (Full HD 60fps), sauberes Verlassen. - rooms_list.dart: Voice-Channel in der normalen Raumliste war per Klick nicht beitretbar (oeffnete nur Chat) -> _toggleVoiceChannel wie Space-Liste. - livekit_call_manager.dart: _callGeneration entwertet laufenden connect bei Auflegen/Wechsel (keine Geisterverbindung mit offenem Mikro); Fehlerpfade setzen error sichtbar statt still zu verschwinden. - rooms_panel_core.dart: raeumt activeVoiceRoomIdProvider auf, wenn der Voice-Service inaktiv wird, + Fehler-SnackBar. - test/live_call_setup_test.dart: gegateter Setup-Helfer fuer den Testraum. UNGETESTET: echtes 2-Peer-Audio, Netz-Abbruch mitten im Call. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -73,6 +73,12 @@ class LiveKitCallManager extends VoiceChannelService {
|
||||
|
||||
Timer? _statsTimer;
|
||||
|
||||
/// Entwertet laufende `startCall`-Verbindungsaufbauten: Wer während des
|
||||
/// (mehrere Sekunden dauernden) `connect` auflegt oder den Kanal wechselt,
|
||||
/// darf KEINE Geisterverbindung zurücklassen – vorher blieb in dem Fall
|
||||
/// das Mikro im alten Kanal offen, während die UI „nicht verbunden" zeigte.
|
||||
int _callGeneration = 0;
|
||||
|
||||
@override
|
||||
bool get isActive => _room != null || isConnecting;
|
||||
@override
|
||||
@@ -93,6 +99,7 @@ class LiveKitCallManager extends VoiceChannelService {
|
||||
void _onRoomChanged() => notifyListeners();
|
||||
|
||||
void _cleanUpAfterDisconnect() {
|
||||
_callGeneration++;
|
||||
_stopStatsLogging();
|
||||
_presenceHeartbeat?.cancel();
|
||||
_presenceHeartbeat = null;
|
||||
@@ -105,7 +112,9 @@ class LiveKitCallManager extends VoiceChannelService {
|
||||
_room = null;
|
||||
_displayName = '';
|
||||
isConnecting = false;
|
||||
error = null;
|
||||
// `error` bewusst NICHT löschen: Ein unerwarteter Abbruch/Join-Fehler
|
||||
// muss für die UI sichtbar bleiben (kein stiller Fehlerpfad). Manuelles
|
||||
// Auflegen (hangUp) setzt error selbst zurück.
|
||||
isMuted = false;
|
||||
isCameraOff = false;
|
||||
isScreenSharing = false;
|
||||
@@ -162,6 +171,7 @@ class LiveKitCallManager extends VoiceChannelService {
|
||||
String? matrixRoomId,
|
||||
}) async {
|
||||
if (isActive) return;
|
||||
final gen = ++_callGeneration;
|
||||
|
||||
final url = 'wss://livekit.steggi-matrix.work';
|
||||
|
||||
@@ -223,6 +233,17 @@ class LiveKitCallManager extends VoiceChannelService {
|
||||
connectOptions: ConnectOptions(rtcConfiguration: rtcConfig),
|
||||
);
|
||||
|
||||
if (gen != _callGeneration) {
|
||||
// Während des Verbindens wurde aufgelegt oder der Kanal gewechselt –
|
||||
// diese Verbindung gehört niemandem mehr: sofort wieder trennen,
|
||||
// sonst bleibt das Mikro unsichtbar im alten Kanal offen.
|
||||
room.removeListener(_onRoomChanged);
|
||||
try {
|
||||
await room.disconnect();
|
||||
} catch (_) {}
|
||||
return;
|
||||
}
|
||||
|
||||
_room = room;
|
||||
isConnecting = false;
|
||||
notifyListeners();
|
||||
@@ -263,7 +284,11 @@ class LiveKitCallManager extends VoiceChannelService {
|
||||
..on<TrackUnpublishedEvent>((_) => notifyListeners())
|
||||
..on<ParticipantConnectedEvent>((_) => notifyListeners())
|
||||
..on<ParticipantDisconnectedEvent>((_) => notifyListeners())
|
||||
..on<RoomDisconnectedEvent>((_) {
|
||||
..on<RoomDisconnectedEvent>((event) {
|
||||
// Kommt nur bei UNERWARTETEN Abbrüchen an (manuelles hangUp
|
||||
// entsorgt den Listener vorher) – Grund sichtbar machen statt
|
||||
// still aus dem Kanal zu verschwinden.
|
||||
error = 'Verbindung getrennt (${event.reason?.name ?? 'unbekannt'})';
|
||||
_cleanUpAfterDisconnect();
|
||||
});
|
||||
|
||||
@@ -318,9 +343,16 @@ class LiveKitCallManager extends VoiceChannelService {
|
||||
_startStatsLogging();
|
||||
} catch (e) {
|
||||
debugPrint('[VOIP] Start call error: $e');
|
||||
if (gen != _callGeneration) {
|
||||
// Inzwischen wurde aufgelegt/neu verbunden – der Zustand gehört
|
||||
// schon dem neuen Aufbau, hier nichts mehr anfassen.
|
||||
return;
|
||||
}
|
||||
isConnecting = false;
|
||||
error = e.toString();
|
||||
_cleanUpAfterDisconnect();
|
||||
// NACH dem Aufräumen setzen, damit der Join-Fehler sichtbar bleibt.
|
||||
error = 'Beitritt fehlgeschlagen: $e';
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -556,6 +588,8 @@ class LiveKitCallManager extends VoiceChannelService {
|
||||
|
||||
@override
|
||||
Future<void> hangUp() async {
|
||||
// Entwertet einen evtl. noch laufenden Verbindungsaufbau (s. _callGeneration).
|
||||
_callGeneration++;
|
||||
_stopStatsLogging();
|
||||
_presenceHeartbeat?.cancel();
|
||||
_presenceHeartbeat = null;
|
||||
|
||||
Reference in New Issue
Block a user