Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/stream_video/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## Upcoming

### 🐞 Fixed
* Fixed race condition in `Call.join` when another connect is already in progress, with proper timeout handling.
* Fixed `consumeAndAcceptActiveCall` to ensure the coordinator WS is connected before consuming incoming calls during cold start.

## 1.3.0

### 🐞 Fixed
Expand Down
36 changes: 25 additions & 11 deletions packages/stream_video/lib/src/call/call.dart
Original file line number Diff line number Diff line change
Expand Up @@ -836,19 +836,33 @@ class Call {
return Result.error('a call with the same cid is in progress');
}

if (state.value.status is CallStatusConnecting) {
_logger.v(() => '[join] await "connecting" change');
if (state.value.status is CallStatusConnecting ||
state.value.status is CallStatusJoining) {
_logger.v(() => '[join] await ongoing connect to resolve');

final status = await state.firstWhere(
(it) => it.status is CallStatusConnected,
timeLimit: _stateManager.callState.preferences.connectTimeout,
);
try {
final currentState = await state.firstWhere(
(it) =>
it.status is! CallStatusConnecting &&
it.status is! CallStatusJoining,
timeLimit: _stateManager.callState.preferences.connectTimeout,
);

if (status is! CallStatusConnected) {
return const Result.success(none);
} else {
_logger.e(() => '[join] original "connect" failed');
return Result.error('original "connect" failed');
if (currentState.status is CallStatusConnected ||
currentState.status is CallStatusJoined) {
_logger.v(() => '[join] ongoing connect succeeded');
return const Result.success(none);
} else {
_logger.e(
() => '[join] ongoing connect failed: ${currentState.status}',
);
return Result.error(
'ongoing connect failed: ${currentState.status}',
);
}
} on TimeoutException {
_logger.e(() => '[join] timed out waiting for ongoing connect');
return Result.error('timed out waiting for ongoing connect');
}
}

Expand Down
50 changes: 34 additions & 16 deletions packages/stream_video/lib/src/stream_video.dart
Original file line number Diff line number Diff line change
Expand Up @@ -755,31 +755,49 @@ class StreamVideo extends Disposable {
final calls = await pushNotificationManager?.activeCalls();
if (calls == null || calls.isEmpty) return false;

// Ensure the coordinator WS is connected before proceeding.
// During cold start, autoConnect may still be in progress so we need to wait for it to complete.
final connectResult = await connect();
if (connectResult.isFailure) {
_logger.e(
() =>
'[consumeAndAcceptActiveCall] failed to connect: '
'${connectResult.getErrorOrNull()}',
);
return false;
}

final callResult = await consumeIncomingCall(
uuid: calls.first.uuid!,
cid: calls.first.callCid!,
preferences: callPreferences,
);

callResult.fold(
success: (result) async {
final call = result.data;
await call.accept();
if (callResult.isFailure) {
_logger.d(
() =>
'[consumeAndAcceptActiveCall] error consuming incoming call: '
'${callResult.getErrorOrNull()}',
);
return false;
}

onCallAccepted?.call(call);
final call = callResult.getDataOrNull();
if (call == null) return false;

return true;
},
failure: (error) {
_logger.d(
() =>
'[consumeAndAcceptActiveCall] error consuming incoming call: $error',
);
return false;
},
);
final acceptResult = await call.accept();
if (acceptResult.isFailure) {
_logger.d(
() =>
'[consumeAndAcceptActiveCall] error accepting call: '
'${acceptResult.getErrorOrNull()}',
);
return false;
}

onCallAccepted?.call(call);

return false;
return true;
}

@Deprecated('Use observeCoreRingingEvents instead.')
Expand Down
5 changes: 5 additions & 0 deletions packages/stream_video_push_notification/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## Upcoming

### 🐞 Fixed
* [iOS] Fixed race condition where push notification events could be lost if the Flutter EventChannel listener wasn't registered yet.

## 1.3.0
* Sync version with `stream_video_flutter` 1.3.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ public class StreamVideoPushNotificationPlugin: NSObject, FlutterPlugin {

public class EventCallbackHandler: NSObject, FlutterStreamHandler {
private var eventSink: FlutterEventSink?
private var pendingEvents: [[String: Any]] = []

public func send(_ event: String, _ body: Any) {
let data: [String: Any] = [
Expand All @@ -100,7 +101,12 @@ public class EventCallbackHandler: NSObject, FlutterStreamHandler {
]

DispatchQueue.main.async { [weak self] in
self?.eventSink?(data)
guard let self = self else { return }
if let eventSink = self.eventSink {
eventSink(data)
} else {
self.pendingEvents.append(data)
}
}
}

Expand All @@ -110,6 +116,10 @@ public class EventCallbackHandler: NSObject, FlutterStreamHandler {
-> FlutterError?
{
self.eventSink = events
for event in pendingEvents {
events(event)
}
pendingEvents.removeAll()
return nil
}

Expand Down
Loading