< 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:152
Uncovered lines:34
Coverable lines:186
Total lines:399
Line coverage:81.7% (152 of 186)
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%88096.15%
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 DCL.ProfanityFiltering;
 7using SocialFeaturesAnalytics;
 8using UnityEngine;
 9using Channel = DCL.Chat.Channels.Channel;
 10
 11namespace DCL.Chat.HUD
 12{
 13    public class ChatChannelHUDController : IHUD
 14    {
 15        private const int INITIAL_PAGE_SIZE = 30;
 16        private const int SHOW_MORE_PAGE_SIZE = 10;
 17        private const float REQUEST_MESSAGES_TIME_OUT = 2;
 18
 8819        public IChatChannelWindowView View { get; private set; }
 20
 21        private readonly DataStore dataStore;
 1022        private BaseVariable<HashSet<string>> visibleTaskbarPanels => dataStore.HUDs.visibleTaskbarPanels;
 23        private readonly IUserProfileBridge userProfileBridge;
 24        private readonly IChatController chatController;
 25        private readonly IMouseCatcher mouseCatcher;
 26        private readonly InputAction_Trigger toggleChatTrigger;
 27        private readonly ISocialAnalytics socialAnalytics;
 28        private readonly IProfanityFilter profanityFilter;
 29        private ChatHUDController chatHudController;
 30        private ChannelMembersHUDController channelMembersHUDController;
 731        private CancellationTokenSource hideLoadingCancellationToken = new CancellationTokenSource();
 32        private bool skipChatInputTrigger;
 33        private float lastRequestTime;
 34        private string channelId;
 35        private Channel channel;
 36        private ChatMessage oldestMessage;
 837        private bool showOnlyOnlineMembersOnPublicChannels => !dataStore.featureFlags.flags.Get().IsFeatureEnabled("matr
 38
 39        private bool isVisible;
 40
 41        public event Action OnPressBack;
 42        public event Action OnClosed;
 43        public event Action<string> OnOpenChannelLeave;
 44
 745        public ChatChannelHUDController(DataStore dataStore,
 46            IUserProfileBridge userProfileBridge,
 47            IChatController chatController,
 48            IMouseCatcher mouseCatcher,
 49            InputAction_Trigger toggleChatTrigger,
 50            ISocialAnalytics socialAnalytics,
 51            IProfanityFilter profanityFilter)
 52        {
 753            this.dataStore = dataStore;
 754            this.userProfileBridge = userProfileBridge;
 755            this.chatController = chatController;
 756            this.mouseCatcher = mouseCatcher;
 757            this.toggleChatTrigger = toggleChatTrigger;
 758            this.socialAnalytics = socialAnalytics;
 759            this.profanityFilter = profanityFilter;
 760        }
 61
 62        public void Initialize(IChatChannelWindowView view = null, bool isVisible = true)
 63        {
 764            view ??= ChatChannelComponentView.Create();
 765            View = view;
 766            view.OnBack -= HandlePressBack;
 767            view.OnBack += HandlePressBack;
 768            view.OnClose -= Hide;
 769            view.OnClose += Hide;
 770            view.OnRequireMoreMessages += RequestOldConversations;
 771            view.OnLeaveChannel += LeaveChannel;
 772            view.OnShowMembersList += ShowMembersList;
 773            view.OnHideMembersList += HideMembersList;
 774            view.OnMuteChanged += MuteChannel;
 75
 776            chatHudController = new ChatHUDController(dataStore, userProfileBridge, false, profanityFilter);
 777            chatHudController.Initialize(view.ChatHUD);
 778            chatHudController.OnSendMessage += HandleSendChatMessage;
 779            chatHudController.OnMessageSentBlockedBySpam += HandleMessageBlockedBySpam;
 80
 781            if (mouseCatcher != null)
 682                mouseCatcher.OnMouseLock += Hide;
 83
 784            toggleChatTrigger.OnTriggered += HandleChatInputTriggered;
 85
 786            channelMembersHUDController = new ChannelMembersHUDController(view.ChannelMembersHUD, chatController, userPr
 87
 788            SetVisibility(isVisible);
 789            this.isVisible = isVisible;
 790        }
 91
 92        public void Setup(string channelId)
 93        {
 694            channelMembersHUDController.SetChannelId(channelId);
 695            this.channelId = channelId;
 696            lastRequestTime = 0;
 97
 698            channel = chatController.GetAllocatedChannel(channelId);
 699            View.Setup(ToPublicChatModel(channel));
 100
 6101            chatHudController.ClearAllEntries();
 6102            oldestMessage = null;
 6103        }
 104
 105        public void SetVisibility(bool visible)
 106        {
 11107            if(isVisible == visible)
 6108                return;
 109
 5110            isVisible = visible;
 111
 5112            SetVisiblePanelList(visible);
 113
 5114            if (visible)
 115            {
 4116                ClearChatControllerListeners();
 117
 4118                chatController.OnAddMessage += HandleMessageReceived;
 4119                chatController.OnChannelLeft += HandleChannelLeft;
 4120                chatController.OnChannelUpdated += HandleChannelUpdated;
 121
 4122                if (channelMembersHUDController.IsVisible)
 0123                    channelMembersHUDController.SetAutomaticReloadingActive(true);
 124
 4125                View?.SetLoadingMessagesActive(false);
 4126                View?.SetOldMessagesLoadingActive(false);
 127
 4128                if (!string.IsNullOrEmpty(channelId))
 129                {
 2130                    var channel = chatController.GetAllocatedChannel(channelId);
 2131                    View.Setup(ToPublicChatModel(channel));
 132
 2133                    RequestMessages(
 134                        channelId,
 135                        INITIAL_PAGE_SIZE);
 136                }
 137
 4138                View.Show();
 4139                Focus();
 140            }
 141            else
 142            {
 1143                ClearChatControllerListeners();
 144
 1145                channelMembersHUDController.SetAutomaticReloadingActive(false);
 1146                chatHudController.UnfocusInputField();
 1147                OnClosed?.Invoke();
 1148                View.Hide();
 149            }
 150
 5151            dataStore.channels.channelToBeOpened.Set(null, notifyEvent: false);
 5152        }
 153
 154        public void Dispose()
 155        {
 7156            ClearChatControllerListeners();
 157
 7158            if (mouseCatcher != null)
 6159                mouseCatcher.OnMouseLock -= Hide;
 160
 7161            toggleChatTrigger.OnTriggered -= HandleChatInputTriggered;
 162
 7163            chatHudController.OnSendMessage -= HandleSendChatMessage;
 7164            chatHudController.OnMessageSentBlockedBySpam -= HandleMessageBlockedBySpam;
 165
 7166            if (View != null)
 167            {
 7168                View.OnBack -= HandlePressBack;
 7169                View.OnClose -= Hide;
 7170                View.OnRequireMoreMessages -= RequestOldConversations;
 7171                View.OnLeaveChannel -= LeaveChannel;
 7172                View.OnMuteChanged -= MuteChannel;
 7173                View.Dispose();
 174            }
 175
 7176            hideLoadingCancellationToken.Dispose();
 7177            channelMembersHUDController.Dispose();
 7178        }
 179
 180        private void HandleSendChatMessage(ChatMessage message)
 181        {
 1182            message.messageType = ChatMessage.Type.PUBLIC;
 1183            message.recipient = channelId;
 184
 1185            var isValidMessage = !string.IsNullOrEmpty(message.body)
 186                                 && !string.IsNullOrWhiteSpace(message.body)
 187                                 && !string.IsNullOrEmpty(message.recipient);
 188
 1189            if (isValidMessage)
 190            {
 1191                chatHudController.ResetInputField();
 1192                chatHudController.FocusInputField();
 193            }
 194            else
 195            {
 0196                SetVisibility(false);
 0197                return;
 198            }
 199
 1200            if (message.body.ToLower().Equals("/leave"))
 201            {
 1202                LeaveChannelFromCommand();
 1203                return;
 204            }
 205
 0206            chatController.Send(message);
 0207        }
 208
 209        private void HandleMessageReceived(ChatMessage[] messages)
 210        {
 1211            var messageLogUpdated = false;
 212
 6213            foreach (var message in messages)
 214            {
 2215                if (!IsMessageFomCurrentChannel(message)) continue;
 216
 2217                UpdateOldestMessage(message);
 218
 2219                message.isChannelMessage = true;
 220                // TODO: right now the channel history is disabled, but we must find a workaround to support history + m
 221                // one approach could be to increment the max amount of messages depending on how many pages you loaded 
 222                // for example: 1 page = 30 messages, 2 pages = 60 messages, and so on..
 2223                chatHudController.AddChatMessage(message, limitMaxEntries: true);
 224
 2225                View?.SetLoadingMessagesActive(false);
 2226                View?.SetOldMessagesLoadingActive(false);
 227
 2228                messageLogUpdated = true;
 229            }
 230
 1231            if (View.IsActive && messageLogUpdated)
 232            {
 233                // The messages from 'channelId' are marked as read if the channel window is currently open
 1234                MarkChannelMessagesAsRead();
 235            }
 1236        }
 237
 238        private void UpdateOldestMessage(ChatMessage message)
 239        {
 2240            if (oldestMessage == null)
 1241                oldestMessage = message;
 1242            else if (message.timestamp < oldestMessage.timestamp)
 0243                oldestMessage = message;
 1244        }
 245
 246        private void Hide()
 247        {
 0248            SetVisibility(false);
 0249            OnClosed?.Invoke();
 0250        }
 251
 0252        private void HandlePressBack() => OnPressBack?.Invoke();
 253
 254        private bool IsMessageFomCurrentChannel(ChatMessage message) =>
 2255            message.sender == channelId || message.recipient == channelId || (View.IsActive && message.messageType == Ch
 256
 5257        private void MarkChannelMessagesAsRead() => chatController.MarkChannelMessagesAsSeen(channelId);
 258
 259        private void HandleChatInputTriggered(DCLAction_Trigger action)
 260        {
 261            // race condition patch caused by unfocusing input field from invalid message on SendChatMessage
 262            // chat input trigger is the same key as sending the chat message from the input field
 0263            if (skipChatInputTrigger)
 264            {
 0265                skipChatInputTrigger = false;
 0266                return;
 267            }
 268
 0269            if (!View.IsActive) return;
 0270            chatHudController.FocusInputField();
 0271        }
 272
 273        private void RequestMessages(string channelId, int limit, string fromMessageId = null)
 274        {
 2275            View?.SetLoadingMessagesActive(true);
 2276            chatController.GetChannelMessages(channelId, limit, fromMessageId);
 2277            hideLoadingCancellationToken.Cancel();
 2278            hideLoadingCancellationToken = new CancellationTokenSource();
 2279            WaitForRequestTimeOutThenHideLoadingFeedback(hideLoadingCancellationToken.Token).Forget();
 2280        }
 281
 282        private void RequestOldConversations()
 283        {
 0284            if (IsLoadingMessages()) return;
 285
 0286            View?.SetOldMessagesLoadingActive(true);
 0287            lastRequestTime = Time.realtimeSinceStartup;
 288
 0289            chatController.GetChannelMessages(
 290                channelId,
 291                SHOW_MORE_PAGE_SIZE,
 292                oldestMessage?.messageId);
 293
 0294            hideLoadingCancellationToken.Cancel();
 0295            hideLoadingCancellationToken = new CancellationTokenSource();
 0296            WaitForRequestTimeOutThenHideLoadingFeedback(hideLoadingCancellationToken.Token).Forget();
 0297        }
 298
 299        private bool IsLoadingMessages() =>
 0300            Time.realtimeSinceStartup - lastRequestTime < REQUEST_MESSAGES_TIME_OUT;
 301
 302        private async UniTaskVoid WaitForRequestTimeOutThenHideLoadingFeedback(CancellationToken cancellationToken)
 303        {
 2304            lastRequestTime = Time.realtimeSinceStartup;
 305
 6306            await UniTask.WaitUntil(() =>
 114307                    Time.realtimeSinceStartup - lastRequestTime > REQUEST_MESSAGES_TIME_OUT,
 308                cancellationToken: cancellationToken);
 2309            if (cancellationToken.IsCancellationRequested) return;
 310
 2311            View?.SetLoadingMessagesActive(false);
 2312            View?.SetOldMessagesLoadingActive(false);
 2313        }
 314
 315        private void LeaveChannel()
 316        {
 1317            dataStore.channels.channelLeaveSource.Set(ChannelLeaveSource.Chat);
 1318            OnOpenChannelLeave?.Invoke(channelId);
 1319        }
 320
 321        private void LeaveChannelFromCommand()
 322        {
 1323            dataStore.channels.channelLeaveSource.Set(ChannelLeaveSource.Command);
 1324            chatController.LeaveChannel(channelId);
 1325        }
 326
 327        private void HandleChannelLeft(string channelId)
 328        {
 1329            if (channelId != this.channelId) return;
 1330            OnPressBack?.Invoke();
 1331        }
 332
 333        private void HandleChannelUpdated(Channel updatedChannel)
 334        {
 0335            if (updatedChannel.ChannelId != channelId)
 0336                return;
 337
 0338            View.Setup(ToPublicChatModel(updatedChannel));
 0339            channelMembersHUDController.SetMembersCount(updatedChannel.MemberCount);
 0340        }
 341
 0342        private void ShowMembersList() => channelMembersHUDController.SetVisibility(true);
 343
 0344        private void HideMembersList() => channelMembersHUDController.SetVisibility(false);
 345
 346        private void MuteChannel(bool muted)
 347        {
 2348            if (muted)
 1349                chatController.MuteChannel(channelId);
 350            else
 1351                chatController.UnmuteChannel(channelId);
 1352        }
 353
 354        private void SetVisiblePanelList(bool visible)
 355        {
 5356            var newSet = visibleTaskbarPanels.Get();
 357
 5358            if (visible)
 4359                newSet.Add("ChatChannel");
 360            else
 1361                newSet.Remove("ChatChannel");
 362
 5363            visibleTaskbarPanels.Set(newSet, true);
 5364        }
 365
 366        private PublicChatModel ToPublicChatModel(Channel channel)
 367        {
 8368            return new PublicChatModel(channelId, channel.Name, channel.Description,
 369                channel.Joined, channel.MemberCount, channel.Muted,
 370                showOnlyOnlineMembersOnPublicChannels);
 371        }
 372
 373        private void ClearChatControllerListeners()
 374        {
 12375            if (chatController == null) return;
 12376            chatController.OnAddMessage -= HandleMessageReceived;
 12377            chatController.OnChannelLeft -= HandleChannelLeft;
 12378            chatController.OnChannelUpdated -= HandleChannelUpdated;
 12379        }
 380
 381        private void Focus()
 382        {
 4383            chatHudController.FocusInputField();
 4384            MarkChannelMessagesAsRead();
 4385        }
 386
 387        private void HandleMessageBlockedBySpam(ChatMessage message)
 388        {
 0389            chatHudController.AddChatMessage(new ChatEntryModel
 390            {
 391                timestamp = (ulong) DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
 392                bodyText = "You sent too many messages in a short period of time. Please wait and try again later.",
 393                messageId = Guid.NewGuid().ToString(),
 394                messageType = ChatMessage.Type.SYSTEM,
 395                subType = ChatEntryModel.SubType.RECEIVED
 396            });
 0397        }
 398    }
 399}