< Summary

Class:FriendsHUDController
Assembly:FriendsHUD
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/FriendsHUD/Scripts/FriendsHUDController.cs
Covered lines:191
Uncovered lines:68
Coverable lines:259
Total lines:523
Line coverage:73.7% (191 of 259)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
FriendsHUDController(...)0%110100%
Initialize(...)0%440100%
Dispose()0%770100%
SetVisibility(...)0%4.254075%
HandleViewClosed()0%2100%
HandleFriendsInitialized()0%2100%
HandleProfileUpdated(...)0%110100%
UpdateBlockStatus()0%13.2111073.68%
HandleRequestSent(...)0%330100%
AreAlreadyFriends(...)0%330100%
HandleUserStatusUpdated(...)0%110100%
UpdateUserStatus(...)0%12.212088.89%
HandleFriendshipUpdated(...)0%13.0313094.44%
HandleFriendProfileUpdated(...)0%3.13077.78%
IsUserBlocked(...)0%3.333066.67%
EnqueueOnPendingToLoad(...)0%330100%
EnqueueOnPendingToLoad(...)0%330100%
ShouldBeDisplayed(...)0%550100%
ShouldBeDisplayed(...)0%660100%
OnFriendNotFound(...)0%110100%
UpdateNotificationsCounter()0%220100%
HandleOpenWhisperChat(...)0%220100%
HandleUnfriend(...)0%2100%
HandleRequestRejected(...)0%6200%
HandleRequestCancelled(...)0%6200%
HandleRequestAccepted(...)0%6200%
DisplayMoreFriends()0%20400%
DisplayMoreFriendRequests()0%20400%
ShowOrHideMoreFriendRequestsToLoadHint()0%2.52050%
ShowOrHideMoreFriendsToLoadHint()0%2.52050%
SearchFriends(...)0%6200%

