< 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:143
Uncovered lines:80
Coverable lines:223
Total lines:472
Line coverage:64.1% (143 of 223)
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%
Show()0%110100%
Hide()0%110100%
Update()0%220100%
Clear()0%2100%
Remove(...)0%440100%
Get(...)0%220100%
Populate(...)0%13.127050%
Set(...)0%3.143075%
RefreshControl()0%3.333066.67%
ClearFilter()0%30500%
Filter(...)0%12300%
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%110100%
HandleFriendBlockRequest(...)0%6200%
HandleUnfriendRequest(...)0%12300%
UpdateLayout()0%2100%
SortDirtyLists()0%4.374071.43%
RequestMoreFriendEntries()0%220100%
Show()0%110100%
Hide()0%2100%
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 event Action<FriendEntryModel> OnWhisper;
 60    public event Action<string> OnDeleteConfirmation;
 61    public event Action OnRequireMoreFriends;
 62
 63    public void Initialize(IChatController chatController,
 64        ILastReadMessagesService lastReadMessagesService,
 65        IFriendsController friendsController,
 66        ISocialAnalytics socialAnalytics)
 67    {
 2368        this.chatController = chatController;
 2369        this.lastReadMessagesService = lastReadMessagesService;
 2370        this.friendsController = friendsController;
 2371        this.socialAnalytics = socialAnalytics;
 2372    }
 73
 74    public override void OnEnable()
 75    {
 2476        base.OnEnable();
 77
 2478        searchBar.Configure(new SearchBarComponentModel {placeHolderText = "Search friend"});
 2479        searchBar.OnSearchText += HandleSearchInputChanged;
 2480        contextMenuPanel.OnBlock += HandleFriendBlockRequest;
 2481        contextMenuPanel.OnUnfriend += HandleUnfriendRequest;
 2482        loadMoreEntriesButton.onClick.AddListener(RequestMoreFriendEntries);
 83
 84        int SortByAlphabeticalOrder(FriendEntryBase u1, FriendEntryBase u2)
 85        {
 086            return string.Compare(u1.Model.userName, u2.Model.userName, StringComparison.InvariantCultureIgnoreCase);
 87        }
 88
 2489        onlineFriendsList.list.SortingMethod = SortByAlphabeticalOrder;
 2490        offlineFriendsList.list.SortingMethod = SortByAlphabeticalOrder;
 2491        searchResultsFriendList.list.SortingMethod = SortByAlphabeticalOrder;
 2492        UpdateLayout();
 2493        UpdateEmptyOrFilledState();
 94
 95        //TODO temporary, remove this and allFriendsList gameobjects later
 2496        allFriendsList.list.gameObject.SetActive(false);
 2497        allFriendsList.headerContainer.gameObject.SetActive(false);
 2498    }
 99
 100    public override void OnDisable()
 101    {
 24102        base.OnDisable();
 103
 24104        searchBar.OnSearchText -= HandleSearchInputChanged;
 24105        contextMenuPanel.OnBlock -= HandleFriendBlockRequest;
 24106        contextMenuPanel.OnUnfriend -= HandleUnfriendRequest;
 24107        loadMoreEntriesButton.onClick.RemoveListener(RequestMoreFriendEntries);
 24108    }
 109
 110    public void Show()
 111    {
 8112        gameObject.SetActive(true);
 8113        enabledHeader.SetActive(true);
 8114        disabledHeader.SetActive(false);
 8115        searchBar.ClearSearch();
 8116    }
 117
 118    public void Hide()
 119    {
 7120        gameObject.SetActive(false);
 7121        enabledHeader.SetActive(false);
 7122        disabledHeader.SetActive(true);
 7123        contextMenuPanel.Hide();
 7124    }
 125
 126    public override void Update()
 127    {
 34128        base.Update();
 129
 34130        if (isLayoutDirty)
 16131            Utils.ForceRebuildLayoutImmediate((RectTransform) filledStateContainer.transform);
 34132        isLayoutDirty = false;
 133
 34134        SortDirtyLists();
 34135        FetchProfilePicturesForVisibleEntries();
 34136        SetQueuedEntries();
 34137    }
 138
 139    public void Clear()
 140    {
 0141        entries.ToList().ForEach(pair => Remove(pair.Key));
 142
 0143        onlineFriendsList.list.Clear();
 0144        offlineFriendsList.list.Clear();
 145
 0146        searchResultsFriendList.list.Clear();
 0147        UpdateEmptyOrFilledState();
 0148        UpdateCounterLabel();
 0149    }
 150
 151    public void Remove(string userId)
 152    {
 10153        if (creationQueue.ContainsKey(userId))
 1154            creationQueue.Remove(userId);
 155
 18156        if (!entries.ContainsKey(userId)) return;
 157
 2158        if (pooleableEntries.TryGetValue(userId, out var pooleableObject))
 159        {
 2160            entryPool.Release(pooleableObject);
 2161            pooleableEntries.Remove(userId);
 162        }
 163
 2164        entries.Remove(userId);
 165
 2166        offlineFriendsList.list.Remove(userId);
 2167        onlineFriendsList.list.Remove(userId);
 168
 2169        searchResultsFriendList.list.Remove(userId);
 170
 2171        UpdateEmptyOrFilledState();
 2172        UpdateCounterLabel();
 2173        UpdateLayout();
 2174    }
 175
 42176    public FriendEntry Get(string userId) => entries.ContainsKey(userId) ? entries[userId] : null;
 177
 178    public void Populate(string userId, FriendEntryModel model)
 179    {
 5180        if (!entries.ContainsKey(userId))
 181        {
 0182            if (creationQueue.ContainsKey(userId))
 0183                creationQueue[userId] = model;
 0184            return;
 185        }
 186
 5187        var entry = entries[userId];
 5188        entry.Populate(model);
 189
 5190        if (filteredEntries?.ContainsKey(userId) ?? false)
 191        {
 0192            offlineFriendsList.list.Remove(userId);
 0193            onlineFriendsList.list.Remove(userId);
 0194            searchResultsFriendList.list.Add(userId, entry);
 0195            searchResultsFriendList.FlagAsPendingToSort();
 0196        }
 197        else
 198        {
 5199            if (model.status == PresenceStatus.ONLINE)
 200            {
 0201                offlineFriendsList.list.Remove(userId);
 0202                onlineFriendsList.list.Add(userId, entry);
 0203                onlineFriendsList.FlagAsPendingToSort();
 0204            }
 205            else
 206            {
 5207                onlineFriendsList.list.Remove(userId);
 5208                offlineFriendsList.list.Add(userId, entry);
 5209                offlineFriendsList.FlagAsPendingToSort();
 210            }
 211        }
 212
 5213        UpdateLayout();
 5214        UpdateEmptyOrFilledState();
 5215        UpdateCounterLabel();
 5216    }
 217
 218    public void Set(string userId, FriendEntryModel model)
 219    {
 5220        if (creationQueue.ContainsKey(userId))
 221        {
 0222            creationQueue[userId] = model;
 0223            return;
 224        }
 225
 5226        if (!entries.ContainsKey(userId))
 227        {
 5228            CreateEntry(userId);
 5229            UpdateLayout();
 230        }
 231
 5232        Populate(userId, model);
 5233    }
 234
 235    public override void RefreshControl()
 236    {
 1237        onlineFriendsList.Show();
 1238        offlineFriendsList.Show();
 239
 1240        if (model.isOnlineFriendsExpanded)
 1241            onlineFriendsList.list.Expand();
 242        else
 0243            onlineFriendsList.list.Collapse();
 244
 1245        if (model.isOfflineFriendsExpanded)
 1246            offlineFriendsList.list.Expand();
 247        else
 0248            offlineFriendsList.list.Collapse();
 0249    }
 250
 251    public void ClearFilter()
 252    {
 0253        filteredEntries = null;
 254
 0255        if (searchResultsFriendList.list.gameObject.activeSelf)
 256        {
 0257            foreach (var pair in entries)
 258            {
 0259                searchResultsFriendList.list.Remove(pair.Key);
 0260                Populate(pair.Key, pair.Value.Model);
 261            }
 262        }
 263
 0264        searchResultsFriendList.Hide();
 265
 0266        offlineFriendsList.Show();
 0267        onlineFriendsList.Show();
 0268        offlineFriendsList.Sort();
 0269        onlineFriendsList.Sort();
 0270        offlineFriendsList.list.Filter(entry => true);
 0271        onlineFriendsList.list.Filter(entry => true);
 0272    }
 273
 274    public void Filter(Dictionary<string, FriendEntryModel> search)
 275    {
 0276        filteredEntries = search;
 277
 0278        offlineFriendsList.Hide();
 0279        onlineFriendsList.Hide();
 280
 0281        if (!searchResultsFriendList.list.gameObject.activeSelf)
 282        {
 0283            foreach (var pair in entries)
 284            {
 0285                searchResultsFriendList.list.Add(pair.Key, pair.Value);
 286
 0287                offlineFriendsList.list.Remove(pair.Key);
 0288                onlineFriendsList.list.Remove(pair.Key);
 289            }
 290        }
 291
 0292        searchResultsFriendList.Show();
 0293        searchResultsFriendList.Sort();
 0294        searchResultsFriendList.list.Filter(entry => search.ContainsKey(entry.Model.userId));
 295
 0296        UpdateCounterLabel();
 0297        HideMoreFriendsToLoadHint();
 0298        UpdateLayout();
 0299    }
 300
 6301    public void Enqueue(string userId, FriendEntryModel model) => creationQueue[userId] = model;
 302
 303    public void ShowMoreFriendsToLoadHint(int pendingFriendsCount)
 304    {
 1305        loadMoreEntriesLabel.SetText(
 306            $"{pendingFriendsCount} friends hidden. Use the search bar to find them or click below to show more.");
 1307        ShowMoreFriendsToLoadHint();
 1308    }
 309
 310    public void HideMoreFriendsToLoadHint()
 311    {
 1312        loadMoreEntriesContainer.SetActive(false);
 1313        UpdateLayout();
 1314    }
 315
 316    private void ShowMoreFriendsToLoadHint()
 317    {
 1318        loadMoreEntriesContainer.SetActive(true);
 1319        UpdateLayout();
 1320    }
 321
 8322    private void HandleSearchInputChanged(string search) => OnSearchRequested?.Invoke(search);
 323
 324    private void SetQueuedEntries()
 325    {
 63326        if (creationQueue.Count == 0) return;
 327
 20328        for (var i = 0; i < CREATION_AMOUNT_PER_FRAME && creationQueue.Count != 0; i++)
 329        {
 5330            var pair = creationQueue.FirstOrDefault();
 5331            creationQueue.Remove(pair.Key);
 5332            Set(pair.Key, pair.Value);
 333        }
 5334    }
 335
 336    private void FetchProfilePicturesForVisibleEntries()
 337    {
 68338        foreach (var entry in entries.Values.Skip(currentAvatarSnapshotIndex).Take(AVATAR_SNAPSHOTS_PER_FRAME))
 339        {
 0340            if (entry.IsVisible(viewport))
 0341                entry.EnableAvatarSnapshotFetching();
 342            else
 0343                entry.DisableAvatarSnapshotFetching();
 344        }
 345
 34346        currentAvatarSnapshotIndex += AVATAR_SNAPSHOTS_PER_FRAME;
 347
 34348        if (currentAvatarSnapshotIndex >= entries.Count)
 34349            currentAvatarSnapshotIndex = 0;
 34350    }
 351
 352    private void UpdateEmptyOrFilledState()
 353    {
 31354        emptyStateContainer.SetActive(entries.Count == 0);
 31355        filledStateContainer.SetActive(entries.Count > 0);
 31356    }
 357
 358    private void CreateEntry(string userId)
 359    {
 5360        entryPool = GetEntryPool();
 5361        var newFriendEntry = entryPool.Get();
 5362        pooleableEntries.Add(userId, newFriendEntry);
 5363        var entry = newFriendEntry.gameObject.GetComponent<FriendEntry>();
 5364        entry.Initialize(chatController, lastReadMessagesService, friendsController, socialAnalytics);
 5365        entries.Add(userId, entry);
 366
 5367        entry.OnMenuToggle -= OnEntryMenuToggle;
 5368        entry.OnMenuToggle += OnEntryMenuToggle;
 5369        entry.OnWhisperClick -= OnEntryWhisperClick;
 5370        entry.OnWhisperClick += OnEntryWhisperClick;
 5371    }
 372
 0373    private void OnEntryWhisperClick(FriendEntry friendEntry) => OnWhisper?.Invoke(friendEntry.Model);
 374
 375    private void OnEntryMenuToggle(FriendEntryBase friendEntry)
 376    {
 0377        contextMenuPanel.Show(friendEntry.Model.userId);
 0378        friendEntry.Dock(contextMenuPanel);
 0379    }
 380
 381    private Pool GetEntryPool()
 382    {
 5383        var entryPool = PoolManager.i.GetPool(FRIEND_ENTRIES_POOL_NAME_PREFIX + name + GetInstanceID());
 5384        if (entryPool != null) return entryPool;
 385
 5386        entryPool = PoolManager.i.AddPool(
 387            FRIEND_ENTRIES_POOL_NAME_PREFIX + name + GetInstanceID(),
 388            Instantiate(entryPrefab).gameObject,
 389            maxPrewarmCount: PRE_INSTANTIATED_ENTRIES,
 390            isPersistent: true);
 5391        entryPool.ForcePrewarm();
 392
 5393        return entryPool;
 394    }
 395
 396    private void UpdateCounterLabel()
 397    {
 7398        onlineFriendsList.countText.SetText("ONLINE ({0})", onlineFriendsList.list.Count());
 7399        offlineFriendsList.countText.SetText("OFFLINE ({0})", offlineFriendsList.list.Count());
 400
 7401        searchResultsFriendList.countText.SetText("Results ({0})", searchResultsFriendList.list.Count());
 7402    }
 403
 404    private void HandleFriendBlockRequest(string userId, bool blockUser)
 405    {
 0406        var friendEntryToBlock = Get(userId);
 0407        if (friendEntryToBlock == null) return;
 408        // instantly refresh ui
 0409        friendEntryToBlock.Model.blocked = blockUser;
 0410        Set(userId, friendEntryToBlock.Model);
 0411    }
 412
 413    private void HandleUnfriendRequest(string userId)
 414    {
 0415        var entry = Get(userId);
 0416        if (entry == null) return;
 0417        Remove(userId);
 0418        OnDeleteConfirmation?.Invoke(userId);
 0419    }
 420
 0421    private void UpdateLayout() => isLayoutDirty = true;
 422
 423    private void SortDirtyLists()
 424    {
 34425        if (offlineFriendsList.IsSortingDirty)
 2426            offlineFriendsList.Sort();
 34427        if (onlineFriendsList.IsSortingDirty)
 0428            onlineFriendsList.Sort();
 429
 34430        if (searchResultsFriendList.IsSortingDirty)
 0431            searchResultsFriendList.Sort();
 34432    }
 433
 1434    private void RequestMoreFriendEntries() => OnRequireMoreFriends?.Invoke();
 435
 436    [Serializable]
 437    private struct FriendListComponents
 438    {
 439        public CollapsableSortedFriendEntryList list;
 440        public TMP_Text countText;
 441        public GameObject headerContainer;
 442
 0443        public bool IsSortingDirty { get; private set; }
 444
 445        public void Show()
 446        {
 2447            list.Show();
 2448            headerContainer.SetActive(true);
 2449        }
 450
 451        public void Hide()
 452        {
 0453            list.Hide();
 0454            headerContainer.SetActive(false);
 0455        }
 456
 0457        public void FlagAsPendingToSort() => IsSortingDirty = true;
 458
 459        public void Sort()
 460        {
 2461            list.Sort();
 2462            IsSortingDirty = false;
 2463        }
 464    }
 465
 466    [Serializable]
 467    private class Model
 468    {
 469        public bool isOnlineFriendsExpanded;
 470        public bool isOfflineFriendsExpanded;
 471    }
 472}