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.
|
// Call controller — manages LiveKit room connection lifecycle.
|
||||||
// Transitions through idle → connecting → active → ended states.
|
// Transitions through idle → connecting → active → ended states.
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:livekit_client/livekit_client.dart';
|
import 'package:livekit_client/livekit_client.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
|
||||||
@@ -16,12 +17,15 @@ part 'call_controller.g.dart';
|
|||||||
class CallController extends _$CallController {
|
class CallController extends _$CallController {
|
||||||
Timer? _durationTimer;
|
Timer? _durationTimer;
|
||||||
Duration _elapsed = Duration.zero;
|
Duration _elapsed = Duration.zero;
|
||||||
|
EventsListener<RoomEvent>? _roomListener;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
CallState build() {
|
CallState build() {
|
||||||
ref.onDispose(() {
|
ref.onDispose(() {
|
||||||
_durationTimer?.cancel();
|
_durationTimer?.cancel();
|
||||||
_durationTimer = null;
|
_durationTimer = null;
|
||||||
|
_roomListener?.dispose();
|
||||||
|
_roomListener = null;
|
||||||
ref.read(liveKitServiceProvider).disconnect();
|
ref.read(liveKitServiceProvider).disconnect();
|
||||||
});
|
});
|
||||||
return const CallState.idle();
|
return const CallState.idle();
|
||||||
@@ -112,5 +116,26 @@ class CallController extends _$CallController {
|
|||||||
state = current.copyWith(duration: _elapsed);
|
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