/**
* Copyright 2019 Heroic Labs and contributors
*
* 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 DemoGame.Scripts.Session;
using UnityEngine;
using UnityEngine.UI;
namespace DemoGame.Scripts.Chat
{
///
/// Manages user interface for chat
///
public class ChatChannelUI : MonoBehaviour
{
#region private serialized variables
[Header("Prefabs")]
///
/// Object used for instantiating local user messages
///
[SerializeField] private GameObject _thisUserMessagePrefab = null;
///
/// Object used for instantiating other users messages
///
[SerializeField] private GameObject _otherUserMessagePrefab = null;
///
/// Object used for instantiating server messages
///
[SerializeField] private GameObject _serverMessagePrefab = null;
[Space()]
[Header("UI Elements")]
///
/// Text component for viewing chat channel name
///
[SerializeField] protected Text _chatNameText = null;
///
/// Button for closing chat
///
[SerializeField] private Button _closeButton = null;
///
/// Button for loading more channel history
///
[SerializeField] private Button _loadMoreHistoryButton = null;
///
/// Button for sending message to channel
///
[SerializeField] private Button _sendMessageButton = null;
///
/// Button for sending edited changes to channel
///
[SerializeField] private Button _editMessageButton = null;
///
/// Button for canceling editing of message
///
[SerializeField] private Button _cancelEditMessageButton = null;
///
/// Input field for typing messages
///
[SerializeField] private InputField _chatInputField = null;
///
/// Parent of all chat messages
///
[SerializeField] private RectTransform _content = null;
#endregion
#region protected and private vatiables
///
/// Current which data is vieweds
///
protected ChatChannel _chatChannel;
///
/// All chat messages
///
private Dictionary _messages = new Dictionary();
///
/// Username of user who send last message
///
private string _lastMessageUsername;
///
/// Currently edited message
///
private ChatMessageUI _editedMessage;
#endregion
#region Mono
///
/// Invoked when object is set to enabled
///
private void OnEnable()
{
//Registering methods to buttons
_sendMessageButton.onClick.AddListener(SendMessage);
_editMessageButton.onClick.AddListener(SendEditRequestAsync);
_cancelEditMessageButton.onClick.AddListener(CancelEditing);
_closeButton.onClick.AddListener(CloseChannelUI);
_chatInputField.onEndEdit.AddListener(SendMessageIfReturnButton);
_loadMoreHistoryButton.onClick.AddListener(LoadHistoryAsync);
//Deactivating hidden elements
_editMessageButton.gameObject.SetActive(false);
_cancelEditMessageButton.gameObject.SetActive(false);
}
///
/// Invoked when object is set to disabled
///
private void OnDisable()
{
//Unregistering methods to buttons
_sendMessageButton.onClick.RemoveListener(SendMessage);
_editMessageButton.onClick.RemoveListener(SendEditRequestAsync);
_cancelEditMessageButton.onClick.RemoveListener(CancelEditing);
_closeButton.onClick.RemoveListener(CloseChannelUI);
_loadMoreHistoryButton.onClick.RemoveListener(LoadHistoryAsync);
}
#endregion
#region public methods
///
/// Sets current and unplug previous if was
///
public virtual void SetChatChannel(ChatChannel chatChannel)
{
//Checking if chat channel was set previously
if (_chatChannel != null)
{
//If previous instance is the same as new - return
if (_chatChannel == chatChannel)
{
return;
}
//Unregister methods from previous channel
_chatChannel.OnChatMessage -= AddMessage;
_chatChannel.OnChatRemove -= RemoveMessage;
_chatChannel.OnChatUpdate -= UpdateMessage;
_chatChannel.OnServerMessage -= AddServerMessage;
}
//Setting new ChatChannel
_chatChannel = chatChannel;
//Register methods to new ChatChannel
_chatChannel.OnChatMessage += AddMessage;
_chatChannel.OnChatRemove += RemoveMessage;
_chatChannel.OnChatUpdate += UpdateMessage;
_chatChannel.OnServerMessage += AddServerMessage;
//Setting channel name
_chatNameText.text = chatChannel.ChannelName;
//Reseting last message username
_lastMessageUsername = "";
//Loading recent history
LoadHistoryAsync();
}
#endregion
#region private methods
///
/// Adds new user message to channel ui
///
private void AddMessage(string messageId, string userId, string username, string content, string date, bool historical)
{
//Declaring temp variables
GameObject messagePrefab;
bool couldBeEdited;
//Checking if user who send message is local user
if (userId == NakamaSessionManager.Instance.Account.User.Id)
{
messagePrefab = _thisUserMessagePrefab;
couldBeEdited = true;
}
else
{
messagePrefab = _otherUserMessagePrefab;
couldBeEdited = false;
}
//Instantiating message object as a child of _content
GameObject messageGO = Instantiate(messagePrefab, _content) as GameObject;
//Getting ChatMessageUI component from already instantiated message
ChatMessageUI message = messageGO.GetComponent();
if (message)
{
//If the message is from the same user as newest we should hide his username on this message
bool hideUsername = (username == _lastMessageUsername) && !historical;
//Initializing message with given data
message.InitMessage(messageId, username, content, date, couldBeEdited, hideUsername);
//Register edit and remove methods to message eventss
message.OnEditMessageClicked += OnEditMessageClicked;
message.OnRemoveMessageClicked += OnRemoveMessageClicked;
//Adding message to messages dict
_messages.Add(messageId, message);
//Setting last
_lastMessageUsername = username;
//If message is historical change order in hierarchy, the latest historical message is the oldest
if (historical)
{
message.transform.SetSiblingIndex(1);
}
}
else
{
Debug.LogError("Invalid _thisUserMessagePrefab or _otherUserMessagePrefab! It should contains ChatMessageUI script.");
Destroy(messageGO);
return;
}
}
///
/// Adds new server message to chanel ui
///
private void AddServerMessage(string messageId, string content, bool historical)
{
//Instantiating message object as a child of _content
GameObject messageGO = Instantiate(_serverMessagePrefab, _content) as GameObject;
//Getting ChatServerMessageUI component from already instantiated message
ChatServerMessageUI message = messageGO.GetComponent();
if (message)
{
//Initializing message with content
message.Init(content);
//Reseting last message user username (server doesn't have username)
_lastMessageUsername = "";
//If message is historical change order in hierarchy, the latest historical message is the oldest
if (historical)
{
message.transform.SetSiblingIndex(1);
}
}
else
{
Debug.LogError("Invalid _serverMessagePrefab! It should contains ChatServerMessageUI script.");
Destroy(messageGO);
return;
}
}
///
/// Updates edited user message in channel ui
///
private void UpdateMessage(string messageId, string content)
{
_messages[messageId].UpdateMessage(content);
}
///
/// Removes message from channel ui
///
private void RemoveMessage(string messageId)
{
_messages[messageId].RemoveMessage();
}
///
/// Clear all previously loaded messages in ui
///
private void ClearMessages()
{
List messages = new List(_messages.Values);
for (int i = 0; i < messages.Count; i++)
{
Destroy(messages[i].gameObject);
}
_messages.Clear();
}
///
/// Sending new message to channel
///
private void SendMessage()
{
if (string.IsNullOrEmpty(_chatInputField.text))
{
return;
}
Debug.Log("Sending message: " + _chatInputField.text);
ChatManager.Instance.SendNewMessage(_chatChannel.Id, _chatInputField.text);
_chatInputField.text = "";
}
///
/// Method that helps to identify if enter key was clicked to commit new message when end editing input field text.
///
private void SendMessageIfReturnButton(string value)
{
if (Input.GetKeyDown(KeyCode.Return) && !string.IsNullOrEmpty(value))
{
SendMessage();
}
}
///
/// Sending update for message to channel
///
private async void SendEditRequestAsync()
{
if (string.IsNullOrEmpty(_chatInputField.text))
{
return;
}
bool success = await ChatManager.Instance.EditMessageAsync(_chatChannel.Id, _editedMessage.Id, _chatInputField.text);
//If operation end with succes close edit view and show normal buttons
if (success)
{
_sendMessageButton.gameObject.SetActive(true);
_editMessageButton.gameObject.SetActive(false);
_cancelEditMessageButton.gameObject.SetActive(false);
_editedMessage.Deselect();
_chatInputField.text = "";
}
}
///
/// Closes edit view and goes back to normal view
///
private void CancelEditing()
{
_sendMessageButton.gameObject.SetActive(true);
_editMessageButton.gameObject.SetActive(false);
_cancelEditMessageButton.gameObject.SetActive(false);
_editedMessage.Deselect();
_chatInputField.text = "";
}
///
/// Shows edit view and starts editing message
///
///
private void OnEditMessageClicked(string messageId)
{
//If other message was edited cancel this edit
if (_editedMessage)
{
_editedMessage.Deselect();
}
//Set new edited message
_editedMessage = _messages[messageId];
//Select message in UI
_editedMessage.SetSelectedState();
//Fill input field with actual message content
_chatInputField.text = _messages[messageId].ContentTextValue;
//Show edit and cancel buttons, hide send button
_sendMessageButton.gameObject.SetActive(false);
_editMessageButton.gameObject.SetActive(true);
_cancelEditMessageButton.gameObject.SetActive(true);
}
///
/// Sends request to channel for removind message
///
private async void OnRemoveMessageClicked(string messageId)
{
await ChatManager.Instance.RemoveMessageAsync(_chatChannel.Id, messageId);
}
///
/// Loads next part of channel history
///
private async void LoadHistoryAsync()
{
//Deactivate button on time of loading
_loadMoreHistoryButton.interactable = false;
//Loading history request
bool nextCursorIsntNull = await ChatManager.Instance.LoadChannelHistoryAsync(_chatChannel);
//If more history is available activate button
if (nextCursorIsntNull)
{
_loadMoreHistoryButton.interactable = true;
}
}
///
/// Closes chat window
///
private async void CloseChannelUI()
{
bool succes = await ChatManager.Instance.LeaveChatChannelAsync(_chatChannel.Id);
if (!succes)
{
Debug.LogError("Error occurred when leaving chat.");
}
ClearMessages();
gameObject.SetActive(false);
}
#endregion
}
}