< Summary

Class:PrivateChatWindowController
Assembly:PrivateChatWindowHUD
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/PrivateChatWindow/PrivateChatWindowController.cs
Covered lines:132
Uncovered lines:41
Coverable lines:173
Total lines:372
Line coverage:76.3% (132 of 173)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
PrivateChatWindowController(...)0%110100%
Initialize(...)0%330100%
Setup(...)0%330100%
SetVisibility(...)0%770100%
Focus()0%110100%
Dispose()0%550100%
HandleSendChatMessage(...)0%6.686073.33%
MinimizeView()0%2100%
HandleMessageReceived(...)0%5.935066.67%
Hide()0%2.152066.67%
Show()0%2100%
HandlePressBack()0%220100%
Unfriend(...)0%2100%
IsMessageFomCurrentConversation(...)0%330100%
MarkUserChatMessagesAsRead()0%110100%
MarkMessagesAsSeenDelayed()0%20400%
HandleInputFieldSelected()0%2100%
HandleViewFocused(...)0%6200%
WaitThenFadeOutMessages()0%20400%
SetVisiblePanelList(...)0%220100%
HandleChatInputTriggered(...)0%12300%
RequestPrivateMessages(...)0%220100%
RequestOldConversations()0%5.035088.89%
IsLoadingMessages()0%110100%
WaitForRequestTimeOutThenHideLoadingFeedback()0%550100%
HandleMessageBlockedBySpam(...)0%2100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/PrivateChatWindow/PrivateChatWindowController.cs

