< Summary

Class:UserContextMenu
Assembly:UserContextMenu
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/UserContextMenu/Scripts/UserContextMenu.cs
Covered lines:127
Uncovered lines:74
Coverable lines:201
Total lines:518
Line coverage:63.1% (127 of 201)
Covered branches:0
Total branches:0
Covered methods:21
Total methods:35
Method coverage:60% (21 of 35)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
UserContextMenu()0%110100%
Show(...)0%220100%
Show(...)0%5.25080%
ShowByUserName(...)0%12300%
SetConfirmationDialog(...)0%2100%
Hide(...)0%2.032080%
SetFriendshipContentActive(...)0%110100%
SetPassportOpenSource(...)0%110100%
Awake()0%220100%
OnDisable()0%110100%
RefreshControl()0%2100%
OnPassportButtonPressed()0%440100%
OnReportUserButtonPressed()0%440100%
OnDeleteUserButtonPressed()0%12300%
OnAddFriendButtonPressed()0%12300%
<OnCancelFriendRequestButtonPressed()0%30500%
OnCancelFriendRequestButtonPressed()0%2100%
OnMessageButtonPressed()0%6200%
OnBlockUserButtonPressed()0%6.325062.5%
OnMentionButtonPressed()0%2100%
ProcessActiveElements(...)0%12120100%
Setup(...)0%13.4913085.71%
SetupFriendship(...)0%20.4517077.14%
OnFriendActionUpdate(...)0%6.976070%
GetSocialAnalytics()0%2.152066.67%
ShowUserNotificationError(...)0%2100%
OnCopyNameButtonPressed()0%2100%
ClampPositionToScreenBordersOnNextFrame()0%5.673033.33%
OnValidate()0%2.062075%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/UserContextMenu/Scripts/UserContextMenu.cs

