Take a sneak peak of our new documentation Read More
Last Updated: 11/15/2022, 11:31:21 AM

# Friends

# Quick Reference

References
using AccelByte.Api;
using AccelByte.Core;
using AccelByte.Models;
Friend Notification Events
AccelBytePlugin.GetLobby().FriendsStatusChanged += result =>{};    
AccelBytePlugin.GetLobby().FriendRequestAccepted += result =>{};   
AccelBytePlugin.GetLobby().OnIncomingFriendRequest += result =>{};
AccelBytePlugin.GetLobby().OnUnfriend += result =>{};              
AccelBytePlugin.GetLobby().FriendRequestCanceled += result =>{};   
AccelBytePlugin.GetLobby().FriendRequestRejected += result =>{};  
Request Friend
AccelBytePlugin.GetLobby().RequestFriend(userInfo.userId, result =>
{
    if (result.IsError)
    {
        Debug.Log($"Failed to send a friends request: error code: {result.Error.Code} message: {result.Error.Message}");
    }
    else
    {
        Debug.Log("Sent Friends Request");
    }
});
Unfriend
AccelBytePlugin.GetLobby().Unfriend(userId, result =>
{
    // Check this is an error
    if (result.IsError)
    {
        Debug.Log($"Failed to unfriend a friend: code: {result.Error.Code}, message: {result.Error.Message}");
    }
    else
    {
        Debug.Log("Successfully unfriend a friend!");
    }
});
Load Friend List
AccelBytePlugin.GetLobby().LoadFriendsList(result =>
{
    // Check this is not an error
    if (!result.IsError)
    {    
        foreach (string friendID in result.Value.friendsId)
        {
            Debug.Log($"Friend : {friendID}");
        }    
    }
    else
    {
        Debug.LogWarning("Error in Getting Friends");
    }
});
List Incoming Friend Requests
// Get all Incoming Friend Requests
AccelBytePlugin.GetLobby().ListIncomingFriends(result =>
{
    // Check for an Error
    if (result.IsError)
    {
        Debug.LogWarning($"Unable to get Incoming Requests Code: {result.Error.Code}, Message: {result.Error.Message}");
    }
    else
    {
        // List incoming friends
        Debug.Log("Get list incoming friends is successfully");
    }
});
List Outgoing Friend Requests
// Get all Outgoing Friend Requests
AccelBytePlugin.GetLobby().ListOutgoingFriends(result =>
{
    // Check for an Error
    if (result.IsError)
    {
        Debug.LogWarning($"Unable to get Outgoing Friend Lists Code: {result.Error.Code}, Message: {result.Error.Message}");
    }
    else
    {
        // List outgoing friends
        Debug.Log("Get list outgoing friends is successfully");
    }
});
Get UserData by UserID
AccelBytePlugin.GetUser().GetUserByUserId(friendID, x =>
{
    //Do something with the user Data
});
Get List of Players by Searching
AccelBytePlugin.GetUser().SearchUsers(query, result =>
{
    if (!result.IsError)
    {
        // List UserID's Found using Query
    }
    else
    {
        Debug.LogWarning($"Unable to Query Users Code: {result.Error.Code}, Message: {result.Error.Message}");
    }
});

# Quickstart Guide

In this tutorial, you will learn how to use the Friend service. This guide assumes that you have already implemented the Lobby Services (opens new window).

This guide will be more abstract than the previous guides as the specifics for how each game will implement Friends can vary dramatically. Skip to the Step by Step (opens new window) section to see how we implemented each feature in our tutorial project.

There are a handful of concepts and classes to familiarize yourself with (you can find most of these classes within the UserModels.cs file inside the plugin SDK).

AccelByte’s SDK uses a mixture of Websocket and HTTP requests, generally presented to you through a wrapper layer that uses callbacks and actions to return your data. Because of this, you must design your UI to accommodate delayed callbacks and, sometimes, several layers of callbacks (e.g., requesting a list of Friends > Requesting specific data about friends > Requesting the avatar of a friend).

We will start by adding simple friend logic into the game.

  1. Create a new script called FriendManagementHandler.cs and attach it to the AccelByteHandler gameObject.

  2. Add the following AccelByte Libraries to the top of the script:

using AccelByte.Api;
using AccelByte.Models;
using AccelByte.Core;
  1. In the AccelByte SDK, the UserID is used for most functions. For example, to add friends, the function needs a User ID as a parameter. In the real UI, the player rarely uses a User ID to add a friend because a User ID is not easily memorized. Instead, you can use the Search function to find a User ID by using a display name, and then add that player as a friend, as in the example below:
void AddFriend(string displayName)
{
    AccelBytePlugin.GetUser().SearchUsers(displayName, result =>
    {
        // If not an error
        if (!result.IsError)
        {
            foreach (PublicUserInfo userInfo in result.Value.data)
            {
                // Check if display name text has a same value as the result
                if (userInfo.displayName == query)
                {
                    AccelBytePlugin.GetLobby().RequestFriend(userInfo.userId, addResult =>
                    {
                        if (addResult.IsError)
                        {
                            Debug.Log($"Failed to send a friends request: error code: {addResult.Error.Code} message: {addResult.Error.Message}");
                        }
                        else
                        {
                            Debug.Log("Sent Friends Request");
                        }
                    });
                }
                else
                {
                    Debug.Log("User is not found");
                }
            }
        }
        else
        {
            Debug.LogWarning($"Unable to Query Users Code: {result.Error.Code}, Message: {result.Error.Message}");
        }
    });
}

  1. You can see the status of the Outgoing Friend Request by using the ListOutgoingFriends function. Any Incoming Friend Requests can be found by using the ListIncomingFriends function. These two functions will return the UserID, not the display name. Use the following code to obtain the Incoming/Outgoing Friend Request lists with display names shown:
private void DisplayPending()
{
    // Get all Incoming Friend Requests
    AccelBytePlugin.GetLobby().ListIncomingFriends(result =>
    {
        // Check for an Error
        if (result.IsError)
        {
            Debug.LogWarning($"Unable to get Incoming Requests Code: {result.Error.Code}, Message: {result.Error.Message}");
        }
        else
        {
            // Loop through all the UserID's returned by the Friends callback and get their PublicUserData
            foreach (string userID in result.Value.friendsId)
            {
                // Request the PublicUserData for the specific Friend
                AccelBytePlugin.GetUser().GetUserByUserId(userID, userResult =>
                {
                    // If it's an Error, report it and do nothing else
                    if (userResult.IsError)
                    {
                        Debug.LogWarning($"Unable to User Code: {userResult.Error.Code}, Message: {userResult.Error.Message}");
                    }
                    // If we have valid data, log the display name
                    else
                    {
                        Debug.Log($"Friend request from : {userResult.Value.displayName}");
                    }
                });
            }
        }
    });

    // Get all Outgoing Friend Requests
    AccelBytePlugin.GetLobby().ListOutgoingFriends(result =>
    {
        // Check for an Error
        if (result.IsError)
        {
            Debug.LogWarning($"Unable to get Outgoing Friend Lists Code: {result.Error.Code}, Message: {result.Error.Message}");
        }
        else
        {
            // Loop through all the UserID's returned by the Friends callback and get their PublicUserData
            foreach (string userID in result.Value.friendsId)
            {
                // Request the PublicUserData for the specific Friend
                AccelBytePlugin.GetUser().GetUserByUserId(userID, userResult =>
                {
                    // If it's an Error, report it and do nothing else
                    if (userResult.IsError)
                    {
                        Debug.LogWarning($"Unable to User Code: {userResult.Error.Code}, Message: {userResult.Error.Message}");
                    }
                    // If we have valid data, log the display name
                    else
                    {
                        Debug.Log($"Pending Friend Request to : {userResult.Value.displayName}");
                    }
                });
            }
        }
    });
}

  1. Use the following code to load the Friend List with the current activity and availability:
private void GetFriends()
{
    AccelBytePlugin.GetLobby().LoadFriendsList(result =>
    {
        //Check this is not an error
        if (!result.IsError)
        {
            //Check if no friends were returned
            if (result.Value.friendsId.Length <= 0)
            {
                return;
            }

            //Fire off Requests to create UI for each friend
            Debug.Log("Loaded Friends List Successfully");

            List<string> userIds = new List<string>();
            foreach (string friendID in result.Value.friendsId)
            {
                userIds.Add(friendID);
            }

            AccelBytePlugin.GetLobby().BulkGetUserPresence(userIds.ToArray(), bulkResult =>
            {
                if (!bulkResult.IsError)
                {
                    foreach (var friend in bulkResult.Value.data)
                    {
                        AccelBytePlugin.GetUser().GetUserByUserId(friend.userID, x =>
                        {
                            Debug.Log($"Friend : {x.Value.displayName}, Status : {friend.activity}, and Availability : {friend.availability.ToString()}");
                        });
                    }
                }
            });
        }
        else
        {
            Debug.LogWarning("Error in Getting Friends");
        }
    });
}

  1. You can also Unfriend the currently displayed friend with the following code:
private void Unfriend(string userId)
{
    AccelBytePlugin.GetLobby().Unfriend(userId, result =>
    {
        if (result.IsError)
        {
            Debug.Log($"Failed to unfriend a friend: code: {result.Error.Code}, message: {result.Error.Message}");
        }
        else
        {
            Debug.Log("Successfully unfriend a friend!");
        }
    });
}

  1. The Friends service features some notification events that will help make your Friends List more responsive. In the following steps, we will show you how to add events in LobbyHandler.cs and on the ConnectToLobby() function, and then how to use the Debug.Log to check that each notification is working correctly.
  • FriendsStatusChanged

