/**
* Copyright 2019 The Knights Of Unity, created by Pawel Stolarczyk
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using DemoGame.Scripts.Chat;
using DemoGame.Scripts.Menus;
using DemoGame.Scripts.Notifications;
using DemoGame.Scripts.Profile;
using DemoGame.Scripts.Session;
using Nakama;
using UnityEngine;
using UnityEngine.UI;
namespace DemoGame.Scripts.Clans
{
///
/// Responsible for managing UI of the Clan managing panel.
/// Holds references to all UI elements and handles user input.
///
public class ClanPanel : Menu
{
#region Fields
#region ClanManagement
[Space]
///
/// Button for creating new clan using .
///
[SerializeField] private Button _createClanButton = null;
///
/// Parent panel containing clan creation UI.
///
[SerializeField] private ClanCreationPanel _clanCreationPrefab = null;
///
/// Button for deleting clan using .
///
[SerializeField] private Button _removeClanButton = null;
///
/// Button used to resend clan member list and search list entries.
///
[SerializeField] private Button _refreshClanButton = null;
#endregion
#region ClanAssociation
[Space]
///
/// Button for joining currently shown clan using .
///
[SerializeField] private Button _joinClanButton = null;
///
/// Button for leaving currently shown clan using .
///
[SerializeField] private Button _leaveClanButton = null;
///
/// Button responsible for joining clan chat.
///
[SerializeField] private Button _chatButton = null;
///
/// List of all members of currently show clan.
///
private List _clanMembers = new List();
///
/// Currently selected member of a clan.
///
private ClanUserEntry _selectedMember = null;
///
/// Name of currently displayed clan.
///
private string _clanName = string.Empty;
///
/// Id of currently displayed clan.
///
private string _clanId = string.Empty;
#endregion
#region Search
[Space]
///
/// Textbox containing keyword used to search for specific clans.
///
[SerializeField] private InputField _clanSearchInput = null;
///
/// Button for starting clan search.
///
[SerializeField] private Button _clanSearchButton = null;
///
/// Max number of clans displayed per page.
///
[SerializeField] private int _clansPerPage = 5;
///
/// Prefab with clan search result entry UI and logic.
/// Instantiated for every entry found.
///
[SerializeField] private ClanSearchResult _clanSearchResultPrefab = null;
///
/// Parent list containing all search entries.
///
[SerializeField] private RectTransform _clanSearchList = null;
#endregion
#region ClanDetails
[Space]
///
/// Prefab with user information and UI.
/// Instantiated for every user in currently displayed clan.
///
[SerializeField] private ClanUserEntry _clanUserEntryPrefab = null;
///
/// Parent list containing all users who belong to currently displayed clan.
///
[SerializeField] private RectTransform _clanUserList = null;
[Space]
///
/// Textbox containing the name of the clan we currently display information of.
///
[SerializeField] private Text _clanDisplayName = null;
///
/// Parent transform containing clan search UI.
/// Tabs are changed using and .
///
[SerializeField] private CanvasGroup _searchTab = null;
///
/// Button used to show using .
///
[SerializeField] private Button _searchTabButton = null;
///
/// Parent transform containing clan details UI.
/// Tabs are changed using and .
///
[SerializeField] private CanvasGroup _detailsTab = null;
///
/// Button used to show using .
///
[SerializeField] private Button _detailsTabButton = null;
#endregion
#region Chat
///
/// Chat panel reference used to send and receive clan messages.
///
[SerializeField] private ChatChannelClanUI _chatChannelClanUI = null;
#endregion
#endregion
#region Properties
///
/// Currently displayed clan in panel.
///
private IApiGroup DisplayedClan { get; set; }
///
/// Returns from for easier access.
///
private Client Client { get { return NakamaSessionManager.Instance.Client; } }
///
/// Returns from for easier access.
///
private ISession Session { get { return NakamaSessionManager.Instance.Session; } }
#endregion
#region Mono
///
/// Adds handlers to buttons.
/// Restarts clan display and shows panel.
/// Awaits Nakama session initialization - on success calls .
///
private void Awake()
{
_createClanButton.onClick.AddListener(() => _clanCreationPrefab.ShowCreationPanel(OnClanChanged));
_removeClanButton.onClick.AddListener(DeleteClan);
_clanSearchButton.onClick.AddListener(SearchClan);
_leaveClanButton.onClick.AddListener(LeaveClan);
_joinClanButton.onClick.AddListener(JoinClan);
_searchTabButton.onClick.AddListener(ShowClanSearch);
_detailsTabButton.onClick.AddListener(ShowMyClanDetails);
_chatButton.onClick.AddListener(StartChat);
_refreshClanButton.onClick.AddListener(RefreshClanMenu);
_clanSearchInput.onEndEdit.AddListener(SearchClanOnReturnClicked);
SetMyClan(null);
ShowClanSearch();
NotificationManager.Instance.OnNotification += NotificationReceived;
base.SetBackButtonHandler(MenuManager.Instance.HideTopMenu);
if (NakamaSessionManager.Instance.IsConnected == false)
{
NakamaSessionManager.Instance.OnConnectionSuccess += Init;
}
else
{
Init();
}
}
#endregion
#region ClanManagement
///
/// Deletes the clan local user currently belongs to.
/// Does nothing if user is not a member of any clan or has insufficient permissions.
/// The name of newly created clan is determined by textfield.
///
private async void DeleteClan()
{
IUserGroupListUserGroup clan = await ClanManager.GetUserClanAsync(Client, Session);
bool good = await ClanManager.DeleteClanAsync(Client, Session, clan.Group);
if (good == true)
{
OnClanLeft();
}
}
///
/// Invoked by .
/// Joins selected clan.
/// If user is already member of this clan, changes tab to .
/// If user is already member of another clan, does nothing.
///
private async void JoinClan()
{
IUserGroupListUserGroup clan = await ClanManager.GetUserClanAsync(Client, Session);
if (clan != null)
{
if (clan.Group.Id == DisplayedClan.Id)
{
Debug.Log("This user has already joined clan with name \"" + DisplayedClan.Name + "\"");
ShowClanDetails(DisplayedClan);
}
else
{
Debug.LogWarning("Cannot join more then one clan. Leave current clan first.");
}
}
else
{
IApiGroup newClan = await ClanManager.JoinClanAsync(Client, Session, DisplayedClan);
if (newClan != null)
{
OnClanChanged(newClan);
}
}
}
///
/// Leaves current clan.
/// Invokes on success.
///
private async void LeaveClan()
{
IUserGroupListUserGroup clan = await ClanManager.GetUserClanAsync(Client, Session);
if (clan == null)
{
Debug.Log("User is not a member of any clan");
return;
}
bool good = await ClanManager.LeaveClanAsync(Client, Session, clan.Group);
if (good == true)
{
OnClanLeft();
}
}
///
/// Searches Nakama database in order to find clans containing a keyword in their names.
/// The keyword is determined by textfield.
///
private async void SearchClan()
{
string name = _clanSearchInput.text;
IApiGroupList clanList = await ClanManager.ListClansAsync(Client, Session, "%" + name + "%", _clansPerPage, null);
if (clanList != null)
{
OnClanListFound(clanList);
}
}
///
/// Invoked whenever user ends search keyword input with Return key.
/// Searches Nakama database in order to find clans containing a keyword in their names.
/// The keyword is determined by textfield.
///
private void SearchClanOnReturnClicked(string text)
{
if (Input.GetKeyDown(KeyCode.Return))
{
SearchClan();
}
}
#endregion
#region Handlers
///
/// Called when user connects to Nakama server.
/// Tries to retrieve the clan local user belongs to.
/// On success calls .
///
private async void Init()
{
NakamaSessionManager.Instance.OnConnectionSuccess -= Init;
IUserGroupListUserGroup clan = await ClanManager.GetUserClanAsync(Client, Session);
if (clan != null)
{
OnClanChanged(clan.Group);
}
}
///
/// Invoked when user changes their clan.
/// Sets the of local user.
/// If is null, shows , else
/// shows .
///
///
private void OnClanChanged(IApiGroup clan)
{
Debug.Log("Clan changed");
SetMyClan(clan);
if (clan == null)
{
ShowClanSearch();
}
else
{
ShowClanDetails(clan);
}
}
///
/// Invoked when leaving a clan.
///
public void OnClanLeft()
{
Debug.Log("Clan left");
OnClanChanged(null);
SetMyState(ClanUserState.None);
}
///
/// Invoked when server returns a list of clans we searched for.
/// Parameter is used to iterate through results.
///
private void OnClanListFound(IApiGroupList clans)
{
// Removing previous results
foreach (Transform transform in _clanSearchList)
{
Destroy(transform.gameObject);
}
if (clans.Groups.Count() > 0)
{
// Creating entiries for every clan found
foreach (IApiGroup clan in clans.Groups)
{
Debug.Log("Found clan: " + clan.Name);
ClanSearchResult result = Instantiate(_clanSearchResultPrefab, _clanSearchList);
result.SetClan(clan, ShowClanDetails);
}
}
else
{
Debug.Log("No clans found");
}
}
///
/// Updates user list based on currently
/// using .
/// Invokes on succes or failure.
///
private async Task UpdateUserListAsync()
{
List userList = await ClanManager.GetClanUsersAsync(Client, Session, DisplayedClan);
if (userList != null)
{
OnClanUserListReceived(userList);
return true;
}
else
{
return false;
}
}
///
/// Updates the list of members of a clan.
/// Instantiates UI containing found user data.
/// If local user is a part of this clan, updates their
/// using .
///
private void OnClanUserListReceived(List userList)
{
_clanMembers.Clear();
// Removing previous results
foreach (Transform child in _clanUserList)
{
Destroy(child.gameObject);
}
// Searching through results in order to find local user.
// If local user belongs to this clan, set their MyUserState
// Knowing the role of local user in searched clan allows for better UI handling
ClanUserState myState = ClanUserState.None;
foreach (IGroupUserListGroupUser user in userList)
{
if (user.User.Id == NakamaSessionManager.Instance.Account.User.Id)
{
myState = (ClanUserState)user.State;
SetMyState(myState);
break;
}
}
// Adding entries for each user found
foreach (IGroupUserListGroupUser user in userList)
{
Debug.Log("Found user in clan: " + user.User.Username);
ClanUserEntry userEntry = Instantiate(_clanUserEntryPrefab, _clanUserList);
userEntry.SetUser(user.User, (ClanUserState)user.State, myState, OnUserSelected, OnUserKick, OnUserPromote, OnUserShowProfile);
_clanMembers.Add(userEntry);
}
}
///
/// Invoked upon selecting user from clan member list.
/// Shows user interaction panel.
///
private void OnUserSelected(ClanUserEntry sender)
{
if (_selectedMember == sender)
{
_selectedMember.HideInteractionPanel();
_selectedMember = null;
}
else
{
if (_selectedMember != null)
{
_selectedMember.HideInteractionPanel();
}
_selectedMember = sender;
_selectedMember.ShowInteractionPanel();
}
}
///
/// Method invoked by clicking on .
/// Removes user from current clan and updates user list.
///
private async void OnUserKick(IApiUser user)
{
IUserGroupListUserGroup clan = await ClanManager.GetUserClanAsync(Client, Session);
if (clan == null)
{
Debug.Log("Not a member of any clan");
}
Debug.Log("Kicked user " + user.Username);
bool good = await ClanManager.KickUserAsync(Client, Session, user, clan.Group);
if (good == true)
{
await UpdateUserListAsync();
}
}
///
/// Method invoked by clicking on .
/// Promotes the user to higher .
///
private async void OnUserPromote(IApiUser user)
{
IUserGroupListUserGroup clan = await ClanManager.GetUserClanAsync(Client, Session);
if (clan == null)
{
Debug.Log("Not a member of any clan");
}
Debug.Log("Promoted user " + user.Username);
bool good = await ClanManager.PromoteUserAsync(Client, Session, user, clan.Group);
if (good == true)
{
await UpdateUserListAsync();
}
}
///
/// Method invoked by clicking on .
/// Shows filled with data.
///
private void OnUserShowProfile(IApiUser user)
{
Debug.Log("Showed user " + user.Username + "'s profile");
ProfilePopup.Instance.Show(user);
}
///
/// Refreshes user list when a member joins or leaves the clan.
/// Changes tabs to clan search when current clan is disbanded.
///
private void NotificationReceived(IApiNotification notification)
{
if (notification.Code == (int)NotificationCode.Clan_RefreshMembers)
{
RefreshClanMenu();
}
if (notification.Code == (int)NotificationCode.Clan_Delete)
{
OnClanLeft();
}
}
#endregion
#region UIManagement
///
/// Update clan menu on clan panel enter.
///
public override void Show()
{
Init();
base.Show();
}
///
/// Requests clan search list and clan member list refresh.
///
public async void RefreshClanMenu()
{
SearchClan();
bool good = await UpdateUserListAsync();
if (good == false)
{
IUserGroupListUserGroup clan = await ClanManager.GetUserClanAsync(Client, Session);
if (clan == null)
{
OnClanChanged(null);
}
}
}
///
/// Shows panel and hides panel.
///
private void ShowClanSearch()
{
HideTab(_detailsTab);
ShowTab(_searchTab);
_searchTabButton.interactable = false;
_detailsTabButton.interactable = true;
}
///
/// Shows panel and hides panel.
/// Updates user list.
///
private async void ShowClanDetails(IApiGroup clan)
{
DisplayedClan = clan;
bool good = await UpdateUserListAsync();
if (good == true)
{
await SetClanManagementButtonsAsync();
HideTab(_searchTab);
ShowTab(_detailsTab);
_clanDisplayName.text = DisplayedClan.Name;
}
_searchTabButton.interactable = true;
_detailsTabButton.interactable = false;
}
///
/// Shows panel and hides panel.
/// Displays local user clan info and updates user list.
///
private async void ShowMyClanDetails()
{
IUserGroupListUserGroup clan = await ClanManager.GetUserClanAsync(Client, Session);
if (clan != null)
{
ShowClanDetails(clan.Group);
}
}
///
/// Hides panel setting its alpha to 0 and doesn't block raycasts.
///
private void HideTab(CanvasGroup tab)
{
tab.alpha = 0;
tab.blocksRaycasts = false;
}
///
/// Shows panel setting its alpha to 1 and blocks raycasts.
///
private void ShowTab(CanvasGroup tab)
{
tab.alpha = 1;
tab.blocksRaycasts = true;
}
///
/// Activates or deactivates buttons responsible for leaving, joining and removing clan
/// depending on whether we belong to currently displayed clan.
///
private async Task SetClanManagementButtonsAsync()
{
IUserGroupListUserGroup clan = await ClanManager.GetUserClanAsync(Client, Session);
if (clan != null && DisplayedClan.Id == clan.Group.Id)
{
_joinClanButton.gameObject.SetActive(false);
_leaveClanButton.gameObject.SetActive(true);
_removeClanButton.gameObject.SetActive(clan.State == (int)ClanUserState.Superadmin);
_chatButton.gameObject.SetActive(true);
}
else
{
_joinClanButton.gameObject.SetActive(true);
_leaveClanButton.gameObject.SetActive(false);
_removeClanButton.gameObject.SetActive(false);
_chatButton.gameObject.SetActive(false);
}
}
///
/// Sets the value of .
/// If is null, disable option to see of my clan.
///
private void SetMyClan(IApiGroup clan)
{
if (clan != null)
{
_clanId = clan.Id;
_clanName = clan.Name;
_clanDisplayName.text = clan.Name;
_detailsTabButton.interactable = true;
_createClanButton.interactable = false;
}
else
{
_clanDisplayName.text = "Clan Panel";
_detailsTabButton.interactable = false;
_createClanButton.interactable = true;
}
}
///
/// Changes the .
/// If user has privileges, this enables .
///
private void SetMyState(ClanUserState state)
{
if (state == ClanUserState.Superadmin)
{
_removeClanButton.gameObject.SetActive(true);
}
else
{
_removeClanButton.gameObject.SetActive(false);
}
}
#endregion
#region Chat
///
/// Shows clan chat panel if possible.
///
private async void StartChat()
{
ChatChannel chatChannel = await ChatManager.Instance.JoinChatWithGroupAsync(_clanId);
if (chatChannel != null)
{
chatChannel.ChannelName = _clanName;
_chatChannelClanUI.SetChatChannel(chatChannel);
_chatChannelClanUI.gameObject.SetActive(true);
}
else
{
Debug.LogError("Couldn't start chat with clan");
}
}
#endregion
}
}