< Summary

Class:PublicChatChannelController
Assembly:WorldChatWindowHUD
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/WorldChatWindowHUD/PublicChatChannelController.cs
Covered lines:145
Uncovered lines:50
Coverable lines:195
Total lines:368
Line coverage:74.3% (145 of 195)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
PublicChatChannelController(...)0%110100%
Initialize(...)0%330100%
Setup(...)0%12300%
Dispose()0%440100%
SendChatMessage(...)0%550100%
SetVisibility(...)0%330100%
SetVisibility(...)0%110100%
ReloadAllChats()0%42600%
ActivatePreviewModeInstantly()0%220100%
MarkChatMessagesAsRead()0%110100%
HandleViewClosed()0%220100%
HandleViewBacked()0%6200%
HandleMessageInputUpdated(...)0%330100%
IsOldPrivateMessage(...)0%220100%
HandleMessageReceived(...)0%7.277082.35%
HandleInputFieldSelected()0%110100%
HandleInputFieldDeselected()0%2.062075%
HandleViewFocused(...)0%42600%
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/WorldChatWindowHUD/PublicChatChannelController.cs

#LineLine coverage
 1using System;
 2using System.Threading;
 3using Cysharp.Threading.Tasks;
 4using DCL;
 5using DCL.Interface;
 6using SocialFeaturesAnalytics;
 7using UnityEngine;
 8
 9public class PublicChatChannelController : IHUD
 10{
 011    public IChannelChatWindowView View { get; private set; }
 12
 13    private enum ChatWindowVisualState { NONE_VISIBLE, INPUT_MODE, PREVIEW_MODE }
 14
 15    public event Action OnBack;
 16    public event Action OnClosed;
 17    public event Action<bool> OnPreviewModeChanged;
 18
 19    private readonly IChatController chatController;
 20    private readonly ILastReadMessagesService lastReadMessagesService;
 21    private readonly IUserProfileBridge userProfileBridge;
 22    private readonly DataStore dataStore;
 23    private readonly IProfanityFilter profanityFilter;
 24    private readonly ISocialAnalytics socialAnalytics;
 25    private readonly IMouseCatcher mouseCatcher;
 26    private readonly InputAction_Trigger toggleChatTrigger;
 27    private ChatHUDController chatHudController;
 28    private double initTimeInSeconds;
 29    private string channelId;
 30    private ChatWindowVisualState currentState;
 1631    private CancellationTokenSource deactivatePreviewCancellationToken = new CancellationTokenSource();
 1632    private CancellationTokenSource deactivateFadeOutCancellationToken = new CancellationTokenSource();
 33
 34    private bool skipChatInputTrigger;
 1635    private string lastPrivateMessageRecipient = string.Empty;
 36
 237    private UserProfile ownProfile => userProfileBridge.GetOwn();
 38
 1639    public PublicChatChannelController(IChatController chatController,
 40        ILastReadMessagesService lastReadMessagesService,
 41        IUserProfileBridge userProfileBridge,
 42        DataStore dataStore,
 43        IProfanityFilter profanityFilter,
 44        ISocialAnalytics socialAnalytics,
 45        IMouseCatcher mouseCatcher,
 46        InputAction_Trigger toggleChatTrigger)
 47    {
 1648        this.chatController = chatController;
 1649        this.lastReadMessagesService = lastReadMessagesService;
 1650        this.userProfileBridge = userProfileBridge;
 1651        this.dataStore = dataStore;
 1652        this.profanityFilter = profanityFilter;
 1653        this.socialAnalytics = socialAnalytics;
 1654        this.mouseCatcher = mouseCatcher;
 1655        this.toggleChatTrigger = toggleChatTrigger;
 1656    }
 57
 58    public void Initialize(IChannelChatWindowView view = null)
 59    {
 1660        view ??= PublicChatChannelComponentView.Create();
 1661        View = view;
 1662        view.OnClose += HandleViewClosed;
 1663        view.OnBack += HandleViewBacked;
 1664        view.OnFocused += HandleViewFocused;
 1665        View.OnClickOverWindow += HandleViewClicked;
 66
 67
 1668        chatHudController = new ChatHUDController(dataStore,
 69            userProfileBridge,
 70            true,
 71            profanityFilter);
 1672        chatHudController.Initialize(view.ChatHUD);
 1673        chatHudController.OnSendMessage += SendChatMessage;
 1674        chatHudController.OnMessageUpdated += HandleMessageInputUpdated;
 1675        chatHudController.OnInputFieldSelected -= HandleInputFieldSelected;
 1676        chatHudController.OnInputFieldSelected += HandleInputFieldSelected;
 1677        chatHudController.OnInputFieldDeselected -= HandleInputFieldDeselected;
 1678        chatHudController.OnInputFieldDeselected += HandleInputFieldDeselected;
 79
 1680        chatController.OnAddMessage -= HandleMessageReceived;
 1681        chatController.OnAddMessage += HandleMessageReceived;
 82
 1683        if (mouseCatcher != null)
 1684            mouseCatcher.OnMouseLock += ActivatePreview;
 85
 1686        toggleChatTrigger.OnTriggered += HandleChatInputTriggered;
 87
 1688        initTimeInSeconds = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() / 1000.0;
 89
 1690        currentState = ChatWindowVisualState.PREVIEW_MODE;
 1691        WaitThenFadeOutMessages(deactivateFadeOutCancellationToken.Token).Forget();
 1692    }
 93
 94    public void Setup(string channelId)
 95    {
 096        if (string.IsNullOrEmpty(channelId) || channelId == this.channelId) return;
 097        this.channelId = channelId;
 98
 99        // TODO: retrieve data from a channel provider
 0100        View.Configure(new PublicChatChannelModel(this.channelId, "nearby",
 101            "Talk to the people around you. If you move far away from someone you will lose contact. All whispers will b
 102
 0103        ReloadAllChats().Forget();
 0104    }
 105
 106    public void Dispose()
 107    {
 15108        View.OnClose -= HandleViewClosed;
 15109        View.OnBack -= HandleViewBacked;
 110
 15111        if (chatController != null)
 15112            chatController.OnAddMessage -= HandleMessageReceived;
 113
 15114        chatHudController.OnSendMessage -= SendChatMessage;
 15115        chatHudController.OnMessageUpdated -= HandleMessageInputUpdated;
 15116        chatHudController.OnInputFieldSelected -= HandleInputFieldSelected;
 15117        chatHudController.OnInputFieldDeselected -= HandleInputFieldDeselected;
 118
 15119        if (mouseCatcher != null)
 15120            mouseCatcher.OnMouseLock -= ActivatePreview;
 121
 15122        toggleChatTrigger.OnTriggered -= HandleChatInputTriggered;
 123
 15124        if (View != null)
 125        {
 15126            View.OnFocused -= HandleViewFocused;
 15127            View.OnClickOverWindow -= HandleViewClicked;
 15128            View.Dispose();
 129        }
 15130    }
 131
 132    private void SendChatMessage(ChatMessage message)
 133    {
 3134        var isValidMessage = !string.IsNullOrEmpty(message.body) && !string.IsNullOrWhiteSpace(message.body);
 3135        var isPrivateMessage = message.messageType == ChatMessage.Type.PRIVATE;
 136
 3137        if (isPrivateMessage && isValidMessage)
 1138            lastPrivateMessageRecipient = message.recipient;
 139        else
 2140            lastPrivateMessageRecipient = null;
 141
 3142        if (isValidMessage)
 143        {
 2144            chatHudController.ResetInputField();
 2145            chatHudController.FocusInputField();
 2146        }
 147        else
 148        {
 1149            skipChatInputTrigger = true;
 1150            chatHudController.ResetInputField(true);
 1151            ActivatePreview();
 1152            return;
 153        }
 154
 2155        if (isPrivateMessage)
 1156            message.body = $"/w {message.recipient} {message.body}";
 157
 2158        chatController.Send(message);
 2159    }
 160
 161    public void SetVisibility(bool visible, bool focusInputField)
 162    {
 11163        if (visible)
 164        {
 8165            View.Show();
 8166            MarkChatMessagesAsRead();
 167
 8168            if (focusInputField)
 1169                chatHudController.FocusInputField();
 1170        }
 171        else
 172        {
 3173            chatHudController.UnfocusInputField();
 3174            View.Hide();
 175        }
 10176    }
 177
 9178    public void SetVisibility(bool visible) => SetVisibility(visible, false);
 179
 180    private async UniTaskVoid ReloadAllChats()
 181    {
 0182        chatHudController.ClearAllEntries();
 183
 184        const int entriesPerFrame = 10;
 185        // TODO: filter entries by channelId
 0186        var list = chatController.GetEntries();
 0187        if (list.Count == 0) return;
 188
 0189        for (var i = list.Count - 1; i >= 0; i--)
 190        {
 0191            var message = list[i];
 0192            if (i % entriesPerFrame == 0) await UniTask.NextFrame();
 0193            HandleMessageReceived(message);
 0194        }
 0195    }
 196
 197    public void ActivatePreviewModeInstantly()
 198    {
 3199        deactivatePreviewCancellationToken.Cancel();
 3200        deactivatePreviewCancellationToken = new CancellationTokenSource();
 3201        deactivateFadeOutCancellationToken.Cancel();
 3202        deactivateFadeOutCancellationToken = new CancellationTokenSource();
 203
 3204        View.ActivatePreviewInstantly();
 3205        chatHudController.ActivatePreview();
 3206        currentState = ChatWindowVisualState.PREVIEW_MODE;
 3207        WaitThenFadeOutMessages(deactivateFadeOutCancellationToken.Token).Forget();
 3208        OnPreviewModeChanged?.Invoke(true);
 2209    }
 210
 8211    private void MarkChatMessagesAsRead() => lastReadMessagesService.MarkAllRead(channelId);
 212
 1213    private void HandleViewClosed() => OnClosed?.Invoke();
 214
 0215    private void HandleViewBacked() => OnBack?.Invoke();
 216
 217    private void HandleMessageInputUpdated(string message)
 218    {
 1219        if (!string.IsNullOrEmpty(lastPrivateMessageRecipient) && message == "/r ")
 1220            chatHudController.SetInputFieldText($"/w {lastPrivateMessageRecipient} ");
 1221    }
 222
 223    private bool IsOldPrivateMessage(ChatMessage message)
 224    {
 5225        if (message.messageType != ChatMessage.Type.PRIVATE) return false;
 3226        var timestampInSeconds = message.timestamp / 1000.0;
 3227        return timestampInSeconds < initTimeInSeconds;
 228    }
 229
 230    private void HandleMessageReceived(ChatMessage message)
 231    {
 5232        if (IsOldPrivateMessage(message)) return;
 233
 3234        chatHudController.AddChatMessage(message, View.IsActive);
 235
 3236        if (View.IsActive)
 0237            MarkChatMessagesAsRead();
 238
 3239        if (message.messageType == ChatMessage.Type.PRIVATE && message.recipient == ownProfile.userId)
 1240            lastPrivateMessageRecipient = userProfileBridge.Get(message.sender).userName;
 241
 3242        deactivatePreviewCancellationToken.Cancel();
 3243        deactivatePreviewCancellationToken = new CancellationTokenSource();
 3244        deactivateFadeOutCancellationToken.Cancel();
 3245        deactivateFadeOutCancellationToken = new CancellationTokenSource();
 246
 3247        if (currentState.Equals(ChatWindowVisualState.NONE_VISIBLE))
 248        {
 0249            ActivatePreview();
 0250        }
 3251        else if (currentState.Equals(ChatWindowVisualState.PREVIEW_MODE))
 252        {
 3253            WaitThenFadeOutMessages(deactivateFadeOutCancellationToken.Token).Forget();
 254        }
 3255    }
 256
 257    private void HandleInputFieldSelected()
 258    {
 1259        deactivatePreviewCancellationToken.Cancel();
 1260        deactivatePreviewCancellationToken = new CancellationTokenSource();
 1261        DeactivatePreview();
 1262    }
 263
 264    private void HandleInputFieldDeselected()
 265    {
 1266        if (View.IsFocused)
 0267            return;
 1268        WaitThenActivatePreview(deactivatePreviewCancellationToken.Token).Forget();
 1269    }
 270
 271    private void HandleViewFocused(bool focused)
 272    {
 0273        if (focused)
 274        {
 0275            deactivatePreviewCancellationToken.Cancel();
 0276            deactivatePreviewCancellationToken = new CancellationTokenSource();
 0277            deactivateFadeOutCancellationToken.Cancel();
 0278            deactivateFadeOutCancellationToken = new CancellationTokenSource();
 0279            if (currentState.Equals(ChatWindowVisualState.NONE_VISIBLE))
 280            {
 0281                ActivatePreviewOnMessages();
 282            }
 0283        }
 284        else
 285        {
 0286            if (chatHudController.IsInputSelected)
 0287                return;
 288
 0289            if (currentState.Equals(ChatWindowVisualState.INPUT_MODE))
 290            {
 0291                WaitThenActivatePreview(deactivatePreviewCancellationToken.Token).Forget();
 0292                return;
 293            }
 294
 0295            if (currentState.Equals(ChatWindowVisualState.PREVIEW_MODE))
 296            {
 0297                WaitThenFadeOutMessages(deactivateFadeOutCancellationToken.Token).Forget();
 298            }
 299        }
 0300    }
 301
 302    private void HandleViewClicked()
 303    {
 0304        if (currentState.Equals(ChatWindowVisualState.INPUT_MODE))
 0305            return;
 0306        DeactivatePreview();
 0307    }
 308
 309    private async UniTaskVoid WaitThenActivatePreview(CancellationToken cancellationToken)
 310    {
 3311        await UniTask.Delay(3000, cancellationToken: cancellationToken);
 1312        await UniTask.SwitchToMainThread(cancellationToken);
 1313        if (cancellationToken.IsCancellationRequested) return;
 1314        currentState = ChatWindowVisualState.PREVIEW_MODE;
 1315        ActivatePreview();
 1316    }
 317
 318    private async UniTaskVoid WaitThenFadeOutMessages(CancellationToken cancellationToken)
 319    {
 62320        await UniTask.Delay(30000, cancellationToken: cancellationToken);
 1321        await UniTask.SwitchToMainThread(cancellationToken);
 1322        if (cancellationToken.IsCancellationRequested) return;
 1323        chatHudController.FadeOutMessages();
 1324        currentState = ChatWindowVisualState.NONE_VISIBLE;
 1325    }
 326
 327    public void ActivatePreview()
 328    {
 4329        View.ActivatePreview();
 4330        chatHudController.ActivatePreview();
 4331        currentState = ChatWindowVisualState.PREVIEW_MODE;
 4332        WaitThenFadeOutMessages(deactivateFadeOutCancellationToken.Token).Forget();
 4333        OnPreviewModeChanged?.Invoke(true);
 4334    }
 335
 336    public void ActivatePreviewOnMessages()
 337    {
 0338        chatHudController.ActivatePreview();
 0339        OnPreviewModeChanged?.Invoke(true);
 0340        currentState = ChatWindowVisualState.PREVIEW_MODE;
 0341    }
 342
 343    public void DeactivatePreview()
 344    {
 3345        deactivatePreviewCancellationToken.Cancel();
 3346        deactivatePreviewCancellationToken = new CancellationTokenSource();
 3347        deactivateFadeOutCancellationToken.Cancel();
 3348        deactivateFadeOutCancellationToken = new CancellationTokenSource();
 349
 3350        View.DeactivatePreview();
 3351        chatHudController.DeactivatePreview();
 3352        OnPreviewModeChanged?.Invoke(false);
 3353        currentState = ChatWindowVisualState.INPUT_MODE;
 3354    }
 355
 356    private void HandleChatInputTriggered(DCLAction_Trigger action)
 357    {
 358        // race condition patch caused by unfocusing input field from invalid message on SendChatMessage
 359        // chat input trigger is the same key as sending the chat message from the input field
 0360        if (skipChatInputTrigger)
 361        {
 0362            skipChatInputTrigger = false;
 0363            return;
 364        }
 0365        if (!View.IsActive) return;
 0366        chatHudController.FocusInputField();
 0367    }
 368}