This event is triggered if a friend’s activity or availability status changes.

AccelBytePlugin.GetLobby().FriendsStatusChanged += result =>
{
        Debug.Log($"Friend status change into status : {result.Value.activity} and availability : {result.Value.availability} from user : {result.Value.userID}");
};

  • FriendRequestAccepted

This event is triggered if a pending Friend Request is accepted by another player.

AccelBytePlugin.GetLobby().FriendRequestAccepted += result =>
{
        Debug.Log($"Accepted a Friend Request from user {result.Value.friendId}");
};

  • OnIncomingFriendRequest

This event is triggered if someone adds the player as a Friend.

AccelBytePlugin.GetLobby().OnIncomingFriendRequest += result =>
{
        Debug.Log($"Received a Friend Request from user {result.Value.friendId}");
};

  • OnUnfriend

This event is triggered if the player is unfriended by a Friend.

AccelBytePlugin.GetLobby().OnUnfriend += result =>
{
        Debug.Log($"Unfriended User {result.Value.friendId}");
};  

  • FriendRequestCanceled

This event is triggered if an Incoming Friend Request is canceled by the requesting player.

AccelBytePlugin.GetLobby().FriendRequestCanceled += result =>
{
        Debug.Log($"Cancelled a Friend Request from user {result.Value.userId}");
};

  • FriendRequestRejected

This event is triggered if the player’s Friend Request is rejected by another player.

AccelBytePlugin.GetLobby().FriendRequestRejected += result =>
{
        Debug.Log($"Rejected a Friend Request from user {result.Value.userId}");
};

Congratulations! You have successfully implemented the basics of the Friends service!

Continue on for a step by step example of the UI and code implementation. Otherwise, you are now ready to move on to Party services (opens new window).

# Step by Step Guide

UI Implementation
  1. For the Friends Page, create a new panel or scene with the following objects:

    • Header text
    • Exit button
    • An Empty panel, for Friends Page content (optional)
  2. Since the Friends Page will have a lot of items to display, we have divided the page into three sections: Friends, Pending, and Blocked. To do this in your own game, add the following UI objects:

    • 3 Buttons: one each for the Friends tab, Pending tab, and Blocked tab.
    • 3 Panels for each tab’s button. To avoid confusion, only set the first panel (Friends panel) as active.

    You can add the UIs into separate panels, such as in the example below:

    Unity

  3. Move to the tab panels. Here, we will prepare the page to list all the Friends items. Add the following to each tab panel:

  4. Friends tab panel:

    • Friends button which will be used to open the Search Friends panel
    • Friends List panel
    • Friends Display placeholder text
    • Friends Display prefab

    Once completed, create a new panel, parent it to Friends List panel, and add the following UIs:

    • Profile image
    • Status Indicator image
    • Display Name text
    • Online Status text
    • Chat button
    • Invite to Party button
    • Unfriend button
    • Block button

    Unity

    Below is an example view of the Friends tab panel, along with its hierarchy:

    Unity

  5. Pending tab panel:

    Incoming Request List:

    • Scroll View with Scrollbar Vertical only
    • Incoming Pending Requests list panel, and parent it under the Scroll View’s content
    • Incoming placeholder text
    • Incoming Request Display prefab

    Create a new panel, parent it to the Incoming Pending Requests list panel, and add the following UI elements:

    • Profile image
    • Display Name text
    • Accept button
    • Decline button
    • Block button

    Unity

    Outgoing Request List:

    • Sent Request List panel, and parent it under the Scroll View’s content
    • Outgoing placeholder text
    • Outgoing Request Display prefab

    Create a new panel, parent it to the Incoming Pending Requests list panel, and add the following UI elements:

    • Profile image
    • Display Name text
    • Cancel button

    Unity

    Below is an example view of the Pending tab panel, along with its hierarchy:

    Unity

  6. Blocked tab panel:

    • Blocked Players List panel
    • Blocked placeholder text
    • Blocked Player Display prefab

    Create a new panel, parent it to the Incoming Blocked Players list panel, and add the following UI elements:

    • Profile image
    • Display Name text
    • Unblock button

    Unity

    Below is an example view of the Blocked tab panel, along with its hierarchy:

    Unity

  7. Lastly, for the Search Friends pop-up, create a new panel, and add the following UI elements:

    • Exit button
    • Friend Search input field
    • Scroll View with Scrollbar Vertical only
    • Search placeholder text
    • Add Friends Display prefab

    Create a new panel, parent it to the Incoming Blocked Players list panel, and add the following UI elements:

    • Profile image
    • Display Name text
    • Add Friend button

    Unity

    Below is an example view of the Search Friends pop-up, along with its hierarchy:

    Unity

  8. To make it easier to move to another panel, create a new menu panel and add the following UI elements:

    • Lobby button
    • Friends button
    • Quit button

    Unity

Code Implementation
  1. Go to the FriendsManagementHandler.cs script. Remove the AddFriend and Unfriend functions that you have previously created. We'll add these functionalities later to another function with additional improvement.

  2. Add the following AccelByte and UnityEngine libraries to the top of the script:

using AccelByte.Api;
using AccelByte.Models;
using UnityEngine.UI;
using UnityEngine.Serialization;

To avoid ambiguous references, add this code to the top of the script:

using Button = UnityEngine.UI.Button;
  1. Create a new dictionary that will hold Friend UserIDs.
private Dictionary<string, FriendStatusPanel> friendUIDictionary = new Dictionary<string, FriendStatusPanel>();
  1. Add the following UI references to FriendsManagementHandler.cs:
[Header("Panels")]
public GameObject FriendsManagementWindow;

[FormerlySerializedAs("FriendsPanel")]
[SerializeField]
private RectTransform friendsPanel;
[SerializeField]
private RectTransform requestsPanel;
[SerializeField]
private RectTransform blockedPanel;

#region Buttons
[Header("Buttons")]

[SerializeField]
private Button friendsTabButton;
[SerializeField]
private Button pendingTabButton;
[SerializeField]
private Button blockedTabButton;

[SerializeField]
private Button exitButton;

#endregion

[Header("Friends Panel")]
[SerializeField]
private Transform friendsDisplayPanel;

[FormerlySerializedAs("FriendsDisplayPrefab")]
[SerializeField]
private GameObject friendsDisplayPrefab;

[SerializeField]
private Button friendsSearchButton;

[SerializeField]
private Transform friendsDisplayPlaceholder;

#region Search
[Header("Search Panel")]
[SerializeField]
private GameObject addFriendsPrefab;
[SerializeField]
private Transform friendsSearchPanel;

[SerializeField]
private InputField friendsSearchInputField;
[SerializeField]
private RectTransform friendsSearchScrollView;
[SerializeField]
private Button friendsSearchQuitButton;

[SerializeField]
private Transform friendsSearchPlaceholder;

#endregion

#region Pending
[Header("Pending Panel")]
[SerializeField]
private RectTransform pendingIncomingRequestsContent;
[SerializeField]
private RectTransform pendingOutgoingRequestsContent;

[SerializeField]
private GameObject pendingIncomingRequestsPrefab;
[SerializeField]
private GameObject pendingOutgoingRequestsPrefab;

[SerializeField]
private Transform pendingIncomingRequestPlaceholder;
[SerializeField]
private Transform pendingOutgoingRequestPlaceholder;

[SerializeField]
private Text pendingIncomingRequestText;
[SerializeField]
private Text pendingOutgoingRequestText;

#endregion

#region Blocked
[Header("Blocked")]
[SerializeField]
private RectTransform blockedContent;

[SerializeField]
private GameObject blockedUserPrefab;

[SerializeField]
private Transform blockedDisplayPlaceholder;

#endregion
  1. Create a function that will destroy all the children of the parent transform to reset the UI.
/// A utility function to Destroy all Children of the parent transform. Optionally do not remove a specific Transform
/// <param name="parent">Parent Object to destroy children</param>
/// <param name="doNotRemove">Optional specified Transform that should NOT be destroyed</param>
private static void LoopThroughTransformAndDestroy(Transform parent, Transform doNotRemove = null)
{
    //Loop through all the children and add them to a List to then be deleted
    List<GameObject> toBeDeleted = new List<GameObject>();
    foreach (Transform t in parent)
    {
        //except the Do Not Remove transform if there is one
        if (t != doNotRemove)
        {
            toBeDeleted.Add(t.gameObject);
        }
    }
    //Loop through list and Delete all Children
    for (int i = 0; i < toBeDeleted.Count; i++)
    {
        Destroy(toBeDeleted[i]);
    }
}
  1. Create a function to display Blocked players in FriendsManagementHandler.cs:
private void DisplayBlocked()
{
    //Cleanup First
LoopThroughTransformAndDestroy(blockedContent.transform,blockedDisplayPlaceholder);

    //Get Blocked List
    AccelBytePlugin.GetLobby().GetListOfBlockedUser(result =>
    {
        //Check for an Error
        if (result.IsError)
        {
            Debug.LogWarning($"Unable to get Blocked Player List Code: {result.Error.Code}, Message: {result.Error.Message}");

            blockedDisplayPlaceholder.gameObject.SetActive(true);        
        }
        else
        {
            blockedDisplayPlaceholder.gameObject.SetActive(false);
            //Loop through all the UserID's returned by the callback and get their PublicUserData
            foreach (BlockedData blockedUser in result.Value.data)
            {
                //Request the PublicUserData for the specific User
                AccelBytePlugin.GetUser().GetUserByUserId(blockedUser.blockedUserId, userResult =>
                {
                    //If it's an Error, report it and do nothing else
                    if (userResult.IsError)
                    {
                        Debug.LogWarning($"Unable to User Code: {userResult.Error.Code}, Message: {userResult.Error.Message}");
                    }
                });        
            }
        }
    });  
}
  1. To change the tab view, prepare an enum variable that will hold the current tab value.
