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:
Bernd Steckmeister
2026-07-06 22:50:58 +02:00
parent ffd70caffd
commit f8f5332d90
7 changed files with 407 additions and 10 deletions
+43 -7
View File
@@ -137,20 +137,56 @@ class _RoomsList extends ConsumerWidget {
context: context, builder: (_) => const CreateJoinDialog()),
),
if (!isCollapsed)
...filtered.map((room) => _RoomItem(
room: room,
isActive: activeRoomId == room.id,
pt: pt,
onTap: () {
...filtered.map((room) {
final isVoice = _isVoiceRoom(room);
return _RoomItem(
room: room,
isActive: isVoice ? false : activeRoomId == room.id,
isInVoice:
isVoice && ref.read(activeVoiceRoomIdProvider) == room.id,
pt: pt,
// Voice-Räume treten dem Kanal bei (wie in der Space-Liste)
// vorher öffnete der Klick hier nur die Chat-Ansicht, ein
// Standalone-Voice-Channel war per Klick gar nicht beitretbar.
onTap: () {
if (isVoice) {
_toggleVoiceChannel(ref, room);
} else {
ref.read(activeRoomIdProvider.notifier).state = room.id;
ref.read(viewModeProvider.notifier).state = ViewMode.chat;
},
)),
}
},
);
}),
],
);
}
}
/// Beitreten/Verlassen eines Voice-Channels aus der Raumliste exakt das
/// Verhalten von `joinVoiceChannel` in rooms_space_list.dart (Toggle beim
/// aktiven Kanal, Wechsel legt vorher auf).
Future<void> _toggleVoiceChannel(WidgetRef ref, Room room) async {
final call = ref.read(voiceChannelProvider);
final activeVoice = ref.read(activeVoiceRoomIdProvider);
if (call.isVoiceChannel && activeVoice == room.id) {
ref.read(activeVoiceRoomIdProvider.notifier).state = null;
await call.hangUp();
return;
}
if (call.isActive) await call.hangUp();
ref.read(activeVoiceRoomIdProvider.notifier).state = room.id;
await call.startCall(
roomName: room.id,
roomDisplayName: room.getLocalizedDisplayname(),
identity: room.client.userID ?? '',
audioOnly: true,
voiceChannel: true,
matrixClient: room.client,
matrixRoomId: room.id,
);
}
class _SectionHeader extends StatefulWidget {
final String title;
final int count;