< Summary

Class:DCL.Chat.HUD.WorldChatWindowComponentView
Assembly:WorldChatWindowHUD
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/WorldChatWindowHUD/WorldChatWindowComponentView.cs
Covered lines:191
Uncovered lines:31
Coverable lines:222
Total lines:477
Line coverage:86% (191 of 222)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
WorldChatWindowComponentView()0%110100%
Create()0%110100%
Awake()0%220100%
OnEnable()0%110100%
Initialize(...)0%110100%
Update()0%330100%
Show()0%110100%
Hide()0%110100%
SetPrivateChat(...)0%110100%
RemovePrivateChat(...)0%110100%
SetPublicChat(...)0%110100%
RemovePublicChat(...)0%2100%
Configure(...)0%2100%
ShowChannelsLoading()0%110100%
HideChannelsLoading()0%110100%
ShowPrivateChatsLoading()0%110100%
HidePrivateChatsLoading()0%110100%
RefreshBlockedDirectMessages(...)0%6200%
HideMoreChatsToLoadHint()0%110100%
ShowMoreChatsToLoadHint(...)0%110100%
HideMoreChatsLoading()0%110100%
ShowMoreChatsLoading()0%110100%
HideSearchLoading()0%110100%
ShowSearchLoading()0%110100%
DisableSearchMode()0%220100%
EnableSearchMode()0%110100%
ContainsPrivateChannel(...)0%6200%
SetCreateChannelButtonActive(...)0%2100%
SetSearchAndCreateContainerActive(...)0%110100%
ShowConnectWallet()0%110100%
HideConnectWallet()0%110100%
RefreshControl()0%12300%
Set(...)0%550100%
Set(...)0%220100%
OpenChannelOptions(...)0%110100%
SortLists()0%2100%
SetChannelsLoadingVisibility(...)0%110100%
SetPrivateChatLoadingVisibility(...)0%330100%
UpdateHeaders()0%110100%
UpdateLayout()0%2100%
SetQueuedEntries()0%660100%
FetchProfilePicturesForVisibleEntries()0%6.026091.67%
RequestMorePrivateChats(...)0%8.068090%
WaitThenRequireMoreEntries()0%440100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/WorldChatWindowHUD/WorldChatWindowComponentView.cs

