< Summary

Class:PrivateChatWindowController
Assembly:PrivateChatWindowHUD
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/PrivateChatWindow/PrivateChatWindowController.cs
Covered lines:160
Uncovered lines:46
Coverable lines:206
Total lines:395
Line coverage:77.6% (160 of 206)
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%440100%
Focus()0%110100%
Dispose()0%550100%
ReloadAllChats()0%880100%
HandleSendChatMessage(...)0%5.765068.75%
HandleCloseInputTriggered(...)0%2100%
MinimizeView()0%2100%
HandleMessageReceived(...)0%5.935066.67%
Hide()0%2.152066.67%
HandlePressBack()0%220100%
Unfriend(...)0%2100%
IsMessageFomCurrentConversation(...)0%330100%
MarkUserChatMessagesAsRead()0%110100%
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%12300%

File(s)

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

#LineLine coverage
 1using System;
 2using System.Threading;
 3using Cysharp.Threading.Tasks;
 4using DCL;
 5using DCL.Interface;
 6using SocialFeaturesAnalytics;
 7
 8public class PrivateChatWindowController : IHUD
 9{
 010    public IPrivateChatComponentView View { get; private set; }
 11
 12    private enum ChatWindowVisualState { NONE_VISIBLE, INPUT_MODE, PREVIEW_MODE }
 13
 14    private readonly DataStore dataStore;
 15    private readonly IUserProfileBridge userProfileBridge;
 16    private readonly IChatController chatController;
 17    private readonly IFriendsController friendsController;
 18    private readonly InputAction_Trigger closeWindowTrigger;
 19    private readonly ILastReadMessagesService lastReadMessagesService;
 20    private readonly ISocialAnalytics socialAnalytics;
 21    private readonly IMouseCatcher mouseCatcher;
 22    private readonly InputAction_Trigger toggleChatTrigger;
 23    private ChatHUDController chatHudController;
 24    private UserProfile conversationProfile;
 25    private bool skipChatInputTrigger;
 26    private ChatWindowVisualState currentState;
 1927    private CancellationTokenSource deactivatePreviewCancellationToken = new CancellationTokenSource();
 1928    private CancellationTokenSource deactivateFadeOutCancellationToken = new CancellationTokenSource();
 29
 1930    private string ConversationUserId { get; set; } = string.Empty;
 31
 32    public event Action OnPressBack;
 33    public event Action OnClosed;
 34    public event Action<bool> OnPreviewModeChanged;
 35
 1936    public PrivateChatWindowController(DataStore dataStore,
 37        IUserProfileBridge userProfileBridge,
 38        IChatController chatController,
 39        IFriendsController friendsController,
 40        InputAction_Trigger closeWindowTrigger,
 41        ILastReadMessagesService lastReadMessagesService,
 42        ISocialAnalytics socialAnalytics,
 43        IMouseCatcher mouseCatcher,
 44        InputAction_Trigger toggleChatTrigger)
 45    {
 1946        this.dataStore = dataStore;
 1947        this.userProfileBridge = userProfileBridge;
 1948        this.chatController = chatController;
 1949        this.friendsController = friendsController;
 1950        this.closeWindowTrigger = closeWindowTrigger;
 1951        this.lastReadMessagesService = lastReadMessagesService;
 1952        this.socialAnalytics = socialAnalytics;
 1953        this.mouseCatcher = mouseCatcher;
 1954        this.toggleChatTrigger = toggleChatTrigger;
 1955    }
 56
 57    public void Initialize(IPrivateChatComponentView view = null)
 58    {
 2059        view ??= PrivateChatWindowComponentView.Create();
 2060        View = view;
 2061        View.Initialize(friendsController, socialAnalytics);
 2062        view.OnPressBack -= HandlePressBack;
 2063        view.OnPressBack += HandlePressBack;
 2064        view.OnClose -= Hide;
 2065        view.OnClose += Hide;
 2066        view.OnMinimize += MinimizeView;
 2067        view.OnUnfriend += Unfriend;
 2068        view.OnFocused += HandleViewFocused;
 2069        view.OnClickOverWindow += HandleViewClicked;
 70
 2071        closeWindowTrigger.OnTriggered -= HandleCloseInputTriggered;
 2072        closeWindowTrigger.OnTriggered += HandleCloseInputTriggered;
 73
 2074        chatHudController = new ChatHUDController(dataStore, userProfileBridge, false);
 2075        chatHudController.Initialize(view.ChatHUD);
 2076        chatHudController.OnInputFieldSelected -= HandleInputFieldSelected;
 2077        chatHudController.OnInputFieldSelected += HandleInputFieldSelected;
 2078        chatHudController.OnInputFieldDeselected -= HandleInputFieldDeselected;
 2079        chatHudController.OnInputFieldDeselected += HandleInputFieldDeselected;
 2080        chatHudController.OnSendMessage += HandleSendChatMessage;
 81
 2082        chatController.OnAddMessage -= HandleMessageReceived;
 2083        chatController.OnAddMessage += HandleMessageReceived;
 84
 2085        if (mouseCatcher != null)
 2086            mouseCatcher.OnMouseLock += ActivatePreview;
 87
 2088        toggleChatTrigger.OnTriggered += HandleChatInputTriggered;
 89
 2090        currentState = ChatWindowVisualState.INPUT_MODE;
 2091    }
 92
 93    public void Setup(string newConversationUserId)
 94    {
 1895        if (string.IsNullOrEmpty(newConversationUserId) || newConversationUserId == ConversationUserId)
 196            return;
 97
 1798        var newConversationUserProfile = userProfileBridge.Get(newConversationUserId);
 99
 17100        ConversationUserId = newConversationUserId;
 17101        conversationProfile = newConversationUserProfile;
 102
 17103        var userStatus = friendsController.GetUserStatus(ConversationUserId);
 104
 17105        View.Setup(newConversationUserProfile,
 106            userStatus.presence == PresenceStatus.ONLINE,
 107            userProfileBridge.GetOwn().IsBlocked(ConversationUserId));
 108
 17109        ReloadAllChats().Forget();
 17110    }
 111
 112    public void SetVisibility(bool visible)
 113    {
 15114        if (View.IsActive == visible)
 2115            return;
 116
 13117        if (visible)
 118        {
 9119            if (conversationProfile != null)
 120            {
 8121                var userStatus = friendsController.GetUserStatus(ConversationUserId);
 8122                View.Setup(conversationProfile,
 123                    userStatus.presence == PresenceStatus.ONLINE,
 124                    userProfileBridge.GetOwn().IsBlocked(ConversationUserId));
 125            }
 126
 9127            View.Show();
 9128            Focus();
 9129        }
 130        else
 131        {
 4132            chatHudController.UnfocusInputField();
 4133            View.Hide();
 134        }
 4135    }
 136
 137    public void Focus()
 138    {
 9139        chatHudController.FocusInputField();
 9140        MarkUserChatMessagesAsRead();
 9141    }
 142
 143    public void Dispose()
 144    {
 19145        if (chatHudController != null)
 146        {
 19147            chatHudController.OnInputFieldSelected -= HandleInputFieldSelected;
 19148            chatHudController.OnInputFieldDeselected -= HandleInputFieldDeselected;
 149        }
 150
 19151        if (chatController != null)
 19152            chatController.OnAddMessage -= HandleMessageReceived;
 153
 19154        if (mouseCatcher != null)
 19155            mouseCatcher.OnMouseLock -= ActivatePreview;
 156
 19157        toggleChatTrigger.OnTriggered -= HandleChatInputTriggered;
 158
 19159        if (View != null)
 160        {
 19161            View.OnPressBack -= HandlePressBack;
 19162            View.OnClose -= Hide;
 19163            View.OnMinimize -= MinimizeView;
 19164            View.OnUnfriend -= Unfriend;
 19165            View.OnFocused -= HandleViewFocused;
 19166            View.OnClickOverWindow -= HandleViewClicked;
 19167            View.Dispose();
 168        }
 19169    }
 170
 171    private async UniTaskVoid ReloadAllChats()
 172    {
 17173        chatHudController.ClearAllEntries();
 174
 175        const int entriesPerFrame = 10;
 17176        var list = chatController.GetEntries();
 17177        if (list.Count == 0)
 15178            return;
 179
 42180        for (var i = list.Count - 1; i >= 0; i--)
 181        {
 19182            var message = list[i];
 19183            if (i != 0 && i % entriesPerFrame == 0)
 3184                await UniTask.NextFrame();
 19185            if (!IsMessageFomCurrentConversation(message))
 186                continue;
 19187            chatHudController.AddChatMessage(message, spamFiltering: false);
 19188        }
 17189    }
 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            skipChatInputTrigger = true;
 0212            chatHudController.ResetInputField(true);
 0213            ActivatePreview();
 0214            return;
 215        }
 216
 217        // If Kernel allowed for private messages without the whisper param we could avoid this line
 1218        message.body = $"/w {message.recipient} {message.body}";
 219
 1220        chatController.Send(message);
 1221    }
 222
 0223    private void HandleCloseInputTriggered(DCLAction_Trigger action) => Hide();
 224
 0225    private void MinimizeView() => SetVisibility(false);
 226
 227    private void HandleMessageReceived(ChatMessage message)
 228    {
 3229        if (!IsMessageFomCurrentConversation(message))
 0230            return;
 231
 3232        chatHudController.AddChatMessage(message, View.IsActive);
 233
 3234        if (View.IsActive)
 235        {
 236            // The messages from 'conversationUserId' are marked as read if his private chat window is currently open
 0237            MarkUserChatMessagesAsRead();
 238        }
 239
 3240        deactivatePreviewCancellationToken.Cancel();
 3241        deactivatePreviewCancellationToken = new CancellationTokenSource();
 3242        deactivateFadeOutCancellationToken.Cancel();
 3243        deactivateFadeOutCancellationToken = new CancellationTokenSource();
 244
 3245        if (currentState.Equals(ChatWindowVisualState.NONE_VISIBLE))
 246        {
 0247            ActivatePreview();
 0248        }
 3249        else if (currentState.Equals(ChatWindowVisualState.PREVIEW_MODE))
 250        {
 0251            WaitThenFadeOutMessages(deactivateFadeOutCancellationToken.Token).Forget();
 252        }
 3253    }
 254
 255    private void Hide()
 256    {
 1257        SetVisibility(false);
 1258        OnClosed?.Invoke();
 0259    }
 260
 1261    private void HandlePressBack() => OnPressBack?.Invoke();
 262
 263    private void Unfriend(string friendId)
 264    {
 0265        friendsController.RemoveFriend(friendId);
 0266        Hide();
 0267    }
 268
 269    private bool IsMessageFomCurrentConversation(ChatMessage message)
 270    {
 22271        return message.messageType == ChatMessage.Type.PRIVATE &&
 272               (message.sender == ConversationUserId || message.recipient == ConversationUserId);
 273    }
 274
 275    private void MarkUserChatMessagesAsRead() =>
 10276        lastReadMessagesService.MarkAllRead(ConversationUserId);
 277
 278    private void HandleInputFieldSelected()
 279    {
 1280        deactivatePreviewCancellationToken.Cancel();
 1281        deactivatePreviewCancellationToken = new CancellationTokenSource();
 1282        DeactivatePreview();
 283        // The messages from 'conversationUserId' are marked as read if the player clicks on the input field of the priv
 1284        MarkUserChatMessagesAsRead();
 1285    }
 286
 287    private void HandleInputFieldDeselected()
 288    {
 1289        if (View.IsFocused)
 0290            return;
 1291        WaitThenActivatePreview(deactivatePreviewCancellationToken.Token).Forget();
 1292    }
 293
 294    private void HandleViewFocused(bool focused)
 295    {
 1296        if (focused)
 297        {
 0298            deactivatePreviewCancellationToken.Cancel();
 0299            deactivatePreviewCancellationToken = new CancellationTokenSource();
 0300            deactivateFadeOutCancellationToken.Cancel();
 0301            deactivateFadeOutCancellationToken = new CancellationTokenSource();
 302
 0303            if (currentState.Equals(ChatWindowVisualState.NONE_VISIBLE))
 304            {
 0305                ActivatePreviewOnMessages();
 306            }
 0307        }
 308        else
 309        {
 1310            if (chatHudController.IsInputSelected)
 0311                return;
 312
 1313            if (currentState.Equals(ChatWindowVisualState.INPUT_MODE))
 314            {
 1315                WaitThenActivatePreview(deactivatePreviewCancellationToken.Token).Forget();
 1316                return;
 317            }
 318
 0319            if (currentState.Equals(ChatWindowVisualState.PREVIEW_MODE))
 320            {
 0321                WaitThenFadeOutMessages(deactivateFadeOutCancellationToken.Token).Forget();
 322            }
 323        }
 0324    }
 325
 326    private void HandleViewClicked()
 327    {
 0328        if (currentState.Equals(ChatWindowVisualState.INPUT_MODE))
 0329            return;
 0330        DeactivatePreview();
 0331    }
 332
 333    private async UniTaskVoid WaitThenActivatePreview(CancellationToken cancellationToken)
 334    {
 6335        await UniTask.Delay(3000, cancellationToken: cancellationToken);
 2336        await UniTask.SwitchToMainThread(cancellationToken);
 2337        if (cancellationToken.IsCancellationRequested)
 0338            return;
 2339        currentState = ChatWindowVisualState.PREVIEW_MODE;
 2340        ActivatePreview();
 2341    }
 342
 343    private async UniTaskVoid WaitThenFadeOutMessages(CancellationToken cancellationToken)
 344    {
 12345        await UniTask.Delay(30000, cancellationToken: cancellationToken);
 4346        await UniTask.SwitchToMainThread(cancellationToken);
 4347        if (cancellationToken.IsCancellationRequested)
 0348            return;
 4349        chatHudController.FadeOutMessages();
 4350        currentState = ChatWindowVisualState.NONE_VISIBLE;
 4351    }
 352
 353    public void ActivatePreview()
 354    {
 4355        View.ActivatePreview();
 4356        chatHudController.ActivatePreview();
 4357        currentState = ChatWindowVisualState.PREVIEW_MODE;
 4358        WaitThenFadeOutMessages(deactivateFadeOutCancellationToken.Token).Forget();
 4359        OnPreviewModeChanged?.Invoke(true);
 3360    }
 361
 362    public void ActivatePreviewOnMessages()
 363    {
 0364        chatHudController.ActivatePreview();
 0365        currentState = ChatWindowVisualState.PREVIEW_MODE;
 0366        OnPreviewModeChanged?.Invoke(true);
 0367    }
 368
 369    public void DeactivatePreview()
 370    {
 2371        deactivatePreviewCancellationToken.Cancel();
 2372        deactivatePreviewCancellationToken = new CancellationTokenSource();
 2373        deactivateFadeOutCancellationToken.Cancel();
 2374        deactivateFadeOutCancellationToken = new CancellationTokenSource();
 375
 2376        View.DeactivatePreview();
 2377        chatHudController.DeactivatePreview();
 2378        OnPreviewModeChanged?.Invoke(false);
 2379        currentState = ChatWindowVisualState.INPUT_MODE;
 2380    }
 381
 382    private void HandleChatInputTriggered(DCLAction_Trigger action)
 383    {
 384        // race condition patch caused by unfocusing input field from invalid message on SendChatMessage
 385        // chat input trigger is the same key as sending the chat message from the input field
 0386        if (skipChatInputTrigger)
 387        {
 0388            skipChatInputTrigger = false;
 0389            return;
 390        }
 0391        if (!View.IsActive)
 0392            return;
 0393        chatHudController.FocusInputField();
 0394    }
 395}