< 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:171
Uncovered lines:72
Coverable lines:243
Total lines:513
Line coverage:70.3% (171 of 243)
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%
OnUpdateUserStatus(...)0%220100%
OnUpdateFriendship(...)0%3.023087.5%
HandleFriendProfileUpdated(...)0%2.012087.5%
IsUserBlocked(...)0%3.333066.67%
GetOrCreateModel(...)0%10100100%
GetOrCreateModel(...)0%6.976070%
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%30500%
DisplayMoreFriendRequests()0%30500%
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.ListByOnlineStatus = dataStore.featureFlags.flags.Get().IsFeatureEnabled("friends_by_online_status");
 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 += OnUpdateFriendship;
 3273            friendsController.OnUpdateUserStatus += OnUpdateUserStatus;
 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 -= OnUpdateFriendship;
 3398            friendsController.OnUpdateUserStatus -= OnUpdateUserStatus;
 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        {
 112121            foreach (var friendId in friends.Keys)
 122            {
 23123                var profile = userProfileBridge.Get(friendId);
 23124                if (profile == null) continue;
 21125                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.Populate(friendId, model);
 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 OnUpdateUserStatus(string userId, FriendsController.UserStatus newStatus)
 212    {
 8213        var shouldDisplay = ShouldBeDisplayed(newStatus);
 8214        var model = GetOrCreateModel(userId, newStatus);
 8215        model.CopyFrom(newStatus);
 216
 8217        if (shouldDisplay)
 6218            View.Set(userId, newStatus.friendshipStatus, model);
 219        else
 2220            EnqueueOnPendingToLoad(userId, newStatus);
 2221    }
 222
 223    private void OnUpdateFriendship(string userId, FriendshipAction friendshipAction)
 224    {
 17225        var userProfile = userProfileBridge.Get(userId);
 226
 17227        if (userProfile == null)
 228        {
 0229            Debug.LogError($"UserProfile is null for {userId}! ... friendshipAction {friendshipAction}");
 0230            return;
 231        }
 232
 17233        userProfile.OnUpdate -= HandleFriendProfileUpdated;
 17234        userProfile.OnUpdate += HandleFriendProfileUpdated;
 235
 17236        var shouldDisplay = ShouldBeDisplayed(userId, friendshipAction);
 17237        var model = GetOrCreateModel(userId, friendshipAction);
 17238        model.CopyFrom(userProfile);
 17239        model.blocked = IsUserBlocked(userId);
 240
 17241        if (shouldDisplay)
 242        {
 13243            View.Set(userId, friendshipAction, model);
 13244            UpdateNotificationsCounter();
 13245        }
 246        else
 4247            EnqueueOnPendingToLoad(userId, friendshipAction);
 4248    }
 249
 250    private void HandleFriendProfileUpdated(UserProfile profile)
 251    {
 1252        var userId = profile.userId;
 1253        if (!friends.ContainsKey(userId)) return;
 254
 1255        var model = friends[userId];
 1256        model.CopyFrom(profile);
 1257        model.blocked = IsUserBlocked(userId);
 258
 1259        View.Populate(userId, model);
 1260    }
 261
 262    private bool IsUserBlocked(string userId)
 263    {
 18264        if (ownUserProfile != null && ownUserProfile.blocked != null)
 18265            return ownUserProfile.blocked.Contains(userId);
 0266        return false;
 267    }
 268
 269    private FriendEntryModel GetOrCreateModel(string userId, FriendshipAction friendshipAction)
 270    {
 17271        if (!friends.ContainsKey(userId))
 272        {
 15273            if (friendshipAction == FriendshipAction.REQUESTED_TO
 274                || friendshipAction == FriendshipAction.REQUESTED_FROM
 275                || friendshipAction == FriendshipAction.CANCELLED
 276                || friendshipAction == FriendshipAction.REJECTED)
 277            {
 8278                friends[userId] = new FriendRequestEntryModel
 279                {
 280                    isReceived = friendshipAction == FriendshipAction.REQUESTED_FROM
 281                };
 8282            }
 283            else
 7284                friends[userId] = new FriendEntryModel();
 7285        }
 286        else
 287        {
 2288            if (friendshipAction == FriendshipAction.REQUESTED_TO
 289                || friendshipAction == FriendshipAction.REQUESTED_FROM
 290                || friendshipAction == FriendshipAction.CANCELLED
 291                || friendshipAction == FriendshipAction.REJECTED)
 292            {
 1293                friends[userId] = new FriendRequestEntryModel(friends[userId],
 294                    friendshipAction == FriendshipAction.REQUESTED_FROM);
 1295            }
 296            else
 1297                friends[userId] = new FriendEntryModel(friends[userId]);
 298        }
 299
 17300        return friends[userId];
 301    }
 302
 303    private FriendEntryModel GetOrCreateModel(string userId, FriendsController.UserStatus newStatus)
 304    {
 8305        if (!friends.ContainsKey(userId))
 306        {
 8307            if (newStatus.friendshipStatus == FriendshipStatus.REQUESTED_TO
 308                || newStatus.friendshipStatus == FriendshipStatus.REQUESTED_FROM)
 309            {
 5310                friends[userId] = new FriendRequestEntryModel
 311                {
 312                    isReceived = newStatus.friendshipStatus == FriendshipStatus.REQUESTED_FROM
 313                };
 5314            }
 315            else
 3316                friends[userId] = new FriendEntryModel();
 3317        }
 318        else
 319        {
 0320            if (newStatus.friendshipStatus == FriendshipStatus.REQUESTED_TO
 321                || newStatus.friendshipStatus == FriendshipStatus.REQUESTED_FROM)
 0322                friends[userId] = new FriendRequestEntryModel(friends[userId],
 323                    newStatus.friendshipStatus == FriendshipStatus.REQUESTED_FROM);
 324            else
 0325                friends[userId] = new FriendEntryModel(friends[userId]);
 326        }
 327
 8328        return friends[userId];
 329    }
 330
 331    private void EnqueueOnPendingToLoad(string userId, FriendsController.UserStatus newStatus)
 332    {
 2333        switch (newStatus.friendshipStatus)
 334        {
 335            case FriendshipStatus.FRIEND:
 1336                pendingFriends.Enqueue(userId);
 1337                View.ShowMoreFriendsToLoadHint(pendingFriends.Count);
 1338                break;
 339            case FriendshipStatus.REQUESTED_FROM:
 1340                pendingRequests.Enqueue(userId);
 1341                View.ShowMoreRequestsToLoadHint(pendingRequests.Count);
 342                break;
 343        }
 1344    }
 345
 346    private void EnqueueOnPendingToLoad(string userId, FriendshipAction friendshipAction)
 347    {
 348        switch (friendshipAction)
 349        {
 350            case FriendshipAction.APPROVED:
 2351                pendingFriends.Enqueue(userId);
 2352                View.ShowMoreFriendsToLoadHint(pendingFriends.Count);
 2353                break;
 354            case FriendshipAction.REQUESTED_FROM:
 2355                pendingRequests.Enqueue(userId);
 2356                View.ShowMoreRequestsToLoadHint(pendingRequests.Count);
 357                break;
 358        }
 2359    }
 360
 361    private bool ShouldBeDisplayed(string userId, FriendshipAction friendshipAction)
 362    {
 17363        return friendshipAction switch
 364        {
 365            FriendshipAction.APPROVED => View.FriendCount <= INITIAL_DISPLAYED_FRIEND_COUNT || View.ContainsFriend(userI
 366            FriendshipAction.REQUESTED_FROM => View.FriendRequestCount <= INITIAL_DISPLAYED_FRIEND_COUNT || View.Contain
 367            _ => true
 368        };
 369    }
 370
 371    private bool ShouldBeDisplayed(FriendsController.UserStatus status)
 372    {
 13373        if (status.presence == PresenceStatus.ONLINE) return true;
 374
 3375        return status.friendshipStatus switch
 376        {
 377            FriendshipStatus.FRIEND => View.FriendCount < INITIAL_DISPLAYED_FRIEND_COUNT || View.ContainsFriend(status.u
 378            FriendshipStatus.REQUESTED_FROM => View.FriendRequestCount < INITIAL_DISPLAYED_FRIEND_COUNT || View.Contains
 379            _ => true
 380        };
 381    }
 382
 383    private void OnFriendNotFound(string name)
 384    {
 1385        View.DisplayFriendUserNotFound();
 1386    }
 387
 388    private void UpdateNotificationsCounter()
 389    {
 17390        if (View.IsActive())
 5391            dataStore.friendNotifications.seenFriends.Set(friendsController.friendCount);
 392
 17393        dataStore.friendNotifications.seenRequests.Set(friendsController.ReceivedRequestCount);
 17394    }
 395
 1396    private void HandleOpenWhisperChat(FriendEntryModel entry) => OnPressWhisper?.Invoke(entry.userId);
 397
 0398    private void HandleUnfriend(string userId) => friendsController.RemoveFriend(userId);
 399
 400    private void HandleRequestRejected(FriendRequestEntryModel entry)
 401    {
 0402        friendsController.RejectFriendship(entry.userId);
 403
 0404        UpdateNotificationsCounter();
 405
 0406        if (ownUserProfile != null)
 0407            socialAnalytics.SendFriendRequestRejected(ownUserProfile.userId, entry.userId,
 408                PlayerActionSource.FriendsHUD);
 0409    }
 410
 411    private void HandleRequestCancelled(FriendRequestEntryModel entry)
 412    {
 0413        friendsController.CancelRequest(entry.userId);
 414
 0415        if (ownUserProfile != null)
 0416            socialAnalytics.SendFriendRequestCancelled(ownUserProfile.userId, entry.userId,
 417                PlayerActionSource.FriendsHUD);
 0418    }
 419
 420    private void HandleRequestAccepted(FriendRequestEntryModel entry)
 421    {
 0422        friendsController.AcceptFriendship(entry.userId);
 423
 0424        if (ownUserProfile != null)
 0425            socialAnalytics.SendFriendRequestApproved(ownUserProfile.userId, entry.userId,
 426                PlayerActionSource.FriendsHUD);
 0427    }
 428
 429    private void DisplayMoreFriends()
 430    {
 0431        for (var i = 0; i < LOAD_FRIENDS_ON_DEMAND_COUNT && pendingFriends.Count > 0; i++)
 432        {
 0433            var userId = pendingFriends.Dequeue();
 0434            if (!friends.ContainsKey(userId)) continue;
 0435            var model = friends[userId];
 0436            var status = friendsController.GetUserStatus(userId);
 0437            if (status == null) continue;
 0438            View.Set(userId, status.friendshipStatus, model);
 439        }
 440
 0441        ShowOrHideMoreFriendsToLoadHint();
 0442    }
 443
 444    private void DisplayMoreFriendRequests()
 445    {
 0446        for (var i = 0; i < LOAD_FRIENDS_ON_DEMAND_COUNT && pendingRequests.Count > 0; i++)
 447        {
 0448            var userId = pendingRequests.Dequeue();
 0449            if (!friends.ContainsKey(userId)) continue;
 0450            var model = friends[userId];
 0451            var status = friendsController.GetUserStatus(userId);
 0452            if (status == null) continue;
 0453            View.Set(userId, status.friendshipStatus, model);
 454        }
 455
 0456        ShowOrHideMoreFriendRequestsToLoadHint();
 0457    }
 458
 459    private void ShowOrHideMoreFriendRequestsToLoadHint()
 460    {
 32461        if (pendingRequests.Count == 0)
 32462            View.HideMoreRequestsToLoadHint();
 463        else
 0464            View.ShowMoreRequestsToLoadHint(pendingRequests.Count);
 0465    }
 466
 467    private void ShowOrHideMoreFriendsToLoadHint()
 468    {
 32469        if (pendingFriends.Count == 0)
 32470            View.HideMoreFriendsToLoadHint();
 471        else
 0472            View.ShowMoreFriendsToLoadHint(pendingFriends.Count);
 0473    }
 474
 475    private void SearchFriends(string search)
 476    {
 0477        if (string.IsNullOrEmpty(search))
 478        {
 0479            View.ClearFriendFilter();
 0480            ShowOrHideMoreFriendsToLoadHint();
 0481            return;
 482        }
 483
 484        Dictionary<string, FriendEntryModel> FilterFriendsByUserNameAndUserId(string search)
 485        {
 0486            var regex = new Regex(search, RegexOptions.IgnoreCase);
 487
 0488            return friends.Values.Where(model =>
 489            {
 0490                var status = friendsController.GetUserStatus(model.userId);
 0491                if (status == null) return false;
 0492                if (status.friendshipStatus != FriendshipStatus.FRIEND) return false;
 0493                if (regex.IsMatch(model.userId)) return true;
 0494                return !string.IsNullOrEmpty(model.userName) && regex.IsMatch(model.userName);
 0495            }).Take(MAX_SEARCHED_FRIENDS).ToDictionary(model => model.userId, model => model);
 496        }
 497
 498        void DisplayMissingFriends(IEnumerable<FriendEntryModel> filteredFriends)
 499        {
 0500            foreach (var model in filteredFriends)
 501            {
 0502                if (View.ContainsFriend(model.userId)) return;
 0503                var status = friendsController.GetUserStatus(model.userId);
 0504                if (status == null) continue;
 0505                View.Set(model.userId, FriendshipStatus.FRIEND, model);
 506            }
 0507        }
 508
 0509        var filteredFriends = FilterFriendsByUserNameAndUserId(search);
 0510        DisplayMissingFriends(filteredFriends.Values);
 0511        View.FilterFriends(filteredFriends);
 0512    }
 513}