File(s)

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

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using System.Text.RegularExpressions;
 5using Cysharp.Threading.Tasks;
 6using DCL;
 7using SocialFeaturesAnalytics;
 8using UnityEngine;
 9
 10public class FriendsHUDController : IHUD
 11{
 12    private const int INITIAL_DISPLAYED_FRIEND_COUNT = 50;
 13    private const int LOAD_FRIENDS_ON_DEMAND_COUNT = 30;
 14    private const int MAX_SEARCHED_FRIENDS = 100;
 15
 3216    private readonly Dictionary<string, FriendEntryModel> friends = new Dictionary<string, FriendEntryModel>();
 3217    private readonly Queue<string> pendingFriends = new Queue<string>();
 3218    private readonly Queue<string> pendingRequests = new Queue<string>();
 19    private readonly DataStore dataStore;
 20    private readonly IFriendsController friendsController;
 21    private readonly IUserProfileBridge userProfileBridge;
 22    private readonly ISocialAnalytics socialAnalytics;
 23    private readonly IChatController chatController;
 24    private readonly ILastReadMessagesService lastReadMessagesService;
 25
 26    private UserProfile ownUserProfile;
 27
 028    public IFriendsHUDComponentView View { get; private set; }
 29
 30    public event Action<string> OnPressWhisper;
 31    public event Action OnFriendsOpened;
 32    public event Action OnFriendsClosed;
 33
 3234    public FriendsHUDController(DataStore dataStore,
 35        IFriendsController friendsController,
 36        IUserProfileBridge userProfileBridge,
 37        ISocialAnalytics socialAnalytics,
 38        IChatController chatController,
 39        ILastReadMessagesService lastReadMessagesService)
 40    {
 3241        this.dataStore = dataStore;
 3242        this.friendsController = friendsController;
 3243        this.userProfileBridge = userProfileBridge;
 3244        this.socialAnalytics = socialAnalytics;
 3245        this.chatController = chatController;
 3246        this.lastReadMessagesService = lastReadMessagesService;
 3247    }
 48
 49    public void Initialize(IFriendsHUDComponentView view = null)
 50    {
 3251        view ??= FriendsHUDComponentView.Create();
 3252        View = view;
 53
 3254        view.Initialize(chatController, lastReadMessagesService, friendsController, socialAnalytics);
 3255        view.RefreshFriendsTab();
 3256        view.OnFriendRequestApproved += HandleRequestAccepted;
 3257        view.OnCancelConfirmation += HandleRequestCancelled;
 3258        view.OnRejectConfirmation += HandleRequestRejected;
 3259        view.OnFriendRequestSent += HandleRequestSent;
 3260        view.OnWhisper += HandleOpenWhisperChat;
 3261        view.OnClose += HandleViewClosed;
 3262        view.OnRequireMoreFriends += DisplayMoreFriends;
 3263        view.OnRequireMoreFriendRequests += DisplayMoreFriendRequests;
 3264        view.OnSearchFriendsRequested += SearchFriends;
 65
 3266        ownUserProfile = userProfileBridge.GetOwn();
 3267        ownUserProfile.OnUpdate -= HandleProfileUpdated;
 3268        ownUserProfile.OnUpdate += HandleProfileUpdated;
 69
 3270        if (friendsController != null)
 71        {
 3272            friendsController.OnUpdateFriendship += HandleFriendshipUpdated;
 3273            friendsController.OnUpdateUserStatus += HandleUserStatusUpdated;
 3274            friendsController.OnFriendNotFound += OnFriendNotFound;
 75
 3276            if (friendsController.isInitialized)
 77            {
 278                view.HideLoadingSpinner();
 279            }
 80            else
 81            {
 3082                view.ShowLoadingSpinner();
 3083                friendsController.OnInitialized -= HandleFriendsInitialized;
 3084                friendsController.OnInitialized += HandleFriendsInitialized;
 85            }
 86        }
 87
 3288        ShowOrHideMoreFriendsToLoadHint();
 3289        ShowOrHideMoreFriendRequestsToLoadHint();
 3290    }
 91
 92    public void Dispose()
 93    {
 3394        if (friendsController != null)
 95        {
 3396            friendsController.OnInitialized -= HandleFriendsInitialized;
 3397            friendsController.OnUpdateFriendship -= HandleFriendshipUpdated;
 3398            friendsController.OnUpdateUserStatus -= HandleUserStatusUpdated;
 99        }
 100
 33101        if (View != null)
 102        {
 33103            View.OnFriendRequestApproved -= HandleRequestAccepted;
 33104            View.OnCancelConfirmation -= HandleRequestCancelled;
 33105            View.OnRejectConfirmation -= HandleRequestRejected;
 33106            View.OnFriendRequestSent -= HandleRequestSent;
 33107            View.OnWhisper -= HandleOpenWhisperChat;
 33108            View.OnDeleteConfirmation -= HandleUnfriend;
 33109            View.OnClose -= HandleViewClosed;
 33110            View.OnRequireMoreFriends -= DisplayMoreFriends;
 33111            View.OnRequireMoreFriendRequests -= DisplayMoreFriendRequests;
 33112            View.OnSearchFriendsRequested -= SearchFriends;
 33113            View.Dispose();
 114        }
 115
 33116        if (ownUserProfile != null)
 33117            ownUserProfile.OnUpdate -= HandleProfileUpdated;
 118
 33119        if (userProfileBridge != null)
 120        {
 106121            foreach (var friendId in friends.Keys)
 122            {
 20123                var profile = userProfileBridge.Get(friendId);
 20124                if (profile == null) continue;
 18125                profile.OnUpdate -= HandleFriendProfileUpdated;
 126            }
 127        }
 33128    }
 129
 130    public void SetVisibility(bool visible)
 131    {
 6132        if (visible)
 133        {
 4134            View.Show();
 4135            UpdateNotificationsCounter();
 4136            OnFriendsOpened?.Invoke();
 0137        }
 138        else
 139        {
 2140            View.Hide();
 2141            OnFriendsClosed?.Invoke();
 142        }
 0143    }
 144
 0145    private void HandleViewClosed() => SetVisibility(false);
 146
 147    private void HandleFriendsInitialized()
 148    {
 0149        friendsController.OnInitialized -= HandleFriendsInitialized;
 0150        View.HideLoadingSpinner();
 0151    }
 152
 2153    private void HandleProfileUpdated(UserProfile profile) => UpdateBlockStatus(profile).Forget();
 154
 155    private async UniTask UpdateBlockStatus(UserProfile profile)
 156    {
 157        const int iterationsPerFrame = 10;
 158
 159        //NOTE(Brian): HashSet to check Contains quicker.
 2160        var allBlockedUsers = profile.blocked != null
 161            ? new HashSet<string>(profile.blocked)
 162            : new HashSet<string>();
 163
 2164        var iterations = 0;
 165
 8166        foreach (var friendPair in friends)
 167        {
 2168            var friendId = friendPair.Key;
 2169            var model = friendPair.Value;
 170
 2171            model.blocked = allBlockedUsers.Contains(friendId);
 2172            await UniTask.SwitchToMainThread();
 2173            View.UpdateBlockStatus(friendId, model.blocked);
 174
 2175            iterations++;
 2176            if (iterations > 0 && iterations % iterationsPerFrame == 0)
 0177                await UniTask.NextFrame();
 2178        }
 2179    }
 180
 181    private void HandleRequestSent(string userNameOrId)
 182    {
 3183        if (AreAlreadyFriends(userNameOrId))
 184        {
 1185            View.ShowRequestSendError(FriendRequestError.AlreadyFriends);
 1186        }
 187        else
 188        {
 2189            friendsController.RequestFriendship(userNameOrId);
 190
 2191            if (ownUserProfile != null)
 2192                socialAnalytics.SendFriendRequestSent(ownUserProfile.userId, userNameOrId, 0,
 193                    PlayerActionSource.FriendsHUD);
 194
 2195            View.ShowRequestSendSuccess();
 196        }
 2197    }
 198
 199    private bool AreAlreadyFriends(string userNameOrId)
 200    {
 3201        var userId = userNameOrId;
 3202        var profile = userProfileBridge.GetByName(userNameOrId);
 203
 3204        if (profile != default)
 1205            userId = profile.userId;
 206
 3207        return friendsController != null
 208               && friendsController.ContainsStatus(userId, FriendshipStatus.FRIEND);
 209    }
 210
 211    private void HandleUserStatusUpdated(string userId, FriendsController.UserStatus status) =>
 8212        UpdateUserStatus(userId, status, ShouldBeDisplayed(status));
 213
 214    private void UpdateUserStatus(string userId, FriendsController.UserStatus status, bool shouldDisplay)
 215    {
 9216        switch (status.friendshipStatus)
 217        {
 218            case FriendshipStatus.FRIEND:
 4219                var friend = friends.ContainsKey(userId)
 220                    ? new FriendEntryModel(friends[userId])
 221                    : new FriendEntryModel();
 4222                friend.CopyFrom(status);
 4223                friend.blocked = IsUserBlocked(userId);
 4224                friends[userId] = friend;
 4225                if (shouldDisplay)
 3226                    View.Set(userId, friend);
 3227                break;
 228            case FriendshipStatus.NOT_FRIEND:
 0229                View.Remove(userId);
 0230                friends.Remove(userId);
 0231                break;
 232            case FriendshipStatus.REQUESTED_TO:
 2233                var sentRequest = friends.ContainsKey(userId)
 234                    ? new FriendRequestEntryModel(friends[userId], false)
 235                    : new FriendRequestEntryModel {isReceived = false};
 2236                sentRequest.CopyFrom(status);
 2237                sentRequest.blocked = IsUserBlocked(userId);
 2238                friends[userId] = sentRequest;
 2239                if (shouldDisplay)
 2240                    View.Set(userId, sentRequest);
 2241                break;
 242            case FriendshipStatus.REQUESTED_FROM:
 3243                var receivedRequest = friends.ContainsKey(userId)
 244                    ? new FriendRequestEntryModel(friends[userId], true)
 245                    : new FriendRequestEntryModel {isReceived = true};
 3246                receivedRequest.CopyFrom(status);
 3247                receivedRequest.blocked = IsUserBlocked(userId);
 3248                friends[userId] = receivedRequest;
 3249                if (shouldDisplay)
 2250                    View.Set(userId, receivedRequest);
 251                break;
 252        }
 253
 9254        if (!shouldDisplay)
 2255            EnqueueOnPendingToLoad(userId, status);
 9256    }
 257
 258    private void HandleFriendshipUpdated(string userId, FriendshipAction friendshipAction)
 259    {
 17260        var userProfile = userProfileBridge.Get(userId);
 261
 17262        if (userProfile == null)
 263        {
 0264            Debug.LogError($"UserProfile is null for {userId}! ... friendshipAction {friendshipAction}");
 0265            return;
 266        }
 267
 17268        userProfile.OnUpdate -= HandleFriendProfileUpdated;
 269
 17270        var shouldDisplay = ShouldBeDisplayed(userId, friendshipAction);
 271
 272        switch (friendshipAction)
 273        {
 274            case FriendshipAction.NONE:
 275            case FriendshipAction.REJECTED:
 276            case FriendshipAction.CANCELLED:
 277            case FriendshipAction.DELETED:
 3278                friends.Remove(userId);
 3279                View.Remove(userId);
 3280                break;
 281            case FriendshipAction.APPROVED:
 7282                var approved = friends.ContainsKey(userId)
 283                    ? new FriendEntryModel(friends[userId])
 284                    : new FriendEntryModel();
 7285                approved.CopyFrom(userProfile);
 7286                approved.blocked = IsUserBlocked(userId);
 7287                friends[userId] = approved;
 7288                if (shouldDisplay)
 5289                    View.Set(userId, approved);
 7290                userProfile.OnUpdate += HandleFriendProfileUpdated;
 7291                break;
 292            case FriendshipAction.REQUESTED_FROM:
 4293                var requestReceived = friends.ContainsKey(userId)
 294                    ? new FriendRequestEntryModel(friends[userId], true)
 295                    : new FriendRequestEntryModel {isReceived = true};
 4296                requestReceived.CopyFrom(userProfile);
 4297                requestReceived.blocked = IsUserBlocked(userId);
 4298                friends[userId] = requestReceived;
 4299                if (shouldDisplay)
 2300                    View.Set(userId, requestReceived);
 4301                userProfile.OnUpdate += HandleFriendProfileUpdated;
 4302                break;
 303            case FriendshipAction.REQUESTED_TO:
 3304                var requestSent = friends.ContainsKey(userId)
 305                    ? new FriendRequestEntryModel(friends[userId], false)
 306                    : new FriendRequestEntryModel {isReceived = false};
 3307                requestSent.CopyFrom(userProfile);
 3308                requestSent.blocked = IsUserBlocked(userId);
 3309                friends[userId] = requestSent;
 3310                if (shouldDisplay)
 3311                    View.Set(userId, requestSent);
 3312                userProfile.OnUpdate += HandleFriendProfileUpdated;
 313                break;
 314        }
 315
 17316        if (shouldDisplay)
 13317            UpdateNotificationsCounter();
 318        else
 4319            EnqueueOnPendingToLoad(userId, friendshipAction);
 4320    }
 321
 322    private void HandleFriendProfileUpdated(UserProfile profile)
 323    {
 1324        var userId = profile.userId;
 1325        if (!friends.ContainsKey(userId)) return;
 1326        friends[userId].CopyFrom(profile);
 327
 1328        var status = friendsController.GetUserStatus(profile.userId);
 1329        if (status == null) return;
 330
 1331        UpdateUserStatus(userId, status, ShouldBeDisplayed(status));
 1332    }
 333
 334    private bool IsUserBlocked(string userId)
 335    {
 23336        if (ownUserProfile != null && ownUserProfile.blocked != null)
 23337            return ownUserProfile.blocked.Contains(userId);
 0338        return false;
 339    }
 340
 341    private void EnqueueOnPendingToLoad(string userId, FriendsController.UserStatus newStatus)
 342    {
 2343        switch (newStatus.friendshipStatus)
 344        {
 345            case FriendshipStatus.FRIEND:
 1346                pendingFriends.Enqueue(userId);
 1347                View.ShowMoreFriendsToLoadHint(pendingFriends.Count);
 1348                break;
 349            case FriendshipStatus.REQUESTED_FROM:
 1350                pendingRequests.Enqueue(userId);
 1351                View.ShowMoreRequestsToLoadHint(pendingRequests.Count);
 352                break;
 353        }
 1354    }
 355
 356    private void EnqueueOnPendingToLoad(string userId, FriendshipAction friendshipAction)
 357    {
 358        switch (friendshipAction)
 359        {
 360            case FriendshipAction.APPROVED:
 2361                pendingFriends.Enqueue(userId);
 2362                View.ShowMoreFriendsToLoadHint(pendingFriends.Count);
 2363                break;
 364            case FriendshipAction.REQUESTED_FROM:
 2365                pendingRequests.Enqueue(userId);
 2366                View.ShowMoreRequestsToLoadHint(pendingRequests.Count);
 367                break;
 368        }
 2369    }
 370
 371    private bool ShouldBeDisplayed(string userId, FriendshipAction friendshipAction)
 372    {
 17373        return friendshipAction switch
 374        {
 375            FriendshipAction.APPROVED => View.FriendCount <= INITIAL_DISPLAYED_FRIEND_COUNT ||
 376                                         View.ContainsFriend(userId),
 377            FriendshipAction.REQUESTED_FROM => View.FriendRequestCount <= INITIAL_DISPLAYED_FRIEND_COUNT ||
 378                                               View.ContainsFriendRequest(userId),
 379            _ => true
 380        };
 381    }
 382
 383    private bool ShouldBeDisplayed(FriendsController.UserStatus status)
 384    {
 14385        if (status.presence == PresenceStatus.ONLINE) return true;
 386
 4387        return status.friendshipStatus switch
 388        {
 389            FriendshipStatus.FRIEND => View.FriendCount < INITIAL_DISPLAYED_FRIEND_COUNT ||
 390                                       View.ContainsFriend(status.userId),
 391            FriendshipStatus.REQUESTED_FROM => View.FriendRequestCount < INITIAL_DISPLAYED_FRIEND_COUNT ||
 392                                               View.ContainsFriendRequest(status.userId),
 393            _ => true
 394        };
 395    }
 396
 397    private void OnFriendNotFound(string name)
 398    {
 1399        View.DisplayFriendUserNotFound();
 1400    }
 401
 402    private void UpdateNotificationsCounter()
 403    {
 17404        if (View.IsActive())
 5405            dataStore.friendNotifications.seenFriends.Set(friendsController.friendCount);
 406
 17407        dataStore.friendNotifications.seenRequests.Set(friendsController.ReceivedRequestCount);
 17408    }
 409
 1410    private void HandleOpenWhisperChat(FriendEntryModel entry) => OnPressWhisper?.Invoke(entry.userId);
 411
 0412    private void HandleUnfriend(string userId) => friendsController.RemoveFriend(userId);
 413
 414    private void HandleRequestRejected(FriendRequestEntryModel entry)
 415    {
 0416        friendsController.RejectFriendship(entry.userId);
 417
 0418        UpdateNotificationsCounter();
 419
 0420        if (ownUserProfile != null)
 0421            socialAnalytics.SendFriendRequestRejected(ownUserProfile.userId, entry.userId,
 422                PlayerActionSource.FriendsHUD);
 0423    }
 424
 425    private void HandleRequestCancelled(FriendRequestEntryModel entry)
 426    {
 0427        friendsController.CancelRequest(entry.userId);
 428
 0429        if (ownUserProfile != null)
 0430            socialAnalytics.SendFriendRequestCancelled(ownUserProfile.userId, entry.userId,
 431                PlayerActionSource.FriendsHUD);
 0432    }
 433
 434    private void HandleRequestAccepted(FriendRequestEntryModel entry)
 435    {
 0436        friendsController.AcceptFriendship(entry.userId);
 437
 0438        if (ownUserProfile != null)
 0439            socialAnalytics.SendFriendRequestApproved(ownUserProfile.userId, entry.userId,
 440                PlayerActionSource.FriendsHUD);
 0441    }
 442
 443    private void DisplayMoreFriends()
 444    {
 0445        for (var i = 0; i < LOAD_FRIENDS_ON_DEMAND_COUNT && pendingFriends.Count > 0; i++)
 446        {
 0447            var userId = pendingFriends.Dequeue();
 0448            var status = friendsController.GetUserStatus(userId);
 0449            if (status == null) continue;
 0450            UpdateUserStatus(userId, status, true);
 451        }
 452
 0453        ShowOrHideMoreFriendsToLoadHint();
 0454    }
 455
 456    private void DisplayMoreFriendRequests()
 457    {
 0458        for (var i = 0; i < LOAD_FRIENDS_ON_DEMAND_COUNT && pendingRequests.Count > 0; i++)
 459        {
 0460            var userId = pendingRequests.Dequeue();
 0461            var status = friendsController.GetUserStatus(userId);
 0462            if (status == null) continue;
 0463            HandleUserStatusUpdated(userId, status);
 464        }
 465
 0466        ShowOrHideMoreFriendRequestsToLoadHint();
 0467    }
 468
 469    private void ShowOrHideMoreFriendRequestsToLoadHint()
 470    {
 32471        if (pendingRequests.Count == 0)
 32472            View.HideMoreRequestsToLoadHint();
 473        else
 0474            View.ShowMoreRequestsToLoadHint(pendingRequests.Count);
 0475    }
 476
 477    private void ShowOrHideMoreFriendsToLoadHint()
 478    {
 32479        if (pendingFriends.Count == 0)
 32480            View.HideMoreFriendsToLoadHint();
 481        else
 0482            View.ShowMoreFriendsToLoadHint(pendingFriends.Count);
 0483    }
 484
 485    private void SearchFriends(string search)
 486    {
 0487        if (string.IsNullOrEmpty(search))
 488        {
 0489            View.ClearFriendFilter();
 0490            ShowOrHideMoreFriendsToLoadHint();
 0491            return;
 492        }
 493
 494        Dictionary<string, FriendEntryModel> FilterFriendsByUserNameAndUserId(string search)
 495        {
 0496            var regex = new Regex(search, RegexOptions.IgnoreCase);
 497
 0498            return friends.Values.Where(model =>
 499            {
 0500                var status = friendsController.GetUserStatus(model.userId);
 0501                if (status == null) return false;
 0502                if (status.friendshipStatus != FriendshipStatus.FRIEND) return false;
 0503                if (regex.IsMatch(model.userId)) return true;
 0504                return !string.IsNullOrEmpty(model.userName) && regex.IsMatch(model.userName);
 0505            }).Take(MAX_SEARCHED_FRIENDS).ToDictionary(model => model.userId, model => model);
 506        }
 507
 508        void DisplayMissingFriends(IEnumerable<FriendEntryModel> filteredFriends)
 509        {
 0510            foreach (var model in filteredFriends)
 511            {
 0512                if (View.ContainsFriend(model.userId)) return;
 0513                var status = friendsController.GetUserStatus(model.userId);
 0514                if (status == null) continue;
 0515                UpdateUserStatus(model.userId, status, true);
 516            }
 0517        }
 518
 0519        var filteredFriends = FilterFriendsByUserNameAndUserId(search);
 0520        DisplayMissingFriends(filteredFriends.Values);
 0521        View.FilterFriends(filteredFriends);
 0522    }
 523}