#LineLine coverage
 1using Cysharp.Threading.Tasks;
 2using DCL;
 3using DCL.Interface;
 4using SocialFeaturesAnalytics;
 5
 6using System;
 7using System.Collections.Generic;
 8using System.Linq;
 9using System.Threading;
 10using UnityEngine;
 11
 12public class PrivateChatWindowController : IHUD
 13{
 14    private const int USER_PRIVATE_MESSAGES_TO_REQUEST_FOR_INITIAL_LOAD = 30;
 15    private const float REQUEST_MESSAGES_TIME_OUT = 2;
 16    internal const int USER_PRIVATE_MESSAGES_TO_REQUEST_FOR_SHOW_MORE = 10;
 17
 018    public IPrivateChatComponentView View { get; private set; }
 19
 20    private enum ChatWindowVisualState { NONE_VISIBLE, INPUT_MODE }
 21
 22    private readonly DataStore dataStore;
 23    private readonly IUserProfileBridge userProfileBridge;
 24    private readonly IChatController chatController;
 25    private readonly IFriendsController friendsController;
 26    private readonly ISocialAnalytics socialAnalytics;
 27    private readonly IMouseCatcher mouseCatcher;
 28    private readonly InputAction_Trigger toggleChatTrigger;
 29    private ChatHUDController chatHudController;
 30    private UserProfile conversationProfile;
 31    private bool skipChatInputTrigger;
 32    private float lastRequestTime;
 33    private ChatWindowVisualState currentState;
 1634    private CancellationTokenSource deactivateFadeOutCancellationToken = new CancellationTokenSource();
 1635    private CancellationTokenSource markMessagesAsSeenCancellationToken = new CancellationTokenSource();
 36    private bool shouldRequestMessages;
 37
 3238    internal BaseVariable<HashSet<string>> visibleTaskbarPanels => dataStore.HUDs.visibleTaskbarPanels;
 1639    internal string ConversationUserId { get; set; } = string.Empty;
 40
 41    public event Action OnBack;
 42    public event Action OnClosed;
 43
 1644    public PrivateChatWindowController(DataStore dataStore,
 45        IUserProfileBridge userProfileBridge,
 46        IChatController chatController,
 47        IFriendsController friendsController,
 48        ISocialAnalytics socialAnalytics,
 49        IMouseCatcher mouseCatcher,
 50        InputAction_Trigger toggleChatTrigger)
 51    {
 1652        this.dataStore = dataStore;
 1653        this.userProfileBridge = userProfileBridge;
 1654        this.chatController = chatController;
 1655        this.friendsController = friendsController;
 1656        this.socialAnalytics = socialAnalytics;
 1657        this.mouseCatcher = mouseCatcher;
 1658        this.toggleChatTrigger = toggleChatTrigger;
 1659    }
 60
 61    public void Initialize(IPrivateChatComponentView view = null)
 62    {
 1763        view ??= PrivateChatWindowComponentView.Create();
 1764        View = view;
 1765        View.Initialize(friendsController, socialAnalytics);
 1766        view.OnPressBack -= HandlePressBack;
 1767        view.OnPressBack += HandlePressBack;
 1768        view.OnClose -= Hide;
 1769        view.OnClose += Hide;
 1770        view.OnMinimize += MinimizeView;
 1771        view.OnUnfriend += Unfriend;
 72
 1773        if (mouseCatcher != null)
 1774            mouseCatcher.OnMouseLock += Hide;
 75
 1776        view.OnRequireMoreMessages += RequestOldConversations;
 77
 1778        chatHudController = new ChatHUDController(dataStore, userProfileBridge, false);
 1779        chatHudController.Initialize(view.ChatHUD);
 1780        chatHudController.OnInputFieldSelected += HandleInputFieldSelected;
 1781        chatHudController.OnSendMessage += HandleSendChatMessage;
 1782        chatHudController.OnMessageSentBlockedBySpam += HandleMessageBlockedBySpam;
 83
 1784        chatController.OnAddMessage -= HandleMessageReceived;
 1785        chatController.OnAddMessage += HandleMessageReceived;
 86
 1787        toggleChatTrigger.OnTriggered += HandleChatInputTriggered;
 88
 1789        currentState = ChatWindowVisualState.INPUT_MODE;
 1790    }
 91
 92    public void Setup(string newConversationUserId)
 93    {
 1494        if (string.IsNullOrEmpty(newConversationUserId) || newConversationUserId == ConversationUserId)
 195            return;
 96
 1397        var newConversationUserProfile = userProfileBridge.Get(newConversationUserId);
 98
 1399        ConversationUserId = newConversationUserId;
 13100        conversationProfile = newConversationUserProfile;
 13101        chatHudController.ClearAllEntries();
 13102        shouldRequestMessages = true;
 13103    }
 104
 105    public void SetVisibility(bool visible)
 106    {
 19107        if (View.IsActive == visible)
 3108            return;
 109
 16110        SetVisiblePanelList(visible);
 16111        if (visible)
 112        {
 11113            View?.SetLoadingMessagesActive(false);
 11114            View?.SetOldMessagesLoadingActive(false);
 115
 11116            if (conversationProfile != null)
 117            {
 10118                var userStatus = friendsController.GetUserStatus(ConversationUserId);
 10119                View.Setup(conversationProfile,
 120                    userStatus.presence == PresenceStatus.ONLINE,
 121                    userProfileBridge.GetOwn().IsBlocked(ConversationUserId));
 122
 10123                if (shouldRequestMessages)
 124                {
 9125                    RequestPrivateMessages(
 126                        ConversationUserId,
 127                        USER_PRIVATE_MESSAGES_TO_REQUEST_FOR_INITIAL_LOAD,
 128                        null);
 129
 9130                    shouldRequestMessages = false;
 131                }
 132            }
 133
 11134            View.Show();
 11135            Focus();
 11136        }
 137        else
 138        {
 5139            chatHudController.UnfocusInputField();
 5140            View.Hide();
 141        }
 5142    }
 143
 144    public void Focus()
 145    {
 11146        chatHudController.FocusInputField();
 11147        MarkUserChatMessagesAsRead();
 11148    }
 149
 150    public void Dispose()
 151    {
 16152        if (chatHudController != null)
 153        {
 16154            chatHudController.OnInputFieldSelected -= HandleInputFieldSelected;
 16155            chatHudController.OnSendMessage -= HandleSendChatMessage;
 16156            chatHudController.OnMessageSentBlockedBySpam -= HandleMessageBlockedBySpam;
 157        }
 158
 16159        if (chatController != null)
 16160            chatController.OnAddMessage -= HandleMessageReceived;
 161
 16162        if (mouseCatcher != null)
 16163            mouseCatcher.OnMouseLock -= Hide;
 164
 16165        toggleChatTrigger.OnTriggered -= HandleChatInputTriggered;
 166
 16167        if (View != null)
 168        {
 16169            View.OnPressBack -= HandlePressBack;
 16170            View.OnClose -= Hide;
 16171            View.OnMinimize -= MinimizeView;
 16172            View.OnUnfriend -= Unfriend;
 16173            View.OnFocused -= HandleViewFocused;
 16174            View.OnRequireMoreMessages -= RequestOldConversations;
 16175            View.Dispose();
 176        }
 16177    }
 178
 179    private void HandleSendChatMessage(ChatMessage message)
 180    {
 1181        if (string.IsNullOrEmpty(conversationProfile.userName))
 0182            return;
 183
 1184        message.messageType = ChatMessage.Type.PRIVATE;
 1185        message.recipient = conversationProfile.userName;
 186
 1187        bool isValidMessage = !string.IsNullOrEmpty(message.body)
 188                              && !string.IsNullOrWhiteSpace(message.body)
 189                              && !string.IsNullOrEmpty(message.recipient);
 190
 1191        if (isValidMessage)
 192        {
 1193            chatHudController.ResetInputField();
 1194            chatHudController.FocusInputField();
 1195        }
 196        else
 197        {
 0198            SetVisibility(false);
 0199            OnClosed?.Invoke();
 0200            return;
 201        }
 202
 203        // If Kernel allowed for private messages without the whisper param we could avoid this line
 1204        message.body = $"/w {message.recipient} {message.body}";
 205
 1206        chatController.Send(message);
 1207    }
 208
 0209    private void MinimizeView() => SetVisibility(false);
 210
 211    private void HandleMessageReceived(ChatMessage message)
 212    {
 3213        if (!IsMessageFomCurrentConversation(message))
 0214            return;
 215
 3216        chatHudController.AddChatMessage(message, limitMaxEntries: false);
 217
 3218        if (View.IsActive)
 219        {
 0220            markMessagesAsSeenCancellationToken.Cancel();
 0221            markMessagesAsSeenCancellationToken = new CancellationTokenSource();
 222            // since there could be many messages coming in a row, avoid making the call instantly for each message
 223            // instead make just one call after the iteration finishes
 0224            MarkMessagesAsSeenDelayed(markMessagesAsSeenCancellationToken.Token).Forget();
 225        }
 226
 3227        View?.SetLoadingMessagesActive(false);
 3228        View?.SetOldMessagesLoadingActive(false);
 229
 3230        deactivateFadeOutCancellationToken.Cancel();
 3231        deactivateFadeOutCancellationToken = new CancellationTokenSource();
 3232    }
 233
 234    private void Hide()
 235    {
 2236        SetVisibility(false);
 2237        OnClosed?.Invoke();
 0238    }
 239
 240    private void Show()
 241    {
 0242        SetVisibility(true);
 0243    }
 244
 1245    private void HandlePressBack() => OnBack?.Invoke();
 246
 247    private void Unfriend(string friendId)
 248    {
 0249        friendsController.RemoveFriend(friendId);
 0250        Hide();
 0251    }
 252
 253    private bool IsMessageFomCurrentConversation(ChatMessage message)
 254    {
 3255        return message.messageType == ChatMessage.Type.PRIVATE &&
 256               (message.sender == ConversationUserId || message.recipient == ConversationUserId);
 257    }
 258
 259    private void MarkUserChatMessagesAsRead() =>
 11260        chatController.MarkMessagesAsSeen(ConversationUserId);
 261
 262    private async UniTask MarkMessagesAsSeenDelayed(CancellationToken cancellationToken)
 263    {
 0264        await UniTask.NextFrame(cancellationToken);
 0265        if (cancellationToken.IsCancellationRequested) return;
 0266        MarkUserChatMessagesAsRead();
 0267    }
 268
 269    private void HandleInputFieldSelected()
 270    {
 0271        Show();
 272        // The messages from 'conversationUserId' are marked as read if the player clicks on the input field of the priv
 273        //MarkUserChatMessagesAsRead();
 0274    }
 275
 276    private void HandleViewFocused(bool focused)
 277    {
 0278        if (focused)
 279        {
 0280            deactivateFadeOutCancellationToken.Cancel();
 0281            deactivateFadeOutCancellationToken = new CancellationTokenSource();
 282        }
 0283    }
 284
 285    private async UniTaskVoid WaitThenFadeOutMessages(CancellationToken cancellationToken)
 286    {
 0287        await UniTask.SwitchToMainThread(cancellationToken);
 0288        if (cancellationToken.IsCancellationRequested)
 0289            return;
 0290        chatHudController.FadeOutMessages();
 0291        currentState = ChatWindowVisualState.NONE_VISIBLE;
 0292    }
 293
 294    private void SetVisiblePanelList(bool visible)
 295    {
 16296        HashSet<string> newSet = visibleTaskbarPanels.Get();
 16297        if (visible)
 11298            newSet.Add("PrivateChatChannel");
 299        else
 5300            newSet.Remove("PrivateChatChannel");
 301
 16302        visibleTaskbarPanels.Set(newSet, true);
 16303    }
 304
 305    private void HandleChatInputTriggered(DCLAction_Trigger action)
 306    {
 307        // race condition patch caused by unfocusing input field from invalid message on SendChatMessage
 308        // chat input trigger is the same key as sending the chat message from the input field
 0309        if (skipChatInputTrigger)
 310        {
 0311            skipChatInputTrigger = false;
 0312            return;
 313        }
 314
 0315        if (!View.IsActive)
 0316            return;
 0317        chatHudController.FocusInputField();
 0318    }
 319
 320    internal void RequestPrivateMessages(string userId, int limit, string fromMessageId)
 321    {
 10322        View?.SetLoadingMessagesActive(true);
 10323        chatController.GetPrivateMessages(userId, limit, fromMessageId);
 10324        WaitForRequestTimeOutThenHideLoadingFeedback().Forget();
 10325    }
 326
 327    internal void RequestOldConversations()
 328    {
 1329        if (IsLoadingMessages()) return;
 330
 1331        var currentPrivateMessages = chatController.GetPrivateAllocatedEntriesByUser(ConversationUserId);
 332
 1333        var oldestMessageId = currentPrivateMessages
 3334            .OrderBy(x => x.timestamp)
 1335            .Select(x => x.messageId)
 336            .FirstOrDefault();
 337
 1338        chatController.GetPrivateMessages(
 339            ConversationUserId,
 340            USER_PRIVATE_MESSAGES_TO_REQUEST_FOR_SHOW_MORE,
 341            oldestMessageId);
 342
 1343        lastRequestTime = Time.realtimeSinceStartup;
 1344        View?.SetOldMessagesLoadingActive(true);
 1345        WaitForRequestTimeOutThenHideLoadingFeedback().Forget();
 1346    }
 347
 348    private bool IsLoadingMessages() =>
 1349        Time.realtimeSinceStartup - lastRequestTime < REQUEST_MESSAGES_TIME_OUT;
 350
 351    private async UniTaskVoid WaitForRequestTimeOutThenHideLoadingFeedback()
 352    {
 11353        lastRequestTime = Time.realtimeSinceStartup;
 354
 2005355        await UniTask.WaitUntil(() => Time.realtimeSinceStartup - lastRequestTime > REQUEST_MESSAGES_TIME_OUT);
 356
 11357        View?.SetLoadingMessagesActive(false);
 11358        View?.SetOldMessagesLoadingActive(false);
 11359    }
 360
 361    private void HandleMessageBlockedBySpam(ChatMessage message)
 362    {
 0363        chatHudController.AddChatMessage(new ChatEntryModel
 364        {
 365            timestamp = (ulong) DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
 366            bodyText = "You sent too many messages in a short period of time. Please wait and try again later.",
 367            messageId = Guid.NewGuid().ToString(),
 368            messageType = ChatMessage.Type.SYSTEM,
 369            subType = ChatEntryModel.SubType.RECEIVED
 370        });
 0371    }
 372}