public enum FriendsMode
{
    Default,
    Friends,
    Pending,
    Blocked
};

private FriendsMode _displayMode = FriendsMode.Default;
  1. Create a function that states what will happen if we change the tab UIs based on the current FriendsMode.
private FriendsMode DisplayMode
{
    get => _displayMode;
    set
    {
        switch (value)
        {
            case FriendsMode.Default:
                friendsPanel.gameObject.SetActive(true);
                requestsPanel.gameObject.SetActive(false);
                blockedPanel.gameObject.SetActive(false);
                break;

            case FriendsMode.Friends:
                friendsPanel.gameObject.SetActive(true);
                requestsPanel.gameObject.SetActive(false);
                blockedPanel.gameObject.SetActive(false);
                GetFriends();
                break;

            case FriendsMode.Pending:
                requestsPanel.gameObject.SetActive(true);
                friendsPanel.gameObject.SetActive(false);
                blockedPanel.gameObject.SetActive(false);
                DisplayPending();
                break;

            case FriendsMode.Blocked:
                blockedPanel.gameObject.SetActive(true);
                friendsPanel.gameObject.SetActive(false);
                requestsPanel.gameObject.SetActive(false);
                DisplayBlocked();
                break;
        }
    }
}
  1. Create a function to search for a player in FriendsManagementHandler.cs:
private void SearchForFriends(string query)
{
    AccelBytePlugin.GetUser().SearchUsers(query, result =>
    {
        if (!result.IsError)
        {
            ListQueriedusers(result.Value);
        }
        else
        {
            Debug.LogWarning($"Unable to Query Users Code: {result.Error.Code}, Message: {result.Error.Message}");
        }
    });
}
  1. Create a function to update the Friends query result in the UI:
private void ListQueriedusers(PagedPublicUsersInfo pagedInfo)
{
    //Cleanup First
    LoopThroughTransformAndDestroy(friendsSearchScrollView.transform, friendsSearchPlaceholder);

    if (pagedInfo.data.Length <=0)
    {
        friendsSearchPlaceholder.gameObject.SetActive(true);
    }
    else
    {
        friendsSearchPlaceholder.gameObject.SetActive(false);
        foreach (PublicUserInfo info in pagedInfo.data)
        {
            FriendsAddPanel addPanel = Instantiate(addFriendsPrefab,friendsSearchScrollView).GetComponent<FriendsAddPanel>();
            addPanel.Create(info);
        }
    }
}
  1. Create a function to open the AddFriendPanel prefab.
private void DisplaySearch()
{
    friendsSearchPanel.gameObject.SetActive(true);  
    LoopThroughTransformAndDestroy(friendsSearchScrollView.transform, friendsSearchPlaceholder);
    friendsSearchPlaceholder.gameObject.SetActive(true);
}
  1. Create a function to update the Friends Data in the UI.
public void UpdateFriends(FriendsStatusNotif notification)
{
    //Find the friend and update it's UI
    if (friendUIDictionary.ContainsKey(notification.userID))
    {
        friendUIDictionary[notification.userID].UpdateUser(notification);
    }
    //Otherwise We should handle this in some way, possibly creating a Friend UI Piece
    else
    {
        Debug.Log("Unregistered Friend received a Notification");
    }
}
  1. Use the Lobby’s notification event to call the UpdateFriends() function every time a friend’s status updates. To do this, create a function in NotificationHandler.cs:
/// Called when friend status is changed
public void OnFriendsStatusChanged(Result<FriendsStatusNotif> result)
{
    GetComponent<FriendsManagementHandler>().UpdateFriends(result.Value);
}

You can create more functions for other events and use the debug log to verify if there are any updates.

/// Called when friend request is accepted
public void OnFriendRequestAccepted(Result<Friend> result)
{
    Debug.Log($"Accepted a Friend Request from user {result.Value.friendId}");
}

/// Called when there is incoming friend request
public void OnIncomingFriendRequest(Result<Friend> result)
{
    Debug.Log($"Received a Friend Request from user {result.Value.friendId}");
}

/// Called when friend is unfriend
public void OnUnfriend(Result<Friend> result)
{
    Debug.Log($"Unfriended User {result.Value.friendId}");
}

/// Called when friend request is canceled
public void OnFriendRequestCanceled(Result<Acquaintance> result)
{
    Debug.Log($"Cancelled a Friend Request from user {result.Value.userId}");
}

/// Called when friend request is rejected
public void OnFriendRequestRejected(Result<Acquaintance> result)
{
    Debug.Log($"Rejected a Friend Request from user {result.Value.userId}");
}
  1. Create a script called MenuHandler.cs to handle the menu panel and attach it to the AccelByteHandler gameObject, then add the following code to the MenuHandler.cs:
public Transform Menu;

public Button FriendsButton;

private bool initialize = false;

public void Create()
{
    if (initialize) return;

    initialize = true;
}
  1. Open LobbyHandler.cs and navigate to the ConnectToLobby() function, then add the following code:
public void ConnectToLobby()
{
    ...

    //Init menu handler
    GetComponent<MenuHandler>().Create();
    GetComponent<MenuHandler>().Menu.gameObject.SetActive(true);

    ...

    //Friends
    _lobby.FriendsStatusChanged += notificationHandler.OnFriendsStatusChanged;
    _lobby.FriendRequestAccepted += notificationHandler.OnFriendRequestAccepted;
    _lobby.OnIncomingFriendRequest += notificationHandler.OnIncomingFriendRequest;
    _lobby.FriendRequestCanceled += notificationHandler.OnFriendRequestCanceled;
    _lobby.FriendRequestRejected += notificationHandler.OnFriendRequestRejected;
    _lobby.OnUnfriend += notificationHandler.OnUnfriend;  
    ...
}
  1. While still in LobbyHandler.cs, add the following code to the RemoveLobbyListeners() function to reset the Friends Notification delegate:
public void RemoveLobbyListeners()
{
    ...
    //Friends
    _lobby.FriendsStatusChanged -= notificationHandler.OnFriendsStatusChanged;
    _lobby.FriendRequestAccepted -= notificationHandler.OnFriendRequestAccepted;
    _lobby.OnIncomingFriendRequest -= notificationHandler.OnIncomingFriendRequest;
    _lobby.FriendRequestCanceled -= notificationHandler.OnFriendRequestCanceled;
    _lobby.FriendRequestRejected -= notificationHandler.OnFriendRequestRejected;
    _lobby.OnUnfriend -= notificationHandler.OnUnfriend;
}
  1. Once completed, you can create a function that will set up your Friends UI state and listener in FriendsManagementHandler.cs.
private bool setupStatus = false;
...

public void Setup()
{
    // reset the exit button's listener, then add the listener based on the exit screen type
    exitButton.onClick.RemoveAllListeners();
    exitButton.onClick.AddListener(() =>
    {
        FriendsManagementWindow.SetActive(false);
        GetComponent<MenuHandler>().Menu.gameObject.SetActive(true);
    });

    // Check whether the FriendsPanel already set up or not
    if (setupStatus)
    {
        DisplayMode = FriendsMode.Default;
        return;
    }
    // Run the setup if it still hasn't
    else
    {
        setupStatus = true;

        DisplayMode = FriendsMode.Friends;

        // reset listeners, so it won't triggered more than once
        friendsTabButton.onClick.RemoveAllListeners();
        pendingTabButton.onClick.RemoveAllListeners();
        blockedTabButton.onClick.RemoveAllListeners();

        // add the listeners
        friendsTabButton.onClick.AddListener(() => DisplayMode = FriendsMode.Friends);
        pendingTabButton.onClick.AddListener(() => DisplayMode = FriendsMode.Pending);
        blockedTabButton.onClick.AddListener(() => DisplayMode = FriendsMode.Blocked);
        friendsSearchPanel.gameObject.SetActive(false);
        friendsDisplayPlaceholder.gameObject.SetActive(true);
        friendsSearchQuitButton.onClick.AddListener(() => friendsSearchPanel.gameObject.SetActive(false));
        friendsSearchButton.onClick.AddListener(DisplaySearch);
        friendsSearchInputField.onEndEdit.AddListener(SearchForFriends);
        friendsSearchButton.onClick.AddListener(() => friendsSearchPanel.gameObject.SetActive(true));
    }
}
  1. To prepare the Friends panel so it can be opened from the Menu, add the following code to the MenuHandler.cs:
public void Create()
{
    ...

    FriendsButton.onClick.AddListener(() =>
    {
GetComponent<FriendsManagementHandler>().Setup();
        Menu.gameObject.SetActive(false);
GetComponent<FriendsManagementHandler>().FriendsManagementWindow.SetActive(true);
    });
}
  1. In the Unity Editor, navigate to the AccelByteHandler gameObject and drag the following objects to their references:

Unity

  1. Create a new script called FriendStatusPanel.cs and attach it to the FriendDisplay prefab.

  2. Add the following AccelByte and UnityEngine libraries to the top of the script:

using AccelByte.Api;
using AccelByte.Models;
using UnityEngine;
using UnityEngine.UI;

To avoid ambiguous references, add this code to the top of the script:

using Image = UnityEngine.UI.Image;
  1. Add the following references to the necessary UIs in FriendStatusPanel.cs:
[SerializeField]
private Image profilePicture;
[SerializeField]
private Image statusDisplay;

[SerializeField]
private Button chatButton;
[SerializeField]
private Button inviteToPartyButton;
[SerializeField]
private Button unfriendButton;
[SerializeField]
private Button blockButton;

[SerializeField]
private Text displayNameText;
[SerializeField]
private Text onlineStatusText;

Once completed, create a variable that will hold the User Data:

PublicUserData _userData;
  1. Create a function to update the Friends list online statuses UI in FriendStatusPanel.cs.
public void SetOnlineStatus(FriendsStatusNotif notification)
{
        switch (notification.availability)
        {
            case "offline":
                onlineStatusText.text = "Offline";
                statusDisplay.color = Color.black;
                break;
            case "online":
                onlineStatusText.text = "Online";
                statusDisplay.color = Color.green;
                break;
            case "busy":
                onlineStatusText.text = "Busy";
                statusDisplay.color = Color.yellow;
                break;
            case "invisible":
                onlineStatusText.text = "Offline";
                statusDisplay.color = Color.black;
                break;
            default:
                onlineStatusText.text = $"INVALID UNHANDLED {notification.availability}";
                statusDisplay.color = Color.magenta;
                break;
        }

        Debug.Log($"Friend Status for {notification.userID} changed to {notification.availability}");
}
  1. While still in FriendStatusPanel.cs, create functions to update user data in the UI and the player’s profile picture (avatar).
public void Create(PublicUserData pud)
{
        _userData = pud;
        displayNameText.text = _userData.displayName;
}
  1. While still in FriendStatusPanel.cs, create a new function that will handle all the buttons in the display prefab, and then add a listener to Unfriend with the SDK service.
public void SetupButton()
{
    unfriendButton.onClick.AddListener(() =>
    {
        AccelBytePlugin.GetLobby().Unfriend(_userData.userId, result =>
        {
            if (result.IsError)
            {
                Debug.Log($"Failed to unfriend a friend: code: {result.Error.Code}, message: {result.Error.Message}");
            }
            else
            {
                Debug.Log("Successfully unfriend a friend!");
                LobbyHandler.Instance.GetComponent<FriendsManagementHandler>().RefreshFriendsList();
            }
        });
    });
}
  1. In the Unity Editor, on your FriendsDisplay prefab, drag the following objects to the corresponding references:

Unity

  1. Create a script called FriendIncomingPanel.cs.

  2. Add the following AccelByte and UnityEngine Libraries to the top of the script:

using AccelByte.Api;
using AccelByte.Models;
using UnityEngine;
using UnityEngine.UI;
using Image = UnityEngine.UI.Image;
  1. Add some UI references and a variable to hold the User Data:
[SerializeField]
private Image profilePicture;

[SerializeField]
private Text displayNameText;
[SerializeField]
private Button acceptFriendButton;
[SerializeField]
private Button declineFriendButton;
[SerializeField]
private Button blockFriendButton;

private PublicUserData _userData;
  1. Create functions to Accept and Reject a Friend Request.
/// An initialisation Function required to be called to Populate the UI appropriately
public void Create(PublicUserData userData)
{
    //Cache the PublicUserData
    _userData = userData;
    //Set the Display Name
    displayNameText.text = _userData.displayName;
    //Setup the Button to Accept a Friend Request
    acceptFriendButton.onClick.AddListener(() =>
    {
        //Make the Call to Accept the Friend Request using the cached PublicUserData UserID
        AccelBytePlugin.GetLobby().AcceptFriend(_userData.userId, result =>
        {
            if (result.IsError)
            {
                //We would probably want to display some user-feedback if the request is not successful
                Debug.Log($"Failed to accept a friend request: error code: {result.Error.Code} message: {result.Error.Message}");
            }
            else
            {
                Debug.Log("Accepted Friend Request");
                //Destroy the UI Piece if the request was successful to remove it from the list
                Destroy(gameObject);
            }
        });

    });
    //Setup the Button to Decline a Friend Request
    declineFriendButton.onClick.AddListener(() =>
    {
        //Make the Call to Decline the Friend Request using the cached PublicUserData UserID
        AccelBytePlugin.GetLobby().RejectFriend(_userData.userId, result =>
        {
            if (result.IsError)
            {
                //We would probably want to display some user-feedback if the request is not successful
                Debug.Log($"Failed to decline a friend request: error code: {result.Error.Code} message: {result.Error.Message}");
            }
            else
            {
                Debug.Log("Declined Friend Request");
                //Destroy the UI Piece if the request was successful to remove it from the list
                Destroy(gameObject);
            }
        });

    });
    //Setup the Button to Block a Player
    blockFriendButton.onClick.AddListener(() =>
    {
        //Make the Call to Block a Player using the cached PublicUserData UserID
        AccelBytePlugin.GetLobby().BlockPlayer(_userData.userId, result =>
        {
            if (result.IsError)
            {
                //We would probably want to display some user-feedback if the request is not successful
                Debug.Log($"Failed to block a friend request: error code: {result.Error.Code} message: {result.Error.Message}");
            }
            else
            {
                Debug.Log("Block Friend Request");
                //Destroy the UI Piece if the request was successful to remove it from the list
                Destroy(gameObject);
            }
        });

    });
}
  1. In the Unity Editor, in your IncomingRequestDisplay prefab, drag the following objects to the exposed variables in the script:

Unity

  1. Create a script called FriendsOutgoingPanel.cs.

  2. Add the following AccelByte and UnityEngine libraries to the top of the script:

using AccelByte.Api;
using AccelByte.Models;
using UnityEngine;
using UnityEngine.UI;
using Image = UnityEngine.UI.Image;
  1. Add the following references for the UI and a variable that will hold the User Data:
[SerializeField]
private Image profilePicture;

[SerializeField]
private Text displayNameText;
[SerializeField]
private Button cancelRequestButton;

private PublicUserData _userData;
  1. Create a function to cancel a Friend Request and obtain the player’s avatar, then update it in the UI.
public void Create(PublicUserData userData)
{
    //Cache the PublicUserData
    _userData = userData;
    //Set the Display Name
    displayNameText.text = _userData.displayName;
    //Setup the Button to Cancel A Friend Request
    cancelRequestButton.onClick.AddListener(() =>
    {
        //Make the Call to Reject the Friend Request using the cached PublicUserData UserID
        AccelBytePlugin.GetLobby().CancelFriendRequest(_userData.userId, result =>
        {
            if (result.IsError)
            {
                //We would probably want to display some user-feedback if the request is not successful
                Debug.Log($"Failed to cancel a friend request: error code: {result.Error.Code} message: {result.Error.Message}");
            }
            else
            {
                Debug.Log("Cancelled Friend Request");
                //Destroy the UI Piece if the request was successful to remove it from the list
                Destroy(gameObject);
            }
        });
    });
}
  1. In the Unity Editor, on your OutgoingRequestDisplay prefab, drag the following objects to the variable references in the script:

Unity

  1. Duplicate the FriendsOutgoingPanel.cs and rename the duplicate FriendsBlockedPanel.cs. Revise the UI references in this script as follows:
[SerializeField]
private Image profilePicture;
[SerializeField]
private Text displayNameText;
[SerializeField]
private Button unblockButton;

private PublicUserData _userData;
  1. Delete any button setups for AddListener and add the following code:
public void Create(PublicUserData userData)
{
        ...
        //Setup the Button to Unblock a User
        unblockButton.onClick.AddListener(() =>
        {
            //Make the Call to Unblock the given User using the cached PublicUserData UserID
            AccelBytePlugin.GetLobby().UnblockPlayer(_userData.userId, result =>
            {
                if (result.IsError)
                {
                        //We would probably want to display some user-feedback if the request is not successful
                        Debug.Log($"Failed to unblock a player: error code: {result.Error.Code} message: {result.Error.Message}");
                }
                else
                {
                        Debug.Log("Unblocked a Player");
                        //Destroy the UI Piece if the request was successful to remove it from the list
                        Destroy(gameObject);
                }
            });
        });
        ...
}
  1. In the Unity Editor, open the BlockedDisplay prefab and drag the following objects to the public variable in the script:

Unity

  1. Duplicate the FriendsOutgoingPanel.cs and rename the duplicate to FriendsAddPanel.cs. Revise the UI references in the script as follows:
[SerializeField]
private Image profilePicture;
[SerializeField]
private Text displayNameText;
[SerializeField]
private Button addFriendButton;

private PublicUserData _userData;
  1. Delete any button setups for AddListener and add the following code:
public void Create(PublicUserData userData)
{
        ...
        //Setup the Button to Request a Friend
        addFriendButton.onClick.AddListener(() =>
        {
            //Make the call to initiate the Friend Request
            AccelBytePlugin.GetLobby().RequestFriend(_userData.userId, result =>
            {
                if (result.IsError)
                {
                        Debug.Log($"Failed to send a friends request: error code: {result.Error.Code} message: {result.Error.Message}");
                }
                else
                {
                        Debug.Log("Sent Friends Request");
                        //If we were successful, set the button to be non-intractable and change the Text
                        addFriendButton.interactable = false;
                        addFriendButton.GetComponentInChildren<Text>().text = "Request Sent";
                }
            });
        });
        ...
}
  1. In the Unity Editor, open the AddFriendsDisplay prefab and drag the following objects to the variable in the script component:

Unity

  1. Now, in FriendsManagementHandler.cs, create a function that will create a new FriendStatusPanel prefab in accordance with the userData.
private void CreateFriendUI(PublicUserData userData)
{
    FriendStatusPanel panel = Instantiate(friendsDisplayPrefab, friendsDisplayPanel).GetComponent<FriendStatusPanel>();
    panel.Create(userData);
    if (!friendUIDictionary.ContainsKey(userData.userId))
    {
        friendUIDictionary.Add(userData.userId, panel);
    }

    // set up the any FriendStatusPanel's related button
    panel.SetupButton();
}
  1. Modify the function in FriendsManagementHandler.cs to obtain all available friend data, such as in the following example:
/// Get Friends and Display them
private void GetFriends()
{

    //Cleanup First    LoopThroughTransformAndDestroy(friendsDisplayPanel.transform, friendsDisplayPlaceholder);

    AccelBytePlugin.GetLobby().LoadFriendsList(result =>
    {
        //Check this is not an error
        if (!result.IsError)
        {
            //Check if no friends were returned
            if (result.Value.friendsId.Length <= 0)
            {
                //Display the Placeholder Text
                friendsDisplayPlaceholder.gameObject.SetActive(true);
                return;
            }
            //Hide the Placeholder Text
            friendsDisplayPlaceholder.gameObject.SetActive(false);


            //Fire off Requests to create UI for each friend
            Debug.Log("Loaded Friends List Succesfully");
            foreach (string friendID in result.Value.friendsId)
            {
                ...
                AccelBytePlugin.GetUser().GetUserByUserId(friendID, x =>
                {
                    CreateFriendUI(x.Value);
                });
            }    
        }
        else
        {
            //Display the Placeholder
            friendsDisplayPlaceholder.gameObject.SetActive(true);
            ...
        }
    });
}
  1. Once completed, you will need to modify a function to list any pending Incoming/Outgoing Friend Requests. In FriendsManagementHandler.cs, modify the following codes:
private void DisplayPending()
{
    //Cleanup First, remove all Children from the Contents OTHER than the Placeholders
    LoopThroughTransformAndDestroy(pendingIncomingRequestsContent.transform, pendingIncomingRequestPlaceholder);
    LoopThroughTransformAndDestroy(pendingOutgoingRequestsContent.transform, pendingOutgoingRequestPlaceholder);

    //Get all Incoming Friend Requests
    AccelBytePlugin.GetLobby().ListIncomingFriends(result =>
    {
        //Check for an Error
        if (result.IsError)
        {
            ...
            //Set the Placeholder Text to be Active so it doesn't just look broken
            pendingIncomingRequestPlaceholder.gameObject.SetActive(true);
        }
        else
        {
            //If there are Zero Incoming Requests, set the PlaceHolder to be active
            if (result.Value.friendsId.Length <= 0)
            {
                pendingIncomingRequestPlaceholder.gameObject.SetActive(true);
            }
            //Otherwise set the PlaceHolder to be inactive
            else
            {
                pendingIncomingRequestPlaceholder.gameObject.SetActive(false);
            }

            //Loop through all the UserID's returned by the Friends callback and get their PublicUserData
            foreach (string userID in result.Value.friendsId)
            {
                //Request the PublicUserData for the specific Friend
                AccelBytePlugin.GetUser().GetUserByUserId(userID, userResult =>
                {
                    if (userResult.IsError)
                    ...
                    else
                    {
                        FriendsIncomingPanel incomingPanel = Instantiate(pendingIncomingRequestsPrefab, pendingIncomingRequestsContent).GetComponent<FriendsIncomingPanel>();
                        //Pass the PublicUserData into this function
                        incomingPanel.Create(userResult.Value);                       
                    }
                });
            }
        }
    });

    AccelBytePlugin.GetLobby().ListOutgoingFriends(result =>
    {
        if (result.IsError)
        ...
        else
        {
            if (result.Value.friendsId.Length <= 0)
            {
pendingOutgoingRequestPlaceholder.gameObject.SetActive(true);
            }
            else
            {
pendingOutgoingRequestPlaceholder.gameObject.SetActive(false);
            }
            foreach (string userID in result.Value.friendsId)
            {
                AccelBytePlugin.GetUser().GetUserByUserId(userID, userResult =>
                {
                    if (userResult.IsError)
                    ...
                    else
                    {
                        FriendsOutgoingPanel outgoingPanel = Instantiate(pendingOutgoingRequestsPrefab, pendingOutgoingRequestsContent).GetComponent<FriendsOutgoingPanel>();
                        outgoingPanel.Create(userResult.Value);
                    }
                });
            }
        }
    });  
}
  1. Add the following code in the DisplayBlocked() function to instantiate the display prefab for blocked players in FriendsManagementHandler.cs:
...
AccelBytePlugin.GetUser().GetUserByUserId(blockedUser.blockedUserId, userResult =>
{
    ...
    if (userResult.IsError)
    ...
    //If we have valid data, Instantiate the Prefab for the specific UI Piece and call relevant functions
    else
    {
        FriendsBlockedPanel blockedPanel = Instantiate(blockedUserPrefab, blockedContent).GetComponent<FriendsBlockedPanel>();
        //Pass the PublicUserData into this function
        blockedPanel.Create(userResult.Value);  
    }
});        
...
  1. In the Unity Editor, navigate to the AccelByteHandler gameObject and drag the following objects to their references in the FriendsManagementHandler script component:
  • Panel and buttons references

Unity

  • Friends panel and Search panel references

Unity

  • Pending panel and Blocked panel references

Unity

Congratulations! You have now fully implemented the Friends service.

Proceed to the next section to learn how to implement Party services (opens new window).

# Full Code

FriendsManagementHandler.cs
// Copyright (c) 2021 - 2022 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

using System.Collections.Generic;
using UnityEngine;
using AccelByte.Api;
using AccelByte.Models;
using UnityEngine.Serialization;
using UnityEngine.UI;
using Button = UnityEngine.UI.Button;

public class FriendsManagementHandler : MonoBehaviour
{
    private Dictionary<string, FriendStatusPanel> friendUIDictionary = new Dictionary<string, FriendStatusPanel>();

    public GameObject FriendsManagementWindow;

    [FormerlySerializedAs("FriendsPanel")]
    [SerializeField]
    private RectTransform friendsPanel;
    [SerializeField]
    private RectTransform requestsPanel;
    [SerializeField]
    private RectTransform blockedPanel;

    #region Buttons

    [SerializeField]
    private Button friendsTabButton;
    [SerializeField]
    private Button pendingTabButton;
    [SerializeField]
    private Button blockedTabButton;

    [SerializeField]
    private Button exitButton;

    #endregion

    [SerializeField]
    private Transform friendsDisplayPanel;

    [FormerlySerializedAs("FriendsDisplayPrefab")]
    [SerializeField]
    private GameObject friendsDisplayPrefab;

    [SerializeField]
    private Button friendsSearchButton;

    [SerializeField]
    private Transform friendsDisplayPlaceholder;
    [SerializeField]
    private Transform blockedDisplayPlaceholder;

    #region Search

    [SerializeField]
    private GameObject addFriendsPrefab;
    [SerializeField]
    private Transform friendsSearchPanel;

    [SerializeField]
    private InputField friendsSearchInputField;
    [SerializeField]
    private RectTransform friendsSearchScrollView;
    [SerializeField]
    private Button friendsSearchQuitButton;

    [SerializeField]
    private Transform friendsSearchPlaceholder;

    #endregion

    #region Pending

    [SerializeField]
    private RectTransform pendingIncomingRequestsContent;
    [SerializeField]
    private RectTransform pendingOutgoingRequestsContent;
    [SerializeField]
    private RectTransform blockedContent;

    [SerializeField]
    private GameObject pendingOutgoingRequestsPrefab;
    [SerializeField]
    private GameObject pendingIncomingRequestsPrefab;
    [SerializeField]
    private GameObject blockedUserPrefab;

    [SerializeField]
    private Transform pendingIncomingRequestPlaceholder;
    [SerializeField]
    private Transform pendingOutgoingRequestPlaceholder;

    [SerializeField]
    private Text pendingIncomingRequestText;
    [SerializeField]
    private Text pendingOutgoingRequestText;

    #endregion

    private bool setupStatus = false;

    public enum FriendsMode
    {
        Default,
        Friends,
        Pending,
        Blocked
    };

    private FriendsMode _displayMode = FriendsMode.Default;

    private FriendsMode DisplayMode
    {
        get => _displayMode;
        set
        {
            switch (value)
            {
                case FriendsMode.Default:
                    friendsPanel.gameObject.SetActive(true);
                    requestsPanel.gameObject.SetActive(false);
                    blockedPanel.gameObject.SetActive(false);
                    break;
                case FriendsMode.Friends:
                    friendsPanel.gameObject.SetActive(true);
                    requestsPanel.gameObject.SetActive(false);
                    blockedPanel.gameObject.SetActive(false);
                    GetFriends();
                    break;
                case FriendsMode.Pending:
                    requestsPanel.gameObject.SetActive(true);
                    friendsPanel.gameObject.SetActive(false);
                    blockedPanel.gameObject.SetActive(false);
                    DisplayPending();
                    break;
                case FriendsMode.Blocked:
                    blockedPanel.gameObject.SetActive(true);
                    friendsPanel.gameObject.SetActive(false);
                    requestsPanel.gameObject.SetActive(false);
                    DisplayBlocked();
                    break;
            }
        }
    }

