< Summary

Class:DCL.Social.Chat.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:45
Coverable lines:236
Total lines:509
Line coverage:80.9% (191 of 236)
Covered branches:0
Total branches:0
Covered methods:38
Total methods:47
Method coverage:80.8% (38 of 47)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
WorldChatWindowComponentView()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%30500%
RefreshPrivateChatPresence(...)0%12300%
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%
SetChannelsPromoteLabelVisible(...)0%110100%
RefreshControl()0%12300%
Set(...)0%550100%
Set(...)0%220100%
OpenChannelOptions(...)0%110100%
SortLists()0%110100%
SetChannelsLoadingVisibility(...)0%110100%
SetPrivateChatLoadingVisibility(...)0%330100%
UpdateHeaders()0%110100%
UpdateLayout()0%110100%
SetQueuedEntries()0%660100%
FetchProfilePicturesForVisibleEntries()0%6.026092.31%
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 DCL.Helpers;
 2using System;
 3using System.Collections;
 4using System.Collections.Generic;
 5using System.Linq;
 6using TMPro;
 7using UIComponents.CollapsableSortedList;
 8using UnityEngine;
 9using UnityEngine.Pool;
 10using UnityEngine.UI;
 11
 12namespace DCL.Social.Chat
 13{
 14    public class WorldChatWindowComponentView : BaseComponentView, IWorldChatWindowView, IComponentModelConfig<WorldChat
 15    {
 16        private const int CREATION_AMOUNT_PER_FRAME = 5;
 17        private const int AVATAR_SNAPSHOTS_PER_FRAME = 5;
 18        private const string NEARBY_CHANNEL = "nearby";
 19        private const float REQUEST_MORE_ENTRIES_SCROLL_THRESHOLD = 0.005f;
 20        private const float MIN_TIME_TO_REQUIRE_MORE_ENTRIES = 0.5f;
 21
 22        [SerializeField] internal CollapsablePublicChannelListComponentView publicChannelList;
 23        [SerializeField] internal CollapsableDirectChatListComponentView directChatList;
 24        [SerializeField] internal CollapsableChatSearchListComponentView searchResultsList;
 25        [SerializeField] internal GameObject searchLoading;
 26        [SerializeField] internal Button closeButton;
 27        [SerializeField] internal GameObject channelsLoadingContainer;
 28        [SerializeField] internal GameObject directChatsLoadingContainer;
 29        [SerializeField] internal GameObject emptyDirectChatsContainer;
 30        [SerializeField] internal GameObject channelsHeader;
 31        [SerializeField] internal GameObject directChannelHeader;
 32        [SerializeField] internal GameObject searchResultsHeader;
 33        [SerializeField] internal TMP_Text channelsHeaderLabel;
 34        [SerializeField] internal TMP_Text directChatsHeaderLabel;
 35        [SerializeField] internal TMP_Text searchResultsHeaderLabel;
 36        [SerializeField] internal ScrollRect scroll;
 37        [SerializeField] internal SearchBarComponentView searchBar;
 38        [SerializeField] internal GameObject searchAndCreateContainer;
 39        [SerializeField] internal Button openChannelSearchButton;
 40        [SerializeField] internal ChannelContextualMenu channelContextualMenu;
 41        [SerializeField] internal Button createChannelButton;
 42        [SerializeField] internal GameObject searchBarContainer;
 43        [SerializeField] internal CollapsableListToggleButton directChatsCollapseButton;
 44        [SerializeField] internal CollapsableListToggleButton publicChatsChatsCollapseButton;
 45        [SerializeField] private WorldChatWindowModel model;
 46        [SerializeField] private GameObject channelsPromoteLabel;
 47
 48        [Header("Load More Entries")]
 49        [SerializeField] internal GameObject loadMoreEntriesContainer;
 50        [SerializeField] internal TMP_Text loadMoreEntriesLabel;
 51        [SerializeField] internal GameObject loadMoreEntriesLoading;
 52
 53        [Header("Guest")]
 54        [SerializeField] internal GameObject connectWalletContainer;
 55        [SerializeField] internal Button connectWalletButton;
 56        [SerializeField] internal Button whatIsWalletButton;
 57
 4158        private readonly Dictionary<string, PrivateChatModel> privateChatsCreationQueue =
 59            new Dictionary<string, PrivateChatModel>();
 60
 4161        private readonly Dictionary<string, PublicChatModel> publicChatsCreationQueue =
 62            new Dictionary<string, PublicChatModel>();
 63
 64        private bool isSortingDirty;
 65        private bool isLayoutDirty;
 66        private int currentAvatarSnapshotIndex;
 67        private bool isLoadingPrivateChannels;
 68        private bool isSearchMode;
 69        private string optionsChannelId;
 70        private Vector2 lastScrollPosition;
 71        private float loadMoreEntriesRestrictionTime;
 72        private Coroutine requireMoreEntriesRoutine;
 73        private bool isConnectWalletMode;
 74
 75        public event Action OnClose;
 76        public event Action<string> OnOpenPrivateChat;
 77        public event Action<string> OnOpenPublicChat;
 78        public event Action<string> OnSearchChatRequested;
 79        public event Action OnRequireMorePrivateChats;
 80        public event Action OnOpenChannelSearch;
 81        public event Action<string> OnLeaveChannel;
 82        public event Action OnCreateChannel;
 83        public event Action OnSignUp;
 84        public event Action OnRequireWalletReadme;
 85        public event Action<string> OnCopyChannelNameRequested;
 86
 087        public RectTransform Transform => (RectTransform) transform;
 088        public bool IsActive => gameObject.activeInHierarchy;
 89
 90        public override void Awake()
 91        {
 4092            base.Awake();
 93
 94            int SortByAlphabeticalOrder(PublicChatEntry u1, PublicChatEntry u2)
 95            {
 196                if (u1.Model.name == NEARBY_CHANNEL)
 197                    return -1;
 098                if (u2.Model.name == NEARBY_CHANNEL)
 099                    return 1;
 100
 0101                return string.Compare(u1.Model.name, u2.Model.name, StringComparison.InvariantCultureIgnoreCase);
 102            }
 103
 40104            openChannelSearchButton.onClick.AddListener(() => OnOpenChannelSearch?.Invoke());
 41105            closeButton.onClick.AddListener(() => OnClose?.Invoke());
 86106            directChatList.SortingMethod = (a, b) => b.Model.lastMessageTimestamp.CompareTo(a.Model.lastMessageTimestamp
 41107            directChatList.OnOpenChat += entry => OnOpenPrivateChat?.Invoke(entry.Model.userId);
 40108            searchResultsList.OnOpenPrivateChat += entry => OnOpenPrivateChat?.Invoke(entry.Model.userId);
 40109            searchResultsList.OnOpenPublicChat += entry => OnOpenPublicChat?.Invoke(entry.Model.channelId);
 40110            publicChannelList.SortingMethod = SortByAlphabeticalOrder;
 41111            publicChannelList.OnOpenChat += entry => OnOpenPublicChat?.Invoke(entry.Model.channelId);
 41112            searchBar.OnSearchText += text => OnSearchChatRequested?.Invoke(text);
 40113            scroll.onValueChanged.AddListener(RequestMorePrivateChats);
 41114            channelContextualMenu.OnLeave += () => OnLeaveChannel?.Invoke(optionsChannelId);
 40115            channelContextualMenu.OnNameCopied += channelName => OnCopyChannelNameRequested?.Invoke(channelName);
 40116            createChannelButton.onClick.AddListener(() => OnCreateChannel?.Invoke());
 41117            connectWalletButton.onClick.AddListener(() => OnSignUp?.Invoke());
 41118            whatIsWalletButton.onClick.AddListener(() => OnRequireWalletReadme?.Invoke());
 40119            UpdateHeaders();
 40120        }
 121
 122        public override void OnEnable()
 123        {
 41124            base.OnEnable();
 41125            UpdateLayout();
 41126        }
 127
 128        public void Initialize(
 129            IChatController chatController,
 130            DataStore_Mentions mentionsDataStore)
 131        {
 34132            directChatList.Initialize(chatController, mentionsDataStore);
 34133            publicChannelList.Initialize(chatController, mentionsDataStore);
 34134            searchResultsList.Initialize(chatController, mentionsDataStore);
 34135        }
 136
 137        public void Update()
 138        {
 291139            SetQueuedEntries();
 140
 291141            if (isSortingDirty)
 142            {
 26143                directChatList.Sort();
 26144                searchResultsList.Sort();
 26145                publicChannelList.Sort();
 146            }
 147
 291148            isSortingDirty = false;
 149
 291150            if (isLayoutDirty)
 41151                ((RectTransform) scroll.transform).ForceUpdateLayout();
 291152            isLayoutDirty = false;
 153
 291154            FetchProfilePicturesForVisibleEntries();
 291155        }
 156
 2157        public void Show() => gameObject.SetActive(true);
 158
 2159        public void Hide() => gameObject.SetActive(false);
 160
 38161        public void SetPrivateChat(PrivateChatModel model) => privateChatsCreationQueue[model.userId] = model;
 162
 163        public void RemovePrivateChat(string userId)
 164        {
 1165            privateChatsCreationQueue.Remove(userId);
 1166            directChatList.Remove(userId);
 1167            searchResultsList.Remove(userId);
 1168            UpdateHeaders();
 1169            UpdateLayout();
 1170        }
 171
 21172        public void SetPublicChat(PublicChatModel model) => publicChatsCreationQueue[model.channelId] = model;
 173
 174        public void RemovePublicChat(string channelId)
 175        {
 0176            publicChatsCreationQueue.Remove(channelId);
 0177            publicChannelList.Remove(channelId);
 0178            UpdateHeaders();
 0179            UpdateLayout();
 0180        }
 181
 182        public void Configure(WorldChatWindowModel newModel)
 183        {
 0184            model = newModel;
 0185            RefreshControl();
 0186        }
 187
 1188        public void ShowChannelsLoading() => SetChannelsLoadingVisibility(true);
 189
 2190        public void HideChannelsLoading() => SetChannelsLoadingVisibility(false);
 191
 2192        public void ShowPrivateChatsLoading() => SetPrivateChatLoadingVisibility(true);
 193
 1194        public void HidePrivateChatsLoading() => SetPrivateChatLoadingVisibility(false);
 195
 196        public void RefreshBlockedDirectMessages(List<string> blockedUsers)
 197        {
 0198            if (blockedUsers == null) return;
 199
 0200            var keysToUpdate = ListPool<string>.Get();
 201
 0202            foreach (KeyValuePair<string, PrivateChatModel> privateChatModel in privateChatsCreationQueue)
 203            {
 0204                if (blockedUsers.Contains(privateChatModel.Key) != privateChatModel.Value.isBlocked) { keysToUpdate.Add(
 205            }
 206
 0207            foreach (string key in keysToUpdate)
 208            {
 0209                PrivateChatModel refreshedPrivateChatModel = privateChatsCreationQueue[key];
 0210                refreshedPrivateChatModel.isBlocked = blockedUsers.Contains(key);
 0211                privateChatsCreationQueue[key] = refreshedPrivateChatModel;
 212            }
 213
 0214            ListPool<string>.Release(keysToUpdate);
 215
 0216            directChatList.RefreshBlockedEntries(blockedUsers);
 0217        }
 218
 219        public void RefreshPrivateChatPresence(string userId, bool isOnline)
 220        {
 0221            if (!ContainsPrivateChannel(userId))
 0222                return;
 223
 0224            if (privateChatsCreationQueue.ContainsKey(userId))
 225            {
 0226                PrivateChatModel refreshedPrivateChatModel = privateChatsCreationQueue[userId];
 0227                refreshedPrivateChatModel.isOnline = isOnline;
 0228                privateChatsCreationQueue[userId] = refreshedPrivateChatModel;
 229            }
 230            else
 0231                directChatList.RefreshPresence(userId, isOnline);
 0232        }
 233
 234        public void HideMoreChatsToLoadHint()
 235        {
 1236            loadMoreEntriesContainer.SetActive(false);
 1237            emptyDirectChatsContainer.SetActive(directChatList.Count() == 0);
 1238            UpdateLayout();
 1239        }
 240
 241        public void ShowMoreChatsToLoadHint(int count)
 242        {
 2243            loadMoreEntriesLabel.SetText(
 244                $"{count} chats hidden. Use the search bar to find them or click below to show more.");
 2245            loadMoreEntriesContainer.SetActive(true);
 2246            emptyDirectChatsContainer.SetActive(false);
 2247            UpdateLayout();
 2248        }
 249
 250        public void HideMoreChatsLoading()
 251        {
 293252            loadMoreEntriesLoading.SetActive(false);
 293253        }
 254
 255        public void ShowMoreChatsLoading()
 256        {
 2257            loadMoreEntriesLoading.SetActive(true);
 2258        }
 259
 260        public void HideSearchLoading()
 261        {
 1262            searchLoading.SetActive(false);
 1263        }
 264
 265        public void ShowSearchLoading()
 266        {
 1267            searchLoading.SetActive(true);
 1268            searchLoading.transform.SetAsLastSibling();
 1269        }
 270
 271    public void DisableSearchMode()
 272    {
 4273        isSearchMode = false;
 4274        searchResultsList.Hide();
 4275        publicChannelList.Show();
 4276        publicChannelList.Sort();
 277
 4278        if (!isLoadingPrivateChannels)
 279        {
 3280            directChatList.Show();
 3281            directChatList.Sort();
 3282            channelsHeader.SetActive(true);
 3283            directChannelHeader.SetActive(true);
 284        }
 285
 4286        searchResultsHeader.SetActive(false);
 4287        searchResultsList.Clear();
 4288        searchBar.ClearSearch(false);
 289
 4290        UpdateHeaders();
 4291    }
 292
 293    public void EnableSearchMode()
 294    {
 6295        isSearchMode = true;
 296
 6297        searchResultsList.Clear();
 6298        searchResultsList.Show();
 6299        searchResultsList.Sort();
 6300        publicChannelList.Hide();
 6301        directChatList.Hide();
 6302        channelsHeader.SetActive(false);
 6303        directChannelHeader.SetActive(false);
 6304        searchResultsHeader.SetActive(true);
 305
 6306        UpdateHeaders();
 6307        searchLoading.transform.SetAsLastSibling();
 6308    }
 309
 0310        public bool ContainsPrivateChannel(string userId) => privateChatsCreationQueue.ContainsKey(userId)
 311                                                             || directChatList.Get(userId) != null;
 312
 0313        public void SetCreateChannelButtonActive(bool isActive) => createChannelButton.gameObject.SetActive(isActive);
 314
 1315        public void SetSearchAndCreateContainerActive(bool isActive) => searchAndCreateContainer.SetActive(isActive);
 316
 317        public void ShowConnectWallet()
 318        {
 1319            isConnectWalletMode = true;
 1320            connectWalletContainer.SetActive(true);
 1321            searchBarContainer.SetActive(false);
 1322            directChatsCollapseButton.SetInteractability(false);
 1323            publicChatsChatsCollapseButton.SetInteractability(false);
 1324        }
 325
 326        public void HideConnectWallet()
 327        {
 2328            isConnectWalletMode = false;
 2329            connectWalletContainer.SetActive(false);
 2330            searchBarContainer.SetActive(true);
 2331            directChatsCollapseButton.SetInteractability(true);
 2332            publicChatsChatsCollapseButton.SetInteractability(true);
 2333        }
 334
 1335        public void SetChannelsPromoteLabelVisible(bool isVisible) => channelsPromoteLabel.SetActive(isVisible);
 336
 337        public override void RefreshControl()
 338        {
 0339            publicChannelList.Clear();
 0340            foreach (var entry in model.publicChannels)
 0341                publicChannelList.Set(entry.channelId, entry);
 0342            SetChannelsLoadingVisibility(model.isLoadingChannels);
 343
 0344            directChatList.Clear();
 0345            foreach (var entry in model.privateChats)
 0346                directChatList.Set(entry.userId, entry);
 0347            SetPrivateChatLoadingVisibility(model.isLoadingDirectChats);
 0348        }
 349
 350        private void Set(PrivateChatModel model)
 351        {
 37352            string userId = model.userId;
 353
 37354            var entry = new PrivateChatEntryModel(
 355                userId,
 356                model.userName,
 357                model.recentMessage != null ? model.recentMessage.body : string.Empty,
 358                model.faceSnapshotUrl,
 359                model.isBlocked,
 360                model.isOnline,
 361                model.recentMessage?.timestamp ?? 0);
 362
 37363            if (isSearchMode)
 18364                searchResultsList.Set(entry);
 365            else
 19366                directChatList.Set(userId, entry);
 367
 37368            UpdateHeaders();
 37369            UpdateLayout();
 37370            SortLists();
 37371        }
 372
 373        private void Set(PublicChatModel model)
 374        {
 21375            var channelId = model.channelId;
 21376            var entryModel = new PublicChatEntryModel(channelId, model.name, model.joined, model.memberCount, showOnlyOn
 377            PublicChatEntry entry;
 378
 21379            if (isSearchMode)
 380            {
 8381                searchResultsList.Set(entryModel);
 8382                entry = (PublicChatEntry) searchResultsList.Get(channelId);
 383            }
 384            else
 385            {
 13386                publicChannelList.Set(channelId, entryModel);
 13387                entry = publicChannelList.Get(channelId);
 388            }
 389
 21390            entry.OnOpenOptions -= OpenChannelOptions;
 21391            entry.OnOpenOptions += OpenChannelOptions;
 392
 21393            UpdateHeaders();
 21394            UpdateLayout();
 21395            SortLists();
 21396        }
 397
 398        private void OpenChannelOptions(PublicChatEntry entry)
 399        {
 1400            optionsChannelId = entry.Model.channelId;
 1401            entry.Dock(channelContextualMenu);
 1402            channelContextualMenu.SetHeaderTitle($"#{entry.Model.name.ToLower()}");
 1403            channelContextualMenu.Show();
 1404        }
 405
 58406        private void SortLists() => isSortingDirty = true;
 407
 408        private void SetChannelsLoadingVisibility(bool visible)
 409        {
 3410            model.isLoadingChannels = visible;
 3411            channelsLoadingContainer.SetActive(visible);
 3412            publicChannelList.gameObject.SetActive(!visible);
 3413        }
 414
 415        private void SetPrivateChatLoadingVisibility(bool visible)
 416        {
 3417            model.isLoadingDirectChats = visible;
 3418            directChatsLoadingContainer.SetActive(visible);
 419
 3420            if (visible)
 2421                directChatList.Hide();
 1422            else if (!isSearchMode)
 1423                directChatList.Show();
 424
 3425            scroll.enabled = !visible;
 3426            isLoadingPrivateChannels = visible;
 3427        }
 428
 429        private void UpdateHeaders()
 430        {
 109431            directChatsHeaderLabel.text = $"Direct Messages ({directChatList.Count()})";
 109432            searchResultsHeaderLabel.text = $"Results ({searchResultsList.Count()})";
 109433            searchResultsHeader.SetActive(searchResultsList.Count() > 0);
 109434            channelsHeaderLabel.text = $"Channels ({publicChannelList.Count()})";
 109435        }
 436
 103437        private void UpdateLayout() => isLayoutDirty = true;
 438
 439        private void SetQueuedEntries()
 440        {
 291441            if (privateChatsCreationQueue.Count > 0)
 442            {
 110443                for (var i = 0; i < CREATION_AMOUNT_PER_FRAME && privateChatsCreationQueue.Count > 0; i++)
 444                {
 37445                    var (userId, model) = privateChatsCreationQueue.First();
 37446                    privateChatsCreationQueue.Remove(userId);
 37447                    Set(model);
 448                }
 449            }
 450
 624451            for (var i = 0; i < CREATION_AMOUNT_PER_FRAME && publicChatsCreationQueue.Count > 0; i++)
 452            {
 21453                var (userId, model) = publicChatsCreationQueue.First();
 21454                publicChatsCreationQueue.Remove(userId);
 21455                Set(model);
 456            }
 457
 291458            HideMoreChatsLoading();
 291459        }
 460
 461        private void FetchProfilePicturesForVisibleEntries()
 462        {
 299463            if (isSearchMode) return;
 464
 604465            foreach (var entry in directChatList.Entries.Values.Skip(currentAvatarSnapshotIndex)
 466                         .Take(AVATAR_SNAPSHOTS_PER_FRAME))
 467            {
 19468                if (entry.IsVisible((RectTransform) scroll.transform))
 19469                    entry.EnableAvatarSnapshotFetching();
 470                else
 0471                    entry.DisableAvatarSnapshotFetching();
 472
 19473                entry.RefreshControl();
 474            }
 475
 283476            currentAvatarSnapshotIndex += AVATAR_SNAPSHOTS_PER_FRAME;
 477
 283478            if (currentAvatarSnapshotIndex >= directChatList.Entries.Count)
 283479                currentAvatarSnapshotIndex = 0;
 283480        }
 481
 482        private void RequestMorePrivateChats(Vector2 position)
 483        {
 24484            if (!loadMoreEntriesContainer.activeInHierarchy
 485                || loadMoreEntriesLoading.activeInHierarchy
 486                || Time.realtimeSinceStartup - loadMoreEntriesRestrictionTime < MIN_TIME_TO_REQUIRE_MORE_ENTRIES
 22487                || isConnectWalletMode) return;
 488
 2489            if (position.y < REQUEST_MORE_ENTRIES_SCROLL_THRESHOLD && lastScrollPosition.y >= REQUEST_MORE_ENTRIES_SCROL
 490            {
 1491                if (requireMoreEntriesRoutine != null)
 0492                    StopCoroutine(requireMoreEntriesRoutine);
 493
 1494                ShowMoreChatsLoading();
 1495                requireMoreEntriesRoutine = StartCoroutine(WaitThenRequireMoreEntries());
 496
 1497                loadMoreEntriesRestrictionTime = Time.realtimeSinceStartup;
 498            }
 499
 2500            lastScrollPosition = position;
 2501        }
 502
 503        private IEnumerator WaitThenRequireMoreEntries()
 504        {
 1505            yield return new WaitForSeconds(1f);
 1506            OnRequireMorePrivateChats?.Invoke();
 1507        }
 508    }
 509}