Skip to content

Commit fd2dffb

Browse files
authored
Do not join active call (#499)
1 parent 844162f commit fd2dffb

File tree

7 files changed

+127
-36
lines changed

7 files changed

+127
-36
lines changed

dogfooding/lib/app/app_content.dart

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,15 @@
1-
// 🎯 Dart imports:
21
import 'dart:async';
32

4-
// 🐦 Flutter imports:
53
import 'package:firebase_core/firebase_core.dart';
4+
import 'package:firebase_messaging/firebase_messaging.dart';
65
import 'package:flutter/foundation.dart';
76
import 'package:flutter/material.dart';
8-
9-
// 📦 Package imports:
10-
import 'package:firebase_messaging/firebase_messaging.dart';
7+
import 'package:flutter_dogfooding/router/routes.dart';
118
import 'package:google_fonts/google_fonts.dart';
129
import 'package:stream_chat_flutter/stream_chat_flutter.dart' hide User;
1310
import 'package:stream_video_flutter/stream_video_flutter.dart';
1411
import 'package:uni_links/uni_links.dart';
1512

16-
// 🌎 Project imports:
17-
import 'package:flutter_dogfooding/router/routes.dart';
1813
import '../core/repos/app_preferences.dart';
1914
import '../di/injector.dart';
2015
import '../firebase_options.dart';
@@ -67,6 +62,7 @@ class _StreamDogFoodingAppContentState
6762
extends State<StreamDogFoodingAppContent> {
6863
late final _userAuthController = locator.get<UserAuthController>();
6964

65+
late final _logger = taggedLogger(tag: 'StreamDogFoodingAppContent');
7066
late final _router = initRouter(_userAuthController);
7167

7268
@override
@@ -150,6 +146,7 @@ class _StreamDogFoodingAppContentState
150146
}
151147

152148
void _onCallAccept(ActionCallAccept event) async {
149+
_logger.d(() => '[onCallAccept] event: $event');
153150
final streamVideo = locator.get<StreamVideo>();
154151

155152
final uuid = event.data.uuid;
@@ -163,7 +160,7 @@ class _StreamDogFoodingAppContentState
163160
var acceptResult = await callToJoin.accept();
164161

165162
// Return if cannot accept call
166-
if(acceptResult.isFailure) {
163+
if (acceptResult.isFailure) {
167164
debugPrint('Error accepting call: $call');
168165
return;
169166
}
@@ -177,6 +174,7 @@ class _StreamDogFoodingAppContentState
177174
}
178175

179176
void _onCallDecline(ActionCallDecline event) async {
177+
_logger.d(() => '[onCallDecline] event: $event');
180178
final streamVideo = locator.get<StreamVideo>();
181179

182180
final uuid = event.data.uuid;

packages/stream_video/lib/src/call/call.dart

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ typedef OnCallPermissionRequest = void Function(
3030
typedef GetCurrentUserId = String? Function();
3131

3232
typedef SetActiveCall = Future<void> Function(Call?);
33+
typedef GetActiveCallCid = StreamCallCid? Function();
3334

3435
const _idState = 1;
3536
const _idUserId = 2;
@@ -53,6 +54,7 @@ class Call {
5354
required CoordinatorClient coordinatorClient,
5455
required StateEmitter<User?> currentUser,
5556
required SetActiveCall setActiveCall,
57+
required GetActiveCallCid getActiveCallCid,
5658
RetryPolicy? retryPolicy,
5759
SdpPolicy? sdpPolicy,
5860
CallPreferences? preferences,
@@ -63,6 +65,7 @@ class Call {
6365
coordinatorClient: coordinatorClient,
6466
currentUser: currentUser,
6567
setActiveCall: setActiveCall,
68+
getActiveCallCid: getActiveCallCid,
6669
retryPolicy: retryPolicy,
6770
sdpPolicy: sdpPolicy,
6871
preferences: preferences,
@@ -77,6 +80,7 @@ class Call {
7780
required CoordinatorClient coordinatorClient,
7881
required StateEmitter<User?> currentUser,
7982
required SetActiveCall setActiveCall,
83+
required GetActiveCallCid getActiveCallCid,
8084
RetryPolicy? retryPolicy,
8185
SdpPolicy? sdpPolicy,
8286
CallPreferences? preferences,
@@ -87,6 +91,7 @@ class Call {
8791
coordinatorClient: coordinatorClient,
8892
currentUser: currentUser,
8993
setActiveCall: setActiveCall,
94+
getActiveCallCid: getActiveCallCid,
9095
retryPolicy: retryPolicy,
9196
sdpPolicy: sdpPolicy,
9297
preferences: preferences,
@@ -101,6 +106,7 @@ class Call {
101106
required CoordinatorClient coordinatorClient,
102107
required StateEmitter<User?> currentUser,
103108
required SetActiveCall setActiveCall,
109+
required GetActiveCallCid getActiveCallCid,
104110
RetryPolicy? retryPolicy,
105111
SdpPolicy? sdpPolicy,
106112
CallPreferences? preferences,
@@ -111,6 +117,7 @@ class Call {
111117
coordinatorClient: coordinatorClient,
112118
currentUser: currentUser,
113119
setActiveCall: setActiveCall,
120+
getActiveCallCid: getActiveCallCid,
114121
retryPolicy: retryPolicy,
115122
sdpPolicy: sdpPolicy,
116123
preferences: preferences,
@@ -122,6 +129,7 @@ class Call {
122129
required CoordinatorClient coordinatorClient,
123130
required StateEmitter<User?> currentUser,
124131
required SetActiveCall setActiveCall,
132+
required GetActiveCallCid getActiveCallCid,
125133
RetryPolicy? retryPolicy,
126134
SdpPolicy? sdpPolicy,
127135
CallPreferences? preferences,
@@ -145,6 +153,7 @@ class Call {
145153
coordinatorClient: coordinatorClient,
146154
currentUser: currentUser,
147155
setActiveCall: setActiveCall,
156+
getActiveCallCid: getActiveCallCid,
148157
preferences: finalCallPreferences,
149158
stateManager: stateManager,
150159
credentials: credentials,
@@ -157,6 +166,7 @@ class Call {
157166
Call._({
158167
required StateEmitter<User?> currentUser,
159168
required SetActiveCall setActiveCall,
169+
required GetActiveCallCid getActiveCallCid,
160170
required CoordinatorClient coordinatorClient,
161171
required CallPreferences preferences,
162172
required CallStateNotifier stateManager,
@@ -177,6 +187,7 @@ class Call {
177187
.whereNotNull()
178188
.distinct(),
179189
_setActiveCall = setActiveCall,
190+
_getActiveCallCid = getActiveCallCid,
180191
_coordinatorClient = coordinatorClient,
181192
_preferences = preferences,
182193
_retryPolicy = retryPolicy,
@@ -196,6 +207,7 @@ class Call {
196207
final GetCurrentUserId _getCurrentUserId;
197208
final Stream<String> _currentUserIdUpdates;
198209
final SetActiveCall _setActiveCall;
210+
final GetActiveCallCid _getActiveCallCid;
199211
final CoordinatorClient _coordinatorClient;
200212
final RetryPolicy _retryPolicy;
201213
final CallPreferences _preferences;
@@ -383,29 +395,35 @@ class Call {
383395

384396
@Deprecated('Lobby view no longer needs joining to coordinator')
385397
Future<Result<None>> joinLobby() async {
386-
_logger.d(() => '[join] no args');
398+
_logger.d(() => '[joinLobby] no args');
387399
_stateManager.lifecycleCallJoining(const CallJoining());
388400
final joinedResult = await _joinIfNeeded();
389401
if (joinedResult is Success<CallCredentials>) {
390-
_logger.v(() => '[join] completed');
402+
_logger.v(() => '[joinLobby] completed');
391403
return const Result.success(none);
392404
} else {
393405
final failedResult = joinedResult as Failure;
394-
_logger.e(() => '[join] failed: $failedResult');
406+
_logger.e(() => '[joinLobby] failed: $failedResult');
395407
final error = failedResult.error;
396408
_stateManager.lifecycleCallConnectFailed(ConnectFailed(error));
397409
return failedResult;
398410
}
399411
}
400412

401413
Future<Result<None>> join() async {
402-
_logger.i(() => '[connect] status: ${_status.value}');
414+
_logger.i(() => '[join] status: ${_status.value}');
403415
if (_status.value == _ConnectionStatus.connected) {
404-
_logger.w(() => '[connect] rejected (connected)');
416+
_logger.w(() => '[join] rejected (connected)');
405417
return const Result.success(none);
406418
}
419+
if (_getActiveCallCid() == callCid) {
420+
_logger.w(
421+
() => '[join] rejected (a call with the same cid is in progress)',
422+
);
423+
return Result.error('a call with the same cid is in progress');
424+
}
407425
if (_status.value == _ConnectionStatus.connecting) {
408-
_logger.v(() => '[connect] await "connecting" change');
426+
_logger.v(() => '[join] await "connecting" change');
409427
final status = await _status.firstWhere(
410428
(it) => it != _ConnectionStatus.connecting,
411429
timeLimit: _preferences.connectTimeout,
@@ -423,10 +441,10 @@ class Call {
423441
.storeIn(_idConnect, _cancelables)
424442
.valueOrDefault(Result.error('connect cancelled'));
425443
if (result.isSuccess) {
426-
_logger.v(() => '[connect] finished: $result');
444+
_logger.v(() => '[join] finished: $result');
427445
_status.value = _ConnectionStatus.connected;
428446
} else {
429-
_logger.e(() => '[connect] failed: $result');
447+
_logger.e(() => '[join] failed: $result');
430448
await leave();
431449
}
432450
return result;
@@ -458,26 +476,26 @@ class Call {
458476
}
459477

460478
Future<Result<None>> _connect() async {
461-
_logger.d(() => '[connect] options: $_connectOptions');
479+
_logger.d(() => '[join] options: $_connectOptions');
462480
final validation = await _stateManager.validateUserId(_getCurrentUserId);
463481
if (validation.isFailure) {
464-
_logger.w(() => '[connect] rejected (validation): $validation');
482+
_logger.w(() => '[join] rejected (validation): $validation');
465483
return validation;
466484
}
467-
_logger.v(() => '[connect] validated');
485+
_logger.v(() => '[join] validated');
468486

469487
final state = this.state.value;
470488
final status = state.status;
471489
if (!status.isConnectable) {
472-
_logger.w(() => '[connect] rejected (not Connectable): $status');
490+
_logger.w(() => '[join] rejected (not Connectable): $status');
473491
return Result.error('invalid status: $status');
474492
}
475493
_observeState();
476494
_observeEvents();
477495
_observeUserId();
478496
final result = await _awaitIfNeeded();
479497
if (result.isFailure) {
480-
_logger.e(() => '[connect] waiting failed: $result');
498+
_logger.e(() => '[join] waiting failed: $result');
481499

482500
_stateManager.lifecycleCallTimeout(const CallTimeout());
483501

@@ -486,28 +504,28 @@ class Call {
486504

487505
_stateManager
488506
.lifecycleCallConnectingAction(CallConnecting(_reconnectAttempt));
489-
_logger.v(() => '[connect] joining to coordinator');
507+
_logger.v(() => '[join] joining to coordinator');
490508
final joinedResult = await _joinIfNeeded();
491509
if (joinedResult is! Success<CallCredentials>) {
492-
_logger.e(() => '[connect] joining failed: $joinedResult');
510+
_logger.e(() => '[join] coordinator joining failed: $joinedResult');
493511
final error = (joinedResult as Failure).error;
494512
_stateManager.lifecycleCallConnectFailed(ConnectFailed(error));
495513
return result;
496514
}
497515

498-
_logger.v(() => '[connect] starting sfu session');
516+
_logger.v(() => '[join] starting sfu session');
499517
final sessionResult = await _startSession(joinedResult.data);
500518
if (sessionResult is! Success<None>) {
501-
_logger.w(() => '[connect] sfu session start failed: $sessionResult');
519+
_logger.w(() => '[join] sfu session start failed: $sessionResult');
502520
final error = (sessionResult as Failure).error;
503521
_stateManager.lifecycleCallConnectFailed(ConnectFailed(error));
504522
return sessionResult;
505523
}
506-
_logger.v(() => '[connect] started session');
524+
_logger.v(() => '[join] started session');
507525
_stateManager.lifecycleCallConnected(const CallConnected());
508526
await _applyConnectOptions();
509527

510-
_logger.v(() => '[connect] completed');
528+
_logger.v(() => '[join] completed');
511529
return const Result.success(none);
512530
}
513531

@@ -662,19 +680,19 @@ class Call {
662680

663681
Future<Result<None>> leave() async {
664682
final state = this.state.value;
665-
_logger.i(() => '[disconnect] ${_status.value}; state: $state');
683+
_logger.i(() => '[leave] ${_status.value}; state: $state');
666684
if (state.status.isDisconnected) {
667-
_logger.w(() => '[disconnect] rejected (state.status is disconnected)');
685+
_logger.w(() => '[leave] rejected (state.status is disconnected)');
668686
return const Result.success(none);
669687
}
670688
if (_status.value == _ConnectionStatus.disconnected) {
671-
_logger.w(() => '[disconnect] rejected (status is disconnected)');
689+
_logger.w(() => '[leave] rejected (status is disconnected)');
672690
return const Result.success(none);
673691
}
674692
_status.value = _ConnectionStatus.disconnected;
675-
await _clear('disconnect');
693+
await _clear('leave');
676694
_stateManager.lifecycleCallDisconnected(const CallDisconnected());
677-
_logger.v(() => '[disconnect] finished');
695+
_logger.v(() => '[leave] finished');
678696
return const Result.success(none);
679697
}
680698

packages/stream_video/lib/src/core/client_state.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import '../call/call.dart';
2+
import '../models/call_cid.dart';
23
import '../models/user.dart';
34
import '../shared_emitter.dart';
45
import '../state_emitter.dart';
@@ -53,6 +54,8 @@ class MutableClientState implements ClientState {
5354
connection.value = ConnectionState.disconnected(user.value.id);
5455
}
5556

57+
StreamCallCid? getActiveCallCid() => activeCall.valueOrNull!.callCid;
58+
5659
Future<void> setActiveCall(Call? call) async {
5760
final ongoingCall = activeCall.valueOrNull;
5861
if (ongoingCall != null && call != null) {

0 commit comments

Comments
 (0)