diff --git a/Packages/StreamVideo/Runtime/Core/LowLevelClient/RtcSession.cs b/Packages/StreamVideo/Runtime/Core/LowLevelClient/RtcSession.cs index e6d5ce9c..d3cb74e7 100644 --- a/Packages/StreamVideo/Runtime/Core/LowLevelClient/RtcSession.cs +++ b/Packages/StreamVideo/Runtime/Core/LowLevelClient/RtcSession.cs @@ -415,7 +415,7 @@ public async Task StopAsync(string reason = "") } catch (Exception e) { - _logs.Error($"Failed to send final stats on leave: {e.Message}"); + _logs.Warning($"Failed to send final stats on leave: {e.Message}"); } #if STREAM_DEBUG_ENABLED diff --git a/Packages/StreamVideo/Runtime/Core/LowLevelClient/WebSockets/SfuWebSocket.cs b/Packages/StreamVideo/Runtime/Core/LowLevelClient/WebSockets/SfuWebSocket.cs index 60030a9b..9d22123e 100644 --- a/Packages/StreamVideo/Runtime/Core/LowLevelClient/WebSockets/SfuWebSocket.cs +++ b/Packages/StreamVideo/Runtime/Core/LowLevelClient/WebSockets/SfuWebSocket.cs @@ -77,7 +77,7 @@ public void SendLeaveCallRequest(string reason = "") if (reason == null) { - throw new ArgumentException($"{nameof(reason)} is null."); + reason = "reason not provided"; } #if STREAM_DEBUG_ENABLED @@ -298,15 +298,22 @@ protected override async Task OnDisconnectingAsync(string closeMessage) using (new TimeLogScope("Sending leave call request", Logs.Info)) { - SendLeaveCallRequest(closeMessage); - - for (int i = 0; i < 60; i++) + try { - if (SendQueueCount > 0) + SendLeaveCallRequest(closeMessage); + + for (int i = 0; i < 60; i++) { - await Task.Delay(5); + if (SendQueueCount > 0) + { + await Task.Delay(5); + } } } + catch (Exception e) + { + Logs.Warning("Failed to send LeaveCallRequest during disconnect: " + e.Message); + } } await base.OnDisconnectingAsync(closeMessage); diff --git a/Packages/StreamVideo/Runtime/Core/Models/CallSession.cs b/Packages/StreamVideo/Runtime/Core/Models/CallSession.cs index 8ed29a77..cd79e4cb 100644 --- a/Packages/StreamVideo/Runtime/Core/Models/CallSession.cs +++ b/Packages/StreamVideo/Runtime/Core/Models/CallSession.cs @@ -53,8 +53,20 @@ void IStateLoadableFrom.LoadFromDto _acceptedBy.TryReplaceValuesFromDto(dto.AcceptedBy); EndedAt = dto.EndedAt; Id = dto.Id; - _participants.TryReplaceTrackedObjects(dto.Participants, cache.CallParticipants); - _participantsCountByRole.TryReplaceValuesFromDto(dto.ParticipantsCountByRole); + + // CallSessionResponseInternalDTO usually (or always?) contains no participants. Participants are updated from the SFU join response + // But SFU response can arrive before API response, so we can't override participants here because this clears the list + foreach (var dtoParticipant in dto.Participants) + { + var participant = cache.TryCreateOrUpdate(dtoParticipant); + if (!_participants.Contains(participant)) + { + _participants.Add(participant); + } + } + + // StreamTODO: figure out how to best handle this. Should we update it from coordinator or only the SFU + //_participantsCountByRole.TryReplaceValuesFromDto(dto.ParticipantsCountByRole); _rejectedBy.TryReplaceValuesFromDto(dto.RejectedBy); StartedAt = dto.StartedAt; LiveEndedAt = dto.LiveEndedAt;