< Summary

Class:DefaultChatEntry
Assembly:ChatHUD
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/ChatWidgetHUD/DefaultChatEntry.cs
Covered lines:69
Uncovered lines:79
Coverable lines:148
Total lines:347
Line coverage:46.6% (69 of 148)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
DefaultChatEntry()0%110100%
Populate(...)0%110100%
PopulateTask()0%990100%
GetUserString(...)0%9.029093.75%
GetCoordinatesLink(...)0%330100%
PlaySfx(...)0%76.9910012.5%
IsRecentMessage(...)0%6200%
PreloadSceneMetadata(...)0%220100%
OnPointerClick(...)0%42600%
OnPointerEnter(...)0%12300%
OnPointerExit(...)0%17.85020%
OnDisable()0%110100%
OnDestroy()0%110100%
SetFadeout(...)0%6200%
FadeOut()0%12300%
DockContextMenu(...)0%2100%
DockHoverPanel(...)0%2100%
Update()0%110100%
CheckHoverCoordinates()0%13.35030.77%
ProcessHoverPanelTimer()0%14.115028.57%
ProcessHoverGotoPanelTimer()0%14.115028.57%
RemoveTabs(...)0%2.152066.67%
UnixTimeStampToLocalDateTime(...)0%110100%

File(s)

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

