< Summary

Class:DefaultChatEntry
Assembly:ChatHUD
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/ChatWidgetHUD/DefaultChatEntry.cs
Covered lines:62
Uncovered lines:128
Coverable lines:190
Total lines:437
Line coverage:32.6% (62 of 190)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
DefaultChatEntry()0%110100%
Awake()0%110100%
Populate(...)0%2100%
PopulateTask()0%990100%
GetUserString(...)0%770100%
GetCoordinatesLink(...)0%330100%
PlaySfx(...)0%76.9910012.5%
PreloadSceneMetadata(...)0%220100%
OnPointerClick(...)0%1101000%
OnPointerEnter(...)0%6200%
OnPointerExit(...)0%17.85020%
OnDisable()0%110100%
OnDestroy()0%110100%
SetFadeout(...)0%6200%
DeactivatePreview()0%20400%
FadeOut()0%12300%
ActivatePreview()0%20400%
ActivatePreviewInstantly()0%20400%
DeactivatePreviewInstantly()0%6200%
DockContextMenu(...)0%2100%
DockHoverPanel(...)0%2100%
Update()0%110100%
CheckHoverCoordinates()0%8.124036.36%
ProcessHoverPanelTimer()0%14.115028.57%
ProcessHoverGotoPanelTimer()0%14.115028.57%
RemoveTabs(...)0%2.152066.67%
GetDefaultSenderString(...)0%2.152066.67%
UnixTimeStampToLocalDateTime(...)0%110100%
InterpolatePreviewColor()0%20400%

File(s)

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

