feat: auto-end call when remote party leaves
Listens for ParticipantDisconnectedEvent on the LiveKit Room. When no remote participants remain (1:1 call ended by other side), waits 5 seconds then auto-hangs up. The delay allows for brief reconnections. Also ensures our app properly cleans up when Element X user hangs up on their end — our side won't sit connected to an empty LiveKit room. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
// Version: 1.2.0 | Created: 2026-04-01 | Updated: 2026-04-02
|
||||
// Version: 1.3.0 | Created: 2026-04-01 | Updated: 2026-04-03
|
||||
// Call controller — manages LiveKit room connection lifecycle.
|
||||
// Transitions through idle → connecting → active → ended states.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:livekit_client/livekit_client.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
@@ -16,12 +17,15 @@ part 'call_controller.g.dart';
|
||||
class CallController extends _$CallController {
|
||||
Timer? _durationTimer;
|
||||
Duration _elapsed = Duration.zero;
|
||||
EventsListener<RoomEvent>? _roomListener;
|
||||
|
||||
@override
|
||||
CallState build() {
|
||||
ref.onDispose(() {
|
||||
_durationTimer?.cancel();
|
||||
_durationTimer = null;
|
||||
_roomListener?.dispose();
|
||||
_roomListener = null;
|
||||
ref.read(liveKitServiceProvider).disconnect();
|
||||
});
|
||||
return const CallState.idle();
|
||||
@@ -112,5 +116,26 @@ class CallController extends _$CallController {
|
||||
state = current.copyWith(duration: _elapsed);
|
||||
}
|
||||
});
|
||||
|
||||
// Auto-end when the remote party leaves (1:1 call behaviour).
|
||||
// Wait 5 seconds after the last remote participant disconnects before
|
||||
// ending — gives time for brief network interruptions to recover.
|
||||
_roomListener?.dispose();
|
||||
_roomListener = room.createListener();
|
||||
_roomListener!.on<ParticipantDisconnectedEvent>((event) {
|
||||
debugPrint(
|
||||
'[Call] Participant left: ${event.participant.identity}, '
|
||||
'remaining: ${room.remoteParticipants.length}',
|
||||
);
|
||||
if (room.remoteParticipants.isEmpty) {
|
||||
debugPrint('[Call] No remote participants — auto-ending in 5s');
|
||||
Future.delayed(const Duration(seconds: 5), () {
|
||||
// Re-check in case someone rejoined during the delay.
|
||||
if (room.remoteParticipants.isEmpty && state is CallActive) {
|
||||
endCall();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user