    public void UpdateFriends(FriendsStatusNotif notification)
    {
        //Find the friend and update it's UI
        if (friendUIDictionary.ContainsKey(notification.userID))
        {
friendUIDictionary[notification.userID].SetOnlineStatus(notification);
        }
        //Otherwise We should handle this in some way, possibly creating a Friend UI Piece
        else
        {
            Debug.Log("Unregistered Friend received a Notification");
        }
    }

    /// <summary>
    /// Setup UI and prepare State
    /// </summary>
    /// <param name="exitType"> name of the destination panel</param>
    public void Setup()
    {
        // reset the exit button's listener, then add the listener based on the exit screen type
        exitButton.onClick.RemoveAllListeners();
        exitButton.onClick.AddListener(() =>
        {
            FriendsManagementWindow.SetActive(false);
            GetComponent<MenuHandler>().Menu.gameObject.SetActive(true);
        });

        // Check whether the FriendsPanel already set up or not
        if (setupStatus)
        {
            DisplayMode = FriendsMode.Default;
            return;
        }
        // Run the setup if it still hasn't
        else
        {
            setupStatus = true;

            DisplayMode = FriendsMode.Friends;

            // reset listeners, so it won't triggered more than once
            friendsTabButton.onClick.RemoveAllListeners();
            pendingTabButton.onClick.RemoveAllListeners();
            blockedTabButton.onClick.RemoveAllListeners();

            // add the listeners
            friendsTabButton.onClick.AddListener(() => DisplayMode = FriendsMode.Friends);
            pendingTabButton.onClick.AddListener(() => DisplayMode = FriendsMode.Pending);
            blockedTabButton.onClick.AddListener(() => DisplayMode = FriendsMode.Blocked);
            friendsSearchPanel.gameObject.SetActive(false);
            friendsDisplayPlaceholder.gameObject.SetActive(true);
            friendsSearchQuitButton.onClick.AddListener(() => friendsSearchPanel.gameObject.SetActive(false));
            friendsSearchButton.onClick.AddListener(DisplaySearch);
            friendsSearchInputField.onEndEdit.AddListener(SearchForFriends);
            friendsSearchButton.onClick.AddListener(() => friendsSearchPanel.gameObject.SetActive(true));
        }
    }

    /// <summary>
    /// Get Friends and Display them
    /// </summary>
    private void GetFriends()
    {
        //Cleanup First
        LoopThroughTransformAndDestroy(friendsDisplayPanel.transform, friendsDisplayPlaceholder);

        AccelBytePlugin.GetLobby().LoadFriendsList(result =>
        {            
            //Check this is not an error
            if (!result.IsError)
            {
                //Cleanup First
                LoopThroughTransformAndDestroy(friendsDisplayPanel.transform, friendsDisplayPlaceholder);

                //Check if no friends were returned
                if (result.Value.friendsId.Length <= 0)
                {
                    //Display the Placeholder Text
                    friendsDisplayPlaceholder.gameObject.SetActive(true);
                    return;
                }
                //Hide the Placeholder Text
                friendsDisplayPlaceholder.gameObject.SetActive(false);


                //Fire off Requests to create UI for each friend
                Debug.Log("Loaded Friends List Succesfully");
                foreach (string friendID in result.Value.friendsId)
                {
                    Debug.Log($"Friend : {friendID}");
                    AccelBytePlugin.GetUser().GetUserByUserId(friendID, x =>
                    {
                        CreateFriendUI(x.Value);
                    });
                }    
            }
            else
            {
                //Display the Placeholder
                friendsDisplayPlaceholder.gameObject.SetActive(true);
                Debug.LogWarning("Error in Getting Friends");
            }    
        });
    }

    private void CreateFriendUI(PublicUserData userData)
    {
        FriendStatusPanel panel = Instantiate(friendsDisplayPrefab, friendsDisplayPanel).GetComponent<FriendStatusPanel>();
        panel.Create(userData);
        if (!friendUIDictionary.ContainsKey(userData.userId))
        {
            friendUIDictionary.Add(userData.userId, panel);
        }

        panel.SetupButton();
    }

    private void DisplaySearch()
    {
        friendsSearchPanel.gameObject.SetActive(true);
        LoopThroughTransformAndDestroy(friendsSearchScrollView.transform, friendsSearchPlaceholder);
        friendsSearchPlaceholder.gameObject.SetActive(true);
    }

    private void SearchForFriends(string query)
    {
            AccelBytePlugin.GetUser().SearchUsers(query, result =>
            {
                if (!result.IsError)
                {
                    ListQueriedusers(result.Value);
                }
                else
                {
                    Debug.LogWarning($"Unable to Query Users Code: {result.Error.Code}, Message: {result.Error.Message}");
                }
            });
    }

    private void ListQueriedusers(PagedPublicUsersInfo pagedInfo)
    {
            //Cleanup First
            LoopThroughTransformAndDestroy(friendsSearchScrollView.transform, friendsSearchPlaceholder);

            if (pagedInfo.data.Length <=0)
            {
                friendsSearchPlaceholder.gameObject.SetActive(true);
            }
            else
            {
                friendsSearchPlaceholder.gameObject.SetActive(false);
                foreach (PublicUserInfo info in pagedInfo.data)
                {
                    FriendsAddPanel addPanel = Instantiate(addFriendsPrefab,friendsSearchScrollView).GetComponent<FriendsAddPanel>();
                    addPanel.Create(info);
                }
            }
    }

    private void DisplayPending()
    {
        //Cleanup First, remove all Children from the Contents OTHER than the Placeholders
LoopThroughTransformAndDestroy(pendingIncomingRequestsContent.transform, pendingIncomingRequestPlaceholder);
LoopThroughTransformAndDestroy(pendingOutgoingRequestsContent.transform, pendingOutgoingRequestPlaceholder);

        //Get all Incoming Friend Requests
        AccelBytePlugin.GetLobby().ListIncomingFriends(result =>
        {
            //Check for an Error
            if (result.IsError)
            {
                Debug.LogWarning($"Unable to get Incoming Requests Code: {result.Error.Code}, Message: {result.Error.Message}");
                //Set the Placeholder Text to be Active so it doesn't just look broken
pendingIncomingRequestPlaceholder.gameObject.SetActive(true);
            }
            else
            {
                //If there are Zero Incoming Requests, set the PlaceHolder to be active
                if (result.Value.friendsId.Length <= 0)
                {
pendingIncomingRequestPlaceholder.gameObject.SetActive(true);
                }
                //Otherwise set the PlaceHolder to be inactive
                else
                {
pendingIncomingRequestPlaceholder.gameObject.SetActive(false);
                }    

                //Loop through all the UserID's returned by the Friends callback and get their PublicUserData
                foreach (string userID in result.Value.friendsId)
                {
                    //Request the PublicUserData for the specific Friend
                    AccelBytePlugin.GetUser().GetUserByUserId(userID, userResult =>
                    {
                        //If it's an Error, report it and do nothing else
                        if (userResult.IsError)
                        {
                            Debug.LogWarning($"Unable to User Code: {userResult.Error.Code}, Message: {userResult.Error.Message}");
                        }
                        //If we have valid data, Instantiate the Prefab for the specific UI Piece and call relevant functions
                        else
                        {
                            FriendsIncomingPanel incomingPanel = Instantiate(pendingIncomingRequestsPrefab, pendingIncomingRequestsContent).GetComponent<FriendsIncomingPanel>();
                            //Pass the PublicUserData into this function
                            incomingPanel.Create(userResult.Value);
                        }
                    });
                }
            }
        });

        AccelBytePlugin.GetLobby().ListOutgoingFriends(result =>
        {
            if (result.IsError)
            {
                Debug.LogWarning($"Unable to get Outgoing Requests Code: {result.Error.Code}, Message: {result.Error.Message}");
            }
            else
            {
                if (result.Value.friendsId.Length <= 0)
                {
                    pendingOutgoingRequestPlaceholder.gameObject.SetActive(true);
                }
                else
                {
                    pendingOutgoingRequestPlaceholder.gameObject.SetActive(false);
                }
                foreach (string userID in result.Value.friendsId)
                {
                    AccelBytePlugin.GetUser().GetUserByUserId(userID, userResult =>
                    {
                        if (userResult.IsError)
                        {
                            Debug.LogWarning($"Unable to User Code: {userResult.Error.Code}, Message: {userResult.Error.Message}");
                        }
                        else
                        {
                            FriendsOutgoingPanel outgoingPanel = Instantiate(pendingOutgoingRequestsPrefab, pendingOutgoingRequestsContent).GetComponent<FriendsOutgoingPanel>();
                            outgoingPanel.Create(userResult.Value);                          
                        }                  
                    });                    
                }
            }
        });
    }