#LineLine coverage
 1using Cysharp.Threading.Tasks;
 2using DCL;
 3using DCL.Interface;
 4using DCL.Social.Friends;
 5using DCL.Tasks;
 6using DCLServices.CopyPaste.Analytics;
 7using SocialFeaturesAnalytics;
 8using System;
 9using System.Collections;
 10using System.Linq;
 11using System.Threading;
 12using TMPro;
 13using UIComponents.ContextMenu;
 14using UnityEngine;
 15using UnityEngine.UI;
 16using Environment = DCL.Environment;
 17
 18/// <summary>
 19/// Contextual menu with different options about an user.
 20/// </summary>
 21[RequireComponent(typeof(RectTransform))]
 22
 23// TODO: refactor into MVC
 24public class UserContextMenu : ContextMenuComponentView
 25{
 26    private const string BLOCK_BTN_BLOCK_TEXT = "Block";
 27    private const string BLOCK_BTN_UNBLOCK_TEXT = "Unblock";
 28    private const string OPEN_PASSPORT_NORMAL_SOURCE = "FriendsHUD";
 29    private const string OPEN_PASSPORT_MENTION_SOURCE = "Mention";
 30    private const MenuConfigFlags HEADER_FLAGS = MenuConfigFlags.Name | MenuConfigFlags.Friendship;
 31    private const MenuConfigFlags USES_FRIENDS_API_FLAGS = MenuConfigFlags.Friendship | MenuConfigFlags.Message;
 32
 33    [Flags]
 34    public enum MenuConfigFlags
 35    {
 36        Name = 1,
 37        Friendship = 2,
 38        Message = 4,
 39        Passport = 8,
 40        Block = 16,
 41        Report = 32,
 42        Mention = 64,
 43    }
 44
 45    [Header("Optional: Set Confirmation Dialog")]
 46    [SerializeField] internal UserContextConfirmationDialog confirmationDialog;
 47
 48    [Header("Enable Actions")]
 19349    [SerializeField] internal MenuConfigFlags menuConfigFlags = MenuConfigFlags.Passport | MenuConfigFlags.Block | MenuC
 19350    [SerializeField] internal bool enableSendMessage = true;
 51
 52    [Header("Containers")]
 53    [SerializeField] internal GameObject headerContainer;
 54    [SerializeField] internal GameObject friendshipContainer;
 55    [SerializeField] internal GameObject friendAddContainer;
 56    [SerializeField] internal GameObject friendRemoveContainer;
 57    [SerializeField] internal GameObject friendRequestedContainer;
 58
 59    [Header("Texts")]
 60    [SerializeField] internal TextMeshProUGUI userName;
 61    [SerializeField] internal TextMeshProUGUI blockText;
 62
 63    [Header("Buttons")]
 64    [SerializeField] internal Button passportButton;
 65    [SerializeField] internal Button blockButton;
 66    [SerializeField] internal Button reportButton;
 67    [SerializeField] internal Button addFriendButton;
 68    [SerializeField] internal Button cancelFriendButton;
 69    [SerializeField] internal Button deleteFriendButton;
 70    [SerializeField] internal Button messageButton;
 71    [SerializeField] internal Button mentionButton;
 72    [SerializeField] internal Button copyNameButton;
 73
 74    [Header("Misc")]
 75    [SerializeField] private ShowHideAnimator nameCopiedToast;
 76
 77    public static event Action<string> OnOpenPrivateChatRequest;
 78
 2379    public bool isVisible => gameObject.activeSelf;
 080    public string UserId => userId;
 81
 82    public event Action OnShowMenu;
 83    public event Action<string> OnPassport;
 84    public event Action<string> OnReport;
 85    public event Action<string, bool> OnBlock;
 86    public event Action<string> OnUnfriend;
 87    public event Action OnHide;
 88
 89    private static BaseVariable<(string playerId, string source)> currentPlayerId;
 90    private string userId;
 91    private bool isBlocked;
 92    private MenuConfigFlags currentConfigFlags;
 93    private IConfirmationDialog currentConfirmationDialog;
 19394    private CancellationTokenSource friendOperationsCancellationToken = new ();
 95    private bool isFromMentionContextMenu;
 96    private IFriendsController friendsControllerInternal;
 97    private IClipboard clipboardInternal;
 6298    private bool isFriendsEnabled => DataStore.i.featureFlags.flags.Get().IsFeatureEnabled("friends_enabled");
 99
 100    private IFriendsController friendsController
 101    {
 102        get
 103        {
 28104            return friendsControllerInternal ??= Environment.i.serviceLocator.Get<IFriendsController>();
 105        }
 106    }
 107
 0108    private IClipboard clipboard => clipboardInternal ??= Clipboard.Create();
 0109    private ICopyPasteAnalyticsService copyPasteAnalyticsService => Environment.i.serviceLocator.Get<ICopyPasteAnalytics
 110
 111    internal ISocialAnalytics socialAnalytics;
 112
 113    /// <summary>
 114    /// Show context menu
 115    /// </summary>
 116    /// <param name="userId"> user id</param>
 117    public void Show(string userId)
 118    {
 5119        if (string.IsNullOrEmpty(userId))
 1120            return;
 121
 4122        Show(userId, menuConfigFlags);
 4123    }
 124
 125    /// <summary>
 126    /// Show context menu
 127    /// </summary>
 128    /// <param name="userId"> user id</param>
 129    /// <param name="configFlags">set buttons to enable in menu</param>
 130    public void Show(string userId, MenuConfigFlags configFlags)
 131    {
 6132        this.userId = userId;
 6133        ProcessActiveElements(configFlags);
 134
 6135        if (!Setup(userId, configFlags))
 0136            return;
 137
 6138        if (currentConfirmationDialog == null && confirmationDialog != null)
 0139            SetConfirmationDialog(confirmationDialog);
 140
 6141        gameObject.SetActive(true);
 142        // wait until the layout is rebuilt, otherwise there is an undesired offset
 6143        StartCoroutine(ClampPositionToScreenBordersOnNextFrame());
 144
 6145        OnShowMenu?.Invoke();
 1146    }
 147
 148    /// <summary>
 149    /// Show context menu
 150    /// </summary>
 151    /// <param name="userName">User name</param>
 152    public void ShowByUserName(string userName)
 153    {
 0154        var userProfile = UserProfileController.userProfilesCatalog
 155                                               .GetValues()
 0156                                               .FirstOrDefault(p => p.userName.Equals(userName, StringComparison.Ordinal
 157
 0158        if (userProfile != null)
 159        {
 0160            if (!Setup(userProfile.userId, menuConfigFlags))
 161            {
 0162                ShowUserNotificationError(userName);
 0163                return;
 164            }
 165
 0166            Show(userProfile.userId, currentConfigFlags);
 167        }
 168        else
 0169            ShowUserNotificationError(userName);
 0170    }
 171
 172    /// <summary>
 173    /// Set confirmation popup to reference use
 174    /// </summary>
 175    /// <param name="confirmationPopup">confirmation popup reference</param>
 176    public void SetConfirmationDialog(IConfirmationDialog confirmationPopup)
 177    {
 0178        this.currentConfirmationDialog = confirmationPopup;
 0179    }
 180
 181    public override void Hide(bool instant = false)
 182    {
 5183        base.Hide(instant);
 184
 5185        friendOperationsCancellationToken = friendOperationsCancellationToken.SafeRestart();
 5186        gameObject.SetActive(false);
 5187        OnHide?.Invoke();
 0188    }
 189
 190    /// <summary>
 191    /// Shows/Hides the friendship container
 192    /// </summary>
 193    public void SetFriendshipContentActive(bool isActive) =>
 2194        friendshipContainer.SetActive(isActive);
 195
 196    public void SetPassportOpenSource(bool isFromMention)
 197    {
 34198        isFromMentionContextMenu = isFromMention;
 34199    }
 200
 201    public override void Awake()
 202    {
 9203        base.Awake();
 204
 9205        currentPlayerId = DataStore.i.HUDs.currentPlayerId;
 9206        passportButton.onClick.AddListener(OnPassportButtonPressed);
 9207        blockButton.onClick.AddListener(OnBlockUserButtonPressed);
 9208        reportButton.onClick.AddListener(OnReportUserButtonPressed);
 9209        deleteFriendButton.onClick.AddListener(OnDeleteUserButtonPressed);
 9210        addFriendButton.onClick.AddListener(OnAddFriendButtonPressed);
 9211        cancelFriendButton.onClick.AddListener(OnCancelFriendRequestButtonPressed);
 9212        messageButton.onClick.AddListener(OnMessageButtonPressed);
 9213        copyNameButton.onClick.AddListener(OnCopyNameButtonPressed);
 214
 9215        if (mentionButton != null)
 9216            mentionButton.onClick.AddListener(OnMentionButtonPressed);
 9217    }
 218
 219    public override void OnDisable()
 220    {
 9221        base.OnDisable();
 222
 9223        friendsController.OnUpdateFriendship -= OnFriendActionUpdate;
 9224    }
 225
 226    public override void RefreshControl()
 227    {
 0228    }
 229
 230    private void OnPassportButtonPressed()
 231    {
 1232        OnPassport?.Invoke(userId);
 1233        currentPlayerId.Set((userId, isFromMentionContextMenu ? OPEN_PASSPORT_MENTION_SOURCE : OPEN_PASSPORT_NORMAL_SOUR
 1234        Hide();
 235
 1236        AudioScriptableObjects.dialogOpen.Play(true);
 1237    }
 238
 239    private void OnReportUserButtonPressed()
 240    {
 1241        OnReport?.Invoke(userId);
 1242        WebInterface.SendReportPlayer(userId, UserProfileController.userProfilesCatalog.Get(userId)?.userName);
 1243        GetSocialAnalytics().SendPlayerReport(PlayerReportIssueType.None, 0, PlayerActionSource.ProfileContextMenu, user
 1244        Hide();
 1245    }
 246
 247    private void OnDeleteUserButtonPressed()
 248    {
 0249        DataStore.i.notifications.GenericConfirmation.Set(GenericConfirmationNotificationData.CreateUnFriendData(
 250            UserProfileController.userProfilesCatalog.Get(userId)?.userName,
 251            () =>
 252            {
 0253                friendOperationsCancellationToken = friendOperationsCancellationToken.SafeRestart();
 0254                friendsController.RemoveFriendAsync(userId, friendOperationsCancellationToken.Token).Forget();
 0255                OnUnfriend?.Invoke(userId);
 0256            }), true);
 257
 0258        GetSocialAnalytics().SendFriendDeleted(UserProfile.GetOwnUserProfile().userId, userId, PlayerActionSource.Profil
 0259        Hide();
 0260    }
 261
 262    private void OnAddFriendButtonPressed()
 263    {
 264        // NOTE: if we don't add this, the friend request has strange behaviors
 0265        UserProfileController.i.AddUserProfileToCatalog(new UserProfileModel()
 266        {
 267            userId = userId,
 268            name = UserProfileController.userProfilesCatalog.Get(userId)?.userName
 269        });
 270
 0271        DataStore.i.HUDs.sendFriendRequest.Set(userId, true);
 0272        DataStore.i.HUDs.sendFriendRequestSource.Set((int)PlayerActionSource.ProfileContextMenu);
 0273    }
 274
 275    private void OnCancelFriendRequestButtonPressed()
 276    {
 277        async UniTaskVoid CancelFriendRequestAsync(string userId, CancellationToken cancellationToken = default)
 278        {
 279            try
 280            {
 0281                FriendRequest request = await friendsController.CancelRequestByUserIdAsync(userId, cancellationToken);
 282
 0283                GetSocialAnalytics()
 284                   .SendFriendRequestCancelled(request.From, request.To,
 285                        PlayerActionSource.ProfileContextMenu.ToString(), request.FriendRequestId);
 0286            }
 0287            catch (Exception e) when (e is not OperationCanceledException)
 288            {
 0289                e.ReportFriendRequestErrorToAnalyticsByUserId(userId, PlayerActionSource.ProfileContextMenu.ToString(),
 290                    friendsController, socialAnalytics);
 291
 0292                throw;
 293            }
 0294        }
 295
 0296        friendOperationsCancellationToken = friendOperationsCancellationToken.SafeRestart();
 0297        CancelFriendRequestAsync(userId, friendOperationsCancellationToken.Token).Forget();
 0298    }
 299
 300    private void OnMessageButtonPressed()
 301    {
 0302        OnOpenPrivateChatRequest?.Invoke(userId);
 0303        Hide();
 0304    }
 305
 306    private void OnBlockUserButtonPressed()
 307    {
 1308        bool blockUser = !isBlocked;
 309
 1310        if (blockUser)
 311        {
 1312            DataStore.i.notifications.GenericConfirmation.Set(GenericConfirmationNotificationData.CreateBlockUserData(
 313                UserProfileController.userProfilesCatalog.Get(userId)?.userName,
 314                () =>
 315                {
 1316                    WebInterface.SendBlockPlayer(userId);
 1317                    GetSocialAnalytics().SendPlayerBlocked(friendsController.IsFriend(userId), PlayerActionSource.Profil
 1318                    OnBlock?.Invoke(userId, blockUser);
 1319                }), true);
 320        }
 321        else
 322        {
 0323            WebInterface.SendUnblockPlayer(userId);
 0324            GetSocialAnalytics().SendPlayerUnblocked(friendsController.IsFriend(userId), PlayerActionSource.ProfileConte
 0325            OnBlock?.Invoke(userId, blockUser);
 326        }
 327
 1328        Hide();
 1329    }
 330
 331    private void OnMentionButtonPressed()
 332    {
 0333        DataStore.i.mentions.someoneMentionedFromContextMenu.Set($"@{userName.text}", true);
 0334        GetSocialAnalytics().SendMentionCreated(MentionCreationSource.ProfileContextMenu, userId);
 0335        Hide();
 0336    }
 337
 338    private void ProcessActiveElements(MenuConfigFlags flags)
 339    {
 25340        bool isOwnUser = UserProfile.GetOwnUserProfile().userId == userId;
 341
 25342        headerContainer.SetActive((flags & HEADER_FLAGS) != 0);
 25343        userName.gameObject.SetActive((flags & MenuConfigFlags.Name) != 0);
 25344        friendshipContainer.SetActive((flags & MenuConfigFlags.Friendship) != 0 && isFriendsEnabled && !isOwnUser);
 25345        deleteFriendButton.gameObject.SetActive((flags & MenuConfigFlags.Friendship) != 0 && isFriendsEnabled && !isOwnU
 25346        passportButton.gameObject.SetActive((flags & MenuConfigFlags.Passport) != 0);
 25347        blockButton.gameObject.SetActive((flags & MenuConfigFlags.Block) != 0 && !isOwnUser);
 25348        reportButton.gameObject.SetActive((flags & MenuConfigFlags.Report) != 0 && !isOwnUser);
 25349        messageButton.gameObject.SetActive((flags & MenuConfigFlags.Message) != 0 && !isBlocked && enableSendMessage && 
 350
 25351        if (mentionButton != null)
 25352            mentionButton.gameObject.SetActive((flags & MenuConfigFlags.Mention) != 0 && DataStore.i.HUDs.chatInputVisib
 25353    }
 354
 355    private bool Setup(string userId, MenuConfigFlags configFlags)
 356    {
 6357        this.userId = userId;
 358
 6359        UserProfile profile = UserProfileController.userProfilesCatalog.Get(userId);
 360
 6361        if (profile == null)
 362        {
 0363            ShowUserNotificationError(userId);
 0364            return false;
 365        }
 366
 6367        if (profile.isGuest || !UserProfile.GetOwnUserProfile().hasConnectedWeb3)
 0368            configFlags &= ~USES_FRIENDS_API_FLAGS;
 369
 6370        currentConfigFlags = configFlags;
 6371        ProcessActiveElements(configFlags);
 372
 6373        if ((configFlags & MenuConfigFlags.Block) != 0)
 374        {
 4375            isBlocked = UserProfile.GetOwnUserProfile().blocked.Contains(userId);
 4376            blockText.text = isBlocked ? BLOCK_BTN_UNBLOCK_TEXT : BLOCK_BTN_BLOCK_TEXT;
 377        }
 378
 6379        if ((configFlags & MenuConfigFlags.Name) != 0)
 380        {
 4381            string name = profile?.userName;
 4382            userName.text = name;
 383        }
 384
 6385        if ((configFlags & USES_FRIENDS_API_FLAGS) != 0)
 386        {
 6387            UserStatus status = friendsController.GetUserStatus(userId);
 6388            SetupFriendship(status?.friendshipStatus ?? FriendshipStatus.NOT_FRIEND);
 6389            friendsController.OnUpdateFriendship -= OnFriendActionUpdate;
 6390            friendsController.OnUpdateFriendship += OnFriendActionUpdate;
 391        }
 392
 6393        return true;
 394    }
 395
 396    private void SetupFriendship(FriendshipStatus friendshipStatus)
 397    {
 12398        bool friendshipEnabled = (currentConfigFlags & MenuConfigFlags.Friendship) != 0 && isFriendsEnabled;
 12399        bool messageEnabled = (currentConfigFlags & MenuConfigFlags.Message) != 0 && isFriendsEnabled;
 400
 12401        if (friendshipStatus == FriendshipStatus.FRIEND)
 402        {
 2403            if (friendshipEnabled)
 404            {
 1405                friendAddContainer.SetActive(false);
 1406                friendRemoveContainer.SetActive(true);
 1407                friendRequestedContainer.SetActive(false);
 1408                deleteFriendButton.gameObject.SetActive(true);
 409            }
 410
 3411            if (messageEnabled) { messageButton.gameObject.SetActive(!isBlocked && enableSendMessage); }
 412        }
 10413        else if (friendshipStatus == FriendshipStatus.REQUESTED_TO)
 414        {
 2415            if (friendshipEnabled)
 416            {
 1417                friendAddContainer.SetActive(false);
 1418                friendRemoveContainer.SetActive(false);
 1419                friendRequestedContainer.SetActive(true);
 1420                deleteFriendButton.gameObject.SetActive(false);
 421            }
 422
 3423            if (messageEnabled) { messageButton.gameObject.SetActive(false); }
 424        }
 8425        else if (friendshipStatus == FriendshipStatus.NOT_FRIEND)
 426        {
 8427            if (friendshipEnabled)
 428            {
 6429                friendAddContainer.SetActive(true);
 6430                friendRemoveContainer.SetActive(false);
 6431                friendRequestedContainer.SetActive(false);
 6432                deleteFriendButton.gameObject.SetActive(false);
 433            }
 434
 14435            if (messageEnabled) { messageButton.gameObject.SetActive(false); }
 436        }
 0437        else if (friendshipStatus == FriendshipStatus.REQUESTED_FROM)
 438        {
 0439            if (friendshipEnabled)
 440            {
 0441                friendAddContainer.SetActive(true);
 0442                friendRemoveContainer.SetActive(false);
 0443                friendRequestedContainer.SetActive(false);
 0444                deleteFriendButton.gameObject.SetActive(false);
 445            }
 446
 0447            if (messageEnabled) { messageButton.gameObject.SetActive(false); }
 448        }
 4449    }
 450
 451    private void OnFriendActionUpdate(string userId, FriendshipAction action)
 452    {
 6453        if (this.userId != userId) { return; }
 454
 455        switch (action)
 456        {
 457            case FriendshipAction.APPROVED:
 2458                SetupFriendship(FriendshipStatus.FRIEND);
 2459                break;
 460            case FriendshipAction.REQUESTED_TO:
 2461                SetupFriendship(FriendshipStatus.REQUESTED_TO);
 2462                break;
 463            case FriendshipAction.DELETED:
 464            case FriendshipAction.CANCELLED:
 465            case FriendshipAction.REJECTED:
 466            case FriendshipAction.NONE:
 2467                SetupFriendship(FriendshipStatus.NOT_FRIEND);
 2468                break;
 469            case FriendshipAction.REQUESTED_FROM:
 0470                SetupFriendship(FriendshipStatus.REQUESTED_FROM);
 471                break;
 472        }
 0473    }
 474
 475    private ISocialAnalytics GetSocialAnalytics()
 476    {
 2477        if (socialAnalytics == null)
 478        {
 0479            socialAnalytics = new SocialAnalytics(
 480                Environment.i.platform.serviceProviders.analytics,
 481                new UserProfileWebInterfaceBridge());
 482        }
 483
 2484        return socialAnalytics;
 485    }
 486
 487    private static void ShowUserNotificationError(string userIdOrName)
 488    {
 0489        DataStore.i.notifications.DefaultErrorNotification.Set("This user was not found.", true);
 0490        Debug.LogError($"User {userIdOrName} was not found in the catalog!");
 0491    }
 492
 493    private void OnCopyNameButtonPressed()
 494    {
 0495        clipboard.WriteText($"@{userName.text}");
 0496        nameCopiedToast.gameObject.SetActive(true);
 0497        nameCopiedToast.ShowDelayHide(3);
 0498        copyPasteAnalyticsService.Copy("name");
 0499    }
 500
 501    private IEnumerator ClampPositionToScreenBordersOnNextFrame()
 502    {
 6503        yield return null;
 0504        ClampPositionToScreenBorders(transform.position);
 0505    }
 506
 507#if UNITY_EDITOR
 508
 509    //This is just to process buttons and container visibility on editor
 510    private void OnValidate()
 511    {
 13512        if (headerContainer == null)
 0513            return;
 514
 13515        ProcessActiveElements(menuConfigFlags);
 13516    }
 517#endif
 518}