< Summary

Class:DCL.Social.Chat.ChatChannelHUDController
Assembly:WorldChatWindowHUD
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/WorldChatWindowHUD/ChatChannelHUDController.cs
Covered lines:175
Uncovered lines:30
Coverable lines:205
Total lines:445
Line coverage:85.3% (175 of 205)
Covered branches:0
Total branches:0
Covered methods:27
Total methods:35
Method coverage:77.1% (27 of 35)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
ChatChannelHUDController(...)0%110100%
Initialize(...)0%220100%
CopyNameToClipboard(...)0%110100%
Setup(...)0%110100%
SetVisibility(...)0%10100100%
Dispose()0%440100%
HandleSendChatMessage(...)0%5.585071.43%
HandleMessageReceived(...)0%880100%
CheckOwnPlayerMentionInChannels(...)0%770100%
UpdateOldestMessage(...)0%3.073080%
Hide()0%6200%
HandlePressBack()0%6200%
IsMessageFomCurrentChannel(...)0%440100%
MarkChannelMessagesAsRead()0%110100%
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%110100%
HideMembersList()0%2100%
MuteChannel(...)0%220100%
SetVisiblePanelList(...)0%220100%
ToPublicChatModel(...)0%110100%
ClearChatControllerListeners()0%2.032080%
Focus()0%110100%
HandleMessageBlockedBySpam(...)0%2100%
SomeoneMentionedFromContextMenu(...)0%6200%

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.Social.Chat;
 6using DCL.Interface;
 7using DCL.ProfanityFiltering;
 8using DCL.Social.Chat.Mentions;
 9using DCLServices.CopyPaste.Analytics;
 10using SocialFeaturesAnalytics;
 11using UnityEngine;
 12using Channel = DCL.Chat.Channels.Channel;
 13
 14namespace DCL.Social.Chat
 15{
 16    public class ChatChannelHUDController : IHUD
 17    {
 18        private const int INITIAL_PAGE_SIZE = 30;
 19        private const int SHOW_MORE_PAGE_SIZE = 10;
 20        private const float REQUEST_MESSAGES_TIME_OUT = 2;
 21
 18422        public IChatChannelWindowView View { get; private set; }
 23
 24        private readonly DataStore dataStore;
 1425        private BaseVariable<HashSet<string>> visibleTaskbarPanels => dataStore.HUDs.visibleTaskbarPanels;
 26        private readonly IUserProfileBridge userProfileBridge;
 27        private readonly IChatController chatController;
 28        private readonly IMouseCatcher mouseCatcher;
 29        private readonly ISocialAnalytics socialAnalytics;
 30        private readonly IProfanityFilter profanityFilter;
 31        private readonly IChatMentionSuggestionProvider chatMentionSuggestionProvider;
 32        private readonly IClipboard clipboard;
 33        private readonly ICopyPasteAnalyticsService copyPasteAnalyticsService;
 34        private ChatHUDController chatHudController;
 35        private ChannelMembersHUDController channelMembersHUDController;
 1436        private CancellationTokenSource hideLoadingCancellationToken = new ();
 37        private float lastRequestTime;
 38        private string channelId;
 39        private Channel channel;
 40        private ChatMessage oldestMessage;
 1441        private bool showOnlyOnlineMembersOnPublicChannels => !dataStore.featureFlags.flags.Get().IsFeatureEnabled("matr
 42
 43        private bool isVisible;
 44
 45        public event Action OnPressBack;
 46        public event Action OnClosed;
 47        public event Action<string> OnOpenChannelLeave;
 48
 1449        public ChatChannelHUDController(DataStore dataStore,
 50            IUserProfileBridge userProfileBridge,
 51            IChatController chatController,
 52            IMouseCatcher mouseCatcher,
 53            ISocialAnalytics socialAnalytics,
 54            IProfanityFilter profanityFilter,
 55            IChatMentionSuggestionProvider chatMentionSuggestionProvider,
 56            IClipboard clipboard,
 57            ICopyPasteAnalyticsService copyPasteAnalyticsService)
 58        {
 1459            this.dataStore = dataStore;
 1460            this.userProfileBridge = userProfileBridge;
 1461            this.chatController = chatController;
 1462            this.mouseCatcher = mouseCatcher;
 1463            this.socialAnalytics = socialAnalytics;
 1464            this.profanityFilter = profanityFilter;
 1465            this.chatMentionSuggestionProvider = chatMentionSuggestionProvider;
 1466            this.clipboard = clipboard;
 1467            this.copyPasteAnalyticsService = copyPasteAnalyticsService;
 1468        }
 69
 70        public void Initialize(IChatChannelWindowView view, bool isVisible = true)
 71        {
 1472            View = view;
 1473            view.OnBack -= HandlePressBack;
 1474            view.OnBack += HandlePressBack;
 1475            view.OnClose -= Hide;
 1476            view.OnClose += Hide;
 1477            view.OnRequireMoreMessages += RequestOldConversations;
 1478            view.OnLeaveChannel += LeaveChannel;
 1479            view.OnShowMembersList += ShowMembersList;
 1480            view.OnHideMembersList += HideMembersList;
 1481            view.OnMuteChanged += MuteChannel;
 1482            view.OnCopyNameRequested += CopyNameToClipboard;
 83
 1484            dataStore.mentions.someoneMentionedFromContextMenu.OnChange += SomeoneMentionedFromContextMenu;
 85
 1486            chatHudController = new ChatHUDController(dataStore, userProfileBridge, false,
 387               (name, count, ct) => chatMentionSuggestionProvider.GetProfilesFromChatChannelsStartingWith(name, channelI
 88                socialAnalytics, chatController, clipboard, copyPasteAnalyticsService, profanityFilter);
 89
 1490            chatHudController.Initialize(view.ChatHUD);
 1491            chatHudController.SortingStrategy = new ChatEntrySortingByTimestamp();
 1492            chatHudController.OnSendMessage += HandleSendChatMessage;
 1493            chatHudController.OnMessageSentBlockedBySpam += HandleMessageBlockedBySpam;
 1494            chatController.OnAddMessage += HandleMessageReceived;
 95
 1496            if (mouseCatcher != null)
 1397                mouseCatcher.OnMouseLock += Hide;
 98
 1499            channelMembersHUDController = new ChannelMembersHUDController(view.ChannelMembersHUD, chatController, userPr
 100
 14101            SetVisibility(isVisible);
 14102            this.isVisible = isVisible;
 14103        }
 104
 105        private void CopyNameToClipboard(string channelName)
 106        {
 1107            clipboard.WriteText(channelName);
 1108            copyPasteAnalyticsService.Copy("channel_name");
 1109        }
 110
 111        public void Setup(string channelId)
 112        {
 12113            channelMembersHUDController.SetChannelId(channelId);
 12114            this.channelId = channelId;
 12115            lastRequestTime = 0;
 116
 12117            channel = chatController.GetAllocatedChannel(channelId);
 12118            View.Setup(ToPublicChatModel(channel));
 119
 12120            chatHudController.ClearAllEntries();
 12121            oldestMessage = null;
 12122        }
 123
 124        public void SetVisibility(bool visible)
 125        {
 23126            if (isVisible != visible)
 127            {
 7128                isVisible = visible;
 129
 7130                SetVisiblePanelList(visible);
 7131                chatHudController.SetVisibility(visible);
 7132                dataStore.HUDs.chatInputVisible.Set(visible);
 133            }
 134
 23135            if (visible)
 136            {
 5137                ClearChatControllerListeners();
 138
 5139                chatController.OnChannelLeft += HandleChannelLeft;
 5140                chatController.OnChannelUpdated += HandleChannelUpdated;
 141
 5142                if (channelMembersHUDController.IsVisible)
 1143                    channelMembersHUDController.SetAutomaticReloadingActive(true);
 144
 5145                View?.SetLoadingMessagesActive(false);
 5146                View?.SetOldMessagesLoadingActive(false);
 147
 5148                if (!string.IsNullOrEmpty(channelId))
 149                {
 2150                    var channel = chatController.GetAllocatedChannel(channelId);
 2151                    View.Setup(ToPublicChatModel(channel));
 152
 2153                    RequestMessages(
 154                        channelId,
 155                        INITIAL_PAGE_SIZE);
 156                }
 157
 5158                View?.ChatHUD.ResetInputField();
 5159                View?.Show();
 5160                Focus();
 161            }
 162            else
 163            {
 18164                ClearChatControllerListeners();
 165
 18166                channelMembersHUDController.SetAutomaticReloadingActive(false);
 18167                chatHudController.UnfocusInputField();
 18168                OnClosed?.Invoke();
 18169                View.Hide();
 170            }
 171
 23172            dataStore.channels.channelToBeOpened.Set(null, notifyEvent: false);
 23173        }
 174
 175        public void Dispose()
 176        {
 14177            ClearChatControllerListeners();
 178
 14179            if (mouseCatcher != null)
 13180                mouseCatcher.OnMouseLock -= Hide;
 181
 14182            chatHudController.OnSendMessage -= HandleSendChatMessage;
 14183            chatHudController.OnMessageSentBlockedBySpam -= HandleMessageBlockedBySpam;
 14184            chatHudController.Dispose();
 185
 14186            if (View != null)
 187            {
 14188                View.OnBack -= HandlePressBack;
 14189                View.OnClose -= Hide;
 14190                View.OnRequireMoreMessages -= RequestOldConversations;
 14191                View.OnLeaveChannel -= LeaveChannel;
 14192                View.OnMuteChanged -= MuteChannel;
 14193                View.Dispose();
 194            }
 14195            dataStore.mentions.someoneMentionedFromContextMenu.OnChange -= SomeoneMentionedFromContextMenu;
 196
 14197            hideLoadingCancellationToken.Dispose();
 14198            channelMembersHUDController.Dispose();
 199
 14200            if (chatController != null)
 14201                chatController.OnAddMessage -= HandleMessageReceived;
 14202        }
 203
 204        private void HandleSendChatMessage(ChatMessage message)
 205        {
 1206            message.messageType = ChatMessage.Type.PUBLIC;
 1207            message.recipient = channelId;
 1208            message.channelName = channel.Name;
 209
 1210            var isValidMessage = !string.IsNullOrEmpty(message.body)
 211                                 && !string.IsNullOrWhiteSpace(message.body)
 212                                 && !string.IsNullOrEmpty(message.recipient);
 213
 1214            if (isValidMessage)
 215            {
 1216                chatHudController.ResetInputField();
 1217                chatHudController.FocusInputField();
 218            }
 219            else
 220            {
 0221                SetVisibility(false);
 0222                return;
 223            }
 224
 1225            if (message.body.ToLower().Equals("/leave"))
 226            {
 1227                LeaveChannelFromCommand();
 1228                return;
 229            }
 230
 0231            chatController.Send(message);
 0232        }
 233
 234        private void HandleMessageReceived(ChatMessage[] messages)
 235        {
 3236            var messageLogUpdated = false;
 237
 3238            var ownPlayerAlreadyMentioned = false;
 239
 14240            foreach (var message in messages)
 241            {
 4242                if (!ownPlayerAlreadyMentioned)
 4243                    ownPlayerAlreadyMentioned = CheckOwnPlayerMentionInChannels(message);
 244
 4245                if (!isVisible) continue;
 246
 2247                if (!IsMessageFomCurrentChannel(message)) continue;
 248
 2249                UpdateOldestMessage(message);
 250
 251                // TODO: right now the channel history is disabled, but we must find a workaround to support history + m
 252                // one approach could be to increment the max amount of messages depending on how many pages you loaded 
 253                // for example: 1 page = 30 messages, 2 pages = 60 messages, and so on..
 2254                chatHudController.SetChatMessage(message, limitMaxEntries: true);
 255
 2256                dataStore.channels.SetAvailableMemberInChannel(message.sender, channelId);
 257
 2258                View?.SetLoadingMessagesActive(false);
 2259                View?.SetOldMessagesLoadingActive(false);
 260
 2261                messageLogUpdated = true;
 262            }
 263
 3264            if (View.IsActive && messageLogUpdated)
 265            {
 266                // The messages from 'channelId' are marked as read if the channel window is currently open
 1267                MarkChannelMessagesAsRead();
 268            }
 3269        }
 270
 271        private bool CheckOwnPlayerMentionInChannels(ChatMessage message)
 272        {
 4273            var ownUserProfile = userProfileBridge.GetOwn();
 274
 4275            if (message.sender == ownUserProfile.userId ||
 276                message.messageType != ChatMessage.Type.PUBLIC ||
 277                string.IsNullOrEmpty(message.recipient) ||
 278                (message.recipient == channelId && View.IsActive) ||
 279                !MentionsUtils.IsUserMentionedInText(ownUserProfile.userName, message.body))
 3280                return false;
 281
 1282            dataStore.mentions.ownPlayerMentionedInChannel.Set(message.recipient, true);
 1283            return true;
 284        }
 285
 286        private void UpdateOldestMessage(ChatMessage message)
 287        {
 2288            if (oldestMessage == null)
 1289                oldestMessage = message;
 1290            else if (message.timestamp < oldestMessage.timestamp)
 0291                oldestMessage = message;
 1292        }
 293
 294        private void Hide()
 295        {
 0296            SetVisibility(false);
 0297            OnClosed?.Invoke();
 0298        }
 299
 300        private void HandlePressBack() =>
 0301            OnPressBack?.Invoke();
 302
 303        private bool IsMessageFomCurrentChannel(ChatMessage message) =>
 2304            message.sender == channelId || message.recipient == channelId || (View.IsActive && message.messageType == Ch
 305
 306        private void MarkChannelMessagesAsRead() =>
 6307            chatController.MarkChannelMessagesAsSeen(channelId);
 308
 309        private void RequestMessages(string channelId, int limit, string fromMessageId = null)
 310        {
 2311            View?.SetLoadingMessagesActive(true);
 2312            chatController.GetChannelMessages(channelId, limit, fromMessageId);
 2313            hideLoadingCancellationToken.Cancel();
 2314            hideLoadingCancellationToken = new CancellationTokenSource();
 2315            WaitForRequestTimeOutThenHideLoadingFeedback(hideLoadingCancellationToken.Token).Forget();
 2316        }
 317
 318        private void RequestOldConversations()
 319        {
 0320            if (IsLoadingMessages()) return;
 321
 0322            View?.SetOldMessagesLoadingActive(true);
 0323            lastRequestTime = Time.realtimeSinceStartup;
 324
 0325            chatController.GetChannelMessages(
 326                channelId,
 327                SHOW_MORE_PAGE_SIZE,
 328                oldestMessage?.messageId);
 329
 0330            hideLoadingCancellationToken.Cancel();
 0331            hideLoadingCancellationToken = new CancellationTokenSource();
 0332            WaitForRequestTimeOutThenHideLoadingFeedback(hideLoadingCancellationToken.Token).Forget();
 0333        }
 334
 335        private bool IsLoadingMessages() =>
 0336            Time.realtimeSinceStartup - lastRequestTime < REQUEST_MESSAGES_TIME_OUT;
 337
 338        private async UniTaskVoid WaitForRequestTimeOutThenHideLoadingFeedback(CancellationToken cancellationToken)
 339        {
 2340            lastRequestTime = Time.realtimeSinceStartup;
 341
 6342            await UniTask.WaitUntil(() =>
 45343                    Time.realtimeSinceStartup - lastRequestTime > REQUEST_MESSAGES_TIME_OUT,
 344                cancellationToken: cancellationToken);
 345
 2346            if (cancellationToken.IsCancellationRequested) return;
 347
 2348            View?.SetLoadingMessagesActive(false);
 2349            View?.SetOldMessagesLoadingActive(false);
 2350        }
 351
 352        private void LeaveChannel()
 353        {
 1354            dataStore.channels.channelLeaveSource.Set(ChannelLeaveSource.Chat);
 1355            OnOpenChannelLeave?.Invoke(channelId);
 1356        }
 357
 358        private void LeaveChannelFromCommand()
 359        {
 1360            dataStore.channels.channelLeaveSource.Set(ChannelLeaveSource.Command);
 1361            chatController.LeaveChannel(channelId);
 1362        }
 363
 364        private void HandleChannelLeft(string channelId)
 365        {
 1366            if (channelId != this.channelId) return;
 1367            OnPressBack?.Invoke();
 1368        }
 369
 370        private void HandleChannelUpdated(Channel updatedChannel)
 371        {
 0372            if (updatedChannel.ChannelId != channelId)
 0373                return;
 374
 0375            View.Setup(ToPublicChatModel(updatedChannel));
 0376            channelMembersHUDController.SetMembersCount(updatedChannel.MemberCount);
 0377        }
 378
 379        private void ShowMembersList() =>
 1380            channelMembersHUDController.SetVisibility(true);
 381
 382        private void HideMembersList() =>
 0383            channelMembersHUDController.SetVisibility(false);
 384
 385        private void MuteChannel(bool muted)
 386        {
 2387            if (muted)
 1388                chatController.MuteChannel(channelId);
 389            else
 1390                chatController.UnmuteChannel(channelId);
 1391        }
 392
 393        private void SetVisiblePanelList(bool visible)
 394        {
 7395            var newSet = visibleTaskbarPanels.Get();
 396
 7397            if (visible)
 5398                newSet.Add("ChatChannel");
 399            else
 2400                newSet.Remove("ChatChannel");
 401
 7402            visibleTaskbarPanels.Set(newSet, true);
 7403        }
 404
 405        private PublicChatModel ToPublicChatModel(Channel channel)
 406        {
 14407            return new PublicChatModel(channelId, channel.Name, channel.Description,
 408                channel.Joined, channel.MemberCount, channel.Muted,
 409                showOnlyOnlineMembersOnPublicChannels);
 410        }
 411
 412        private void ClearChatControllerListeners()
 413        {
 37414            if (chatController == null) return;
 37415            chatController.OnChannelLeft -= HandleChannelLeft;
 37416            chatController.OnChannelUpdated -= HandleChannelUpdated;
 37417        }
 418
 419        private void Focus()
 420        {
 5421            chatHudController.FocusInputField();
 5422            MarkChannelMessagesAsRead();
 5423        }
 424
 425        private void HandleMessageBlockedBySpam(ChatMessage message)
 426        {
 0427            chatHudController.SetChatMessage(new ChatEntryModel
 428            {
 429                timestamp = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
 430                bodyText = "You sent too many messages in a short period of time. Please wait and try again later.",
 431                messageId = Guid.NewGuid().ToString(),
 432                messageType = ChatMessage.Type.SYSTEM,
 433                subType = ChatEntryModel.SubType.RECEIVED
 434            }).Forget();
 0435        }
 436
 437        private void SomeoneMentionedFromContextMenu(string mention, string _)
 438        {
 0439            if (!View.IsActive)
 0440                return;
 441
 0442            View.ChatHUD.AddTextIntoInputField(mention);
 0443        }
 444    }
 445}