    private void DisplayBlocked()
    {
        //Cleanup First
LoopThroughTransformAndDestroy(blockedContent.transform,blockedDisplayPlaceholder);

        //Get Blocked List
        AccelBytePlugin.GetLobby().GetListOfBlockedUser(result =>
        {
            //Check for an Error
            if (result.IsError)
            {
                Debug.LogWarning($"Unable to get Blocked Player List Code: {result.Error.Code}, Message: {result.Error.Message}");

                blockedDisplayPlaceholder.gameObject.SetActive(true);
            }
            else
            {
                blockedDisplayPlaceholder.gameObject.SetActive(false);
                //Loop through all the UserID's returned by the callback and get their PublicUserData
                foreach (BlockedData blockedUser in result.Value.data)
                {
                    //Request the PublicUserData for the specific User
                    AccelBytePlugin.GetUser().GetUserByUserId(blockedUser.blockedUserId, userResult =>
                    {
                        //If it's an Error, report it and do nothing else
                        if (userResult.IsError)
                        {
                            Debug.LogWarning($"Unable to User Code: {userResult.Error.Code}, Message: {userResult.Error.Message}");
                        }
                        //If we have valid data, Instantiate the Prefab for the specific UI Piece and call relevant functions
                        else
                        {
                            FriendsBlockedPanel blockedPanel = Instantiate(blockedUserPrefab, blockedContent).GetComponent<FriendsBlockedPanel>();
                            //Pass the PublicUserData into this function
                            blockedPanel.Create(userResult.Value);                      
                        }                  
                    });                  
                }
            }
        });
    }

    /// <summary>
    /// A utility function to Destroy all Children of the parent transform. Optionally do not remove a specific Transform
    /// </summary>
    /// <param name="parent">Parent Object to destroy children</param>
    /// <param name="doNotRemove">Optional specified Transform that should NOT be destroyed</param>
    private static void LoopThroughTransformAndDestroy(Transform parent, Transform doNotRemove = null)
    {
        //Loop through all the children and add them to a List to then be deleted
        List<GameObject> toBeDeleted = new List<GameObject>();
        foreach (Transform t in parent)
        {
            //except the Do Not Remove transform if there is one
            if (t != doNotRemove)
            {
                    toBeDeleted.Add(t.gameObject);
            }
        }
        //Loop through list and Delete all Children
        for (int i = 0; i < toBeDeleted.Count; i++)
        {
            Destroy(toBeDeleted[i]);
        }
    }
}
FriendStatusPanel.cs
// Copyright (c) 2021 - 2022 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

using System.Collections;
using System.Collections.Generic;
using AccelByte.Api;
using AccelByte.Models;
using UnityEngine;
using UnityEngine.UI;
using Image = UnityEngine.UI.Image;


public class FriendStatusPanel : MonoBehaviour
{
    [SerializeField]
    private Image profilePicture;
    [SerializeField]
    private Image statusDisplay;

    [SerializeField]
    private Button chatButton;
    [SerializeField]
    private Button inviteToPartyButton;
    [SerializeField]
    private Button unfriendButton;
    [SerializeField]
    private Button blockButton;

    [SerializeField]
    private Text displayNameText;
    [SerializeField]
    private Text onlineStatusText;


    PublicUserData _userData;


    void SetOnlineStatus(FriendsStatusNotif notification)
    {
        switch (notification.availability)
        {
            case "offline":
                onlineStatusText.text = "Offline";
                statusDisplay.color = Color.black;
                break;
            case "online":
                onlineStatusText.text = "Online";
                statusDisplay.color = Color.green;
                break;
            case "busy":
                onlineStatusText.text = "Busy";
                statusDisplay.color = Color.yellow;
                break;
            case "invisible":
                onlineStatusText.text = "Offline";
                statusDisplay.color = Color.black;
                break;
            default:
                onlineStatusText.text = $"INVALID UNHANDLED {notification.availability}";
                statusDisplay.color = Color.magenta;
                break;
        }

        Debug.Log($"Friend Status for {notification.userID} changed to {notification.availability}");
    }

    public void Create(PublicUserData pud)
    {
        _userData = pud;
        displayNameText.text = _userData.displayName;
    }

    public void UpdateUser(FriendsStatusNotif notification)
    {
        SetOnlineStatus(notification);

    }

    /// <summary>
    /// Setup UI Button Listener
    /// </summary>
    public void SetupButton()
    {
        unfriendButton.onClick.AddListener(() =>
        {
            AccelBytePlugin.GetLobby().Unfriend(_userData.userId, result =>
            {
                if (result.IsError)
                {
                    Debug.Log($"Failed to unfriend a friend: code: {result.Error.Code}, message: {result.Error.Message}");
                }
                else
                {
                    Debug.Log("Successfully unfriend a friend!");
                    LobbyHandler.Instance.GetComponent<FriendsManagementHandler>().RefreshFriendsList();
                }
            });
        });
    }
}
FriendsIncomingPanel.cs
// Copyright (c) 2021 - 2022 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

using AccelByte.Api;
using AccelByte.Models;
using UnityEngine;
using UnityEngine.UI;
using Image = UnityEngine.UI.Image;

/// <summary>
/// Used to control the UI for the Incoming Pending Friend Request
/// </summary>
public class FriendsIncomingPanel : MonoBehaviour
{
    [SerializeField]
    private Image profilePicture;

    [SerializeField]
    private Text displayNameText;
    [SerializeField]
    private Button acceptFriendButton;
    [SerializeField]
    private Button declineFriendButton;
    [SerializeField]
    private Button blockFriendButton;

    private PublicUserData _userData;

    /// <summary>
    /// An initialisation Function required to be called to Populate the UI appropriately
    /// </summary>
    /// <param name="userData">The PublicUserData that is required to Populate the UI</param>
    public void Create(PublicUserData userData)
    {
        //Cache the PublicUserData
        _userData = userData;
        //Set the Display Name
        displayNameText.text = _userData.displayName;
        //Setup the Button to Accept a Friend Request
        acceptFriendButton.onClick.AddListener(() =>
        {
            //Make the Call to Accept the Friend Request using the cached PublicUserData UserID
            AccelBytePlugin.GetLobby().AcceptFriend(_userData.userId, result =>
            {
                if (result.IsError)
                {
                    //We would probably want to display some user-feedback if the request is not successful
                    Debug.Log($"Failed to accept a friend request: error code: {result.Error.Code} message: {result.Error.Message}");
                }
                else
                {
                    Debug.Log("Accepted Friend Request");
                    //Destroy the UI Piece if the request was successful to remove it from the list
                    Destroy(gameObject);
                }
            });            
        });
        //Setup the Button to Decline a Friend Request
        declineFriendButton.onClick.AddListener(() =>
        {
            //Make the Call to Decline the Friend Request using the cached PublicUserData UserID
            AccelBytePlugin.GetLobby().RejectFriend(_userData.userId, result =>
            {
                if (result.IsError)
                {
                    //We would probably want to display some user-feedback if the request is not successful
                    Debug.Log($"Failed to decline a friend request: error code: {result.Error.Code} message: {result.Error.Message}");
                }
                else
                {
                    Debug.Log("Declined Friend Request");
                    //Destroy the UI Piece if the request was successful to remove it from the list
                    Destroy(gameObject);
                }
            });
        });
        //Setup the Button to Block a Player
        blockFriendButton.onClick.AddListener(() =>
        {
            //Make the Call to Block a Player using the cached PublicUserData UserID
            AccelBytePlugin.GetLobby().BlockPlayer(_userData.userId, result =>
            {
                if (result.IsError)
                {
                    //We would probably want to display some user-feedback if the request is not successful
                    Debug.Log($"Failed to block a friend request: error code: {result.Error.Code} message: {result.Error.Message}");
                }
                else
                {
                    Debug.Log("Block Friend Request");
                    //Destroy the UI Piece if the request was successful to remove it from the list
                    Destroy(gameObject);
                }
            });
        });
    }   
}
FriendsOutgoingPanel.cs
// Copyright (c) 2021 - 2022 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

using AccelByte.Api;
using AccelByte.Models;
using UnityEngine;
using UnityEngine.UI;
using Image = UnityEngine.UI.Image;

public class FriendsOutgoingPanel : MonoBehaviour
{
     [SerializeField]
    private Image profilePicture;

    [SerializeField]
    private Text displayNameText;
    [SerializeField]
    private Button cancelRequestButton;

    private PublicUserData _userData;

    /// <summary>
    /// An initialisation Function required to be called to Populate the UI appropriately
    /// </summary>
    /// <param name="userData">The PublicUserData that is required to Populate the UI</param>
    public void Create(PublicUserData userData)
    {
        //Cache the PublicUserData
        _userData = userData;
        //Set the Display Name
        displayNameText.text = _userData.displayName;
        //Setup the Button to Cancel A Friend Request
        cancelRequestButton.onClick.AddListener(() =>
        {
            //Make the Call to Reject the Friend Request using the cached PublicUserData UserID
AccelBytePlugin.GetLobby().CancelFriendRequest(_userData.userId, result =>
            {
                if (result.IsError)
                {
                    //We would probably want to display some user-feedback if the request is not successful
                    Debug.Log($"Failed to cancel a friend request: error code: {result.Error.Code} message: {result.Error.Message}");
                }
                else
                {
                    Debug.Log("Cancelled Friend Request");
                    //Destroy the UI Piece if the request was successful to remove it from the list
                    Destroy(gameObject);
                }
            });
        });
    }
}
FriendsBlockedPanel.cs
// Copyright (c) 2021 - 2022 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

using AccelByte.Api;
using AccelByte.Models;
using UnityEngine;
using UnityEngine.UI;
using Image = UnityEngine.UI.Image;

public class FriendsBlockedPanel : MonoBehaviour
{

    [SerializeField]
    private Image profilePicture;
    [SerializeField]
    private Text displayNameText;
    [SerializeField]
    private Button unblockButton;

    private PublicUserData _userData;

