< Summary

Class:DCL.Chat.HUD.ChatChannelHUDController
Assembly:WorldChatWindowHUD
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/WorldChatWindowHUD/ChatChannelHUDController.cs
Covered lines:147
Uncovered lines:34
Coverable lines:181
Total lines:388
Line coverage:81.2% (147 of 181)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
ChatChannelHUDController(...)0%110100%
Initialize(...)0%330100%
Setup(...)0%110100%
SetVisibility(...)0%77095.65%
Dispose()0%330100%
HandleSendChatMessage(...)0%5.735069.23%
HandleMessageReceived(...)0%660100%
UpdateOldestMessage(...)0%3.073080%
Hide()0%6200%
HandlePressBack()0%6200%
IsMessageFomCurrentChannel(...)0%440100%
MarkChannelMessagesAsRead()0%110100%
HandleChatInputTriggered(...)0%12300%
RequestMessages(...)0%220100%
RequestOldConversations()0%30500%
IsLoadingMessages()0%2100%
WaitForRequestTimeOutThenHideLoadingFeedback()0%6.056088.89%
LeaveChannel()0%220100%
LeaveChannelFromCommand()0%110100%
HandleChannelLeft(...)0%3.143075%
HandleChannelUpdated(...)0%6200%
ShowMembersList()0%2100%
HideMembersList()0%2100%
MuteChannel(...)0%220100%
SetVisiblePanelList(...)0%220100%
ToPublicChatModel(...)0%110100%
ClearChatControllerListeners()0%2.022083.33%
Focus()0%110100%
HandleMessageBlockedBySpam(...)0%2100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/WorldChatWindowHUD/ChatChannelHUDController.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Threading;
 4using Cysharp.Threading.Tasks;
 5using DCL.Interface;
 6using SocialFeaturesAnalytics;
 7using UnityEngine;
 8using Channel = DCL.Chat.Channels.Channel;
 9
 10namespace DCL.Chat.HUD
 11{
 12    public class ChatChannelHUDController : IHUD
 13    {
 14        private const int INITIAL_PAGE_SIZE = 30;
 15        private const int SHOW_MORE_PAGE_SIZE = 10;
 16        private const float REQUEST_MESSAGES_TIME_OUT = 2;
 17
 8518        public IChatChannelWindowView View { get; private set; }
 19
 20        private readonly DataStore dataStore;
 821        private BaseVariable<HashSet<string>> visibleTaskbarPanels => dataStore.HUDs.visibleTaskbarPanels;
 22        private readonly IUserProfileBridge userProfileBridge;
 23        private readonly IChatController chatController;
 24        private readonly IMouseCatcher mouseCatcher;
 25        private readonly InputAction_Trigger toggleChatTrigger;
 26        private readonly ISocialAnalytics socialAnalytics;
 27        private readonly IProfanityFilter profanityFilter;
 28        private ChatHUDController chatHudController;
 29        private ChannelMembersHUDController channelMembersHUDController;
 730        private CancellationTokenSource hideLoadingCancellationToken = new CancellationTokenSource();
 31        private bool skipChatInputTrigger;
 32        private float lastRequestTime;
 33        private string channelId;
 34        private Channel channel;
 35        private ChatMessage oldestMessage;
 836        private bool showOnlyOnlineMembersOnPublicChannels => !dataStore.featureFlags.flags.Get().IsFeatureEnabled("matr
 37
 38        public event Action OnPressBack;
 39        public event Action OnClosed;
 40        public event Action<string> OnOpenChannelLeave;
 41
 742        public ChatChannelHUDController(DataStore dataStore,
 43            IUserProfileBridge userProfileBridge,
 44            IChatController chatController,
 45            IMouseCatcher mouseCatcher,
 46            InputAction_Trigger toggleChatTrigger,
 47            ISocialAnalytics socialAnalytics,
 48            IProfanityFilter profanityFilter)
 49        {
 750            this.dataStore = dataStore;
 751            this.userProfileBridge = userProfileBridge;
 752            this.chatController = chatController;
 753            this.mouseCatcher = mouseCatcher;
 754            this.toggleChatTrigger = toggleChatTrigger;
 755            this.socialAnalytics = socialAnalytics;
 756            this.profanityFilter = profanityFilter;
 757        }
 58
 59        public void Initialize(IChatChannelWindowView view = null)
 60        {
 761            view ??= ChatChannelComponentView.Create();
 762            View = view;
 763            view.OnBack -= HandlePressBack;
 764            view.OnBack += HandlePressBack;
 765            view.OnClose -= Hide;
 766            view.OnClose += Hide;
 767            view.OnRequireMoreMessages += RequestOldConversations;
 768            view.OnLeaveChannel += LeaveChannel;
 769            view.OnShowMembersList += ShowMembersList;
 770            view.OnHideMembersList += HideMembersList;
 771            view.OnMuteChanged += MuteChannel;
 72
 773            chatHudController = new ChatHUDController(dataStore, userProfileBridge, false, profanityFilter);
 774            chatHudController.Initialize(view.ChatHUD);
 775            chatHudController.OnSendMessage += HandleSendChatMessage;
 776            chatHudController.OnMessageSentBlockedBySpam += HandleMessageBlockedBySpam;
 77
 778            if (mouseCatcher != null)
 679                mouseCatcher.OnMouseLock += Hide;
 80
 781            toggleChatTrigger.OnTriggered += HandleChatInputTriggered;
 82
 783            channelMembersHUDController = new ChannelMembersHUDController(view.ChannelMembersHUD, chatController, userPr
 784        }
 85
 86        public void Setup(string channelId)
 87        {
 688            channelMembersHUDController.SetChannelId(channelId);
 689            this.channelId = channelId;
 690            lastRequestTime = 0;
 91
 692            channel = chatController.GetAllocatedChannel(channelId);
 693            View.Setup(ToPublicChatModel(channel));
 94
 695            chatHudController.ClearAllEntries();
 696            oldestMessage = null;
 697        }
 98
 99        public void SetVisibility(bool visible)
 100        {
 4101            SetVisiblePanelList(visible);
 102
 4103            if (visible)
 104            {
 3105                ClearChatControllerListeners();
 106
 3107                chatController.OnAddMessage += HandleMessageReceived;
 3108                chatController.OnChannelLeft += HandleChannelLeft;
 3109                chatController.OnChannelUpdated += HandleChannelUpdated;
 110
 3111                if (channelMembersHUDController.IsVisible)
 0112                    channelMembersHUDController.SetAutomaticReloadingActive(true);
 113
 3114                View?.SetLoadingMessagesActive(false);
 3115                View?.SetOldMessagesLoadingActive(false);
 116
 3117                if (!string.IsNullOrEmpty(channelId))
 118                {
 2119                    var channel = chatController.GetAllocatedChannel(channelId);
 2120                    View.Setup(ToPublicChatModel(channel));
 121
 2122                    RequestMessages(
 123                        channelId,
 124                        INITIAL_PAGE_SIZE);
 125                }
 126
 3127                View.Show();
 3128                Focus();
 129            }
 130            else
 131            {
 1132                ClearChatControllerListeners();
 133
 1134                channelMembersHUDController.SetAutomaticReloadingActive(false);
 1135                chatHudController.UnfocusInputField();
 1136                OnClosed?.Invoke();
 1137                View.Hide();
 138            }
 139
 4140            dataStore.channels.channelToBeOpened.Set(null, notifyEvent: false);
 4141        }
 142
 143        public void Dispose()
 144        {
 7145            ClearChatControllerListeners();
 146
 7147            if (mouseCatcher != null)
 6148                mouseCatcher.OnMouseLock -= Hide;
 149
 7150            toggleChatTrigger.OnTriggered -= HandleChatInputTriggered;
 151
 7152            chatHudController.OnSendMessage -= HandleSendChatMessage;
 7153            chatHudController.OnMessageSentBlockedBySpam -= HandleMessageBlockedBySpam;
 154
 7155            if (View != null)
 156            {
 7157                View.OnBack -= HandlePressBack;
 7158                View.OnClose -= Hide;
 7159                View.OnRequireMoreMessages -= RequestOldConversations;
 7160                View.OnLeaveChannel -= LeaveChannel;
 7161                View.OnMuteChanged -= MuteChannel;
 7162                View.Dispose();
 163            }
 164
 7165            hideLoadingCancellationToken.Dispose();
 7166            channelMembersHUDController.Dispose();
 7167        }
 168
 169        private void HandleSendChatMessage(ChatMessage message)
 170        {
 1171            message.messageType = ChatMessage.Type.PUBLIC;
 1172            message.recipient = channelId;
 173
 1174            var isValidMessage = !string.IsNullOrEmpty(message.body)
 175                                 && !string.IsNullOrWhiteSpace(message.body)
 176                                 && !string.IsNullOrEmpty(message.recipient);
 177
 1178            if (isValidMessage)
 179            {
 1180                chatHudController.ResetInputField();
 1181                chatHudController.FocusInputField();
 182            }
 183            else
 184            {
 0185                SetVisibility(false);
 0186                return;
 187            }
 188
 1189            if (message.body.ToLower().Equals("/leave"))
 190            {
 1191                LeaveChannelFromCommand();
 1192                return;
 193            }
 194
 0195            chatController.Send(message);
 0196        }
 197
 198        private void HandleMessageReceived(ChatMessage[] messages)
 199        {
 1200            var messageLogUpdated = false;
 201
 6202            foreach (var message in messages)
 203            {
 2204                if (!IsMessageFomCurrentChannel(message)) continue;
 205
 2206                UpdateOldestMessage(message);
 207
 2208                message.isChannelMessage = true;
 209                // TODO: right now the channel history is disabled, but we must find a workaround to support history + m
 210                // one approach could be to increment the max amount of messages depending on how many pages you loaded 
 211                // for example: 1 page = 30 messages, 2 pages = 60 messages, and so on..
 2212                chatHudController.AddChatMessage(message, limitMaxEntries: true);
 213
 2214                View?.SetLoadingMessagesActive(false);
 2215                View?.SetOldMessagesLoadingActive(false);
 216
 2217                messageLogUpdated = true;
 218            }
 219
 1220            if (View.IsActive && messageLogUpdated)
 221            {
 222                // The messages from 'channelId' are marked as read if the channel window is currently open
 1223                MarkChannelMessagesAsRead();
 224            }
 1225        }
 226
 227        private void UpdateOldestMessage(ChatMessage message)
 228        {
 2229            if (oldestMessage == null)
 1230                oldestMessage = message;
 1231            else if (message.timestamp < oldestMessage.timestamp)
 0232                oldestMessage = message;
 1233        }
 234
 235        private void Hide()
 236        {
 0237            SetVisibility(false);
 0238            OnClosed?.Invoke();
 0239        }
 240
 0241        private void HandlePressBack() => OnPressBack?.Invoke();
 242
 243        private bool IsMessageFomCurrentChannel(ChatMessage message) =>
 2244            message.sender == channelId || message.recipient == channelId || (View.IsActive && message.messageType == Ch
 245
 4246        private void MarkChannelMessagesAsRead() => chatController.MarkChannelMessagesAsSeen(channelId);
 247
 248        private void HandleChatInputTriggered(DCLAction_Trigger action)
 249        {
 250            // race condition patch caused by unfocusing input field from invalid message on SendChatMessage
 251            // chat input trigger is the same key as sending the chat message from the input field
 0252            if (skipChatInputTrigger)
 253            {
 0254                skipChatInputTrigger = false;
 0255                return;
 256            }
 257
 0258            if (!View.IsActive) return;
 0259            chatHudController.FocusInputField();
 0260        }
 261
 262        private void RequestMessages(string channelId, int limit, string fromMessageId = null)
 263        {
 2264            View?.SetLoadingMessagesActive(true);
 2265            chatController.GetChannelMessages(channelId, limit, fromMessageId);
 2266            hideLoadingCancellationToken.Cancel();
 2267            hideLoadingCancellationToken = new CancellationTokenSource();
 2268            WaitForRequestTimeOutThenHideLoadingFeedback(hideLoadingCancellationToken.Token).Forget();
 2269        }
 270
 271        private void RequestOldConversations()
 272        {
 0273            if (IsLoadingMessages()) return;
 274
 0275            View?.SetOldMessagesLoadingActive(true);
 0276            lastRequestTime = Time.realtimeSinceStartup;
 277
 0278            chatController.GetChannelMessages(
 279                channelId,
 280                SHOW_MORE_PAGE_SIZE,
 281                oldestMessage?.messageId);
 282
 0283            hideLoadingCancellationToken.Cancel();
 0284            hideLoadingCancellationToken = new CancellationTokenSource();
 0285            WaitForRequestTimeOutThenHideLoadingFeedback(hideLoadingCancellationToken.Token).Forget();
 0286        }
 287
 288        private bool IsLoadingMessages() =>
 0289            Time.realtimeSinceStartup - lastRequestTime < REQUEST_MESSAGES_TIME_OUT;
 290
 291        private async UniTaskVoid WaitForRequestTimeOutThenHideLoadingFeedback(CancellationToken cancellationToken)
 292        {
 2293            lastRequestTime = Time.realtimeSinceStartup;
 294
 6295            await UniTask.WaitUntil(() =>
 446296                    Time.realtimeSinceStartup - lastRequestTime > REQUEST_MESSAGES_TIME_OUT,
 297                cancellationToken: cancellationToken);
 2298            if (cancellationToken.IsCancellationRequested) return;
 299
 2300            View?.SetLoadingMessagesActive(false);
 2301            View?.SetOldMessagesLoadingActive(false);
 2302        }
 303
 304        private void LeaveChannel()
 305        {
 1306            dataStore.channels.channelLeaveSource.Set(ChannelLeaveSource.Chat);
 1307            OnOpenChannelLeave?.Invoke(channelId);
 1308        }
 309
 310        private void LeaveChannelFromCommand()
 311        {
 1312            dataStore.channels.channelLeaveSource.Set(ChannelLeaveSource.Command);
 1313            chatController.LeaveChannel(channelId);
 1314        }
 315
 316        private void HandleChannelLeft(string channelId)
 317        {
 1318            if (channelId != this.channelId) return;
 1319            OnPressBack?.Invoke();
 1320        }
 321
 322        private void HandleChannelUpdated(Channel updatedChannel)
 323        {
 0324            if (updatedChannel.ChannelId != channelId)
 0325                return;
 326
 0327            View.Setup(ToPublicChatModel(updatedChannel));
 0328            channelMembersHUDController.SetMembersCount(updatedChannel.MemberCount);
 0329        }
 330
 0331        private void ShowMembersList() => channelMembersHUDController.SetVisibility(true);
 332
 0333        private void HideMembersList() => channelMembersHUDController.SetVisibility(false);
 334
 335        private void MuteChannel(bool muted)
 336        {
 2337            if (muted)
 1338                chatController.MuteChannel(channelId);
 339            else
 1340                chatController.UnmuteChannel(channelId);
 1341        }
 342
 343        private void SetVisiblePanelList(bool visible)
 344        {
 4345            var newSet = visibleTaskbarPanels.Get();
 346
 4347            if (visible)
 3348                newSet.Add("ChatChannel");
 349            else
 1350                newSet.Remove("ChatChannel");
 351
 4352            visibleTaskbarPanels.Set(newSet, true);
 4353        }
 354
 355        private PublicChatModel ToPublicChatModel(Channel channel)
 356        {
 8357            return new PublicChatModel(channelId, channel.Name, channel.Description,
 358                channel.Joined, channel.MemberCount, channel.Muted,
 359                showOnlyOnlineMembersOnPublicChannels);
 360        }
 361
 362        private void ClearChatControllerListeners()
 363        {
 11364            if (chatController == null) return;
 11365            chatController.OnAddMessage -= HandleMessageReceived;
 11366            chatController.OnChannelLeft -= HandleChannelLeft;
 11367            chatController.OnChannelUpdated -= HandleChannelUpdated;
 11368        }
 369
 370        private void Focus()
 371        {
 3372            chatHudController.FocusInputField();
 3373            MarkChannelMessagesAsRead();
 3374        }
 375
 376        private void HandleMessageBlockedBySpam(ChatMessage message)
 377        {
 0378            chatHudController.AddChatMessage(new ChatEntryModel
 379            {
 380                timestamp = (ulong) DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
 381                bodyText = "You sent too many messages in a short period of time. Please wait and try again later.",
 382                messageId = Guid.NewGuid().ToString(),
 383                messageType = ChatMessage.Type.SYSTEM,
 384                subType = ChatEntryModel.SubType.RECEIVED
 385            });
 0386        }
 387    }
 388}