#LineLine coverage
 1using System;
 2using System.Collections;
 3using System.Collections.Generic;
 4using System.Threading;
 5using Cysharp.Threading.Tasks;
 6using DCL;
 7using DCL.Helpers;
 8using DCL.Interface;
 9using DCL.SettingsCommon;
 10using TMPro;
 11using UnityEngine;
 12using UnityEngine.EventSystems;
 13using UnityEngine.UI;
 14
 15public class DefaultChatEntry : ChatEntry, IPointerClickHandler, IPointerEnterHandler, IPointerExitHandler
 16{
 17    [SerializeField] internal TextMeshProUGUI body;
 2018    [SerializeField] internal float timeToHoverPanel = 1f;
 2019    [SerializeField] internal float timeToHoverGotoPanel = 1f;
 2020    [SerializeField] internal bool showUserName = true;
 21    [SerializeField] private RectTransform hoverPanelPositionReference;
 22    [SerializeField] private RectTransform contextMenuPositionReference;
 23    [NonSerialized] public string messageLocalDateTime;
 24
 25    [Header("Preview Mode")] [SerializeField]
 26    internal Image previewBackgroundImage;
 27
 28    [SerializeField] internal Color previewBackgroundColor;
 29    [SerializeField] internal Color previewFontColor;
 30
 31    private float hoverPanelTimer;
 32    private float hoverGotoPanelTimer;
 33    private bool isOverCoordinates;
 34    private ParcelCoordinates currentCoordinates;
 35    private ChatEntryModel model;
 36
 37    private Color originalBackgroundColor;
 38    private Color originalFontColor;
 2039    internal CancellationTokenSource populationTaskCancellationTokenSource = new CancellationTokenSource();
 40
 041    public override ChatEntryModel Model => model;
 42
 43    public event Action<string> OnPress;
 44    public event Action<DefaultChatEntry> OnPressRightButton;
 45    public event Action<DefaultChatEntry> OnTriggerHover;
 46    public event Action<DefaultChatEntry, ParcelCoordinates> OnTriggerHoverGoto;
 47    public event Action OnCancelHover;
 48    public event Action OnCancelGotoHover;
 49
 50    private void Awake()
 51    {
 1252        originalBackgroundColor = previewBackgroundImage.color;
 1253        originalFontColor = body.color;
 1254    }
 55
 056    public override void Populate(ChatEntryModel chatEntryModel) { PopulateTask(chatEntryModel, populationTaskCancellati
 57
 58    internal async UniTask PopulateTask(ChatEntryModel chatEntryModel, CancellationToken cancellationToken)
 59    {
 1260        model = chatEntryModel;
 61
 1262        chatEntryModel.bodyText = RemoveTabs(chatEntryModel.bodyText);
 1263        var userString = GetUserString(chatEntryModel);
 64
 65        // Due to a TMPro bug in Unity 2020 LTS we have to wait several frames before setting the body.text to avoid a
 66        // client crash. More info at https://github.com/decentraland/unity-renderer/pull/2345#issuecomment-1155753538
 67        // TODO: Remove hack in a newer Unity/TMPro version
 3668        await UniTask.NextFrame(cancellationToken);
 3669        await UniTask.NextFrame(cancellationToken);
 3670        await UniTask.NextFrame(cancellationToken);
 71
 1272        if (!string.IsNullOrEmpty(userString) && showUserName)
 973            body.text = $"{userString} {chatEntryModel.bodyText}";
 74        else
 375            body.text = chatEntryModel.bodyText;
 76
 1277        body.text = GetCoordinatesLink(body.text);
 78
 1279        messageLocalDateTime = UnixTimeStampToLocalDateTime(chatEntryModel.timestamp).ToString();
 80
 1281        (transform as RectTransform).ForceUpdateLayout();
 82
 1283        PlaySfx(chatEntryModel);
 1284    }
 85
 86    private string GetUserString(ChatEntryModel chatEntryModel)
 87    {
 1288        var userString = GetDefaultSenderString(chatEntryModel.senderName);
 1289        switch (chatEntryModel.messageType)
 90        {
 91            case ChatMessage.Type.PUBLIC:
 292                userString = chatEntryModel.subType switch
 93                {
 94
 95                    ChatEntryModel.SubType.RECEIVED => userString,
 96                    ChatEntryModel.SubType.SENT => $"<b>You:</b>",
 97                    _ => userString
 98                };
 299                break;
 100            case ChatMessage.Type.PRIVATE:
 9101                userString = chatEntryModel.subType switch
 102                {
 103
 104                    ChatEntryModel.SubType.RECEIVED => $"<b><color=#5EBD3D>From {chatEntryModel.senderName}:</color></b>
 105                    ChatEntryModel.SubType.SENT => $"<b>To {chatEntryModel.recipientName}:</b>",
 106                    _ => userString
 107                };
 108                break;
 109        }
 110
 12111        return userString;
 112    }
 113
 114    private string GetCoordinatesLink(string body)
 115    {
 12116        if (!CoordinateUtils.HasValidTextCoordinates(body))
 9117            return body;
 3118        var textCoordinates = CoordinateUtils.GetTextCoordinates(body);
 119
 16120        for (var i = 0; i < textCoordinates.Count; i++)
 121        {
 122            // TODO: the preload should not be here
 5123            PreloadSceneMetadata(CoordinateUtils.ParseCoordinatesString(textCoordinates[i]));
 124
 5125            body = body.Replace(textCoordinates[i],
 126                $"</noparse><link={textCoordinates[i]}><color=#4886E3><u>{textCoordinates[i]}</u></color></link><noparse
 127        }
 128
 3129        return body;
 130    }
 131
 132    private void PlaySfx(ChatEntryModel chatEntryModel)
 133    {
 12134        if (HUDAudioHandler.i == null)
 12135            return;
 136
 137        // Check whether or not this message is new, and chat sounds are enabled in settings
 0138        if (chatEntryModel.timestamp > HUDAudioHandler.i.chatLastCheckedTimestamp &&
 139            Settings.i.audioSettings.Data.chatSFXEnabled)
 140        {
 0141            switch (chatEntryModel.messageType)
 142            {
 143                case ChatMessage.Type.PUBLIC:
 144                    // Check whether or not the message was sent by the local player
 0145                    if (chatEntryModel.senderId == UserProfile.GetOwnUserProfile().userId)
 0146                        AudioScriptableObjects.chatSend.Play(true);
 147                    else
 0148                        AudioScriptableObjects.chatReceiveGlobal.Play(true);
 0149                    break;
 150                case ChatMessage.Type.PRIVATE:
 0151                    switch (chatEntryModel.subType)
 152                    {
 153                        case ChatEntryModel.SubType.RECEIVED:
 0154                            AudioScriptableObjects.chatReceivePrivate.Play(true);
 0155                            break;
 156                        case ChatEntryModel.SubType.SENT:
 0157                            AudioScriptableObjects.chatSend.Play(true);
 0158                            break;
 159                        default:
 160                            break;
 161                    }
 162
 163                    break;
 164                case ChatMessage.Type.SYSTEM:
 0165                    AudioScriptableObjects.chatReceiveGlobal.Play(true);
 166                    break;
 167                default:
 168                    break;
 169            }
 170        }
 171
 0172        HUDAudioHandler.i.RefreshChatLastCheckedTimestamp();
 0173    }
 174
 175    private void PreloadSceneMetadata(ParcelCoordinates parcelCoordinates)
 176    {
 5177        if (MinimapMetadata.GetMetadata().GetSceneInfo(parcelCoordinates.x, parcelCoordinates.y) == null)
 5178            WebInterface.RequestScenesInfoAroundParcel(new Vector2(parcelCoordinates.x, parcelCoordinates.y), 2);
 5179    }
 180
 181    public void OnPointerClick(PointerEventData pointerEventData)
 182    {
 0183        if (pointerEventData.button == PointerEventData.InputButton.Left)
 184        {
 0185            int linkIndex = TMP_TextUtilities.FindIntersectingLink(body, pointerEventData.position, DataStore.i.camera.h
 0186            if (linkIndex != -1)
 187            {
 0188                DataStore.i.HUDs.gotoPanelVisible.Set(true);
 0189                TMP_LinkInfo linkInfo = body.textInfo.linkInfo[linkIndex];
 0190                ParcelCoordinates parcelCoordinate =
 191                    CoordinateUtils.ParseCoordinatesString(linkInfo.GetLinkID().ToString());
 0192                DataStore.i.HUDs.gotoPanelCoordinates.Set(parcelCoordinate);
 193            }
 194
 0195            if (Model.messageType != ChatMessage.Type.PRIVATE)
 0196                return;
 197
 0198            OnPress?.Invoke(Model.otherUserId);
 0199        }
 0200        else if (pointerEventData.button == PointerEventData.InputButton.Right)
 201        {
 0202            if ((Model.messageType != ChatMessage.Type.PUBLIC && Model.messageType != ChatMessage.Type.PRIVATE) ||
 203                Model.senderId == UserProfile.GetOwnUserProfile().userId)
 0204                return;
 205
 0206            OnPressRightButton?.Invoke(this);
 207        }
 0208    }
 209
 210    public void OnPointerEnter(PointerEventData pointerEventData)
 211    {
 0212        if (pointerEventData == null)
 0213            return;
 214
 0215        hoverPanelTimer = timeToHoverPanel;
 0216    }
 217
 218    public void OnPointerExit(PointerEventData pointerEventData)
 219    {
 12220        if (pointerEventData == null)
 12221            return;
 222
 0223        hoverPanelTimer = 0f;
 0224        int linkIndex = TMP_TextUtilities.FindIntersectingLink(body, pointerEventData.position, DataStore.i.camera.hudsC
 0225        if (linkIndex == -1)
 226        {
 0227            isOverCoordinates = false;
 0228            hoverGotoPanelTimer = 0;
 0229            OnCancelGotoHover?.Invoke();
 230        }
 231
 0232        OnCancelHover?.Invoke();
 0233    }
 234
 24235    private void OnDisable() { OnPointerExit(null); }
 236
 24237    private void OnDestroy() { populationTaskCancellationTokenSource.Cancel(); }
 238
 239    public override void SetFadeout(bool enabled)
 240    {
 0241        if (!enabled)
 242        {
 0243            group.alpha = 1;
 0244            fadeEnabled = false;
 0245            return;
 246        }
 247
 0248        fadeEnabled = true;
 0249    }
 250
 251    public override void DeactivatePreview()
 252    {
 0253        if (!gameObject.activeInHierarchy)
 254        {
 0255            previewBackgroundImage.color = originalBackgroundColor;
 0256            body.color = originalFontColor;
 0257            return;
 258        }
 259
 0260        if (previewInterpolationRoutine != null)
 0261            StopCoroutine(previewInterpolationRoutine);
 262
 0263        if (previewInterpolationAlphaRoutine != null)
 0264            StopCoroutine(previewInterpolationAlphaRoutine);
 265
 0266        group.alpha = 1;
 0267        previewInterpolationRoutine =
 268            StartCoroutine(InterpolatePreviewColor(originalBackgroundColor, originalFontColor, 0.5f));
 0269    }
 270
 271    public override void FadeOut()
 272    {
 0273        if (!gameObject.activeInHierarchy)
 274        {
 0275            group.alpha = 0;
 0276            return;
 277        }
 278
 0279        if (previewInterpolationAlphaRoutine != null)
 0280            StopCoroutine(previewInterpolationAlphaRoutine);
 281
 0282        previewInterpolationAlphaRoutine = StartCoroutine(InterpolateAlpha(0, 0.5f));
 0283    }
 284
 285    public override void ActivatePreview()
 286    {
 0287        if (!gameObject.activeInHierarchy)
 288        {
 0289            ActivatePreviewInstantly();
 0290            return;
 291        }
 292
 0293        if (previewInterpolationRoutine != null)
 0294            StopCoroutine(previewInterpolationRoutine);
 295
 0296        if (previewInterpolationAlphaRoutine != null)
 0297            StopCoroutine(previewInterpolationAlphaRoutine);
 298
 0299        previewInterpolationRoutine =
 300            StartCoroutine(InterpolatePreviewColor(previewBackgroundColor, previewFontColor, 0.5f));
 301
 0302        previewInterpolationAlphaRoutine = StartCoroutine(InterpolateAlpha(1, 0.5f));
 0303    }
 304
 305    public override void ActivatePreviewInstantly()
 306    {
 0307        if (!gameObject.activeInHierarchy)
 0308            return;
 309
 0310        if (previewInterpolationRoutine != null)
 0311            StopCoroutine(previewInterpolationRoutine);
 312
 0313        previewBackgroundImage.color = previewBackgroundColor;
 0314        body.color = previewFontColor;
 0315        group.alpha = 1;
 316
 0317        if (previewInterpolationAlphaRoutine != null)
 0318            StopCoroutine(previewInterpolationAlphaRoutine);
 319
 0320        previewInterpolationAlphaRoutine = StartCoroutine(InterpolateAlpha(1, 0.5f));
 0321    }
 322
 323    public override void DeactivatePreviewInstantly()
 324    {
 0325        if (previewInterpolationRoutine != null)
 0326            StopCoroutine(previewInterpolationRoutine);
 327
 0328        previewBackgroundImage.color = originalBackgroundColor;
 0329        body.color = originalFontColor;
 0330    }
 331
 332    public void DockContextMenu(RectTransform panel)
 333    {
 0334        panel.pivot = contextMenuPositionReference.pivot;
 0335        panel.position = contextMenuPositionReference.position;
 0336    }
 337
 338    public void DockHoverPanel(RectTransform panel)
 339    {
 0340        panel.pivot = hoverPanelPositionReference.pivot;
 0341        panel.position = hoverPanelPositionReference.position;
 0342    }
 343
 344    private void Update()
 345    {
 36346        CheckHoverCoordinates();
 36347        ProcessHoverPanelTimer();
 36348        ProcessHoverGotoPanelTimer();
 36349    }
 350
 351    private void CheckHoverCoordinates()
 352    {
 36353        if (isOverCoordinates)
 0354            return;
 355
 36356        int linkIndex = TMP_TextUtilities.FindIntersectingLink(body, Input.mousePosition, DataStore.i.camera.hudsCamera.
 357
 36358        if (linkIndex == -1)
 36359            return;
 360
 0361        isOverCoordinates = true;
 0362        TMP_LinkInfo linkInfo = body.textInfo.linkInfo[linkIndex];
 0363        currentCoordinates = CoordinateUtils.ParseCoordinatesString(linkInfo.GetLinkID().ToString());
 0364        hoverGotoPanelTimer = timeToHoverGotoPanel;
 0365        OnCancelHover?.Invoke();
 0366    }
 367
 368    private void ProcessHoverPanelTimer()
 369    {
 36370        if (hoverPanelTimer <= 0f || isOverCoordinates)
 36371            return;
 372
 0373        hoverPanelTimer -= Time.deltaTime;
 0374        if (hoverPanelTimer <= 0f)
 375        {
 0376            hoverPanelTimer = 0f;
 0377            OnTriggerHover?.Invoke(this);
 378        }
 0379    }
 380
 381    private void ProcessHoverGotoPanelTimer()
 382    {
 36383        if (hoverGotoPanelTimer <= 0f || !isOverCoordinates)
 36384            return;
 385
 0386        hoverGotoPanelTimer -= Time.deltaTime;
 0387        if (hoverGotoPanelTimer <= 0f)
 388        {
 0389            hoverGotoPanelTimer = 0f;
 0390            OnTriggerHoverGoto?.Invoke(this, currentCoordinates);
 391        }
 0392    }
 393
 394    private string RemoveTabs(string text)
 395    {
 12396        if (string.IsNullOrEmpty(text))
 0397            return "";
 398
 399        //NOTE(Brian): ContentSizeFitter doesn't fare well with tabs, so i'm replacing these
 400        //             with spaces.
 12401        return text.Replace("\t", "    ");
 402    }
 403
 404    private static string GetDefaultSenderString(string sender)
 405    {
 12406        if (!string.IsNullOrEmpty(sender))
 12407            return $"<b>{sender}:</b>";
 0408        return "";
 409    }
 410
 411    private static DateTime UnixTimeStampToLocalDateTime(ulong unixTimeStampMilliseconds)
 412    {
 413        // TODO see if we can simplify with 'DateTimeOffset.FromUnixTimeMilliseconds'
 12414        DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
 12415        dtDateTime = dtDateTime.AddMilliseconds(unixTimeStampMilliseconds).ToLocalTime();
 12416        return dtDateTime;
 417    }
 418
 419    private IEnumerator InterpolatePreviewColor(Color backgroundColor, Color fontColor, float duration)
 420    {
 0421        var t = 0f;
 422
 0423        while (t < duration)
 424        {
 0425            t += Time.deltaTime;
 426
 0427            previewBackgroundImage.color = Color.Lerp(previewBackgroundImage.color, backgroundColor, t / duration);
 0428            body.color = Color.Lerp(body.color, fontColor, t / duration);
 429
 0430            yield return null;
 431        }
 432
 0433        previewBackgroundImage.color = backgroundColor;
 0434        body.color = fontColor;
 0435    }
 436
 437}