diff --git a/Assets/Plugins/StreamChat/Core/State/Caches/ICacheExt.cs b/Assets/Plugins/StreamChat/Core/State/Caches/ICacheExt.cs index da118495..8fc93f10 100644 --- a/Assets/Plugins/StreamChat/Core/State/Caches/ICacheExt.cs +++ b/Assets/Plugins/StreamChat/Core/State/Caches/ICacheExt.cs @@ -21,6 +21,14 @@ public static StreamChannel TryCreateOrUpdate(this ICache cache, ChannelResponse => dto == null ? null : cache.Channels.CreateOrUpdate(dto, out _); + + public static StreamChannel TryCreateOrUpdate(this ICache cache, ChannelResponseInternalDTO dto, out bool wasCreated) + { + wasCreated = false; + return dto == null + ? null + : cache.Channels.CreateOrUpdate(dto, out wasCreated); + } public static StreamChannel TryCreateOrUpdate(this ICache cache, ChannelStateResponseFieldsInternalDTO dto) => dto == null diff --git a/Assets/Plugins/StreamChat/Core/StreamChatClient.cs b/Assets/Plugins/StreamChat/Core/StreamChatClient.cs index 60bf0ea6..866817c8 100644 --- a/Assets/Plugins/StreamChat/Core/StreamChatClient.cs +++ b/Assets/Plugins/StreamChat/Core/StreamChatClient.cs @@ -864,45 +864,135 @@ private void OnMarkReadNotification(NotificationMarkReadEventInternalDTO eventDt private void OnAddedToChannelNotification(NotificationAddedToChannelEventInternalDTO eventDto) { - var channel = _cache.TryCreateOrUpdate(eventDto.Channel); + var channel = _cache.TryCreateOrUpdate(eventDto.Channel, out var wasCreated); var member = _cache.TryCreateOrUpdate(eventDto.Member); _cache.TryCreateOrUpdate(eventDto.Member.User); + + if (!wasCreated) + { + AddedToChannelAsMember?.Invoke(channel, member); + return; + } - AddedToChannelAsMember?.Invoke(channel, member); + // Watch channel, otherwise WS events won't be received + GetOrCreateChannelWithIdAsync(channel.Type, channel.Id).ContinueWith(t => + { + if (t.IsFaulted) + { + _logs.Error($"Failed to watch channel with type: {channel.Type} & id: {channel.Id} " + + $"before triggering the {nameof(AddedToChannelAsMember)} event. Inspect the following exception."); + _logs.Exception(t.Exception); + return; + } + + AddedToChannelAsMember?.Invoke(channel, member); + }); } private void OnRemovedFromChannelNotification( NotificationRemovedFromChannelEventInternalDTO eventDto) { - var channel = _cache.TryCreateOrUpdate(eventDto.Channel); + var channel = _cache.TryCreateOrUpdate(eventDto.Channel, out var wasCreated); var member = _cache.TryCreateOrUpdate(eventDto.Member); _cache.TryCreateOrUpdate(eventDto.Member.User); - RemovedFromChannelAsMember?.Invoke(channel, member); + if (!wasCreated) + { + RemovedFromChannelAsMember?.Invoke(channel, member); + return; + } + + // Watch channel, otherwise WS events won't be received + GetOrCreateChannelWithIdAsync(channel.Type, channel.Id).ContinueWith(t => + { + if (t.IsFaulted) + { + _logs.Error($"Failed to watch channel with type: {channel.Type} & id: {channel.Id} " + + $"before triggering the {nameof(RemovedFromChannelAsMember)} event. Inspect the following exception."); + _logs.Exception(t.Exception); + return; + } + + RemovedFromChannelAsMember?.Invoke(channel, member); + }); } private void OnInvitedNotification(NotificationInvitedEventInternalDTO eventDto) { - var channel = _cache.TryCreateOrUpdate(eventDto.Channel); + var channel = _cache.TryCreateOrUpdate(eventDto.Channel, out var wasCreated); var user = _cache.TryCreateOrUpdate(eventDto.User); + + if (!wasCreated) + { + ChannelInviteReceived?.Invoke(channel, user); + return; + } + + // Watch channel, otherwise WS events won't be received + GetOrCreateChannelWithIdAsync(channel.Type, channel.Id).ContinueWith(t => + { + if (t.IsFaulted) + { + _logs.Error($"Failed to watch channel with type: {channel.Type} & id: {channel.Id} " + + $"before triggering the {nameof(ChannelInviteReceived)} event. Inspect the following exception."); + _logs.Exception(t.Exception); + return; + } - ChannelInviteReceived?.Invoke(channel, user); + ChannelInviteReceived?.Invoke(channel, user); + }); } private void OnInviteAcceptedNotification(NotificationInviteAcceptedEventInternalDTO eventDto) { - var channel = _cache.TryCreateOrUpdate(eventDto.Channel); + var channel = _cache.TryCreateOrUpdate(eventDto.Channel, out var wasCreated); var user = _cache.TryCreateOrUpdate(eventDto.User); + + if (!wasCreated) + { + ChannelInviteAccepted?.Invoke(channel, user); + return; + } + + // Watch channel, otherwise WS events won't be received + GetOrCreateChannelWithIdAsync(channel.Type, channel.Id).ContinueWith(t => + { + if (t.IsFaulted) + { + _logs.Error($"Failed to watch channel with type: {channel.Type} & id: {channel.Id} " + + $"before triggering the {nameof(ChannelInviteAccepted)} event. Inspect the following exception."); + _logs.Exception(t.Exception); + return; + } - ChannelInviteAccepted?.Invoke(channel, user); + ChannelInviteAccepted?.Invoke(channel, user); + }); } private void OnInviteRejectedNotification(NotificationInviteRejectedEventInternalDTO eventDto) { - var channel = _cache.TryCreateOrUpdate(eventDto.Channel); + var channel = _cache.TryCreateOrUpdate(eventDto.Channel, out var wasCreated); var user = _cache.TryCreateOrUpdate(eventDto.User); + + if (!wasCreated) + { + ChannelInviteRejected?.Invoke(channel, user); + return; + } + + // Watch channel, otherwise WS events won't be received + GetOrCreateChannelWithIdAsync(channel.Type, channel.Id).ContinueWith(t => + { + if (t.IsFaulted) + { + _logs.Error($"Failed to watch channel with type: {channel.Type} & id: {channel.Id} " + + $"before triggering the {nameof(ChannelInviteRejected)} event. Inspect the following exception."); + _logs.Exception(t.Exception); + return; + } - ChannelInviteRejected?.Invoke(channel, user); + ChannelInviteRejected?.Invoke(channel, user); + }); } private void OnReactionReceived(ReactionNewEventInternalDTO eventDto) diff --git a/Assets/Plugins/StreamChat/Tests/StatefulClient/BaseStateIntegrationTests.cs b/Assets/Plugins/StreamChat/Tests/StatefulClient/BaseStateIntegrationTests.cs index 4dd45c40..c39c9e1d 100644 --- a/Assets/Plugins/StreamChat/Tests/StatefulClient/BaseStateIntegrationTests.cs +++ b/Assets/Plugins/StreamChat/Tests/StatefulClient/BaseStateIntegrationTests.cs @@ -49,11 +49,12 @@ protected static IEnumerable OtherAdminUsersCredentials /// /// Create temp channel with random id that will be removed in [TearDown] /// - protected async Task CreateUniqueTempChannelAsync(string name = null, bool watch = true) + protected async Task CreateUniqueTempChannelAsync(string name = null, bool watch = true, StreamChatClient overrideClient = null) { var channelId = "random-channel-11111-" + Guid.NewGuid(); + var client = overrideClient ?? Client; - var channelState = await Client.InternalGetOrCreateChannelWithIdAsync(ChannelType.Messaging, channelId, name, watch: watch); + var channelState = await client.InternalGetOrCreateChannelWithIdAsync(ChannelType.Messaging, channelId, name, watch: watch); _tempChannels.Add(channelState); return channelState; } @@ -89,7 +90,7 @@ protected static IEnumerator ConnectAndExecute(Func test) yield return ConnectAndExecuteAsync(test).RunAsIEnumerator(statefulClient: Client); } - protected Task GetConnectedOtherClient() + protected Task GetConnectedOtherClient() => StreamTestClients.Instance.ConnectOtherStateClientAsync(); //StreamTodo: figure out syntax to wrap call in using that will subscribe to observing an event if possible diff --git a/Assets/Plugins/StreamChat/Tests/StatefulClient/ChannelMembersTests.cs b/Assets/Plugins/StreamChat/Tests/StatefulClient/ChannelMembersTests.cs index c29c145d..cbcb3e2e 100644 --- a/Assets/Plugins/StreamChat/Tests/StatefulClient/ChannelMembersTests.cs +++ b/Assets/Plugins/StreamChat/Tests/StatefulClient/ChannelMembersTests.cs @@ -335,6 +335,53 @@ private async Task When_user_added_to_not_watched_channel_expect_user_receive_ad Assert.AreEqual(channel, eventChannel); Assert.AreEqual(Client.LocalUserData.User, eventMember.User); } + + [UnityTest] + public IEnumerator When_user_added_to_not_watched_channel_expect_received_channel_being_watched() + => ConnectAndExecute( + When_user_added_to_not_watched_channel_expect_received_channel_being_watched_Async); + + private async Task When_user_added_to_not_watched_channel_expect_received_channel_being_watched_Async() + { + var otherClient = await GetConnectedOtherClient(); + var otherClientChannel = await CreateUniqueTempChannelAsync(watch: false, overrideClient: otherClient); + + var receivedEvent = false; + IStreamChannelMember eventMember = null; + IStreamChannel eventChannel = null; + Client.AddedToChannelAsMember += (channel2, member) => + { + receivedEvent = true; + eventMember = member; + eventChannel = channel2; + }; + + await otherClientChannel.AddMembersAsync(hideHistory: default, optionalMessage: default, Client.LocalUserData.User); + await WaitWhileFalseAsync(() => receivedEvent); + + Assert.IsTrue(receivedEvent); + Assert.IsNotNull(eventChannel); + Assert.IsNotNull(eventMember); + Assert.AreEqual(otherClientChannel.Cid, eventChannel.Cid); + Assert.AreEqual(Client.LocalUserData.User, eventMember.User); + + var receivedMessageEvent = false; + var receivedMessage = string.Empty; + IStreamChannel receivedMessageChannel = null; + otherClientChannel.MessageReceived += (messageChannel, message) => + { + receivedMessageEvent = true; + receivedMessage = message.Text; + receivedMessageChannel = messageChannel; + }; + + await otherClientChannel.SendNewMessageAsync("Hello"); + await WaitWhileFalseAsync(() => receivedEvent); + + Assert.IsTrue(receivedMessageEvent); + Assert.AreEqual(otherClientChannel.Cid, receivedMessageChannel.Cid); + Assert.AreEqual(receivedMessage, "Hello"); + } [UnityTest] public IEnumerator When_user_removed_from_not_watched_channel_expect_user_removed_from_channel_event() @@ -371,6 +418,7 @@ private async Task When_user_removed_from_not_watched_channel_expect_user_remove Assert.AreEqual(channel, eventChannel); Assert.AreEqual(Client.LocalUserData.User, eventMember.User); } + } } #endif \ No newline at end of file diff --git a/Assets/Plugins/StreamChat/Tests/StreamTestClients.cs b/Assets/Plugins/StreamChat/Tests/StreamTestClients.cs index 706ea1c7..fd5936ac 100644 --- a/Assets/Plugins/StreamChat/Tests/StreamTestClients.cs +++ b/Assets/Plugins/StreamChat/Tests/StreamTestClients.cs @@ -96,7 +96,7 @@ public IEnumerator ReconnectLowLevelClientClient() public Task ConnectStateClientAsync() => ConnectStateClientAsync(StateClient, _stateClientCredentials); - public Task ConnectOtherStateClientAsync() + public Task ConnectOtherStateClientAsync() => ConnectStateClientAsync(OtherStateClient, _otherUserCredentials); private static StreamTestClients _instance; @@ -132,7 +132,7 @@ private StreamTestClients() _otherUsersCredentials = adminData.Skip(3).ToList(); } - private static async Task ConnectStateClientAsync(IStreamChatClient client, + private static async Task ConnectStateClientAsync(StreamChatClient client, AuthCredentials credentials) { if (client.IsConnected) @@ -151,6 +151,7 @@ private static async Task ConnectStateClientAsync(IStreamChat Debug.Log($"Wait for {nameof(StatefulClient)} to connect user with ID: {credentials.UserId}"); #endif + client.LowLevelClient.Update(0.1f); await Task.Delay(1); if (timer.ElapsedMilliseconds > timeout)