< Summary

Class:ChatHUDView
Assembly:ChatHUD
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/ChatWidgetHUD/ChatHUDView.cs
Covered lines:53
Uncovered lines:131
Coverable lines:184
Total lines:398
Line coverage:28.8% (53 of 184)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
ChatHUDView()0%110100%
add_OnShowMenu(...)0%220100%
remove_OnShowMenu(...)0%220100%
add_OnInputFieldSelected(...)0%110100%
remove_OnInputFieldSelected(...)0%2.862040%
add_OnInputFieldDeselected(...)0%110100%
remove_OnInputFieldDeselected(...)0%2.862040%
Create()0%2100%
Awake()0%220100%
OnEnable()0%110100%
OnDisable()0%110100%
Update()0%4.024088.89%
ResetInputField(...)0%6200%
RefreshControl()0%20400%
FocusInputField()0%110100%
UnfocusInputField()0%220100%
SetInputFieldText(...)0%2100%
ActivatePreview()0%2.312057.14%
DeactivatePreview()0%6200%
FadeOutMessages()0%2.52050%
SetFadeoutMode(...)0%20400%
AddEntry(...)0%56700%
RemoveFirstEntry()0%6200%
Hide(...)0%6200%
OnMessageCancelHover()0%2100%
ClearAllEntries()0%6200%
IsEntryVisible(...)0%2100%
OnInputFieldSubmit(...)0%12300%
OnInputFieldSelect(...)0%2100%
OnInputFieldDeselect(...)0%2100%
OnOpenContextMenu(...)0%2100%
OnMessageTriggerHover(...)0%12300%
OnMessageCoordinatesTriggerHover(...)0%2100%
OnMessageCancelGotoHover()0%2100%
SortEntries()0%2100%
SortEntriesImmediate()0%20400%
HandleNextChatInHistoryInput(...)0%6200%
HandlePreviousChatInHistoryInput(...)0%6200%
UpdateLayout()0%2100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/ChatWidgetHUD/ChatHUDView.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using DCL.Helpers;
 5using DCL.Interface;
 6using TMPro;
 7using UnityEngine;
 8using UnityEngine.Events;
 9using UnityEngine.EventSystems;
 10using UnityEngine.UI;
 11
 12public class ChatHUDView : BaseComponentView, IChatHUDComponentView
 13{
 14    private const string VIEW_PATH = "SocialBarV1/Chat";
 15
 16    public TMP_InputField inputField;
 17    public RectTransform chatEntriesContainer;
 18    public ScrollRect scrollRect;
 19    public GameObject messageHoverPanel;
 20    public GameObject messageHoverGotoPanel;
 21    public TextMeshProUGUI messageHoverText;
 22    public TextMeshProUGUI messageHoverGotoText;
 23    public UserContextMenu contextMenu;
 24    public UserContextConfirmationDialog confirmationDialog;
 25    [SerializeField] private DefaultChatEntryFactory defaultChatEntryFactory;
 26    [SerializeField] private Model model;
 27    [SerializeField] private InputAction_Trigger nextChatInHistoryInput;
 28    [SerializeField] private InputAction_Trigger previousChatInHistoryInput;
 29
 2730    [NonSerialized] protected List<ChatEntry> entries = new List<ChatEntry>();
 31
 2732    private readonly ChatMessage currentMessage = new ChatMessage();
 2733    private readonly Dictionary<Action, UnityAction<string>> inputFieldSelectedListeners =
 34        new Dictionary<Action, UnityAction<string>>();
 2735    private readonly Dictionary<Action, UnityAction<string>> inputFieldUnselectedListeners =
 36        new Dictionary<Action, UnityAction<string>>();
 37
 38    private int updateLayoutDelayedFrames;
 39    private bool isSortingDirty;
 40
 041    protected bool IsFadeoutModeEnabled => model.enableFadeoutMode;
 42
 43    public event Action<string> OnMessageUpdated;
 44
 45    public event Action OnShowMenu
 46    {
 47        add
 48        {
 249            if (contextMenu != null)
 250                contextMenu.OnShowMenu += value;
 251        }
 52        remove
 53        {
 254            if (contextMenu != null)
 255                contextMenu.OnShowMenu -= value;
 256        }
 57    }
 58
 59    public event Action OnInputFieldSelected
 60    {
 61        add
 62        {
 063            void Action(string s) => value.Invoke();
 264            inputFieldSelectedListeners[value] = Action;
 265            inputField.onSelect.AddListener(Action);
 266        }
 67        remove
 68        {
 469            if (!inputFieldSelectedListeners.ContainsKey(value)) return;
 070            inputField.onSelect.RemoveListener(inputFieldSelectedListeners[value]);
 071            inputFieldSelectedListeners.Remove(value);
 072        }
 73    }
 74
 75    public event Action OnInputFieldDeselected
 76    {
 77        add
 78        {
 079            void Action(string s) => value.Invoke();
 280            inputFieldUnselectedListeners[value] = Action;
 281            inputField.onDeselect.AddListener(Action);
 282        }
 83        remove
 84        {
 485            if (!inputFieldUnselectedListeners.ContainsKey(value)) return;
 086            inputField.onDeselect.RemoveListener(inputFieldUnselectedListeners[value]);
 087            inputFieldUnselectedListeners.Remove(value);
 088        }
 89    }
 90
 91    public event Action OnPreviousChatInHistory;
 92    public event Action OnNextChatInHistory;
 93
 94    public event Action<ChatMessage> OnSendMessage;
 95
 096    public int EntryCount => entries.Count;
 097    public IChatEntryFactory ChatEntryFactory { get; set; }
 198    public bool IsInputFieldSelected => inputField.isFocused;
 99
 100    public static ChatHUDView Create()
 101    {
 0102        var view = Instantiate(Resources.Load<GameObject>(VIEW_PATH)).GetComponent<ChatHUDView>();
 0103        return view;
 104    }
 105
 106    public override void Awake()
 107    {
 24108        base.Awake();
 24109        inputField.onSubmit.AddListener(OnInputFieldSubmit);
 24110        inputField.onSelect.AddListener(OnInputFieldSelect);
 24111        inputField.onDeselect.AddListener(OnInputFieldDeselect);
 24112        inputField.onValueChanged.AddListener(str => OnMessageUpdated?.Invoke(str));
 24113        ChatEntryFactory ??= defaultChatEntryFactory;
 24114        model.enableFadeoutMode = true;
 24115    }
 116
 117    public override void OnEnable()
 118    {
 25119        base.OnEnable();
 25120        UpdateLayout();
 25121        nextChatInHistoryInput.OnTriggered += HandleNextChatInHistoryInput;
 25122        previousChatInHistoryInput.OnTriggered += HandlePreviousChatInHistoryInput;
 25123    }
 124
 125    public override void OnDisable()
 126    {
 25127        base.OnDisable();
 25128        nextChatInHistoryInput.OnTriggered -= HandleNextChatInHistoryInput;
 25129        previousChatInHistoryInput.OnTriggered -= HandlePreviousChatInHistoryInput;
 25130    }
 131
 132    public override void Update()
 133    {
 910134        base.Update();
 135
 910136        if (updateLayoutDelayedFrames > 0)
 137        {
 18138            updateLayoutDelayedFrames--;
 139
 18140            if (updateLayoutDelayedFrames <= 0)
 4141                chatEntriesContainer.ForceUpdateLayout(delayed: false);
 142        }
 143
 910144        if (isSortingDirty)
 0145            SortEntriesImmediate();
 910146        isSortingDirty = false;
 910147    }
 148
 149    public void ResetInputField(bool loseFocus = false)
 150    {
 0151        inputField.text = string.Empty;
 0152        inputField.caretColor = Color.white;
 0153        if (loseFocus)
 0154            UnfocusInputField();
 0155    }
 156
 157    public override void RefreshControl()
 158    {
 0159        if (model.isInputFieldFocused)
 0160            FocusInputField();
 0161        SetInputFieldText(model.inputFieldText);
 0162        SetFadeoutMode(model.enableFadeoutMode);
 0163        if (model.isInPreviewMode)
 0164            ActivatePreview();
 165        else
 0166            DeactivatePreview();
 0167        ClearAllEntries();
 0168        foreach (var entry in model.entries)
 0169            AddEntry(entry);
 0170    }
 171
 172    public void FocusInputField()
 173    {
 1174        inputField.ActivateInputField();
 1175        inputField.Select();
 1176    }
 177
 1178    public void UnfocusInputField() => EventSystem.current?.SetSelectedGameObject(null);
 179
 180    public void SetInputFieldText(string text)
 181    {
 0182        model.inputFieldText = text;
 0183        inputField.text = text;
 0184        inputField.MoveTextEnd(false);
 0185    }
 186
 187    public void ActivatePreview()
 188    {
 2189        model.isInPreviewMode = true;
 190
 4191        for (var i = 0; i < entries.Count; i++)
 192        {
 0193            var entry = entries[i];
 0194            entry.ActivatePreview();
 195        }
 2196    }
 197    public void DeactivatePreview()
 198    {
 0199        model.isInPreviewMode = false;
 200
 0201        for (var i = 0; i < entries.Count; i++)
 202        {
 0203            var entry = entries[i];
 0204            entry.DeactivatePreview();
 205        }
 0206    }
 207    public void FadeOutMessages()
 208    {
 4209        for (var i = 0; i < entries.Count; i++)
 210        {
 0211            var entry = entries[i];
 0212            entry.FadeOut();
 213        }
 2214    }
 215
 216    private void SetFadeoutMode(bool enabled)
 217    {
 0218        model.enableFadeoutMode = enabled;
 219
 0220        for (int i = 0; i < entries.Count; i++)
 221        {
 0222            var entry = entries[i];
 223
 0224            if (enabled)
 225            {
 0226                entry.SetFadeout(IsEntryVisible(entry));
 0227            }
 228            else
 229            {
 0230                entry.SetFadeout(false);
 231            }
 232        }
 233
 0234        if (enabled)
 235        {
 0236            confirmationDialog.Hide();
 237        }
 0238    }
 239
 240    public virtual void AddEntry(ChatEntryModel model, bool setScrollPositionToBottom = false)
 241    {
 0242        var chatEntry = ChatEntryFactory.Create(model);
 0243        chatEntry.transform.SetParent(chatEntriesContainer, false);
 244
 0245        if(this.model.enableFadeoutMode)
 0246            chatEntry.SetFadeout(true);
 247        else
 0248            chatEntry.SetFadeout(false);
 249
 0250        chatEntry.Populate(model);
 251
 0252        if (model.messageType == ChatMessage.Type.PUBLIC
 253            || model.messageType == ChatMessage.Type.PRIVATE)
 0254            chatEntry.OnPressRightButton += OnOpenContextMenu;
 255
 0256        chatEntry.OnTriggerHover += OnMessageTriggerHover;
 0257        chatEntry.OnTriggerHoverGoto += OnMessageCoordinatesTriggerHover;
 0258        chatEntry.OnCancelHover += OnMessageCancelHover;
 0259        chatEntry.OnCancelGotoHover += OnMessageCancelGotoHover;
 260
 0261        entries.Add(chatEntry);
 262
 0263        if (this.model.isInPreviewMode)
 0264            chatEntry.ActivatePreviewInstantly();
 265
 0266        SortEntries();
 0267        UpdateLayout();
 268
 0269        if (setScrollPositionToBottom && scrollRect.verticalNormalizedPosition > 0)
 0270            scrollRect.verticalNormalizedPosition = 0;
 0271    }
 272
 273    public void RemoveFirstEntry()
 274    {
 0275        if (entries.Count <= 0) return;
 0276        Destroy(entries[0].gameObject);
 0277        entries.Remove(entries[0]);
 0278    }
 279
 280    public override void Hide(bool instant = false)
 281    {
 0282        base.Hide(instant);
 0283        if (contextMenu == null) return;
 0284        contextMenu.Hide();
 0285        confirmationDialog.Hide();
 0286    }
 287
 288    public void OnMessageCancelHover()
 289    {
 0290        messageHoverPanel.SetActive(false);
 0291        messageHoverText.text = string.Empty;
 0292    }
 293
 294    public virtual void ClearAllEntries()
 295    {
 0296        foreach (var entry in entries)
 0297            Destroy(entry.gameObject);
 0298        entries.Clear();
 0299    }
 300
 301    private bool IsEntryVisible(ChatEntry entry)
 302    {
 0303        int visibleCorners =
 304            (entry.transform as RectTransform).CountCornersVisibleFrom(scrollRect.viewport.transform as RectTransform);
 0305        return visibleCorners > 0;
 306    }
 307
 308    private void OnInputFieldSubmit(string message)
 309    {
 0310        currentMessage.body = message;
 0311        currentMessage.sender = string.Empty;
 0312        currentMessage.messageType = ChatMessage.Type.NONE;
 0313        currentMessage.recipient = string.Empty;
 314
 315        // A TMP_InputField is automatically marked as 'wasCanceled' when the ESC key is pressed
 0316        if (inputField.wasCanceled)
 0317            currentMessage.body = string.Empty;
 318
 0319        OnSendMessage?.Invoke(currentMessage);
 0320    }
 321
 322    private void OnInputFieldSelect(string message)
 323    {
 0324        AudioScriptableObjects.inputFieldFocus.Play(true);
 0325    }
 326
 327    private void OnInputFieldDeselect(string message)
 328    {
 0329        AudioScriptableObjects.inputFieldUnfocus.Play(true);
 0330    }
 331
 332    private void OnOpenContextMenu(DefaultChatEntry chatEntry)
 333    {
 0334        chatEntry.DockContextMenu((RectTransform) contextMenu.transform);
 0335        contextMenu.transform.parent = transform;
 0336        contextMenu.Show(chatEntry.Model.senderId);
 0337    }
 338
 339    protected virtual void OnMessageTriggerHover(DefaultChatEntry chatEntry)
 340    {
 0341        if (contextMenu == null || contextMenu.isVisible)
 0342            return;
 343
 0344        messageHoverText.text = chatEntry.messageLocalDateTime;
 0345        chatEntry.DockHoverPanel((RectTransform) messageHoverPanel.transform);
 0346        messageHoverPanel.SetActive(true);
 0347    }
 348
 349    private void OnMessageCoordinatesTriggerHover(DefaultChatEntry chatEntry, ParcelCoordinates parcelCoordinates)
 350    {
 0351        messageHoverGotoText.text = $"{parcelCoordinates} INFO";
 0352        chatEntry.DockHoverPanel((RectTransform) messageHoverGotoPanel.transform);
 0353        messageHoverGotoPanel.SetActive(true);
 0354    }
 355
 356    private void OnMessageCancelGotoHover()
 357    {
 0358        messageHoverGotoPanel.SetActive(false);
 0359        messageHoverGotoText.text = string.Empty;
 0360    }
 361
 0362    private void SortEntries() => isSortingDirty = true;
 363
 364    private void SortEntriesImmediate()
 365    {
 0366        entries = entries.OrderBy(x => x.Model.timestamp).ToList();
 367
 0368        int count = entries.Count;
 0369        for (int i = 0; i < count; i++)
 370        {
 0371            if (entries[i].transform.GetSiblingIndex() != i)
 0372                entries[i].transform.SetSiblingIndex(i);
 373        }
 0374    }
 375
 0376    private void HandleNextChatInHistoryInput(DCLAction_Trigger action) => OnNextChatInHistory?.Invoke();
 377
 0378    private void HandlePreviousChatInHistoryInput(DCLAction_Trigger action) => OnPreviousChatInHistory?.Invoke();
 379
 380    private void UpdateLayout()
 381    {
 382        // we have to wait several frames before updating the layout
 383        // every message entry waits for 3 frames before updating its own layout
 384        // we gotta force the layout updating after that
 385        // TODO: simplify this change to a bool when we update to a working TextMeshPro version
 0386        updateLayoutDelayedFrames = 4;
 0387    }
 388
 389    [Serializable]
 390    private struct Model
 391    {
 392        public bool isInPreviewMode;
 393        public bool isInputFieldFocused;
 394        public string inputFieldText;
 395        public bool enableFadeoutMode;
 396        public ChatEntryModel[] entries;
 397    }
 398}