#LineLine coverage
 1using System;
 2using System.Threading;
 3using Cysharp.Threading.Tasks;
 4using DCL;
 5using DCL.Helpers;
 6using DCL.Interface;
 7using DCL.SettingsCommon;
 8using TMPro;
 9using UnityEngine;
 10using UnityEngine.EventSystems;
 11
 12public class DefaultChatEntry : ChatEntry, IPointerClickHandler, IPointerEnterHandler, IPointerExitHandler
 13{
 14    [SerializeField] internal TextMeshProUGUI body;
 3315    [SerializeField] internal float timeToHoverPanel = 1f;
 3316    [SerializeField] internal float timeToHoverGotoPanel = 1f;
 3317    [SerializeField] internal bool showUserName = true;
 18    [SerializeField] private RectTransform hoverPanelPositionReference;
 19    [SerializeField] private RectTransform contextMenuPositionReference;
 20    [NonSerialized] public string messageLocalDateTime;
 21
 22    private float hoverPanelTimer;
 23    private float hoverGotoPanelTimer;
 24    private bool isOverCoordinates;
 25    private bool isShowingPreview;
 26    private ParcelCoordinates currentCoordinates;
 27    private ChatEntryModel model;
 28
 3329    private readonly CancellationTokenSource populationTaskCancellationTokenSource = new CancellationTokenSource();
 30
 031    public override ChatEntryModel Model => model;
 32
 33    public event Action<DefaultChatEntry> OnUserNameClicked;
 34    public event Action<DefaultChatEntry> OnTriggerHover;
 35    public event Action<DefaultChatEntry, ParcelCoordinates> OnTriggerHoverGoto;
 36    public event Action OnCancelHover;
 37    public event Action OnCancelGotoHover;
 38    public event Action OnHover;
 39
 40    public override void Populate(ChatEntryModel chatEntryModel) =>
 1241        PopulateTask(chatEntryModel, populationTaskCancellationTokenSource.Token).Forget();
 42
 43    private async UniTask PopulateTask(ChatEntryModel chatEntryModel, CancellationToken cancellationToken)
 44    {
 1245        model = chatEntryModel;
 46
 1247        chatEntryModel.bodyText = RemoveTabs(chatEntryModel.bodyText);
 1248        var userString = GetUserString(chatEntryModel);
 49
 50        // Due to a TMPro bug in Unity 2020 LTS we have to wait several frames before setting the body.text to avoid a
 51        // client crash. More info at https://github.com/decentraland/unity-renderer/pull/2345#issuecomment-1155753538
 52        // TODO: Remove hack in a newer Unity/TMPro version
 3653        await UniTask.NextFrame(cancellationToken);
 3654        await UniTask.NextFrame(cancellationToken);
 3655        await UniTask.NextFrame(cancellationToken);
 56
 1257        if (!string.IsNullOrEmpty(userString) && showUserName)
 958            body.text = $"{userString} {chatEntryModel.bodyText}";
 59        else
 360            body.text = chatEntryModel.bodyText;
 61
 1262        body.text = GetCoordinatesLink(body.text);
 63
 1264        messageLocalDateTime = UnixTimeStampToLocalDateTime(chatEntryModel.timestamp).ToString();
 65
 1266        (transform as RectTransform).ForceUpdateLayout();
 67
 1268        PlaySfx(chatEntryModel);
 1269    }
 70
 71    private string GetUserString(ChatEntryModel chatEntryModel)
 72    {
 1273        if (string.IsNullOrEmpty(model.senderName)) return "";
 74
 1275        var baseName = model.senderName;
 76
 1277        switch (chatEntryModel.subType)
 78        {
 79            case ChatEntryModel.SubType.SENT:
 780                switch (chatEntryModel.messageType)
 81                {
 82                    case ChatMessage.Type.PUBLIC:
 683                    case ChatMessage.Type.PRIVATE when chatEntryModel.isChannelMessage:
 184                        baseName = "You";
 185                        break;
 86                    case ChatMessage.Type.PRIVATE:
 687                        baseName = $"To {chatEntryModel.recipientName}";
 688                        break;
 89                }
 90
 91                break;
 92            case ChatEntryModel.SubType.RECEIVED:
 593                switch (chatEntryModel.messageType)
 94                {
 95                    case ChatMessage.Type.PRIVATE:
 396                        baseName = $"<color=#5EBD3D>From <link=username://{baseName}>{baseName}</link></color>";
 397                        break;
 98                    case ChatMessage.Type.PUBLIC:
 199                        baseName = $"<link=username://{baseName}>{baseName}</link>";
 100                        break;
 101                }
 102                break;
 103        }
 104
 12105        baseName = $"<b>{baseName}:</b>";
 106
 12107        return baseName;
 108    }
 109
 110    private string GetCoordinatesLink(string body)
 111    {
 12112        if (!CoordinateUtils.HasValidTextCoordinates(body))
 9113            return body;
 3114        var textCoordinates = CoordinateUtils.GetTextCoordinates(body);
 115
 16116        for (var i = 0; i < textCoordinates.Count; i++)
 117        {
 118            // TODO: the preload should not be here
 5119            PreloadSceneMetadata(CoordinateUtils.ParseCoordinatesString(textCoordinates[i]));
 120
 5121            body = body.Replace(textCoordinates[i],
 122                $"</noparse><link={textCoordinates[i]}><color=#4886E3><u>{textCoordinates[i]}</u></color></link><noparse
 123        }
 124
 3125        return body;
 126    }
 127
 128    private void PlaySfx(ChatEntryModel chatEntryModel)
 129    {
 12130        if (HUDAudioHandler.i == null)
 12131            return;
 132
 0133        if (IsRecentMessage(chatEntryModel) && Settings.i.audioSettings.Data.chatSFXEnabled)
 134        {
 0135            switch (chatEntryModel.messageType)
 136            {
 137                case ChatMessage.Type.PUBLIC:
 138                    // Check whether or not the message was sent by the local player
 0139                    if (chatEntryModel.senderId == UserProfile.GetOwnUserProfile().userId)
 0140                        AudioScriptableObjects.chatSend.Play(true);
 141                    else
 0142                        AudioScriptableObjects.chatReceiveGlobal.Play(true);
 0143                    break;
 144                case ChatMessage.Type.PRIVATE:
 0145                    switch (chatEntryModel.subType)
 146                    {
 147                        case ChatEntryModel.SubType.RECEIVED:
 0148                            AudioScriptableObjects.chatReceivePrivate.Play(true);
 0149                            break;
 150                        case ChatEntryModel.SubType.SENT:
 0151                            AudioScriptableObjects.chatSend.Play(true);
 0152                            break;
 153                    }
 154
 155                    break;
 156                case ChatMessage.Type.SYSTEM:
 0157                    AudioScriptableObjects.chatReceiveGlobal.Play(true);
 158                    break;
 159            }
 160        }
 161
 0162        HUDAudioHandler.i.RefreshChatLastCheckedTimestamp();
 0163    }
 164
 165    private bool IsRecentMessage(ChatEntryModel chatEntryModel)
 166    {
 0167        return chatEntryModel.timestamp > HUDAudioHandler.i.chatLastCheckedTimestamp
 168               && (DateTimeOffset.UtcNow - DateTimeOffset.FromUnixTimeMilliseconds((long) chatEntryModel.timestamp))
 169               .TotalSeconds < 30;
 170    }
 171
 172    private void PreloadSceneMetadata(ParcelCoordinates parcelCoordinates)
 173    {
 5174        if (MinimapMetadata.GetMetadata().GetSceneInfo(parcelCoordinates.x, parcelCoordinates.y) == null)
 5175            WebInterface.RequestScenesInfoAroundParcel(new Vector2(parcelCoordinates.x, parcelCoordinates.y), 2);
 5176    }
 177
 178    public void OnPointerClick(PointerEventData pointerEventData)
 179    {
 0180        if (pointerEventData.button == PointerEventData.InputButton.Left)
 181        {
 0182            var linkIndex =
 183                TMP_TextUtilities.FindIntersectingLink(body, pointerEventData.position, body.canvas.worldCamera);
 0184            if (linkIndex == -1) return;
 185
 0186            var link = body.textInfo.linkInfo[linkIndex].GetLinkID();
 187
 0188            if (CoordinateUtils.HasValidTextCoordinates(link))
 189            {
 0190                DataStore.i.HUDs.gotoPanelVisible.Set(true);
 0191                var parcelCoordinate = CoordinateUtils.ParseCoordinatesString(link);
 0192                DataStore.i.HUDs.gotoPanelCoordinates.Set(parcelCoordinate);
 0193            }
 0194            else if (link.StartsWith("username://"))
 0195                OnUserNameClicked?.Invoke(this);
 196        }
 0197    }
 198
 199    public void OnPointerEnter(PointerEventData pointerEventData)
 200    {
 0201        if (pointerEventData == null)
 0202            return;
 203
 0204        hoverPanelTimer = timeToHoverPanel;
 205
 0206        OnHover?.Invoke();
 0207    }
 208
 209    public void OnPointerExit(PointerEventData pointerEventData)
 210    {
 12211        if (pointerEventData == null)
 12212            return;
 213
 0214        hoverPanelTimer = 0f;
 0215        var linkIndex =
 216            TMP_TextUtilities.FindIntersectingLink(body, pointerEventData.position,
 217                DataStore.i.camera.hudsCamera.Get());
 0218        if (linkIndex == -1)
 219        {
 0220            isOverCoordinates = false;
 0221            hoverGotoPanelTimer = 0;
 0222            OnCancelGotoHover?.Invoke();
 223        }
 224
 0225        OnCancelHover?.Invoke();
 0226    }
 227
 228    private void OnDisable()
 229    {
 12230        OnPointerExit(null);
 12231    }
 232
 233    private void OnDestroy()
 234    {
 12235        populationTaskCancellationTokenSource.Cancel();
 12236    }
 237
 238    public override void SetFadeout(bool enabled)
 239    {
 0240        if (!enabled)
 241        {
 0242            group.alpha = 1;
 0243            fadeEnabled = false;
 0244            return;
 245        }
 246
 0247        fadeEnabled = true;
 0248    }
 249
 250    public override void FadeOut()
 251    {
 0252        if (!gameObject.activeInHierarchy)
 253        {
 0254            group.alpha = 0;
 0255            return;
 256        }
 257
 0258        if (previewInterpolationAlphaRoutine != null)
 0259            StopCoroutine(previewInterpolationAlphaRoutine);
 260
 0261        previewInterpolationAlphaRoutine = StartCoroutine(InterpolateAlpha(0, 0.5f));
 0262    }
 263
 264    public void DockContextMenu(RectTransform panel)
 265    {
 0266        panel.pivot = new Vector2(0, 0);
 0267        panel.position = contextMenuPositionReference.position;
 0268    }
 269
 270    public void DockHoverPanel(RectTransform panel)
 271    {
 0272        panel.pivot = hoverPanelPositionReference.pivot;
 0273        panel.position = hoverPanelPositionReference.position;
 0274    }
 275
 276    private void Update()
 277    {
 278        // TODO: why it needs to be in an update? what about OnPointerEnter/OnPointerExit?
 48279        CheckHoverCoordinates();
 48280        ProcessHoverPanelTimer();
 48281        ProcessHoverGotoPanelTimer();
 48282    }
 283
 284    private void CheckHoverCoordinates()
 285    {
 48286        if (isOverCoordinates)
 0287            return;
 288
 48289        var linkIndex =
 290            TMP_TextUtilities.FindIntersectingLink(body, Input.mousePosition, DataStore.i.camera.hudsCamera.Get());
 291
 48292        if (linkIndex == -1)
 48293            return;
 294
 0295        var link = body.textInfo.linkInfo[linkIndex].GetLinkID();
 0296        if (!CoordinateUtils.HasValidTextCoordinates(link)) return;
 297
 0298        isOverCoordinates = true;
 0299        currentCoordinates = CoordinateUtils.ParseCoordinatesString(link);
 0300        hoverGotoPanelTimer = timeToHoverGotoPanel;
 0301        OnCancelHover?.Invoke();
 0302    }
 303
 304    private void ProcessHoverPanelTimer()
 305    {
 48306        if (hoverPanelTimer <= 0f || isOverCoordinates)
 48307            return;
 308
 0309        hoverPanelTimer -= Time.deltaTime;
 0310        if (hoverPanelTimer <= 0f)
 311        {
 0312            hoverPanelTimer = 0f;
 0313            OnTriggerHover?.Invoke(this);
 314        }
 0315    }
 316
 317    private void ProcessHoverGotoPanelTimer()
 318    {
 48319        if (hoverGotoPanelTimer <= 0f || !isOverCoordinates)
 48320            return;
 321
 0322        hoverGotoPanelTimer -= Time.deltaTime;
 0323        if (hoverGotoPanelTimer <= 0f)
 324        {
 0325            hoverGotoPanelTimer = 0f;
 0326            OnTriggerHoverGoto?.Invoke(this, currentCoordinates);
 327        }
 0328    }
 329
 330    private string RemoveTabs(string text)
 331    {
 12332        if (string.IsNullOrEmpty(text))
 0333            return "";
 334
 335        //NOTE(Brian): ContentSizeFitter doesn't fare well with tabs, so i'm replacing these
 336        //             with spaces.
 12337        return text.Replace("\t", "    ");
 338    }
 339
 340    private static DateTime UnixTimeStampToLocalDateTime(ulong unixTimeStampMilliseconds)
 341    {
 342        // TODO see if we can simplify with 'DateTimeOffset.FromUnixTimeMilliseconds'
 12343        DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
 12344        dtDateTime = dtDateTime.AddMilliseconds(unixTimeStampMilliseconds).ToLocalTime();
 12345        return dtDateTime;
 346    }
 347}