< Summary

Class:FriendsTabComponentView
Assembly:FriendsHUD
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/FriendsHUD/Scripts/Tabs/FriendsTabComponentView.cs
Covered lines:149
Uncovered lines:119
Coverable lines:268
Total lines:550
Line coverage:55.5% (149 of 268)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
FriendsTabComponentView()0%110100%
Initialize(...)0%110100%
OnEnable()0%110100%
OnDisable()0%110100%
Expand()0%2.982037.5%
Show()0%110100%
Hide()0%110100%
Update()0%220100%
Clear()0%6200%
Remove(...)0%5.125083.33%
Get(...)0%220100%
Populate(...)0%22.328039.29%
Set(...)0%3.143075%
RefreshControl()0%8.744033.33%
ClearFilter()0%56700%
Filter(...)0%30500%
Enqueue(...)0%110100%
ShowMoreFriendsToLoadHint(...)0%110100%
HideMoreFriendsToLoadHint()0%110100%
ShowMoreFriendsToLoadHint()0%110100%
HandleSearchInputChanged(...)0%220100%
SetQueuedEntries()0%440100%
FetchProfilePicturesForVisibleEntries()0%6.65060%
UpdateEmptyOrFilledState()0%110100%
CreateEntry(...)0%110100%
OnEntryWhisperClick(...)0%6200%
OnEntryMenuToggle(...)0%2100%
GetEntryPool()0%2.022083.33%
UpdateCounterLabel()0%2.312057.14%
HandleFriendBlockRequest(...)0%6200%
HandleUnfriendRequest(...)0%12300%
UpdateLayout()0%2100%
SortDirtyLists()0%11.846045.45%
RequestMoreFriendEntries()0%220100%
Show()0%110100%
Hide()0%110100%
FlagAsPendingToSort()0%2100%
Sort()0%110100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/FriendsHUD/Scripts/Tabs/FriendsTabComponentView.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using DCL;
 5using DCL.Helpers;
 6using SocialFeaturesAnalytics;
 7using TMPro;
 8using UnityEngine;
 9using UnityEngine.UI;
 10
 11public class FriendsTabComponentView : BaseComponentView
 12{
 13    private const int CREATION_AMOUNT_PER_FRAME = 5;
 14    private const int AVATAR_SNAPSHOTS_PER_FRAME = 5;
 15    private const int PRE_INSTANTIATED_ENTRIES = 25;
 16    private const string FRIEND_ENTRIES_POOL_NAME_PREFIX = "FriendEntriesPool_";
 17
 18    [SerializeField] private GameObject enabledHeader;
 19    [SerializeField] private GameObject disabledHeader;
 20    [SerializeField] private GameObject emptyStateContainer;
 21    [SerializeField] private GameObject filledStateContainer;
 22    [SerializeField] private FriendEntry entryPrefab;
 23    [SerializeField] private FriendListComponents onlineFriendsList;
 24    [SerializeField] private FriendListComponents offlineFriendsList;
 25    [SerializeField] private FriendListComponents allFriendsList;
 26    [SerializeField] private FriendListComponents searchResultsFriendList;
 27    [SerializeField] private SearchBarComponentView searchBar;
 28    [SerializeField] private UserContextMenu contextMenuPanel;
 29    [SerializeField] private Model model;
 30    [SerializeField] private RectTransform viewport;
 31
 32    [Header("Load More Entries")] [SerializeField]
 33    internal Button loadMoreEntriesButton;
 34
 35    [SerializeField] internal GameObject loadMoreEntriesContainer;
 36    [SerializeField] internal TMP_Text loadMoreEntriesLabel;
 37
 2438    private readonly Dictionary<string, FriendEntryModel> creationQueue =
 39        new Dictionary<string, FriendEntryModel>();
 40
 2441    private readonly Dictionary<string, PoolableObject> pooleableEntries = new Dictionary<string, PoolableObject>();
 2442    private readonly Dictionary<string, FriendEntry> entries = new Dictionary<string, FriendEntry>();
 43    private Pool entryPool;
 44    private int currentAvatarSnapshotIndex;
 45    private bool isLayoutDirty;
 46    private Dictionary<string, FriendEntryModel> filteredEntries;
 47    private IChatController chatController;
 48    private ILastReadMessagesService lastReadMessagesService;
 49    private IFriendsController friendsController;
 50    private ISocialAnalytics socialAnalytics;
 51
 052    public Dictionary<string, FriendEntry> Entries => entries;
 1853    public int Count => entries.Count + creationQueue.Keys.Count(s => !entries.ContainsKey(s));
 54
 055    public bool DidDeferredCreationCompleted => creationQueue.Count == 0;
 56
 57    public event Action<string> OnSearchRequested;
 58
 59    public bool ListByOnlineStatus
 60    {
 061        get => model.listByOnlineStatus;
 62        set
 63        {
 164            model.listByOnlineStatus = value;
 165            RefreshControl();
 166        }
 67    }
 68
 69    public event Action<FriendEntryModel> OnWhisper;
 70    public event Action<string> OnDeleteConfirmation;
 71    public event Action OnRequireMoreFriends;
 72
 73    public void Initialize(IChatController chatController,
 74        ILastReadMessagesService lastReadMessagesService,
 75        IFriendsController friendsController,
 76        ISocialAnalytics socialAnalytics)
 77    {
 2378        this.chatController = chatController;
 2379        this.lastReadMessagesService = lastReadMessagesService;
 2380        this.friendsController = friendsController;
 2381        this.socialAnalytics = socialAnalytics;
 2382    }
 83
 84    public override void OnEnable()
 85    {
 2486        base.OnEnable();
 87
 2488        searchBar.Configure(new SearchBarComponentModel {placeHolderText = "Search friend"});
 2489        searchBar.OnSearchText += HandleSearchInputChanged;
 2490        contextMenuPanel.OnBlock += HandleFriendBlockRequest;
 2491        contextMenuPanel.OnUnfriend += HandleUnfriendRequest;
 2492        loadMoreEntriesButton.onClick.AddListener(RequestMoreFriendEntries);
 93
 94        int SortByAlphabeticalOrder(FriendEntryBase u1, FriendEntryBase u2)
 95        {
 096            return string.Compare(u1.Model.userName, u2.Model.userName, StringComparison.InvariantCultureIgnoreCase);
 97        }
 98
 2499        onlineFriendsList.list.SortingMethod = SortByAlphabeticalOrder;
 24100        offlineFriendsList.list.SortingMethod = SortByAlphabeticalOrder;
 24101        allFriendsList.list.SortingMethod = SortByAlphabeticalOrder;
 24102        searchResultsFriendList.list.SortingMethod = SortByAlphabeticalOrder;
 24103        UpdateLayout();
 24104        UpdateEmptyOrFilledState();
 24105    }
 106
 107    public override void OnDisable()
 108    {
 24109        base.OnDisable();
 110
 24111        searchBar.OnSearchText -= HandleSearchInputChanged;
 24112        contextMenuPanel.OnBlock -= HandleFriendBlockRequest;
 24113        contextMenuPanel.OnUnfriend -= HandleUnfriendRequest;
 24114        loadMoreEntriesButton.onClick.RemoveListener(RequestMoreFriendEntries);
 24115    }
 116
 117    public void Expand()
 118    {
 23119        if (ListByOnlineStatus)
 120        {
 0121            model.isOfflineFriendsExpanded = true;
 0122            model.isOnlineFriendsExpanded = true;
 0123            onlineFriendsList.list.Expand();
 0124            offlineFriendsList.list.Expand();
 0125        }
 126        else
 23127            allFriendsList.list.Expand();
 23128    }
 129
 130    public void Show()
 131    {
 8132        gameObject.SetActive(true);
 8133        enabledHeader.SetActive(true);
 8134        disabledHeader.SetActive(false);
 8135        searchBar.ClearSearch();
 8136    }
 137
 138    public void Hide()
 139    {
 7140        gameObject.SetActive(false);
 7141        enabledHeader.SetActive(false);
 7142        disabledHeader.SetActive(true);
 7143        contextMenuPanel.Hide();
 7144    }
 145
 146    public override void Update()
 147    {
 34148        base.Update();
 149
 34150        if (isLayoutDirty)
 16151            Utils.ForceRebuildLayoutImmediate((RectTransform) filledStateContainer.transform);
 34152        isLayoutDirty = false;
 153
 34154        SortDirtyLists();
 34155        FetchProfilePicturesForVisibleEntries();
 34156        SetQueuedEntries();
 34157    }
 158
 159    public void Clear()
 160    {
 0161        entries.ToList().ForEach(pair => Remove(pair.Key));
 162
 0163        if (ListByOnlineStatus)
 164        {
 0165            onlineFriendsList.list.Clear();
 0166            offlineFriendsList.list.Clear();
 0167        }
 168        else
 0169            allFriendsList.list.Clear();
 170
 0171        searchResultsFriendList.list.Clear();
 0172        UpdateEmptyOrFilledState();
 0173        UpdateCounterLabel();
 0174    }
 175
 176    public void Remove(string userId)
 177    {
 10178        if (creationQueue.ContainsKey(userId))
 1179            creationQueue.Remove(userId);
 180
 18181        if (!entries.ContainsKey(userId)) return;
 182
 2183        if (pooleableEntries.TryGetValue(userId, out var pooleableObject))
 184        {
 2185            entryPool.Release(pooleableObject);
 2186            pooleableEntries.Remove(userId);
 187        }
 188
 2189        entries.Remove(userId);
 190
 2191        if (ListByOnlineStatus)
 192        {
 0193            offlineFriendsList.list.Remove(userId);
 0194            onlineFriendsList.list.Remove(userId);
 0195        }
 196        else
 2197            allFriendsList.list.Remove(userId);
 198
 2199        searchResultsFriendList.list.Remove(userId);
 200
 2201        UpdateEmptyOrFilledState();
 2202        UpdateCounterLabel();
 2203        UpdateLayout();
 2204    }
 205
 42206    public FriendEntry Get(string userId) => entries.ContainsKey(userId) ? entries[userId] : null;
 207
 208    public void Populate(string userId, FriendEntryModel model)
 209    {
 5210        if (!entries.ContainsKey(userId))
 211        {
 0212            if (creationQueue.ContainsKey(userId))
 0213                creationQueue[userId] = model;
 0214            return;
 215        }
 216
 5217        var entry = entries[userId];
 5218        entry.Populate(model);
 219
 5220        if (filteredEntries?.ContainsKey(userId) ?? false)
 221        {
 0222            offlineFriendsList.list.Remove(userId);
 0223            onlineFriendsList.list.Remove(userId);
 0224            searchResultsFriendList.list.Add(userId, entry);
 0225            searchResultsFriendList.FlagAsPendingToSort();
 0226        }
 227        else
 228        {
 5229            if (ListByOnlineStatus)
 230            {
 0231                if (model.status == PresenceStatus.ONLINE)
 232                {
 0233                    offlineFriendsList.list.Remove(userId);
 0234                    onlineFriendsList.list.Add(userId, entry);
 0235                    onlineFriendsList.FlagAsPendingToSort();
 0236                }
 237                else
 238                {
 0239                    onlineFriendsList.list.Remove(userId);
 0240                    offlineFriendsList.list.Add(userId, entry);
 0241                    offlineFriendsList.FlagAsPendingToSort();
 242                }
 0243            }
 244            else
 245            {
 5246                allFriendsList.list.Add(userId, entry);
 5247                allFriendsList.FlagAsPendingToSort();
 248            }
 249        }
 250
 5251        UpdateLayout();
 5252        UpdateEmptyOrFilledState();
 5253        UpdateCounterLabel();
 5254    }
 255
 256    public void Set(string userId, FriendEntryModel model)
 257    {
 5258        if (creationQueue.ContainsKey(userId))
 259        {
 0260            creationQueue[userId] = model;
 0261            return;
 262        }
 263
 5264        if (!entries.ContainsKey(userId))
 265        {
 5266            CreateEntry(userId);
 5267            UpdateLayout();
 268        }
 269
 5270        Populate(userId, model);
 5271    }
 272
 273    public override void RefreshControl()
 274    {
 1275        if (ListByOnlineStatus)
 276        {
 0277            onlineFriendsList.Show();
 0278            offlineFriendsList.Show();
 0279            allFriendsList.Hide();
 280
 0281            if (model.isOnlineFriendsExpanded)
 0282                onlineFriendsList.list.Expand();
 283            else
 0284                onlineFriendsList.list.Collapse();
 285
 0286            if (model.isOfflineFriendsExpanded)
 0287                offlineFriendsList.list.Expand();
 288            else
 0289                offlineFriendsList.list.Collapse();
 0290        }
 291        else
 292        {
 1293            onlineFriendsList.Hide();
 1294            offlineFriendsList.Hide();
 1295            allFriendsList.Show();
 296        }
 1297    }
 298
 299    public void ClearFilter()
 300    {
 0301        filteredEntries = null;
 302
 0303        if (searchResultsFriendList.list.gameObject.activeSelf)
 304        {
 0305            foreach (var pair in entries)
 306            {
 0307                searchResultsFriendList.list.Remove(pair.Key);
 0308                Populate(pair.Key, pair.Value.Model);
 309            }
 310        }
 311
 0312        searchResultsFriendList.Hide();
 313
 0314        if (ListByOnlineStatus)
 315        {
 0316            offlineFriendsList.Show();
 0317            onlineFriendsList.Show();
 0318            offlineFriendsList.Sort();
 0319            onlineFriendsList.Sort();
 0320            offlineFriendsList.list.Filter(entry => true);
 0321            onlineFriendsList.list.Filter(entry => true);
 0322        }
 323        else
 324        {
 0325            allFriendsList.Show();
 0326            allFriendsList.Sort();
 0327            allFriendsList.list.Filter(entry => true);
 328        }
 0329    }
 330
 331    public void Filter(Dictionary<string, FriendEntryModel> search)
 332    {
 0333        filteredEntries = search;
 334
 0335        if (ListByOnlineStatus)
 336        {
 0337            offlineFriendsList.Hide();
 0338            onlineFriendsList.Hide();
 0339        }
 340        else
 0341            allFriendsList.Hide();
 342
 0343        if (!searchResultsFriendList.list.gameObject.activeSelf)
 344        {
 0345            foreach (var pair in entries)
 346            {
 0347                searchResultsFriendList.list.Add(pair.Key, pair.Value);
 348
 0349                if (ListByOnlineStatus)
 350                {
 0351                    offlineFriendsList.list.Remove(pair.Key);
 0352                    onlineFriendsList.list.Remove(pair.Key);
 0353                }
 354                else
 0355                    allFriendsList.list.Remove(pair.Key);
 356            }
 357        }
 358
 0359        searchResultsFriendList.Show();
 0360        searchResultsFriendList.Sort();
 0361        searchResultsFriendList.list.Filter(entry => search.ContainsKey(entry.Model.userId));
 362
 0363        UpdateCounterLabel();
 0364        HideMoreFriendsToLoadHint();
 0365        UpdateLayout();
 0366    }
 367
 6368    public void Enqueue(string userId, FriendEntryModel model) => creationQueue[userId] = model;
 369
 370    public void ShowMoreFriendsToLoadHint(int pendingFriendsCount)
 371    {
 1372        loadMoreEntriesLabel.SetText(
 373            $"{pendingFriendsCount} friends hidden. Use the search bar to find them or click below to show more.");
 1374        ShowMoreFriendsToLoadHint();
 1375    }
 376
 377    public void HideMoreFriendsToLoadHint()
 378    {
 1379        loadMoreEntriesContainer.SetActive(false);
 1380        UpdateLayout();
 1381    }
 382
 383    private void ShowMoreFriendsToLoadHint()
 384    {
 1385        loadMoreEntriesContainer.SetActive(true);
 1386        UpdateLayout();
 1387    }
 388
 8389    private void HandleSearchInputChanged(string search) => OnSearchRequested?.Invoke(search);
 390
 391    private void SetQueuedEntries()
 392    {
 63393        if (creationQueue.Count == 0) return;
 394
 20395        for (var i = 0; i < CREATION_AMOUNT_PER_FRAME && creationQueue.Count != 0; i++)
 396        {
 5397            var pair = creationQueue.FirstOrDefault();
 5398            creationQueue.Remove(pair.Key);
 5399            Set(pair.Key, pair.Value);
 400        }
 5401    }
 402
 403    private void FetchProfilePicturesForVisibleEntries()
 404    {
 68405        foreach (var entry in entries.Values.Skip(currentAvatarSnapshotIndex).Take(AVATAR_SNAPSHOTS_PER_FRAME))
 406        {
 0407            if (entry.IsVisible(viewport))
 0408                entry.EnableAvatarSnapshotFetching();
 409            else
 0410                entry.DisableAvatarSnapshotFetching();
 411        }
 412
 34413        currentAvatarSnapshotIndex += AVATAR_SNAPSHOTS_PER_FRAME;
 414
 34415        if (currentAvatarSnapshotIndex >= entries.Count)
 34416            currentAvatarSnapshotIndex = 0;
 34417    }
 418
 419    private void UpdateEmptyOrFilledState()
 420    {
 31421        emptyStateContainer.SetActive(entries.Count == 0);
 31422        filledStateContainer.SetActive(entries.Count > 0);
 31423    }
 424
 425    private void CreateEntry(string userId)
 426    {
 5427        entryPool = GetEntryPool();
 5428        var newFriendEntry = entryPool.Get();
 5429        pooleableEntries.Add(userId, newFriendEntry);
 5430        var entry = newFriendEntry.gameObject.GetComponent<FriendEntry>();
 5431        entry.Initialize(chatController, lastReadMessagesService, friendsController, socialAnalytics);
 5432        entries.Add(userId, entry);
 433
 5434        entry.OnMenuToggle -= OnEntryMenuToggle;
 5435        entry.OnMenuToggle += OnEntryMenuToggle;
 5436        entry.OnWhisperClick -= OnEntryWhisperClick;
 5437        entry.OnWhisperClick += OnEntryWhisperClick;
 5438    }
 439
 0440    private void OnEntryWhisperClick(FriendEntry friendEntry) => OnWhisper?.Invoke(friendEntry.Model);
 441
 442    private void OnEntryMenuToggle(FriendEntryBase friendEntry)
 443    {
 0444        contextMenuPanel.Show(friendEntry.Model.userId);
 0445        friendEntry.Dock(contextMenuPanel);
 0446    }
 447
 448    private Pool GetEntryPool()
 449    {
 5450        var entryPool = PoolManager.i.GetPool(FRIEND_ENTRIES_POOL_NAME_PREFIX + name + GetInstanceID());
 5451        if (entryPool != null) return entryPool;
 452
 5453        entryPool = PoolManager.i.AddPool(
 454            FRIEND_ENTRIES_POOL_NAME_PREFIX + name + GetInstanceID(),
 455            Instantiate(entryPrefab).gameObject,
 456            maxPrewarmCount: PRE_INSTANTIATED_ENTRIES,
 457            isPersistent: true);
 5458        entryPool.ForcePrewarm();
 459
 5460        return entryPool;
 461    }
 462
 463    private void UpdateCounterLabel()
 464    {
 7465        if (ListByOnlineStatus)
 466        {
 0467            onlineFriendsList.countText.SetText("ONLINE ({0})", onlineFriendsList.list.Count());
 0468            offlineFriendsList.countText.SetText("OFFLINE ({0})", offlineFriendsList.list.Count());
 0469        }
 470        else
 7471            allFriendsList.countText.SetText("Results ({0})", allFriendsList.list.Count());
 472
 7473        searchResultsFriendList.countText.SetText("Results ({0})", searchResultsFriendList.list.Count());
 7474    }
 475
 476    private void HandleFriendBlockRequest(string userId, bool blockUser)
 477    {
 0478        var friendEntryToBlock = Get(userId);
 0479        if (friendEntryToBlock == null) return;
 480        // instantly refresh ui
 0481        friendEntryToBlock.Model.blocked = blockUser;
 0482        Set(userId, friendEntryToBlock.Model);
 0483    }
 484
 485    private void HandleUnfriendRequest(string userId)
 486    {
 0487        var entry = Get(userId);
 0488        if (entry == null) return;
 0489        Remove(userId);
 0490        OnDeleteConfirmation?.Invoke(userId);
 0491    }
 492
 0493    private void UpdateLayout() => isLayoutDirty = true;
 494
 495    private void SortDirtyLists()
 496    {
 34497        if (ListByOnlineStatus)
 498        {
 0499            if (offlineFriendsList.IsSortingDirty)
 0500                offlineFriendsList.Sort();
 0501            if (onlineFriendsList.IsSortingDirty)
 0502                onlineFriendsList.Sort();
 0503        }
 34504        else if (allFriendsList.IsSortingDirty)
 2505            allFriendsList.Sort();
 506
 34507        if (searchResultsFriendList.IsSortingDirty)
 0508            searchResultsFriendList.Sort();
 34509    }
 510
 1511    private void RequestMoreFriendEntries() => OnRequireMoreFriends?.Invoke();
 512
 513    [Serializable]
 514    private struct FriendListComponents
 515    {
 516        public CollapsableSortedFriendEntryList list;
 517        public TMP_Text countText;
 518        public GameObject headerContainer;
 519
 0520        public bool IsSortingDirty { get; private set; }
 521
 522        public void Show()
 523        {
 1524            list.Show();
 1525            headerContainer.SetActive(true);
 1526        }
 527
 528        public void Hide()
 529        {
 2530            list.Hide();
 2531            headerContainer.SetActive(false);
 2532        }
 533
 0534        public void FlagAsPendingToSort() => IsSortingDirty = true;
 535
 536        public void Sort()
 537        {
 2538            list.Sort();
 2539            IsSortingDirty = false;
 2540        }
 541    }
 542
 543    [Serializable]
 544    private class Model
 545    {
 546        public bool isOnlineFriendsExpanded;
 547        public bool isOfflineFriendsExpanded;
 548        public bool listByOnlineStatus;
 549    }
 550}