< Summary

Class:DCL.Chat.Notifications.MainChatNotificationsComponentView
Assembly:NotificationMessagesHUD
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/NotificationMessagesHUD/MainChatNotificationsComponentView.cs
Covered lines:121
Uncovered lines:88
Coverable lines:209
Total lines:414
Line coverage:57.8% (121 of 209)
Covered branches:0
Total branches:0
Covered methods:15
Total methods:29
Method coverage:51.7% (15 of 29)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
MainChatNotificationsComponentView()0%110100%
Awake()0%330100%
Show(...)0%110100%
Hide(...)0%110100%
GetPanelTransform()0%2100%
ResetNotificationButtonFromScroll(...)0%6200%
ShowPanel()0%12300%
HidePanel()0%12300%
GetNotificationsCount()0%2100%
ShowNotifications()0%6200%
HideNotifications()0%6200%
AddNewChatNotification(...)0%4.014092.31%
AddNewChatNotification(...)0%5.035089.29%
AddNewFriendRequestNotification(...)0%20400%
AnimateNewEntry()0%8.126061.11%
ResetNotificationButton()0%220100%
IncreaseNotificationCount()0%20400%
SetScrollToEnd(...)0%110100%
PopulatePrivateNotification(...)0%44093.33%
PopulatePublicNotification(...)0%440100%
ClickedOnNotification(...)0%6200%
PopulateFriendRequestNotification(...)0%2100%
ClickedOnFriendRequest(...)0%6200%
FocusedOnNotification(...)0%3.333066.67%
FocusedOnPanel(...)0%4.254075%
CheckNotificationCountAndRelease()0%12.764018.18%
GetChatNotificationEntryPool()0%2.022083.33%
GetFriendRequestNotificationEntryPool()0%6200%
RefreshControl()0%2100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/NotificationMessagesHUD/MainChatNotificationsComponentView.cs