#LineLine coverage
 1using System;
 2using System.Collections;
 3using System.Collections.Generic;
 4using System.Linq;
 5using DCL.Helpers;
 6using TMPro;
 7using UIComponents.CollapsableSortedList;
 8using UnityEngine;
 9using UnityEngine.UI;
 10
 11namespace DCL.Chat.HUD
 12{
 13    public class WorldChatWindowComponentView : BaseComponentView, IWorldChatWindowView, IComponentModelConfig<WorldChat
 14    {
 15        private const int CREATION_AMOUNT_PER_FRAME = 5;
 16        private const int AVATAR_SNAPSHOTS_PER_FRAME = 5;
 17        private const string NEARBY_CHANNEL = "nearby";
 18        private const float REQUEST_MORE_ENTRIES_SCROLL_THRESHOLD = 0.005f;
 19        private const float MIN_TIME_TO_REQUIRE_MORE_ENTRIES = 0.5f;
 20
 21        [SerializeField] internal CollapsablePublicChannelListComponentView publicChannelList;
 22        [SerializeField] internal CollapsableDirectChatListComponentView directChatList;
 23        [SerializeField] internal CollapsableChatSearchListComponentView searchResultsList;
 24        [SerializeField] internal GameObject searchLoading;
 25        [SerializeField] internal Button closeButton;
 26        [SerializeField] internal GameObject channelsLoadingContainer;
 27        [SerializeField] internal GameObject directChatsLoadingContainer;
 28        [SerializeField] internal GameObject emptyDirectChatsContainer;
 29        [SerializeField] internal GameObject channelsHeader;
 30        [SerializeField] internal GameObject directChannelHeader;
 31        [SerializeField] internal GameObject searchResultsHeader;
 32        [SerializeField] internal TMP_Text channelsHeaderLabel;
 33        [SerializeField] internal TMP_Text directChatsHeaderLabel;
 34        [SerializeField] internal TMP_Text searchResultsHeaderLabel;
 35        [SerializeField] internal ScrollRect scroll;
 36        [SerializeField] internal SearchBarComponentView searchBar;
 37        [SerializeField] internal GameObject searchAndCreateContainer;
 38        [SerializeField] internal Button openChannelSearchButton;
 39        [SerializeField] internal ChannelContextualMenu channelContextualMenu;
 40        [SerializeField] internal Button createChannelButton;
 41        [SerializeField] internal GameObject searchBarContainer;
 42        [SerializeField] internal CollapsableListToggleButton directChatsCollapseButton;
 43        [SerializeField] internal CollapsableListToggleButton publicChatsChatsCollapseButton;
 44        [SerializeField] private WorldChatWindowModel model;
 45
 46        [Header("Load More Entries")]
 47        [SerializeField] internal GameObject loadMoreEntriesContainer;
 48        [SerializeField] internal TMP_Text loadMoreEntriesLabel;
 49        [SerializeField] internal GameObject loadMoreEntriesLoading;
 50
 51        [Header("Guest")]
 52        [SerializeField] internal GameObject connectWalletContainer;
 53        [SerializeField] internal GameObject walletConnectedContainer;
 54        [SerializeField] internal Button connectWalletButton;
 55        [SerializeField] internal Button whatIsWalletButton;
 56
 4257        private readonly Dictionary<string, PrivateChatModel> privateChatsCreationQueue =
 58            new Dictionary<string, PrivateChatModel>();
 59
 4260        private readonly Dictionary<string, PublicChatModel> publicChatsCreationQueue =
 61            new Dictionary<string, PublicChatModel>();
 62
 63        private bool isSortingDirty;
 64        private bool isLayoutDirty;
 65        private int currentAvatarSnapshotIndex;
 66        private bool isLoadingPrivateChannels;
 67        private bool isSearchMode;
 68        private string optionsChannelId;
 69        private Vector2 lastScrollPosition;
 70        private float loadMoreEntriesRestrictionTime;
 71        private Coroutine requireMoreEntriesRoutine;
 72        private bool isConnectWalletMode;
 73
 74        public event Action OnClose;
 75        public event Action<string> OnOpenPrivateChat;
 76        public event Action<string> OnOpenPublicChat;
 77        public event Action<string> OnSearchChatRequested;
 78        public event Action OnRequireMorePrivateChats;
 79        public event Action OnOpenChannelSearch;
 80        public event Action<string> OnLeaveChannel;
 81        public event Action OnCreateChannel;
 82        public event Action OnSignUp;
 83        public event Action OnRequireWalletReadme;
 84
 085        public RectTransform Transform => (RectTransform) transform;
 086        public bool IsActive => gameObject.activeInHierarchy;
 87
 88        public static WorldChatWindowComponentView Create()
 89        {
 3490            return Instantiate(Resources.Load<WorldChatWindowComponentView>("SocialBarV1/ConversationListHUD"));
 91        }
 92
 93        public override void Awake()
 94        {
 4095            base.Awake();
 96
 97            int SortByAlphabeticalOrder(PublicChatEntry u1, PublicChatEntry u2)
 98            {
 199                if (u1.Model.name == NEARBY_CHANNEL)
 1100                    return -1;
 0101                if (u2.Model.name == NEARBY_CHANNEL)
 0102                    return 1;
 103
 0104                return string.Compare(u1.Model.name, u2.Model.name, StringComparison.InvariantCultureIgnoreCase);
 105            }
 106
 40107            openChannelSearchButton.onClick.AddListener(() => OnOpenChannelSearch?.Invoke());
 41108            closeButton.onClick.AddListener(() => OnClose?.Invoke());
 86109            directChatList.SortingMethod = (a, b) => b.Model.lastMessageTimestamp.CompareTo(a.Model.lastMessageTimestamp
 41110            directChatList.OnOpenChat += entry => OnOpenPrivateChat?.Invoke(entry.Model.userId);
 40111            searchResultsList.OnOpenPrivateChat += entry => OnOpenPrivateChat?.Invoke(entry.Model.userId);
 40112            searchResultsList.OnOpenPublicChat += entry => OnOpenPublicChat?.Invoke(entry.Model.channelId);
 40113            publicChannelList.SortingMethod = SortByAlphabeticalOrder;
 41114            publicChannelList.OnOpenChat += entry => OnOpenPublicChat?.Invoke(entry.Model.channelId);
 41115            searchBar.OnSearchText += text => OnSearchChatRequested?.Invoke(text);
 40116            scroll.onValueChanged.AddListener(RequestMorePrivateChats);
 41117            channelContextualMenu.OnLeave += () => OnLeaveChannel?.Invoke(optionsChannelId);
 40118            createChannelButton.onClick.AddListener(() => OnCreateChannel?.Invoke());
 41119            connectWalletButton.onClick.AddListener(() => OnSignUp?.Invoke());
 41120            whatIsWalletButton.onClick.AddListener(() => OnRequireWalletReadme?.Invoke());
 40121            UpdateHeaders();
 40122        }
 123
 124        public override void OnEnable()
 125        {
 41126            base.OnEnable();
 41127            UpdateLayout();
 41128        }
 129
 130        public void Initialize(IChatController chatController)
 131        {
 34132            directChatList.Initialize(chatController);
 34133            publicChannelList.Initialize(chatController);
 34134            searchResultsList.Initialize(chatController);
 34135        }
 136
 137        public override void Update()
 138        {
 112139            base.Update();
 140
 112141            SetQueuedEntries();
 142
 112143            if (isSortingDirty)
 144            {
 26145                directChatList.Sort();
 26146                searchResultsList.Sort();
 26147                publicChannelList.Sort();
 148            }
 149
 112150            isSortingDirty = false;
 151
 112152            if (isLayoutDirty)
 41153                ((RectTransform) scroll.transform).ForceUpdateLayout();
 112154            isLayoutDirty = false;
 155
 112156            FetchProfilePicturesForVisibleEntries();
 112157        }
 158
 2159        public void Show() => gameObject.SetActive(true);
 160
 2161        public void Hide() => gameObject.SetActive(false);
 162
 38163        public void SetPrivateChat(PrivateChatModel model) => privateChatsCreationQueue[model.user.userId] = model;
 164
 165        public void RemovePrivateChat(string userId)
 166        {
 1167            directChatList.Remove(userId);
 1168            searchResultsList.Remove(userId);
 1169            UpdateHeaders();
 1170            UpdateLayout();
 1171        }
 172
 21173        public void SetPublicChat(PublicChatModel model) => publicChatsCreationQueue[model.channelId] = model;
 174
 175        public void RemovePublicChat(string channelId)
 176        {
 0177            publicChatsCreationQueue.Remove(channelId);
 0178            publicChannelList.Remove(channelId);
 0179            UpdateHeaders();
 0180            UpdateLayout();
 0181        }
 182
 183        public void Configure(WorldChatWindowModel newModel)
 184        {
 0185            model = newModel;
 0186            RefreshControl();
 0187        }
 188
 1189        public void ShowChannelsLoading() => SetChannelsLoadingVisibility(true);
 190
 2191        public void HideChannelsLoading() => SetChannelsLoadingVisibility(false);
 192
 2193        public void ShowPrivateChatsLoading() => SetPrivateChatLoadingVisibility(true);
 194
 1195        public void HidePrivateChatsLoading() => SetPrivateChatLoadingVisibility(false);
 196
 197        public void RefreshBlockedDirectMessages(List<string> blockedUsers)
 198        {
 0199            if (blockedUsers == null) return;
 0200            directChatList.RefreshBlockedEntries(blockedUsers);
 0201        }
 202
 203        public void HideMoreChatsToLoadHint()
 204        {
 1205            loadMoreEntriesContainer.SetActive(false);
 1206            emptyDirectChatsContainer.SetActive(directChatList.Count() == 0);
 1207            UpdateLayout();
 1208        }
 209
 210        public void ShowMoreChatsToLoadHint(int count)
 211        {
 2212            loadMoreEntriesLabel.SetText(
 213                $"{count} chats hidden. Use the search bar to find them or click below to show more.");
 2214            loadMoreEntriesContainer.SetActive(true);
 2215            emptyDirectChatsContainer.SetActive(false);
 2216            UpdateLayout();
 2217        }
 218
 219        public void HideMoreChatsLoading()
 220        {
 114221            loadMoreEntriesLoading.SetActive(false);
 114222        }
 223
 224        public void ShowMoreChatsLoading()
 225        {
 2226            loadMoreEntriesLoading.SetActive(true);
 2227        }
 228
 229        public void HideSearchLoading()
 230        {
 1231            searchLoading.SetActive(false);
 1232        }
 233
 234        public void ShowSearchLoading()
 235        {
 1236            searchLoading.SetActive(true);
 1237            searchLoading.transform.SetAsLastSibling();
 1238        }
 239
 240    public void DisableSearchMode()
 241    {
 4242        isSearchMode = false;
 4243        searchResultsList.Hide();
 4244        publicChannelList.Show();
 4245        publicChannelList.Sort();
 246
 4247        if (!isLoadingPrivateChannels)
 248        {
 3249            directChatList.Show();
 3250            directChatList.Sort();
 3251            channelsHeader.SetActive(true);
 3252            directChannelHeader.SetActive(true);
 253        }
 254
 4255        searchResultsHeader.SetActive(false);
 4256        searchResultsList.Clear();
 4257        searchBar.ClearSearch(false);
 258
 4259        UpdateHeaders();
 4260    }
 261
 262    public void EnableSearchMode()
 263    {
 6264        isSearchMode = true;
 265
 6266        searchResultsList.Clear();
 6267        searchResultsList.Show();
 6268        searchResultsList.Sort();
 6269        publicChannelList.Hide();
 6270        directChatList.Hide();
 6271        channelsHeader.SetActive(false);
 6272        directChannelHeader.SetActive(false);
 6273        searchResultsHeader.SetActive(true);
 274
 6275        UpdateHeaders();
 6276        searchLoading.transform.SetAsLastSibling();
 6277    }
 278
 0279        public bool ContainsPrivateChannel(string userId) => privateChatsCreationQueue.ContainsKey(userId)
 280                                                             || directChatList.Get(userId) != null;
 281
 0282        public void SetCreateChannelButtonActive(bool isActive) => createChannelButton.gameObject.SetActive(isActive);
 283
 1284        public void SetSearchAndCreateContainerActive(bool isActive) => searchAndCreateContainer.SetActive(isActive);
 285
 286        public void ShowConnectWallet()
 287        {
 1288            isConnectWalletMode = true;
 1289            connectWalletContainer.SetActive(true);
 1290            walletConnectedContainer.SetActive(false);
 1291            searchBarContainer.SetActive(false);
 1292            directChatsCollapseButton.SetInteractability(false);
 1293            publicChatsChatsCollapseButton.SetInteractability(false);
 1294        }
 295
 296        public void HideConnectWallet()
 297        {
 2298            isConnectWalletMode = false;
 2299            connectWalletContainer.SetActive(false);
 2300            walletConnectedContainer.SetActive(true);
 2301            searchBarContainer.SetActive(true);
 2302            directChatsCollapseButton.SetInteractability(true);
 2303            publicChatsChatsCollapseButton.SetInteractability(true);
 2304        }
 305
 306        public override void RefreshControl()
 307        {
 0308            publicChannelList.Clear();
 0309            foreach (var entry in model.publicChannels)
 0310                publicChannelList.Set(entry.channelId, entry);
 0311            SetChannelsLoadingVisibility(model.isLoadingChannels);
 312
 0313            directChatList.Clear();
 0314            foreach (var entry in model.privateChats)
 0315                directChatList.Set(entry.userId, entry);
 0316            SetPrivateChatLoadingVisibility(model.isLoadingDirectChats);
 0317        }
 318
 319        private void Set(PrivateChatModel model)
 320        {
 37321            var user = model.user;
 37322            var userId = user.userId;
 323
 37324            var entry = new PrivateChatEntryModel(
 325                user.userId,
 326                user.userName,
 327                model.recentMessage != null ? model.recentMessage.body : string.Empty,
 328                user.face256SnapshotURL,
 329                model.isBlocked,
 330                model.isOnline,
 331                model.recentMessage != null ? model.recentMessage.timestamp : 0);
 332
 37333        if (isSearchMode)
 18334            searchResultsList.Set(entry);
 335        else
 19336            directChatList.Set(userId, entry);
 337
 37338            UpdateHeaders();
 37339            UpdateLayout();
 37340            SortLists();
 37341        }
 342
 343        private void Set(PublicChatModel model)
 344        {
 21345            var channelId = model.channelId;
 21346            var entryModel = new PublicChatEntryModel(channelId, model.name, model.joined, model.memberCount, showOnlyOn
 347            PublicChatEntry entry;
 348
 21349            if (isSearchMode)
 350            {
 8351                searchResultsList.Set(entryModel);
 8352                entry = (PublicChatEntry) searchResultsList.Get(channelId);
 8353            }
 354            else
 355            {
 13356                publicChannelList.Set(channelId, entryModel);
 13357                entry = publicChannelList.Get(channelId);
 358            }
 359
 21360            entry.OnOpenOptions -= OpenChannelOptions;
 21361            entry.OnOpenOptions += OpenChannelOptions;
 362
 21363            UpdateHeaders();
 21364            UpdateLayout();
 21365            SortLists();
 21366        }
 367
 368        private void OpenChannelOptions(PublicChatEntry entry)
 369        {
 1370            optionsChannelId = entry.Model.channelId;
 1371            entry.Dock(channelContextualMenu);
 1372            channelContextualMenu.SetHeaderTitle($"#{entry.Model.name.ToLower()}");
 1373            channelContextualMenu.Show();
 1374        }
 375
 0376        private void SortLists() => isSortingDirty = true;
 377
 378        private void SetChannelsLoadingVisibility(bool visible)
 379        {
 3380            model.isLoadingChannels = visible;
 3381            channelsLoadingContainer.SetActive(visible);
 3382            publicChannelList.gameObject.SetActive(!visible);
 3383        }
 384
 385        private void SetPrivateChatLoadingVisibility(bool visible)
 386        {
 3387            model.isLoadingDirectChats = visible;
 3388            directChatsLoadingContainer.SetActive(visible);
 389
 3390            if (visible)
 2391                directChatList.Hide();
 1392            else if (!isSearchMode)
 1393                directChatList.Show();
 394
 3395            scroll.enabled = !visible;
 3396            isLoadingPrivateChannels = visible;
 3397        }
 398
 399        private void UpdateHeaders()
 400        {
 109401            directChatsHeaderLabel.text = $"Direct Messages ({directChatList.Count()})";
 109402            searchResultsHeaderLabel.text = $"Results ({searchResultsList.Count()})";
 109403            searchResultsHeader.SetActive(searchResultsList.Count() > 0);
 109404            channelsHeaderLabel.text = $"Channels ({publicChannelList.Count()})";
 109405        }
 406
 0407        private void UpdateLayout() => isLayoutDirty = true;
 408
 409        private void SetQueuedEntries()
 410        {
 112411            if (privateChatsCreationQueue.Count > 0)
 412            {
 110413                for (var i = 0; i < CREATION_AMOUNT_PER_FRAME && privateChatsCreationQueue.Count > 0; i++)
 414                {
 37415                    var (userId, model) = privateChatsCreationQueue.First();
 37416                    privateChatsCreationQueue.Remove(userId);
 37417                    Set(model);
 418                }
 419            }
 420
 266421            for (var i = 0; i < CREATION_AMOUNT_PER_FRAME && publicChatsCreationQueue.Count > 0; i++)
 422            {
 21423                var (userId, model) = publicChatsCreationQueue.First();
 21424                publicChatsCreationQueue.Remove(userId);
 21425                Set(model);
 426            }
 427
 112428            HideMoreChatsLoading();
 112429        }
 430
 431        private void FetchProfilePicturesForVisibleEntries()
 432        {
 120433            if (isSearchMode) return;
 434
 246435            foreach (var entry in directChatList.Entries.Values.Skip(currentAvatarSnapshotIndex)
 436                         .Take(AVATAR_SNAPSHOTS_PER_FRAME))
 437            {
 19438                if (entry.IsVisible((RectTransform) scroll.transform))
 19439                    entry.EnableAvatarSnapshotFetching();
 440                else
 0441                    entry.DisableAvatarSnapshotFetching();
 442            }
 443
 104444            currentAvatarSnapshotIndex += AVATAR_SNAPSHOTS_PER_FRAME;
 445
 104446            if (currentAvatarSnapshotIndex >= directChatList.Entries.Count)
 104447                currentAvatarSnapshotIndex = 0;
 104448        }
 449
 450        private void RequestMorePrivateChats(Vector2 position)
 451        {
 24452            if (!loadMoreEntriesContainer.activeInHierarchy
 453                || loadMoreEntriesLoading.activeInHierarchy
 454                || Time.realtimeSinceStartup - loadMoreEntriesRestrictionTime < MIN_TIME_TO_REQUIRE_MORE_ENTRIES
 22455                || isConnectWalletMode) return;
 456
 2457            if (position.y < REQUEST_MORE_ENTRIES_SCROLL_THRESHOLD && lastScrollPosition.y >= REQUEST_MORE_ENTRIES_SCROL
 458            {
 1459                if (requireMoreEntriesRoutine != null)
 0460                    StopCoroutine(requireMoreEntriesRoutine);
 461
 1462                ShowMoreChatsLoading();
 1463                requireMoreEntriesRoutine = StartCoroutine(WaitThenRequireMoreEntries());
 464
 1465                loadMoreEntriesRestrictionTime = Time.realtimeSinceStartup;
 466            }
 467
 2468            lastScrollPosition = position;
 2469        }
 470
 471        private IEnumerator WaitThenRequireMoreEntries()
 472        {
 1473            yield return new WaitForSeconds(1f);
 1474            OnRequireMorePrivateChats?.Invoke();
 1475        }
 476    }
 477}