< Summary

Class:DefaultChatEntry
Assembly:ChatHUD
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/ChatWidgetHUD/DefaultChatEntry.cs
Covered lines:63
Uncovered lines:127
Coverable lines:190
Total lines:438
Line coverage:33.1% (63 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%110100%
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;
 2718    [SerializeField] internal float timeToHoverPanel = 1f;
 2719    [SerializeField] internal float timeToHoverGotoPanel = 1f;
 2720    [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;
 2739    private readonly 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
 56    public override void Populate(ChatEntryModel chatEntryModel) =>
 1257        PopulateTask(chatEntryModel, populationTaskCancellationTokenSource.Token).Forget();
 58
 59    private async UniTask PopulateTask(ChatEntryModel chatEntryModel, CancellationToken cancellationToken)
 60    {
 1261        model = chatEntryModel;
 62
 1263        chatEntryModel.bodyText = RemoveTabs(chatEntryModel.bodyText);
 1264        var userString = GetUserString(chatEntryModel);
 65
 66        // Due to a TMPro bug in Unity 2020 LTS we have to wait several frames before setting the body.text to avoid a
 67        // client crash. More info at https://github.com/decentraland/unity-renderer/pull/2345#issuecomment-1155753538
 68        // TODO: Remove hack in a newer Unity/TMPro version
 3669        await UniTask.NextFrame(cancellationToken);
 3670        await UniTask.NextFrame(cancellationToken);
 3671        await UniTask.NextFrame(cancellationToken);
 72
 1273        if (!string.IsNullOrEmpty(userString) && showUserName)
 974            body.text = $"{userString} {chatEntryModel.bodyText}";
 75        else
 376            body.text = chatEntryModel.bodyText;
 77
 1278        body.text = GetCoordinatesLink(body.text);
 79
 1280        messageLocalDateTime = UnixTimeStampToLocalDateTime(chatEntryModel.timestamp).ToString();
 81
 1282        (transform as RectTransform).ForceUpdateLayout();
 83
 1284        PlaySfx(chatEntryModel);
 1285    }
 86
 87    private string GetUserString(ChatEntryModel chatEntryModel)
 88    {
 1289        var userString = GetDefaultSenderString(chatEntryModel.senderName);
 1290        switch (chatEntryModel.messageType)
 91        {
 92            case ChatMessage.Type.PUBLIC:
 293                userString = chatEntryModel.subType switch
 94                {
 95
 96                    ChatEntryModel.SubType.RECEIVED => userString,
 97                    ChatEntryModel.SubType.SENT => $"<b>You:</b>",
 98                    _ => userString
 99                };
 2100                break;
 101            case ChatMessage.Type.PRIVATE:
 9102                userString = chatEntryModel.subType switch
 103                {
 104
 105                    ChatEntryModel.SubType.RECEIVED => $"<b><color=#5EBD3D>From {chatEntryModel.senderName}:</color></b>
 106                    ChatEntryModel.SubType.SENT => $"<b>To {chatEntryModel.recipientName}:</b>",
 107                    _ => userString
 108                };
 109                break;
 110        }
 111
 12112        return userString;
 113    }
 114
 115    private string GetCoordinatesLink(string body)
 116    {
 12117        if (!CoordinateUtils.HasValidTextCoordinates(body))
 9118            return body;
 3119        var textCoordinates = CoordinateUtils.GetTextCoordinates(body);
 120
 16121        for (var i = 0; i < textCoordinates.Count; i++)
 122        {
 123            // TODO: the preload should not be here
 5124            PreloadSceneMetadata(CoordinateUtils.ParseCoordinatesString(textCoordinates[i]));
 125
 5126            body = body.Replace(textCoordinates[i],
 127                $"</noparse><link={textCoordinates[i]}><color=#4886E3><u>{textCoordinates[i]}</u></color></link><noparse
 128        }
 129
 3130        return body;
 131    }
 132
 133    private void PlaySfx(ChatEntryModel chatEntryModel)
 134    {
 12135        if (HUDAudioHandler.i == null)
 12136            return;
 137
 138        // Check whether or not this message is new, and chat sounds are enabled in settings
 0139        if (chatEntryModel.timestamp > HUDAudioHandler.i.chatLastCheckedTimestamp &&
 140            Settings.i.audioSettings.Data.chatSFXEnabled)
 141        {
 0142            switch (chatEntryModel.messageType)
 143            {
 144                case ChatMessage.Type.PUBLIC:
 145                    // Check whether or not the message was sent by the local player
 0146                    if (chatEntryModel.senderId == UserProfile.GetOwnUserProfile().userId)
 0147                        AudioScriptableObjects.chatSend.Play(true);
 148                    else
 0149                        AudioScriptableObjects.chatReceiveGlobal.Play(true);
 0150                    break;
 151                case ChatMessage.Type.PRIVATE:
 0152                    switch (chatEntryModel.subType)
 153                    {
 154                        case ChatEntryModel.SubType.RECEIVED:
 0155                            AudioScriptableObjects.chatReceivePrivate.Play(true);
 0156                            break;
 157                        case ChatEntryModel.SubType.SENT:
 0158                            AudioScriptableObjects.chatSend.Play(true);
 0159                            break;
 160                        default:
 161                            break;
 162                    }
 163
 164                    break;
 165                case ChatMessage.Type.SYSTEM:
 0166                    AudioScriptableObjects.chatReceiveGlobal.Play(true);
 167                    break;
 168                default:
 169                    break;
 170            }
 171        }
 172
 0173        HUDAudioHandler.i.RefreshChatLastCheckedTimestamp();
 0174    }
 175
 176    private void PreloadSceneMetadata(ParcelCoordinates parcelCoordinates)
 177    {
 5178        if (MinimapMetadata.GetMetadata().GetSceneInfo(parcelCoordinates.x, parcelCoordinates.y) == null)
 5179            WebInterface.RequestScenesInfoAroundParcel(new Vector2(parcelCoordinates.x, parcelCoordinates.y), 2);
 5180    }
 181
 182    public void OnPointerClick(PointerEventData pointerEventData)
 183    {
 0184        if (pointerEventData.button == PointerEventData.InputButton.Left)
 185        {
 0186            int linkIndex = TMP_TextUtilities.FindIntersectingLink(body, pointerEventData.position, DataStore.i.camera.h
 0187            if (linkIndex != -1)
 188            {
 0189                DataStore.i.HUDs.gotoPanelVisible.Set(true);
 0190                TMP_LinkInfo linkInfo = body.textInfo.linkInfo[linkIndex];
 0191                ParcelCoordinates parcelCoordinate =
 192                    CoordinateUtils.ParseCoordinatesString(linkInfo.GetLinkID().ToString());
 0193                DataStore.i.HUDs.gotoPanelCoordinates.Set(parcelCoordinate);
 194            }
 195
 0196            if (Model.messageType != ChatMessage.Type.PRIVATE)
 0197                return;
 198
 0199            OnPress?.Invoke(Model.otherUserId);
 0200        }
 0201        else if (pointerEventData.button == PointerEventData.InputButton.Right)
 202        {
 0203            if ((Model.messageType != ChatMessage.Type.PUBLIC && Model.messageType != ChatMessage.Type.PRIVATE) ||
 204                Model.senderId == UserProfile.GetOwnUserProfile().userId)
 0205                return;
 206
 0207            OnPressRightButton?.Invoke(this);
 208        }
 0209    }
 210
 211    public void OnPointerEnter(PointerEventData pointerEventData)
 212    {
 0213        if (pointerEventData == null)
 0214            return;
 215
 0216        hoverPanelTimer = timeToHoverPanel;
 0217    }
 218
 219    public void OnPointerExit(PointerEventData pointerEventData)
 220    {
 12221        if (pointerEventData == null)
 12222            return;
 223
 0224        hoverPanelTimer = 0f;
 0225        int linkIndex = TMP_TextUtilities.FindIntersectingLink(body, pointerEventData.position, DataStore.i.camera.hudsC
 0226        if (linkIndex == -1)
 227        {
 0228            isOverCoordinates = false;
 0229            hoverGotoPanelTimer = 0;
 0230            OnCancelGotoHover?.Invoke();
 231        }
 232
 0233        OnCancelHover?.Invoke();
 0234    }
 235
 24236    private void OnDisable() { OnPointerExit(null); }
 237
 24238    private void OnDestroy() { populationTaskCancellationTokenSource.Cancel(); }
 239
 240    public override void SetFadeout(bool enabled)
 241    {
 0242        if (!enabled)
 243        {
 0244            group.alpha = 1;
 0245            fadeEnabled = false;
 0246            return;
 247        }
 248
 0249        fadeEnabled = true;
 0250    }
 251
 252    public override void DeactivatePreview()
 253    {
 0254        if (!gameObject.activeInHierarchy)
 255        {
 0256            previewBackgroundImage.color = originalBackgroundColor;
 0257            body.color = originalFontColor;
 0258            return;
 259        }
 260
 0261        if (previewInterpolationRoutine != null)
 0262            StopCoroutine(previewInterpolationRoutine);
 263
 0264        if (previewInterpolationAlphaRoutine != null)
 0265            StopCoroutine(previewInterpolationAlphaRoutine);
 266
 0267        group.alpha = 1;
 0268        previewInterpolationRoutine =
 269            StartCoroutine(InterpolatePreviewColor(originalBackgroundColor, originalFontColor, 0.5f));
 0270    }
 271
 272    public override void FadeOut()
 273    {
 0274        if (!gameObject.activeInHierarchy)
 275        {
 0276            group.alpha = 0;
 0277            return;
 278        }
 279
 0280        if (previewInterpolationAlphaRoutine != null)
 0281            StopCoroutine(previewInterpolationAlphaRoutine);
 282
 0283        previewInterpolationAlphaRoutine = StartCoroutine(InterpolateAlpha(0, 0.5f));
 0284    }
 285
 286    public override void ActivatePreview()
 287    {
 0288        if (!gameObject.activeInHierarchy)
 289        {
 0290            ActivatePreviewInstantly();
 0291            return;
 292        }
 293
 0294        if (previewInterpolationRoutine != null)
 0295            StopCoroutine(previewInterpolationRoutine);
 296
 0297        if (previewInterpolationAlphaRoutine != null)
 0298            StopCoroutine(previewInterpolationAlphaRoutine);
 299
 0300        previewInterpolationRoutine =
 301            StartCoroutine(InterpolatePreviewColor(previewBackgroundColor, previewFontColor, 0.5f));
 302
 0303        previewInterpolationAlphaRoutine = StartCoroutine(InterpolateAlpha(1, 0.5f));
 0304    }
 305
 306    public override void ActivatePreviewInstantly()
 307    {
 0308        if (!gameObject.activeInHierarchy)
 0309            return;
 310
 0311        if (previewInterpolationRoutine != null)
 0312            StopCoroutine(previewInterpolationRoutine);
 313
 0314        previewBackgroundImage.color = previewBackgroundColor;
 0315        body.color = previewFontColor;
 0316        group.alpha = 1;
 317
 0318        if (previewInterpolationAlphaRoutine != null)
 0319            StopCoroutine(previewInterpolationAlphaRoutine);
 320
 0321        previewInterpolationAlphaRoutine = StartCoroutine(InterpolateAlpha(1, 0.5f));
 0322    }
 323
 324    public override void DeactivatePreviewInstantly()
 325    {
 0326        if (previewInterpolationRoutine != null)
 0327            StopCoroutine(previewInterpolationRoutine);
 328
 0329        previewBackgroundImage.color = originalBackgroundColor;
 0330        body.color = originalFontColor;
 0331    }
 332
 333    public void DockContextMenu(RectTransform panel)
 334    {
 0335        panel.pivot = new Vector2(0,0);
 0336        panel.position = contextMenuPositionReference.position;
 0337    }
 338
 339    public void DockHoverPanel(RectTransform panel)
 340    {
 0341        panel.pivot = hoverPanelPositionReference.pivot;
 0342        panel.position = hoverPanelPositionReference.position;
 0343    }
 344
 345    private void Update()
 346    {
 48347        CheckHoverCoordinates();
 48348        ProcessHoverPanelTimer();
 48349        ProcessHoverGotoPanelTimer();
 48350    }
 351
 352    private void CheckHoverCoordinates()
 353    {
 48354        if (isOverCoordinates)
 0355            return;
 356
 48357        int linkIndex = TMP_TextUtilities.FindIntersectingLink(body, Input.mousePosition, DataStore.i.camera.hudsCamera.
 358
 48359        if (linkIndex == -1)
 48360            return;
 361
 0362        isOverCoordinates = true;
 0363        TMP_LinkInfo linkInfo = body.textInfo.linkInfo[linkIndex];
 0364        currentCoordinates = CoordinateUtils.ParseCoordinatesString(linkInfo.GetLinkID().ToString());
 0365        hoverGotoPanelTimer = timeToHoverGotoPanel;
 0366        OnCancelHover?.Invoke();
 0367    }
 368
 369    private void ProcessHoverPanelTimer()
 370    {
 48371        if (hoverPanelTimer <= 0f || isOverCoordinates)
 48372            return;
 373
 0374        hoverPanelTimer -= Time.deltaTime;
 0375        if (hoverPanelTimer <= 0f)
 376        {
 0377            hoverPanelTimer = 0f;
 0378            OnTriggerHover?.Invoke(this);
 379        }
 0380    }
 381
 382    private void ProcessHoverGotoPanelTimer()
 383    {
 48384        if (hoverGotoPanelTimer <= 0f || !isOverCoordinates)
 48385            return;
 386
 0387        hoverGotoPanelTimer -= Time.deltaTime;
 0388        if (hoverGotoPanelTimer <= 0f)
 389        {
 0390            hoverGotoPanelTimer = 0f;
 0391            OnTriggerHoverGoto?.Invoke(this, currentCoordinates);
 392        }
 0393    }
 394
 395    private string RemoveTabs(string text)
 396    {
 12397        if (string.IsNullOrEmpty(text))
 0398            return "";
 399
 400        //NOTE(Brian): ContentSizeFitter doesn't fare well with tabs, so i'm replacing these
 401        //             with spaces.
 12402        return text.Replace("\t", "    ");
 403    }
 404
 405    private static string GetDefaultSenderString(string sender)
 406    {
 12407        if (!string.IsNullOrEmpty(sender))
 12408            return $"<b>{sender}:</b>";
 0409        return "";
 410    }
 411
 412    private static DateTime UnixTimeStampToLocalDateTime(ulong unixTimeStampMilliseconds)
 413    {
 414        // TODO see if we can simplify with 'DateTimeOffset.FromUnixTimeMilliseconds'
 12415        DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
 12416        dtDateTime = dtDateTime.AddMilliseconds(unixTimeStampMilliseconds).ToLocalTime();
 12417        return dtDateTime;
 418    }
 419
 420    private IEnumerator InterpolatePreviewColor(Color backgroundColor, Color fontColor, float duration)
 421    {
 0422        var t = 0f;
 423
 0424        while (t < duration)
 425        {
 0426            t += Time.deltaTime;
 427
 0428            previewBackgroundImage.color = Color.Lerp(previewBackgroundImage.color, backgroundColor, t / duration);
 0429            body.color = Color.Lerp(body.color, fontColor, t / duration);
 430
 0431            yield return null;
 432        }
 433
 0434        previewBackgroundImage.color = backgroundColor;
 0435        body.color = fontColor;
 0436    }
 437
 438}