    /// <summary>
    /// An initialisation Function required to be called to Populate the UI appropriately
    /// </summary>
    /// <param name="userData">The PublicUserData that is required to Populate the UI</param>
    public void Create(PublicUserData userData)
    {
        //Cache the PublicUserData
        _userData = userData;
        //Set the Display Name
        displayNameText.text = _userData.displayName;
        //Setup the Button to Unblock a User
        unblockButton.onClick.AddListener(() =>
        {
            //Make the Call to Unblock the given User using the cached PublicUserData UserID
            AccelBytePlugin.GetLobby().UnblockPlayer(_userData.userId, result =>
            {
                    if (result.IsError)
                    {
                        //We would probably want to display some user-feedback if the request is not successful
                        Debug.Log($"Failed to unblock a player: error code: {result.Error.Code} message: {result.Error.Message}");
                    }
                    else
                    {
                        Debug.Log("Unblocked a Player");
                        //Destroy the UI Piece if the request was successful to remove it from the list
                        Destroy(gameObject);
                    }
            });
        });
    }   
}
FriendsAddPanel.cs
// Copyright (c) 2021 - 2022 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

using AccelByte.Api;
using AccelByte.Models;
using UnityEngine;
using UnityEngine.UI;
using Image = UnityEngine.UI.Image;

/// <summary>
/// Controls the UI Piece associated with Adding a Friend from a Search
/// </summary>
public class FriendsAddPanel : MonoBehaviour
{
    [SerializeField]
    private Image profilePicture;

    [SerializeField]
    private Text displayNameText;
    [SerializeField]
    private Button addFriendButton;

    private PublicUserInfo _userData;

    /// <summary>
    /// An initialisation Function required to be called to Populate the UI appropriately
    /// </summary>
    /// <param name="userInfo">The PublicUserInfo that is required to Populate the UI</param>
    public void Create(PublicUserInfo userInfo)
    {
        //Cache the PublicUserInfo
        _userData = userInfo;
        //Set the Display Name
        displayNameText.text = _userData.displayName;
        //Setup the Button to Request a Friend
        addFriendButton.onClick.AddListener(() =>
        {
            //Make the call to initiate the Friend Request
        AccelBytePlugin.GetLobby().RequestFriend(_userData.userId, result =>
            {
                    if (result.IsError)
                    {
                        Debug.Log($"Failed to send a friends request: error code: {result.Error.Code} message: {result.Error.Message}");
                    }
                    else
                    {
                        Debug.Log("Sent Friends Request");
                        //If we were successful, set the button to be non-intractable and change the Text
                        addFriendButton.interactable = false;
addFriendButton.GetComponentInChildren<Text>().text = "Request Sent";
                    }
            });
        });
    }   
}
MenuHandler.cs
// Copyright (c) 2021 - 2022 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

using UnityEngine;
using UnityEngine.UI;

public class MenuHandler : MonoBehaviour
{
    public Transform Menu;

    public Button FriendsButton;

    private bool isInitialized = false;

    public void Create()
    {
        if (isInitialized) return;

        isInitialized = true;

        FriendsButton.onClick.AddListener(() =>
        {
            GetComponent<FriendsManagementHandler>().Setup(FriendsManagementHandler.ExitMode.Menu);
            Menu.gameObject.SetActive(false);
            GetComponent<FriendsManagementHandler>().FriendsManagementWindow.SetActive(true);
        });
    }
}
LobbyHandler.cs
// Copyright (c) 2021 - 2022 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

using UnityEngine;
using AccelByte.Api;

public class LobbyHandler : MonoBehaviour
{
    /// <summary>
    /// Private Instance
    /// </summary>
    static LobbyHandler _instance;

    /// <summary>
    /// The Instance Getter
    /// </summary>
    public static LobbyHandler Instance => _instance;

    /// <summary>
    /// The Instance Getter
    /// </summary>
    private Lobby _lobby;

    [HideInInspector]
    public NotificationHandler notificationHandler;


    private void Awake()
    {
        // Check if another Instance is already created, and if so delete this one, otherwise destroy the object
        if (_instance != null && _instance != this)
        {
            Destroy(this);
            return;
        }
        else
        {
            _instance = this;
        }

        // Get the the object handler
        notificationHandler = gameObject.GetComponent<NotificationHandler>();
    }

    /// <summary>
    /// Connect to the <see cref="Lobby"/> and setup CallBacks
    /// </summary>
    public void ConnectToLobby()
    {
        //Get a reference to the instance of the Lobby
        _lobby = AccelBytePlugin.GetLobby();

        //Init menu handler
        GetComponent<MenuHandler>().Create();
        GetComponent<MenuHandler>().Menu.gameObject.SetActive(true);

        //Connection
        _lobby.Connected += notificationHandler.OnConnected;
        _lobby.Disconnecting += notificationHandler.OnDisconnecting;
        _lobby.Disconnected += notificationHandler.OnDisconnected;

        //Friends
        _lobby.FriendsStatusChanged += notificationHandler.OnFriendsStatusChanged;
        _lobby.FriendRequestAccepted += notificationHandler.OnFriendRequestAccepted;
        _lobby.OnIncomingFriendRequest += notificationHandler.OnIncomingFriendRequest;
        _lobby.FriendRequestCanceled += notificationHandler.OnFriendRequestCanceled;
        _lobby.FriendRequestRejected += notificationHandler.OnFriendRequestRejected;
        _lobby.OnUnfriend += notificationHandler.OnUnfriend;

        //Connect to the Lobby
        if (!_lobby.IsConnected)
        {
            _lobby.Connect();
        }
    }

    public void RemoveLobbyListeners()
    {
        //Remove delegate from Lobby
        //Connection
        _lobby.Connected -= notificationHandler.OnConnected;
        _lobby.Disconnecting -= notificationHandler.OnDisconnecting;
        _lobby.Disconnected -= notificationHandler.OnDisconnected;

        //Friends
        _lobby.FriendsStatusChanged -= notificationHandler.OnFriendsStatusChanged;
        _lobby.FriendRequestAccepted -= notificationHandler.OnFriendRequestAccepted;
        _lobby.OnIncomingFriendRequest -= notificationHandler.OnIncomingFriendRequest;
        _lobby.FriendRequestCanceled -= notificationHandler.OnFriendRequestCanceled;
        _lobby.FriendRequestRejected -= notificationHandler.OnFriendRequestRejected;
        _lobby.OnUnfriend -= notificationHandler.OnUnfriend;
    }

    public void DisconnectFromLobby()
    {
        if (AccelBytePlugin.GetLobby().IsConnected)
        {
            AccelBytePlugin.GetLobby().Disconnect();
        }
    }

    private void OnApplicationQuit()
    {
        // Attempt to Disconnect from the Lobby when the Game Quits
        DisconnectFromLobby();
    }
}
NotificationHandler.cs
// Copyright (c) 2021 - 2022 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

using UnityEngine;
using AccelByte.Models;
using AccelByte.Core;

public class NotificationHandler : MonoBehaviour
{
    #region Notifications

    // Collection of connection notifications
    #region Connections
    /// <summary>
    /// Called when lobby is connected
    /// </summary>
    public void OnConnected()
    {
        Debug.Log("Lobby Connected");
    }

    /// <summary>
    /// Called when connection is disconnecting
    /// </summary>
    /// <param name="result"> Contains data of message</param>
    public void OnDisconnecting(Result<DisconnectNotif> result)
    {
        Debug.Log($"Lobby Disconnecting {result.Value.message}");
    }

    /// <summary>
    /// Called when connection is being disconnected
    /// </summary>
    /// <param name="result"> Contains data of websocket close code</param>
    public void OnDisconnected(WsCloseCode result)
    {
        Debug.Log($"Lobby Disconnected: {result}");
    }
    #endregion

    // Collection of friend notifications
    #region Friends
    /// <summary>
    /// Called when friend status is changed
    /// </summary>
    /// <param name="result"> Contains data of user id, availability, status, etc</param>
    public void OnFriendsStatusChanged(Result<FriendsStatusNotif> result)
    {
        GetComponent<FriendsManagementHandler>().UpdateFriends(result.Value);
    }

    /// <summary>
    /// Called when friend request is accepted
    /// </summary>
    /// <param name="result"> Contains data of friend's user id</param>
    public void OnFriendRequestAccepted(Result<Friend> result)
    {
        Debug.Log($"Accepted a Friend Request from user {result.Value.friendId}");
    }

    /// <summary>
    /// Called when there is incomming friend request
    /// </summary>
    /// <param name="result"> Contains data of friend's user id</param>
    public void OnIncomingFriendRequest(Result<Friend> result)
    {
        Debug.Log($"Received a Friend Request from user {result.Value.friendId}");
    }

    /// <summary>
    /// Called when friend is unfriend
    /// </summary>
    /// <param name="result"> Contains data of friend's user id</param>
    public void OnUnfriend(Result<Friend> result)
    {
        Debug.Log($"Unfriended User {result.Value.friendId}");
    }

    /// <summary>
    /// Called when friend request is canceled
    /// </summary>
    /// <param name="result"> Contains data of sender user id</param>
    public void OnFriendRequestCanceled(Result<Acquaintance> result)
    {
        Debug.Log($"Cancelled a Friend Request from user {result.Value.userId}");
    }

    /// <summary>
    /// Called when friend request is rejected
    /// </summary>
    /// <param name="result"> Contains data of rejector user id</param>
    public void OnFriendRequestRejected(Result<Acquaintance> result)
    {
        Debug.Log($"Rejected a Friend Request from user {result.Value.userId}");
    }
    #endregion

    #endregion
}