< Summary

Class:PrivateChatWindowController
Assembly:PrivateChatWindowHUD
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/PrivateChatWindow/PrivateChatWindowController.cs
Covered lines:174
Uncovered lines:49
Coverable lines:223
Total lines:442
Line coverage:78% (174 of 223)
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%5.475073.33%
HandleCloseInputTriggered(...)0%2100%
MinimizeView()0%2100%
HandleMessageReceived(...)0%11.37055.56%
Hide()0%2.152066.67%
HandlePressBack()0%220100%
Unfriend(...)0%2100%
IsMessageFomCurrentConversation(...)0%330100%
MarkUserChatMessagesAsRead()0%110100%
MarkMessagesAsSeenDelayed()0%20400%
HandleInputFieldSelected()0%110100%
HandleInputFieldDeselected()0%2.062075%
HandleViewFocused(...)0%17.76031.25%
HandleViewClicked()0%6200%
WaitThenActivatePreview()0%6.736072.73%
WaitThenFadeOutMessages()0%6.736072.73%
ActivatePreview()0%220100%
ActivatePreviewOnMessages()0%6200%
DeactivatePreview()0%220100%
HandleChatInputTriggered(...)0%6200%
RequestPrivateMessages(...)0%220100%
RequestOldConversations()0%5.035088.89%
IsLoadingMessages()0%110100%
WaitForRequestTimeOutThenHideLoadingFeedback()0%550100%

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;
 5using System;
 6using System.Collections.Generic;
 7using System.Linq;
 8using System.Threading;
 9using UnityEngine;
 10
 11public class PrivateChatWindowController : IHUD
 12{
 13    internal const int USER_PRIVATE_MESSAGES_TO_REQUEST_FOR_INITIAL_LOAD = 30;
 14    internal const int USER_PRIVATE_MESSAGES_TO_REQUEST_FOR_SHOW_MORE = 10;
 15    internal const float REQUEST_MESSAGES_TIME_OUT = 2;
 16
 017    public IPrivateChatComponentView View { get; private set; }
 18
 19    private enum ChatWindowVisualState
 20    {
 21        NONE_VISIBLE,
 22        INPUT_MODE,
 23        PREVIEW_MODE
 24    }
 25
 26    private readonly DataStore dataStore;
 27    private readonly IUserProfileBridge userProfileBridge;
 28    private readonly IChatController chatController;
 29    private readonly IFriendsController friendsController;
 30    private readonly InputAction_Trigger closeWindowTrigger;
 31    private readonly ISocialAnalytics socialAnalytics;
 32    private readonly IMouseCatcher mouseCatcher;
 33    private readonly InputAction_Trigger toggleChatTrigger;
 34    private ChatHUDController chatHudController;
 35    private UserProfile conversationProfile;
 36    private float lastRequestTime;
 37    private ChatWindowVisualState currentState;
 1938    private CancellationTokenSource deactivatePreviewCancellationToken = new CancellationTokenSource();
 1939    private CancellationTokenSource deactivateFadeOutCancellationToken = new CancellationTokenSource();
 1940    private CancellationTokenSource markMessagesAsSeenCancellationToken = new CancellationTokenSource();
 41    private bool shouldRequestMessages;
 42
 1943    internal string ConversationUserId { get; set; } = string.Empty;
 44
 45    public event Action OnPressBack;
 46    public event Action OnClosed;
 47    public event Action<bool> OnPreviewModeChanged;
 48
 1949    public PrivateChatWindowController(DataStore dataStore,
 50        IUserProfileBridge userProfileBridge,
 51        IChatController chatController,
 52        IFriendsController friendsController,
 53        InputAction_Trigger closeWindowTrigger,
 54        ISocialAnalytics socialAnalytics,
 55        IMouseCatcher mouseCatcher,
 56        InputAction_Trigger toggleChatTrigger)
 57    {
 1958        this.dataStore = dataStore;
 1959        this.userProfileBridge = userProfileBridge;
 1960        this.chatController = chatController;
 1961        this.friendsController = friendsController;
 1962        this.closeWindowTrigger = closeWindowTrigger;
 1963        this.socialAnalytics = socialAnalytics;
 1964        this.mouseCatcher = mouseCatcher;
 1965        this.toggleChatTrigger = toggleChatTrigger;
 1966    }
 67
 68    public void Initialize(IPrivateChatComponentView view = null)
 69    {
 2070        view ??= PrivateChatWindowComponentView.Create();
 2071        View = view;
 2072        View.Initialize(friendsController, socialAnalytics);
 2073        view.OnPressBack -= HandlePressBack;
 2074        view.OnPressBack += HandlePressBack;
 2075        view.OnClose -= Hide;
 2076        view.OnClose += Hide;
 2077        view.OnMinimize += MinimizeView;
 2078        view.OnUnfriend += Unfriend;
 2079        view.OnFocused += HandleViewFocused;
 2080        view.OnRequireMoreMessages += RequestOldConversations;
 2081        view.OnClickOverWindow += HandleViewClicked;
 82
 2083        closeWindowTrigger.OnTriggered -= HandleCloseInputTriggered;
 2084        closeWindowTrigger.OnTriggered += HandleCloseInputTriggered;
 85
 2086        chatHudController = new ChatHUDController(dataStore, userProfileBridge, false);
 2087        chatHudController.Initialize(view.ChatHUD);
 2088        chatHudController.OnInputFieldSelected -= HandleInputFieldSelected;
 2089        chatHudController.OnInputFieldSelected += HandleInputFieldSelected;
 2090        chatHudController.OnInputFieldDeselected -= HandleInputFieldDeselected;
 2091        chatHudController.OnInputFieldDeselected += HandleInputFieldDeselected;
 2092        chatHudController.OnSendMessage += HandleSendChatMessage;
 93
 2094        chatController.OnAddMessage -= HandleMessageReceived;
 2095        chatController.OnAddMessage += HandleMessageReceived;
 96
 2097        if (mouseCatcher != null)
 2098            mouseCatcher.OnMouseLock += ActivatePreview;
 99
 20100        toggleChatTrigger.OnTriggered += HandleChatInputTriggered;
 101
 20102        currentState = ChatWindowVisualState.INPUT_MODE;
 20103    }
 104
 105    public void Setup(string newConversationUserId)
 106    {
 17107        if (string.IsNullOrEmpty(newConversationUserId) || newConversationUserId == ConversationUserId)
 1108            return;
 109
 16110        var newConversationUserProfile = userProfileBridge.Get(newConversationUserId);
 111
 16112        ConversationUserId = newConversationUserId;
 16113        conversationProfile = newConversationUserProfile;
 16114        chatHudController.ClearAllEntries();
 16115        shouldRequestMessages = true;
 16116    }
 117
 118    public void SetVisibility(bool visible)
 119    {
 19120        if (View.IsActive == visible)
 3121            return;
 122
 16123        if (visible)
 124        {
 12125            View?.SetLoadingMessagesActive(false);
 12126            View?.SetOldMessagesLoadingActive(false);
 127
 12128            if (conversationProfile != null)
 129            {
 11130                var userStatus = friendsController.GetUserStatus(ConversationUserId);
 11131                View.Setup(conversationProfile,
 132                    userStatus.presence == PresenceStatus.ONLINE,
 133                    userProfileBridge.GetOwn().IsBlocked(ConversationUserId));
 134
 11135                if (shouldRequestMessages)
 136                {
 10137                    RequestPrivateMessages(
 138                        ConversationUserId,
 139                        USER_PRIVATE_MESSAGES_TO_REQUEST_FOR_INITIAL_LOAD,
 140                        null);
 141
 10142                    shouldRequestMessages = false;
 143                }
 144            }
 145
 12146            View.Show();
 12147            Focus();
 12148        }
 149        else
 150        {
 4151            chatHudController.UnfocusInputField();
 4152            View.Hide();
 153        }
 4154    }
 155
 156    public void Focus()
 157    {
 12158        chatHudController.FocusInputField();
 12159        MarkUserChatMessagesAsRead();
 12160    }
 161
 162    public void Dispose()
 163    {
 19164        if (chatHudController != null)
 165        {
 19166            chatHudController.OnInputFieldSelected -= HandleInputFieldSelected;
 19167            chatHudController.OnInputFieldDeselected -= HandleInputFieldDeselected;
 168        }
 169
 19170        if (chatController != null)
 19171            chatController.OnAddMessage -= HandleMessageReceived;
 172
 19173        if (mouseCatcher != null)
 19174            mouseCatcher.OnMouseLock -= ActivatePreview;
 175
 19176        toggleChatTrigger.OnTriggered -= HandleChatInputTriggered;
 177
 19178        if (View != null)
 179        {
 19180            View.OnPressBack -= HandlePressBack;
 19181            View.OnClose -= Hide;
 19182            View.OnMinimize -= MinimizeView;
 19183            View.OnUnfriend -= Unfriend;
 19184            View.OnFocused -= HandleViewFocused;
 19185            View.OnRequireMoreMessages -= RequestOldConversations;
 19186            View.OnClickOverWindow -= HandleViewClicked;
 19187            View.Dispose();
 188        }
 19189    }
 190
 191    private void HandleSendChatMessage(ChatMessage message)
 192    {
 1193        if (string.IsNullOrEmpty(conversationProfile.userName))
 0194            return;
 195
 1196        message.messageType = ChatMessage.Type.PRIVATE;
 1197        message.recipient = conversationProfile.userName;
 198
 1199        bool isValidMessage = !string.IsNullOrEmpty(message.body)
 200                              && !string.IsNullOrWhiteSpace(message.body)
 201                              && !string.IsNullOrEmpty(message.recipient);
 202
 1203        if (isValidMessage)
 204        {
 1205            chatHudController.ResetInputField();
 1206            chatHudController.FocusInputField();
 1207        }
 208
 209        else
 210        {
 0211            chatHudController.ResetInputField(true);
 0212            ActivatePreview();
 0213            return;
 214        }
 215
 216        // If Kernel allowed for private messages without the whisper param we could avoid this line
 1217        message.body = $"/w {message.recipient} {message.body}";
 218
 1219        chatController.Send(message);
 1220    }
 221
 0222    private void HandleCloseInputTriggered(DCLAction_Trigger action) => Hide();
 223
 0224    private void MinimizeView() => SetVisibility(false);
 225
 226    private void HandleMessageReceived(ChatMessage message)
 227    {
 3228        if (!IsMessageFomCurrentConversation(message))
 0229            return;
 230
 3231        chatHudController.AddChatMessage(message, limitMaxEntries: false);
 232
 3233        if (View.IsActive)
 234        {
 0235            markMessagesAsSeenCancellationToken.Cancel();
 0236            markMessagesAsSeenCancellationToken = new CancellationTokenSource();
 237            // since there could be many messages coming in a row, avoid making the call instantly for each message
 238            // instead make just one call after the iteration finishes
 0239            MarkMessagesAsSeenDelayed(markMessagesAsSeenCancellationToken.Token).Forget();
 240        }
 241
 3242        View?.SetLoadingMessagesActive(false);
 3243        View?.SetOldMessagesLoadingActive(false);
 244
 3245        deactivatePreviewCancellationToken.Cancel();
 3246        deactivatePreviewCancellationToken = new CancellationTokenSource();
 3247        deactivateFadeOutCancellationToken.Cancel();
 3248        deactivateFadeOutCancellationToken = new CancellationTokenSource();
 249
 3250        switch (currentState)
 251        {
 252            case ChatWindowVisualState.NONE_VISIBLE:
 0253                ActivatePreview();
 0254                break;
 255            case ChatWindowVisualState.PREVIEW_MODE:
 0256                WaitThenFadeOutMessages(deactivateFadeOutCancellationToken.Token).Forget();
 257                break;
 258        }
 0259    }
 260
 261    private void Hide()
 262    {
 1263        SetVisibility(false);
 1264        OnClosed?.Invoke();
 0265    }
 266
 1267    private void HandlePressBack() => OnPressBack?.Invoke();
 268
 269    private void Unfriend(string friendId)
 270    {
 0271        friendsController.RemoveFriend(friendId);
 0272        Hide();
 0273    }
 274
 275    private bool IsMessageFomCurrentConversation(ChatMessage message)
 276    {
 3277        return message.messageType == ChatMessage.Type.PRIVATE &&
 278               (message.sender == ConversationUserId || message.recipient == ConversationUserId);
 279    }
 280
 281    private void MarkUserChatMessagesAsRead() =>
 13282        chatController.MarkMessagesAsSeen(ConversationUserId);
 283
 284    private async UniTask MarkMessagesAsSeenDelayed(CancellationToken cancellationToken)
 285    {
 0286        await UniTask.NextFrame(cancellationToken);
 0287        if (cancellationToken.IsCancellationRequested) return;
 0288        MarkUserChatMessagesAsRead();
 0289    }
 290
 291    private void HandleInputFieldSelected()
 292    {
 1293        deactivatePreviewCancellationToken.Cancel();
 1294        deactivatePreviewCancellationToken = new CancellationTokenSource();
 1295        DeactivatePreview();
 296        // The messages from 'conversationUserId' are marked as read if the player clicks on the input field of the priv
 1297        MarkUserChatMessagesAsRead();
 1298    }
 299
 300    private void HandleInputFieldDeselected()
 301    {
 1302        if (View.IsFocused)
 0303            return;
 1304        WaitThenActivatePreview(deactivatePreviewCancellationToken.Token).Forget();
 1305    }
 306
 307    private void HandleViewFocused(bool focused)
 308    {
 1309        if (focused)
 310        {
 0311            deactivatePreviewCancellationToken.Cancel();
 0312            deactivatePreviewCancellationToken = new CancellationTokenSource();
 0313            deactivateFadeOutCancellationToken.Cancel();
 0314            deactivateFadeOutCancellationToken = new CancellationTokenSource();
 315
 0316            if (currentState.Equals(ChatWindowVisualState.NONE_VISIBLE))
 317            {
 0318                ActivatePreviewOnMessages();
 319            }
 0320        }
 321        else
 322        {
 1323            if (chatHudController.IsInputSelected)
 0324                return;
 325
 1326            if (currentState.Equals(ChatWindowVisualState.INPUT_MODE))
 327            {
 1328                WaitThenActivatePreview(deactivatePreviewCancellationToken.Token).Forget();
 1329                return;
 330            }
 331
 0332            if (currentState.Equals(ChatWindowVisualState.PREVIEW_MODE))
 333            {
 0334                WaitThenFadeOutMessages(deactivateFadeOutCancellationToken.Token).Forget();
 335            }
 336        }
 0337    }
 338
 339    private void HandleViewClicked()
 340    {
 0341        if (currentState.Equals(ChatWindowVisualState.INPUT_MODE))
 0342            return;
 0343        DeactivatePreview();
 0344    }
 345
 346    private async UniTaskVoid WaitThenActivatePreview(CancellationToken cancellationToken)
 347    {
 6348        await UniTask.Delay(3000, cancellationToken: cancellationToken);
 2349        await UniTask.SwitchToMainThread(cancellationToken);
 2350        if (cancellationToken.IsCancellationRequested)
 0351            return;
 2352        currentState = ChatWindowVisualState.PREVIEW_MODE;
 2353        ActivatePreview();
 2354    }
 355
 356    private async UniTaskVoid WaitThenFadeOutMessages(CancellationToken cancellationToken)
 357    {
 12358        await UniTask.Delay(30000, cancellationToken: cancellationToken);
 4359        await UniTask.SwitchToMainThread(cancellationToken);
 4360        if (cancellationToken.IsCancellationRequested)
 0361            return;
 4362        chatHudController.FadeOutMessages();
 4363        currentState = ChatWindowVisualState.NONE_VISIBLE;
 4364    }
 365
 366    public void ActivatePreview()
 367    {
 4368        View.ActivatePreview();
 4369        chatHudController.ActivatePreview();
 4370        currentState = ChatWindowVisualState.PREVIEW_MODE;
 4371        WaitThenFadeOutMessages(deactivateFadeOutCancellationToken.Token).Forget();
 4372        OnPreviewModeChanged?.Invoke(true);
 3373    }
 374
 375    public void ActivatePreviewOnMessages()
 376    {
 0377        chatHudController.ActivatePreview();
 0378        currentState = ChatWindowVisualState.PREVIEW_MODE;
 0379        OnPreviewModeChanged?.Invoke(true);
 0380    }
 381
 382    public void DeactivatePreview()
 383    {
 2384        deactivatePreviewCancellationToken.Cancel();
 2385        deactivatePreviewCancellationToken = new CancellationTokenSource();
 2386        deactivateFadeOutCancellationToken.Cancel();
 2387        deactivateFadeOutCancellationToken = new CancellationTokenSource();
 388
 2389        View.DeactivatePreview();
 2390        chatHudController.DeactivatePreview();
 2391        OnPreviewModeChanged?.Invoke(false);
 2392        currentState = ChatWindowVisualState.INPUT_MODE;
 2393    }
 394
 395    private void HandleChatInputTriggered(DCLAction_Trigger action)
 396    {
 0397        if (!View.IsActive)
 0398            return;
 0399        chatHudController.FocusInputField();
 0400    }
 401
 402    internal void RequestPrivateMessages(string userId, int limit, string fromMessageId)
 403    {
 11404        View?.SetLoadingMessagesActive(true);
 11405        chatController.GetPrivateMessages(userId, limit, fromMessageId);
 11406        WaitForRequestTimeOutThenHideLoadingFeedback().Forget();
 11407    }
 408
 409    internal void RequestOldConversations()
 410    {
 1411        if (IsLoadingMessages()) return;
 412
 1413        var currentPrivateMessages = chatController.GetPrivateAllocatedEntriesByUser(ConversationUserId);
 414
 1415        var oldestMessageId = currentPrivateMessages
 3416            .OrderBy(x => x.timestamp)
 1417            .Select(x => x.messageId)
 418            .FirstOrDefault();
 419
 1420        chatController.GetPrivateMessages(
 421            ConversationUserId,
 422            USER_PRIVATE_MESSAGES_TO_REQUEST_FOR_SHOW_MORE,
 423            oldestMessageId);
 424
 1425        lastRequestTime = Time.realtimeSinceStartup;
 1426        View?.SetOldMessagesLoadingActive(true);
 1427        WaitForRequestTimeOutThenHideLoadingFeedback().Forget();
 1428    }
 429
 430    private bool IsLoadingMessages() =>
 1431        Time.realtimeSinceStartup - lastRequestTime < REQUEST_MESSAGES_TIME_OUT;
 432
 433    private async UniTaskVoid WaitForRequestTimeOutThenHideLoadingFeedback()
 434    {
 12435        lastRequestTime = Time.realtimeSinceStartup;
 436
 2928437        await UniTask.WaitUntil(() => Time.realtimeSinceStartup - lastRequestTime > REQUEST_MESSAGES_TIME_OUT);
 438
 12439        View?.SetLoadingMessagesActive(false);
 12440        View?.SetOldMessagesLoadingActive(false);
 12441    }
 442}