< Summary

Class:ChatEntry
Assembly:ChatHUD
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/ChatWidgetHUD/ChatEntry.cs
Covered lines:77
Uncovered lines:87
Coverable lines:164
Total lines:382
Line coverage:46.9% (77 of 164)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
ChatEntry()0%110100%
Populate(...)0%80.7828059.32%
PreloadSceneMetadata(...)0%6200%
OnPointerClick(...)0%1101000%
OnPointerEnter(...)0%6200%
OnPointerExit(...)0%17.85020%
OnDisable()0%110100%
SetFadeout(...)0%2.382054.55%
Update()0%110100%
CheckHoverCoordinates()0%8.124036.36%
Fade()0%7.933018.18%
ProcessHoverPanelTimer()0%14.115028.57%
ProcessHoverGotoPanelTimer()0%14.115028.57%
RemoveTabs(...)0%2.152066.67%
GetDefaultSenderString(...)0%2.152066.67%
UnixTimeStampToLocalDateTime(...)0%110100%

File(s)

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

#LineLine coverage
 1using DCL.Helpers;
 2using DCL.Interface;
 3using System;
 4using DCL.SettingsCommon;
 5using TMPro;
 6using UnityEngine;
 7using UnityEngine.Events;
 8using UnityEngine.EventSystems;
 9using UnityEngine.Serialization;
 10using System.Collections.Generic;
 11using System.Text.RegularExpressions;
 12using DCL;
 13
 14public class ChatEntry : MonoBehaviour, IPointerClickHandler, IPointerEnterHandler, IPointerExitHandler
 15{
 16    private const string COORDINATES_COLOR_PRIVATE = "#4886E3";
 17    private const string COORDINATES_COLOR_PUBLIC = "#62C6FF";
 18
 19    public struct Model
 20    {
 21        public enum SubType
 22        {
 23            NONE,
 24            PRIVATE_FROM,
 25            PRIVATE_TO
 26        }
 27
 28        public ChatMessage.Type messageType;
 29        public string bodyText;
 30        public string senderId;
 31        public string senderName;
 32        public string recipientName;
 33        public string otherUserId;
 34        public ulong timestamp;
 35
 36        public SubType subType;
 37    }
 38
 4139    [SerializeField] internal float timeToFade = 10f;
 4140    [SerializeField] internal float fadeDuration = 5f;
 41
 42    [SerializeField] internal TextMeshProUGUI username;
 43    [SerializeField] internal TextMeshProUGUI body;
 44
 4145    [SerializeField] internal Color worldMessageColor = Color.white;
 46
 47    [FormerlySerializedAs("privateMessageColor")]
 4148    [SerializeField] internal Color privateToMessageColor = Color.white;
 49
 50    [FormerlySerializedAs("privateMessageColor")]
 4151    [SerializeField] internal Color privateFromMessageColor = Color.white;
 52
 4153    [SerializeField] internal Color systemColor = Color.white;
 4154    [SerializeField] internal Color playerNameColor = Color.yellow;
 4155    [SerializeField] internal Color nonPlayerNameColor = Color.white;
 56    [SerializeField] CanvasGroup group;
 4157    [SerializeField] internal float timeToHoverPanel = 1f;
 4158    [SerializeField] internal float timeToHoverGotoPanel = 1f;
 59
 60    [NonSerialized] public string messageLocalDateTime;
 61
 62    bool fadeEnabled = false;
 63    double fadeoutStartTime;
 64    float hoverPanelTimer = 0;
 65    float hoverGotoPanelTimer = 0;
 66    bool isOverCoordinates = false;
 67    ParcelCoordinates currentCoordinates;
 68
 69    public RectTransform hoverPanelPositionReference;
 70    public RectTransform contextMenuPositionReference;
 71
 072    public Model model { get; private set; }
 73
 74    public event UnityAction<string> OnPress;
 75    public event UnityAction<ChatEntry> OnPressRightButton;
 76    public event UnityAction<ChatEntry> OnTriggerHover;
 77    public event UnityAction<ChatEntry, ParcelCoordinates> OnTriggerHoverGoto;
 78    public event UnityAction OnCancelHover;
 79    public event UnityAction OnCancelGotoHover;
 80
 4181    private List<string> textCoords = new List<string>();
 82
 83    public void Populate(Model chatEntryModel)
 84    {
 4285        this.model = chatEntryModel;
 86
 4287        string userString = GetDefaultSenderString(chatEntryModel.senderName);
 88
 4289        if (chatEntryModel.subType == Model.SubType.PRIVATE_FROM)
 90        {
 691            userString = $"<b>From {chatEntryModel.senderName}:</b>";
 692        }
 3693        else if (chatEntryModel.subType == Model.SubType.PRIVATE_TO)
 94        {
 295            userString = $"<b>To {chatEntryModel.recipientName}:</b>";
 96        }
 97
 4298        switch (chatEntryModel.messageType)
 99        {
 100            case ChatMessage.Type.PUBLIC:
 27101                body.color = worldMessageColor;
 102
 27103                if (username != null)
 27104                    username.color = chatEntryModel.senderName == UserProfile.GetOwnUserProfile().userName ? playerNameC
 27105                break;
 106            case ChatMessage.Type.PRIVATE:
 14107                body.color = worldMessageColor;
 108
 14109                if (username != null)
 110                {
 7111                    if (model.subType == Model.SubType.PRIVATE_TO)
 2112                        username.color = privateToMessageColor;
 113                    else
 5114                        username.color = privateFromMessageColor;
 115                }
 116
 5117                break;
 118            case ChatMessage.Type.SYSTEM:
 1119                body.color = systemColor;
 120
 1121                if (username != null)
 1122                    username.color = systemColor;
 123                break;
 124        }
 125
 42126        chatEntryModel.bodyText = RemoveTabs(chatEntryModel.bodyText);
 127
 42128        if (username != null && !string.IsNullOrEmpty(userString))
 129        {
 35130            if (username != null)
 35131                username.text = userString;
 132
 35133            body.text = $"{userString} {chatEntryModel.bodyText}";
 35134        }
 135        else
 136        {
 7137            if (username != null)
 0138                username.text = "";
 139
 7140            body.text = $"{chatEntryModel.bodyText}";
 141        }
 142
 42143        if (CoordinateUtils.HasValidTextCoordinates(body.text)) {
 0144            List<string> textCoordinates = CoordinateUtils.GetTextCoordinates(body.text);
 0145            for (int i = 0; i < textCoordinates.Count; i++)
 146            {
 0147                PreloadSceneMetadata(CoordinateUtils.ParseCoordinatesString(textCoordinates[i]));
 148                string coordinatesColor;
 0149                if (chatEntryModel.messageType == ChatMessage.Type.PRIVATE)
 0150                    coordinatesColor = COORDINATES_COLOR_PRIVATE;
 151                else
 0152                    coordinatesColor = COORDINATES_COLOR_PUBLIC;
 153
 0154                body.text = body.text.Replace(textCoordinates[i], $"<link={textCoordinates[i]}><color={coordinatesColor}
 155            }
 156        }
 157
 42158        messageLocalDateTime = UnixTimeStampToLocalDateTime(chatEntryModel.timestamp).ToString();
 159
 42160        Utils.ForceUpdateLayout(transform as RectTransform);
 161
 42162        if (fadeEnabled)
 0163            group.alpha = 0;
 164
 42165        if (HUDAudioHandler.i != null)
 166        {
 167            // Check whether or not this message is new, and chat sounds are enabled in settings
 0168            if (chatEntryModel.timestamp > HUDAudioHandler.i.chatLastCheckedTimestamp && Settings.i.audioSettings.Data.c
 169            {
 0170                switch (chatEntryModel.messageType)
 171                {
 172                    case ChatMessage.Type.PUBLIC:
 173                        // Check whether or not the message was sent by the local player
 0174                        if (chatEntryModel.senderId == UserProfile.GetOwnUserProfile().userId)
 0175                            AudioScriptableObjects.chatSend.Play(true);
 176                        else
 0177                            AudioScriptableObjects.chatReceiveGlobal.Play(true);
 0178                        break;
 179                    case ChatMessage.Type.PRIVATE:
 0180                        switch (chatEntryModel.subType)
 181                        {
 182                            case Model.SubType.PRIVATE_FROM:
 0183                                AudioScriptableObjects.chatReceivePrivate.Play(true);
 0184                                break;
 185                            case Model.SubType.PRIVATE_TO:
 0186                                AudioScriptableObjects.chatSend.Play(true);
 0187                                break;
 188                            default:
 189                                break;
 190                        }
 191                        break;
 192                    case ChatMessage.Type.SYSTEM:
 0193                        AudioScriptableObjects.chatReceiveGlobal.Play(true);
 194                        break;
 195                    default:
 196                        break;
 197                }
 198            }
 199
 0200            HUDAudioHandler.i.RefreshChatLastCheckedTimestamp();
 201        }
 42202    }
 203
 204    private void PreloadSceneMetadata(ParcelCoordinates parcelCoordinates)
 205    {
 0206        if (MinimapMetadata.GetMetadata().GetSceneInfo(parcelCoordinates.x, parcelCoordinates.y) == null)
 0207            WebInterface.RequestScenesInfoAroundParcel(new Vector2(parcelCoordinates.x, parcelCoordinates.y), 2);
 0208    }
 209
 210    public void OnPointerClick(PointerEventData pointerEventData)
 211    {
 0212        if (pointerEventData.button == PointerEventData.InputButton.Left)
 213        {
 0214            int linkIndex = TMP_TextUtilities.FindIntersectingLink(body, pointerEventData.position, null);
 0215            if (linkIndex != -1)
 216            {
 0217                DataStore.i.HUDs.gotoPanelVisible.Set(true);
 0218                TMP_LinkInfo linkInfo = body.textInfo.linkInfo[linkIndex];
 0219                ParcelCoordinates parcelCoordinate = CoordinateUtils.ParseCoordinatesString(linkInfo.GetLinkID().ToStrin
 0220                DataStore.i.HUDs.gotoPanelCoordinates.Set(parcelCoordinate);
 221            }
 222
 0223            if (model.messageType != ChatMessage.Type.PRIVATE)
 0224                return;
 225
 0226            OnPress?.Invoke(model.otherUserId);
 0227        }
 0228        else if (pointerEventData.button == PointerEventData.InputButton.Right)
 229        {
 0230            if ((model.messageType != ChatMessage.Type.PUBLIC && model.messageType != ChatMessage.Type.PRIVATE) ||
 231                model.senderId == UserProfile.GetOwnUserProfile().userId)
 0232                return;
 233
 0234            OnPressRightButton?.Invoke(this);
 235        }
 0236    }
 237
 238    public void OnPointerEnter(PointerEventData pointerEventData)
 239    {
 0240        if (pointerEventData == null)
 0241            return;
 242
 0243        hoverPanelTimer = timeToHoverPanel;
 0244    }
 245
 246    public void OnPointerExit(PointerEventData pointerEventData)
 247    {
 39248        if (pointerEventData == null)
 39249            return;
 250
 0251        hoverPanelTimer = 0f;
 0252        int linkIndex = TMP_TextUtilities.FindIntersectingLink(body, pointerEventData.position, null);
 0253        if (linkIndex == -1)
 254        {
 0255            isOverCoordinates = false;
 0256            hoverGotoPanelTimer = 0;
 0257            OnCancelGotoHover?.Invoke();
 258        }
 0259        OnCancelHover?.Invoke();
 0260    }
 261
 78262    void OnDisable() { OnPointerExit(null); }
 263
 264    public void SetFadeout(bool enabled)
 265    {
 40266        if (!enabled)
 267        {
 40268            group.alpha = 1;
 40269            group.blocksRaycasts = true;
 40270            group.interactable = true;
 40271            fadeEnabled = false;
 40272            return;
 273        }
 274
 0275        fadeoutStartTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() / 1000.0;
 0276        fadeEnabled = true;
 0277        group.blocksRaycasts = false;
 0278        group.interactable = false;
 0279    }
 280
 281    void Update()
 282    {
 3283        Fade();
 3284        CheckHoverCoordinates();
 3285        ProcessHoverPanelTimer();
 3286        ProcessHoverGotoPanelTimer();
 3287    }
 288
 289    private void CheckHoverCoordinates()
 290    {
 3291        if (isOverCoordinates)
 0292            return;
 293
 3294        int linkIndex = TMP_TextUtilities.FindIntersectingLink(body, Input.mousePosition, null);
 295
 3296        if (linkIndex == -1)
 3297            return;
 298
 0299        isOverCoordinates = true;
 0300        TMP_LinkInfo linkInfo = body.textInfo.linkInfo[linkIndex];
 0301        currentCoordinates = CoordinateUtils.ParseCoordinatesString(linkInfo.GetLinkID().ToString());
 0302        hoverGotoPanelTimer = timeToHoverGotoPanel;
 0303        OnCancelHover?.Invoke();
 0304    }
 305
 306    void Fade()
 307    {
 3308        if (!fadeEnabled)
 3309            return;
 310
 311        //NOTE(Brian): Small offset using normalized Y so we keep the cascade effect
 0312        double yOffset = (transform as RectTransform).anchoredPosition.y / (double)Screen.height * 2.0;
 313
 0314        double fadeTime = Math.Max(model.timestamp / 1000.0, fadeoutStartTime) + timeToFade - yOffset;
 0315        double currentTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() / 1000.0;
 316
 0317        if (currentTime > fadeTime)
 318        {
 0319            double timeSinceFadeTime = currentTime - fadeTime;
 0320            group.alpha = Mathf.Clamp01(1 - (float)(timeSinceFadeTime / fadeDuration));
 0321        }
 322        else
 323        {
 0324            group.alpha += (1 - group.alpha) * 0.05f;
 325        }
 0326    }
 327
 328    void ProcessHoverPanelTimer()
 329    {
 3330        if (hoverPanelTimer <= 0f || isOverCoordinates)
 3331            return;
 332
 0333        hoverPanelTimer -= Time.deltaTime;
 0334        if (hoverPanelTimer <= 0f)
 335        {
 0336            hoverPanelTimer = 0f;
 337
 0338            OnTriggerHover?.Invoke(this);
 339        }
 0340    }
 341
 342    void ProcessHoverGotoPanelTimer()
 343    {
 3344        if (hoverGotoPanelTimer <= 0f || !isOverCoordinates)
 3345            return;
 346
 0347        hoverGotoPanelTimer -= Time.deltaTime;
 0348        if (hoverGotoPanelTimer <= 0f)
 349        {
 0350            hoverGotoPanelTimer = 0f;
 351
 0352            OnTriggerHoverGoto?.Invoke(this, currentCoordinates);
 353        }
 0354    }
 355
 356    string RemoveTabs(string text)
 357    {
 42358        if (string.IsNullOrEmpty(text))
 0359            return "";
 360
 361        //NOTE(Brian): ContentSizeFitter doesn't fare well with tabs, so i'm replacing these
 362        //             with spaces.
 42363        return text.Replace("\t", "    ");
 364    }
 365
 366    string GetDefaultSenderString(string sender)
 367    {
 42368        if (!string.IsNullOrEmpty(sender))
 42369            return $"<b>{sender}:</b>";
 370
 0371        return "";
 372    }
 373
 374    DateTime UnixTimeStampToLocalDateTime(ulong unixTimeStampMilliseconds)
 375    {
 376        // TODO see if we can simplify with 'DateTimeOffset.FromUnixTimeMilliseconds'
 42377        System.DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc);
 42378        dtDateTime = dtDateTime.AddMilliseconds(unixTimeStampMilliseconds).ToLocalTime();
 379
 42380        return dtDateTime;
 381    }
 382}