#LineLine coverage
 1using Cysharp.Threading.Tasks;
 2using DCL.Helpers;
 3using DG.Tweening;
 4using System;
 5using System.Collections.Generic;
 6using System.Threading;
 7using TMPro;
 8using UnityEngine;
 9using UnityEngine.UI;
 10
 11namespace DCL.Chat.Notifications
 12{
 13    public class MainChatNotificationsComponentView : BaseComponentView, IMainChatNotificationsComponentView
 14    {
 15        [SerializeField] private RectTransform chatEntriesContainer;
 16        [SerializeField] private GameObject chatNotification;
 17        [SerializeField] private GameObject friendRequestNotification;
 18        [SerializeField] private ScrollRect scrollRectangle;
 19        [SerializeField] private Button notificationButton;
 20        [SerializeField] private ShowHideAnimator panelAnimator;
 21        [SerializeField] private ShowHideAnimator scrollbarAnimator;
 22
 23        private const string CHAT_NOTIFICATION_POOL_NAME_PREFIX = "ChatNotificationEntriesPool_";
 24        private const string FRIEND_REQUEST_NOTIFICATION_POOL_NAME_PREFIX = "FriendRequestNotificationEntriesPool_";
 25        private const int MAX_NOTIFICATION_ENTRIES = 30;
 26
 27        public event Action<string> OnClickedChatMessage;
 28        public event IMainChatNotificationsComponentView.ClickedNotificationDelegate OnClickedFriendRequest;
 29        public event Action<bool> OnResetFade;
 30        public event Action<bool> OnPanelFocus;
 31
 932        internal readonly Queue<PoolableObject> poolableQueue = new ();
 933        internal readonly Queue<BaseComponentView> notificationQueue = new ();
 34
 935        private readonly Vector2 notificationOffset = new (0, -56);
 36
 37        private bool isOverMessage;
 38        private bool isOverPanel;
 939        private int notificationCount = 1;
 40        private TMP_Text notificationMessage;
 941        private CancellationTokenSource animationCancellationToken = new ();
 42
 43        public override void Awake()
 44        {
 845            onFocused += FocusedOnPanel;
 846            notificationMessage = notificationButton.GetComponentInChildren<TMP_Text>();
 847            notificationButton?.onClick.RemoveAllListeners();
 848            notificationButton?.onClick.AddListener(() => SetScrollToEnd());
 849            scrollRectangle.onValueChanged.AddListener(ResetNotificationButtonFromScroll);
 850        }
 51
 52        public override void Show(bool instant = false) =>
 153            gameObject.SetActive(true);
 54
 55        public override void Hide(bool instant = false)
 56        {
 157            SetScrollToEnd();
 158            gameObject.SetActive(false);
 159        }
 60
 61        public Transform GetPanelTransform() =>
 062            gameObject.transform;
 63
 64        private void ResetNotificationButtonFromScroll(Vector2 newValue)
 65        {
 066            if (newValue.y <= 0.0) { ResetNotificationButton(); }
 067        }
 68
 69        public void ShowPanel()
 70        {
 071            panelAnimator?.Show();
 072            scrollbarAnimator?.Show();
 073        }
 74
 75        public void HidePanel()
 76        {
 077            panelAnimator?.Hide();
 078            scrollbarAnimator?.Hide();
 079        }
 80
 81        public int GetNotificationsCount() =>
 082            notificationQueue.Count;
 83
 84        public void ShowNotifications()
 85        {
 086            foreach (BaseComponentView notification in notificationQueue)
 087                notification.Show();
 088        }
 89
 90        public void HideNotifications()
 91        {
 092            foreach (BaseComponentView notification in notificationQueue)
 093                notification.Hide();
 094        }
 95
 96        public void AddNewChatNotification(PrivateChatMessageNotificationModel model)
 97        {
 298            Pool entryPool = GetChatNotificationEntryPool();
 299            PoolableObject newNotification = entryPool.Get();
 100
 2101            var entry = newNotification.gameObject.GetComponent<ChatNotificationMessageComponentView>();
 2102            poolableQueue.Enqueue(newNotification);
 2103            notificationQueue.Enqueue(entry);
 104
 2105            entry.OnClickedNotification -= ClickedOnNotification;
 2106            entry.onFocused -= FocusedOnNotification;
 2107            entry.showHideAnimator.OnWillFinishHide -= SetScrollToEnd;
 108
 2109            PopulatePrivateNotification(entry, model);
 110
 2111            entry.transform.SetParent(chatEntriesContainer, false);
 2112            entry.RefreshControl();
 2113            entry.SetTimestamp(Utils.UnixTimeStampToLocalTime(model.Timestamp));
 2114            entry.OnClickedNotification += ClickedOnNotification;
 2115            entry.onFocused += FocusedOnNotification;
 2116            entry.showHideAnimator.OnWillFinishHide += SetScrollToEnd;
 117
 2118            chatEntriesContainer.anchoredPosition += notificationOffset;
 119
 2120            if (isOverPanel)
 121            {
 0122                notificationButton.gameObject.SetActive(true);
 0123                IncreaseNotificationCount();
 124            }
 125            else
 126            {
 2127                ResetNotificationButton();
 2128                animationCancellationToken.Cancel();
 2129                animationCancellationToken = new CancellationTokenSource();
 2130                AnimateNewEntry(entry.gameObject.transform, animationCancellationToken.Token).Forget();
 131            }
 132
 2133            OnResetFade?.Invoke(!isOverMessage && !isOverPanel);
 2134            CheckNotificationCountAndRelease();
 2135        }
 136
 137        public void AddNewChatNotification(PublicChannelMessageNotificationModel model)
 138        {
 3139            Pool entryPool = GetChatNotificationEntryPool();
 3140            PoolableObject newNotification = entryPool.Get();
 141
 3142            var entry = newNotification.gameObject.GetComponent<ChatNotificationMessageComponentView>();
 3143            poolableQueue.Enqueue(newNotification);
 3144            notificationQueue.Enqueue(entry);
 145
 3146            entry.OnClickedNotification -= ClickedOnNotification;
 3147            entry.onFocused -= FocusedOnNotification;
 3148            entry.showHideAnimator.OnWillFinishHide -= SetScrollToEnd;
 149
 3150            PopulatePublicNotification(entry, model);
 151
 3152            entry.transform.SetParent(chatEntriesContainer, false);
 3153            entry.RefreshControl();
 3154            entry.SetTimestamp(Utils.UnixTimeStampToLocalTime(model.Timestamp));
 3155            entry.OnClickedNotification += ClickedOnNotification;
 3156            entry.onFocused += FocusedOnNotification;
 3157            entry.showHideAnimator.OnWillFinishHide += SetScrollToEnd;
 158
 3159            chatEntriesContainer.anchoredPosition += notificationOffset;
 160
 3161            if (isOverPanel)
 162            {
 0163                notificationButton.gameObject.SetActive(true);
 0164                IncreaseNotificationCount();
 165            }
 166            else
 167            {
 3168                ResetNotificationButton();
 3169                animationCancellationToken.Cancel();
 3170                animationCancellationToken = new CancellationTokenSource();
 3171                AnimateNewEntry(entry.gameObject.transform, animationCancellationToken.Token).Forget();
 172            }
 173
 3174            if (model.ShouldPlayMentionSfx)
 0175                AudioScriptableObjects.ChatReceiveMentionEvent.Play(true);
 176
 3177            OnResetFade?.Invoke(!isOverMessage && !isOverPanel);
 3178            CheckNotificationCountAndRelease();
 3179        }
 180
 181        public void AddNewFriendRequestNotification(FriendRequestNotificationModel model)
 182        {
 0183            Pool entryPool = GetFriendRequestNotificationEntryPool();
 0184            PoolableObject newNotification = entryPool.Get();
 185
 0186            var entry = newNotification.gameObject.GetComponent<FriendRequestNotificationComponentView>();
 0187            poolableQueue.Enqueue(newNotification);
 0188            notificationQueue.Enqueue(entry);
 189
 0190            entry.OnClickedNotification -= ClickedOnFriendRequest;
 0191            entry.onFocused -= FocusedOnNotification;
 0192            entry.showHideAnimator.OnWillFinishHide -= SetScrollToEnd;
 193
 0194            PopulateFriendRequestNotification(entry, model);
 195
 0196            entry.transform.SetParent(chatEntriesContainer, false);
 0197            entry.RefreshControl();
 0198            entry.OnClickedNotification += ClickedOnFriendRequest;
 0199            entry.onFocused += FocusedOnNotification;
 0200            entry.showHideAnimator.OnWillFinishHide += SetScrollToEnd;
 201
 0202            chatEntriesContainer.anchoredPosition += notificationOffset;
 203
 0204            if (isOverPanel)
 205            {
 0206                notificationButton.gameObject.SetActive(true);
 0207                IncreaseNotificationCount();
 208            }
 209            else
 210            {
 0211                ResetNotificationButton();
 0212                animationCancellationToken.Cancel();
 0213                animationCancellationToken = new CancellationTokenSource();
 0214                AnimateNewEntry(entry.gameObject.transform, animationCancellationToken.Token).Forget();
 215            }
 216
 0217            OnResetFade?.Invoke(!isOverMessage && !isOverPanel);
 0218            CheckNotificationCountAndRelease();
 219
 220            // TODO: refactor sfx usage into non-static
 0221            AudioScriptableObjects.FriendRequestEvent.Play();
 0222        }
 223
 224        private async UniTaskVoid AnimateNewEntry(Transform notification, CancellationToken cancellationToken)
 225        {
 5226            cancellationToken.ThrowIfCancellationRequested();
 227
 5228            Sequence mySequence = DOTween.Sequence()
 229                                         .AppendInterval(0.2f)
 230                                         .Append(notification.DOScale(1, 0.3f).SetEase(Ease.OutBack));
 231
 232            try
 233            {
 5234                Vector2 endPosition = new Vector2(0, 0);
 5235                Vector2 currentPosition = chatEntriesContainer.anchoredPosition;
 5236                notification.localScale = Vector3.zero;
 200237                DOTween.To(() => currentPosition, x => currentPosition = x, endPosition, 0.8f).SetEase(Ease.OutCubic);
 238
 10239                while (chatEntriesContainer.anchoredPosition.y < 0)
 240                {
 5241                    chatEntriesContainer.anchoredPosition = currentPosition;
 15242                    await UniTask.NextFrame(cancellationToken);
 243                }
 244
 0245                mySequence.Play();
 0246            }
 0247            catch (OperationCanceledException)
 248            {
 0249                if (!DOTween.IsTweening(notification))
 0250                    notification.DOScale(1, 0.3f).SetEase(Ease.OutBack);
 0251            }
 0252        }
 253
 254        private void ResetNotificationButton()
 255        {
 6256            notificationButton.gameObject.SetActive(false);
 6257            notificationCount = 0;
 258
 6259            if (notificationMessage != null)
 6260                notificationMessage.text = notificationCount.ToString();
 6261        }
 262
 263        private void IncreaseNotificationCount()
 264        {
 0265            notificationCount++;
 266
 0267            if (notificationMessage != null)
 0268                notificationMessage.text = notificationCount <= 9 ? notificationCount.ToString() : "9+";
 0269        }
 270
 271        private void SetScrollToEnd(ShowHideAnimator animator = null)
 272        {
 1273            scrollRectangle.normalizedPosition = new Vector2(0, 0);
 1274            ResetNotificationButton();
 1275        }
 276
 277        private void PopulatePrivateNotification(ChatNotificationMessageComponentView view,
 278            PrivateChatMessageNotificationModel model)
 279        {
 2280            string senderName = model.ImTheSender ? "You" : model.SenderUsername;
 281
 2282            view.SetIsPrivate(true);
 2283            view.SetMaxContentCharacters(40 - senderName.Length);
 2284            view.SetMessage(model.Body);
 2285            view.SetNotificationHeader($"DM - {model.PeerUsername}");
 2286            view.SetNotificationSender($"{senderName}:");
 2287            view.SetNotificationTargetId(model.TargetId);
 2288            view.SetImageVisibility(!model.ImTheSender);
 2289            view.SetOwnPlayerMention(model.IsOwnPlayerMentioned);
 290
 2291            if (!string.IsNullOrEmpty(model.ProfilePicture))
 0292                view.SetImage(model.ProfilePicture);
 293
 2294            if (model.ImTheSender)
 1295                view.DockRight();
 296            else
 1297                view.DockLeft();
 1298        }
 299
 300        private void PopulatePublicNotification(ChatNotificationMessageComponentView view,
 301            PublicChannelMessageNotificationModel model)
 302        {
 3303            string channelId = model.ChannelId;
 3304            string channelName = model.ChannelName == "nearby" ? "~nearby" : $"#{model.ChannelName}";
 3305            string senderName = model.ImTheSender ? "You" : model.Username;
 306
 3307            view.SetIsPrivate(false);
 3308            view.SetMaxContentCharacters(40 - senderName.Length);
 3309            view.SetMessage(model.Body);
 3310            view.SetNotificationTargetId(channelId);
 3311            view.SetNotificationHeader(channelName);
 3312            view.SetNotificationSender($"{senderName}:");
 3313            view.SetImageVisibility(false);
 3314            view.SetOwnPlayerMention(model.IsOwnPlayerMentioned);
 315
 3316            if (model.ImTheSender)
 1317                view.DockRight();
 318            else
 2319                view.DockLeft();
 2320        }
 321
 322        private void ClickedOnNotification(string targetId)
 323        {
 0324            OnClickedChatMessage?.Invoke(targetId);
 0325        }
 326
 327        private void PopulateFriendRequestNotification(FriendRequestNotificationComponentView friendRequestNotificationC
 328            FriendRequestNotificationModel model)
 329        {
 0330            friendRequestNotificationComponentView.SetFriendRequestId(model.FriendRequestId);
 0331            friendRequestNotificationComponentView.SetUser(model.UserId, model.UserName);
 0332            friendRequestNotificationComponentView.SetHeader(model.Header);
 0333            friendRequestNotificationComponentView.SetMessage(model.Message);
 0334            DateTime localTime = model.Timestamp.ToLocalTime();
 0335            friendRequestNotificationComponentView.SetTimestamp($"{localTime.Hour}:{localTime.Minute:D2}");
 0336            friendRequestNotificationComponentView.SetIsAccepted(model.IsAccepted);
 0337        }
 338
 339        private void ClickedOnFriendRequest(string friendRequestId, string userId, bool isAcceptedFromPeer) =>
 0340            OnClickedFriendRequest?.Invoke(friendRequestId, userId, isAcceptedFromPeer);
 341
 342        private void FocusedOnNotification(bool isInFocus)
 343        {
 5344            isOverMessage = isInFocus;
 5345            OnResetFade?.Invoke(!isOverMessage && !isOverPanel);
 0346        }
 347
 348        private void FocusedOnPanel(bool isInFocus)
 349        {
 8350            isOverPanel = isInFocus;
 8351            OnPanelFocus?.Invoke(isOverPanel);
 8352            OnResetFade?.Invoke(!isOverMessage && !isOverPanel);
 0353        }
 354
 355        private void CheckNotificationCountAndRelease()
 356        {
 10357            if (poolableQueue.Count < MAX_NOTIFICATION_ENTRIES) return;
 358
 0359            BaseComponentView notificationToDequeue = notificationQueue.Dequeue();
 0360            notificationToDequeue.onFocused -= FocusedOnNotification;
 361
 0362            PoolableObject pooledObj = poolableQueue.Dequeue();
 363
 364            switch (notificationToDequeue)
 365            {
 366                case FriendRequestNotificationComponentView:
 0367                    GetFriendRequestNotificationEntryPool().Release(pooledObj);
 0368                    break;
 369                case ChatNotificationMessageComponentView:
 0370                    GetChatNotificationEntryPool().Release(pooledObj);
 0371                    break;
 372                default:
 0373                    Debug.LogError($"Failed to release notification of type {notificationToDequeue.GetType()}. No object
 374                    break;
 375            }
 0376        }
 377
 378        private Pool GetChatNotificationEntryPool()
 379        {
 5380            var entryPool = PoolManager.i.GetPool(CHAT_NOTIFICATION_POOL_NAME_PREFIX + name + GetInstanceID());
 5381            if (entryPool != null) return entryPool;
 382
 5383            entryPool = PoolManager.i.AddPool(
 384                CHAT_NOTIFICATION_POOL_NAME_PREFIX + name + GetInstanceID(),
 385                Instantiate(chatNotification),
 386                maxPrewarmCount: MAX_NOTIFICATION_ENTRIES,
 387                isPersistent: true);
 388
 5389            entryPool.ForcePrewarm();
 390
 5391            return entryPool;
 392        }
 393
 394        private Pool GetFriendRequestNotificationEntryPool()
 395        {
 0396            var entryPool = PoolManager.i.GetPool(FRIEND_REQUEST_NOTIFICATION_POOL_NAME_PREFIX + name + GetInstanceID())
 397
 0398            if (entryPool != null)
 0399                return entryPool;
 400
 0401            entryPool = PoolManager.i.AddPool(
 402                FRIEND_REQUEST_NOTIFICATION_POOL_NAME_PREFIX + name + GetInstanceID(),
 403                Instantiate(friendRequestNotification),
 404                maxPrewarmCount: MAX_NOTIFICATION_ENTRIES,
 405                isPersistent: true);
 406
 0407            entryPool.ForcePrewarm();
 408
 0409            return entryPool;
 410        }
 411
 0412        public override void RefreshControl() { }
 413    }
 414}