UI HUGE UPDATE

This commit is contained in:
FloatGaming
2026-06-25 11:48:56 +08:00
parent bd2a06f4c7
commit b2a1e307a4
1806 changed files with 359863 additions and 20822 deletions
+349 -2
View File
@@ -8,6 +8,8 @@ using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using Bansonic;
public class globalChatSystem : MonoBehaviour
{
private enum ChatChannelMode
@@ -37,6 +39,9 @@ public class globalChatSystem : MonoBehaviour
public GameObject worldSystemMessagePrefab;
public Transform playerMessageParent;
[Header("extra")]
public Button emojiButton;
[Header("send")]
public InputField messageInputField;
public Button sendButton;
@@ -55,6 +60,8 @@ public class globalChatSystem : MonoBehaviour
[SerializeField] Text channelNameText;
[SerializeField] Button back_to_worldwideChatButton;
[SerializeField] Toggle globalChatToggle;
[SerializeField] Toggle friendChatToggle;
private ArenaRoomService _service;
private ScrollRect _scrollRect;
@@ -75,6 +82,7 @@ public class globalChatSystem : MonoBehaviour
private long _nextFeedSequence = 1;
private ChatChannelMode _currentMode = ChatChannelMode.World;
private string _currentPrivatePartnerId;
private string _lastPrivatePartnerId = string.Empty;
private string _defaultChannelName = string.Empty;
private static string _pendingPrivatePartnerId;
private Coroutine _layoutRefreshCoroutine;
@@ -83,7 +91,11 @@ public class globalChatSystem : MonoBehaviour
private readonly List<RectTransform> _pendingLayoutDirtyRoots = new List<RectTransform>();
private Coroutine _refocusInputCoroutine;
private bool _refocusInputRequested;
private bool _suppressModeToggleEvents;
private bool _modeTogglesAutoCreated;
private ToggleGroup _chatModeToggleGroup;
public static globalChatSystem Instance { get; private set; }
public static event Action<string> ActivePrivateConversationChanged;
private const float WorldChatSendCooldownSeconds = 10f;
private const float PrivateChatSendCooldownSeconds = 1f;
@@ -93,6 +105,9 @@ public class globalChatSystem : MonoBehaviour
private const string DefaultWorldChannelName = "世界聊天";
private const string CooldownPlaceholderFormat = "\u53d1\u9001\u51b7\u5374\u4e2d {0}s";
private const string PrivateChannelNameFormat = "\u6b63\u5728\u4e0e[{0}]\u79c1\u804a";
private const string DefaultFriendChannelName = "\u597d\u53cb\u804a\u5929";
private const string NoRecentPrivateConversationNotice = "\u6682\u65e0\u6700\u8fd1\u79c1\u804a\u5bf9\u8c61";
private const string LastPrivatePartnerPrefKey = "global_chat_last_private_partner_id";
public static void OpenPrivateConversation(string steamId)
{
@@ -132,6 +147,7 @@ public class globalChatSystem : MonoBehaviour
private void Awake()
{
Instance = this;
_lastPrivatePartnerId = PlayerPrefs.GetString(LastPrivatePartnerPrefKey, string.Empty);
}
private void Start()
@@ -182,6 +198,8 @@ public class globalChatSystem : MonoBehaviour
back_to_worldwideChatButton.onClick.AddListener(HandleBackToWorldClicked);
}
EnsureChatModeToggles();
if (_service != null)
{
_service.SetWorldChatCacheLimit(GetEffectiveCacheLimit());
@@ -211,6 +229,8 @@ public class globalChatSystem : MonoBehaviour
ClearDisplayedMessages();
_isViewPinnedToBottom = true;
_windowStartIndex = -1;
EnsureChatModeToggles();
NotifyActivePrivateConversationChanged();
if (!string.IsNullOrWhiteSpace(_pendingPrivatePartnerId))
{
@@ -237,6 +257,8 @@ public class globalChatSystem : MonoBehaviour
_scrollRect.onValueChanged.RemoveListener(OnScrollValueChanged);
}
UnbindChatModeToggleListeners();
if (_chatCooldownRoutine != null)
{
StopCoroutine(_chatCooldownRoutine);
@@ -256,6 +278,7 @@ public class globalChatSystem : MonoBehaviour
}
RestoreDefaultPlaceholder();
NotifyActivePrivateConversationChanged();
}
private void OnDestroy()
@@ -270,6 +293,8 @@ public class globalChatSystem : MonoBehaviour
back_to_worldwideChatButton.onClick.RemoveListener(HandleBackToWorldClicked);
}
UnbindChatModeToggleListeners();
if (sendButton != null)
{
sendButton.onClick.RemoveListener(OnSendClicked);
@@ -365,6 +390,7 @@ public class globalChatSystem : MonoBehaviour
_currentPrivatePartnerId = null;
ResetFeedForChannelSwitch();
ApplyChannelHeader();
NotifyActivePrivateConversationChanged();
await _service.EnsureWorldChatConnected();
await _service.LoadWorldHistory(InitialHistoryLoadLimit);
RenderMessages(_service.GetWorldChatMessages(), false);
@@ -379,8 +405,10 @@ public class globalChatSystem : MonoBehaviour
_currentMode = ChatChannelMode.Private;
_currentPrivatePartnerId = partnerId.Trim();
RememberPrivatePartner(_currentPrivatePartnerId);
ResetFeedForChannelSwitch();
ApplyChannelHeader();
NotifyActivePrivateConversationChanged();
await _service.LoadPrivateHistory(_currentPrivatePartnerId, InitialHistoryLoadLimit);
RenderMessages(_service.GetPrivateChatMessages(_currentPrivatePartnerId), false);
}
@@ -394,10 +422,17 @@ public class globalChatSystem : MonoBehaviour
{
if (back_to_worldwideChatButton != null)
{
back_to_worldwideChatButton.gameObject.SetActive(_currentMode == ChatChannelMode.Private);
back_to_worldwideChatButton.gameObject.SetActive(_currentMode == ChatChannelMode.Private && !HasChatModeToggles());
}
if (channelNameText == null)
if (_modeTogglesAutoCreated && channelNameText != null)
{
channelNameText.gameObject.SetActive(false);
}
UpdateChatModeToggleVisuals();
if (channelNameText == null || _modeTogglesAutoCreated)
{
return;
}
@@ -1168,6 +1203,318 @@ public class globalChatSystem : MonoBehaviour
gameObject.SetActive(false);
}
public static bool IsViewingPrivateConversation(string steamId)
{
if (string.IsNullOrWhiteSpace(steamId) || Instance == null || !Instance.isActiveAndEnabled)
{
return false;
}
return Instance._currentMode == ChatChannelMode.Private
&& string.Equals(Instance._currentPrivatePartnerId, steamId.Trim(), StringComparison.Ordinal);
}
private void EnsureChatModeToggles()
{
if (globalChatToggle == null || friendChatToggle == null)
{
CreateFallbackChatModeToggles();
}
BindChatModeToggleListeners();
UpdateChatModeToggleVisuals();
}
private void BindChatModeToggleListeners()
{
if (globalChatToggle != null)
{
globalChatToggle.onValueChanged.RemoveListener(HandleGlobalChatToggleChanged);
globalChatToggle.onValueChanged.AddListener(HandleGlobalChatToggleChanged);
}
if (friendChatToggle != null)
{
friendChatToggle.onValueChanged.RemoveListener(HandleFriendChatToggleChanged);
friendChatToggle.onValueChanged.AddListener(HandleFriendChatToggleChanged);
}
}
private void UnbindChatModeToggleListeners()
{
if (globalChatToggle != null)
{
globalChatToggle.onValueChanged.RemoveListener(HandleGlobalChatToggleChanged);
}
if (friendChatToggle != null)
{
friendChatToggle.onValueChanged.RemoveListener(HandleFriendChatToggleChanged);
}
}
private void HandleGlobalChatToggleChanged(bool isOn)
{
if (_suppressModeToggleEvents || !isOn)
{
return;
}
_ = SwitchToWorldChannelAsync();
}
private void HandleFriendChatToggleChanged(bool isOn)
{
if (_suppressModeToggleEvents || !isOn)
{
return;
}
OpenRecentPrivateConversationFromToggle();
}
private async void OpenRecentPrivateConversationFromToggle()
{
string partnerId = ResolvePreferredPrivatePartnerId();
if (string.IsNullOrWhiteSpace(partnerId))
{
AddSystemMessage(NoRecentPrivateConversationNotice);
UpdateChatModeToggleVisuals();
return;
}
await SwitchToPrivateChannelAsync(partnerId);
}
private void UpdateChatModeToggleVisuals()
{
if (globalChatToggle != null)
{
SetToggleWithoutNotify(globalChatToggle, _currentMode == ChatChannelMode.World);
}
if (friendChatToggle != null)
{
SetToggleWithoutNotify(friendChatToggle, _currentMode == ChatChannelMode.Private);
}
}
private void SetToggleWithoutNotify(Toggle toggle, bool value)
{
if (toggle == null)
{
return;
}
_suppressModeToggleEvents = true;
toggle.SetIsOnWithoutNotify(value);
_suppressModeToggleEvents = false;
}
private bool HasChatModeToggles()
{
return globalChatToggle != null && friendChatToggle != null;
}
private void CreateFallbackChatModeToggles()
{
if (globalChatToggle != null && friendChatToggle != null)
{
return;
}
RectTransform headerRect = channelNameText != null ? channelNameText.rectTransform.parent as RectTransform : null;
if (headerRect == null)
{
return;
}
if (channelNameText != null)
{
channelNameText.gameObject.SetActive(false);
}
headerRect.sizeDelta = new Vector2(360f, 34f);
if (_chatModeToggleGroup == null)
{
_chatModeToggleGroup = headerRect.GetComponent<ToggleGroup>();
if (_chatModeToggleGroup == null)
{
_chatModeToggleGroup = headerRect.gameObject.AddComponent<ToggleGroup>();
}
_chatModeToggleGroup.allowSwitchOff = false;
}
if (globalChatToggle == null)
{
globalChatToggle = CreateRuntimeModeToggle(headerRect, "globalChatToggle", DefaultWorldChannelName, new Vector2(-90f, 0f));
}
if (friendChatToggle == null)
{
friendChatToggle = CreateRuntimeModeToggle(headerRect, "friendChatToggle", DefaultFriendChannelName, new Vector2(90f, 0f));
}
if (globalChatToggle != null)
{
globalChatToggle.group = _chatModeToggleGroup;
}
if (friendChatToggle != null)
{
friendChatToggle.group = _chatModeToggleGroup;
}
_modeTogglesAutoCreated = globalChatToggle != null && friendChatToggle != null;
}
private Toggle CreateRuntimeModeToggle(RectTransform parent, string objectName, string labelText, Vector2 anchoredPosition)
{
Sprite defaultSprite = Resources.GetBuiltinResource<Sprite>("UISprite.psd");
Font defaultFont = channelNameText != null && channelNameText.font != null
? channelNameText.font
: Resources.GetBuiltinResource<Font>("Arial.ttf");
GameObject toggleObject = new GameObject(objectName, typeof(RectTransform), typeof(Image), typeof(Toggle));
RectTransform toggleRect = toggleObject.GetComponent<RectTransform>();
toggleRect.SetParent(parent, false);
toggleRect.anchorMin = new Vector2(0.5f, 0.5f);
toggleRect.anchorMax = new Vector2(0.5f, 0.5f);
toggleRect.pivot = new Vector2(0.5f, 0.5f);
toggleRect.anchoredPosition = anchoredPosition;
toggleRect.sizeDelta = new Vector2(164f, 30f);
Image background = toggleObject.GetComponent<Image>();
background.sprite = defaultSprite;
background.type = Image.Type.Sliced;
background.color = new Color(1f, 1f, 1f, 0.32f);
Toggle toggle = toggleObject.GetComponent<Toggle>();
toggle.targetGraphic = background;
toggle.transition = Selectable.Transition.ColorTint;
ColorBlock colors = toggle.colors;
colors.normalColor = new Color(1f, 1f, 1f, 0.45f);
colors.highlightedColor = new Color(1f, 1f, 1f, 0.55f);
colors.pressedColor = new Color(0.86f, 0.94f, 1f, 0.85f);
colors.selectedColor = new Color(0.86f, 0.94f, 1f, 0.95f);
colors.disabledColor = new Color(1f, 1f, 1f, 0.2f);
toggle.colors = colors;
GameObject checkmarkObject = new GameObject("Checkmark", typeof(RectTransform), typeof(Image));
RectTransform checkmarkRect = checkmarkObject.GetComponent<RectTransform>();
checkmarkRect.SetParent(toggleRect, false);
checkmarkRect.anchorMin = Vector2.zero;
checkmarkRect.anchorMax = Vector2.one;
checkmarkRect.offsetMin = new Vector2(3f, 3f);
checkmarkRect.offsetMax = new Vector2(-3f, -3f);
Image checkmark = checkmarkObject.GetComponent<Image>();
checkmark.sprite = defaultSprite;
checkmark.type = Image.Type.Sliced;
checkmark.color = new Color(0.63f, 0.87f, 1f, 0.95f);
toggle.graphic = checkmark;
GameObject labelObject = new GameObject("Label", typeof(RectTransform), typeof(Text));
RectTransform labelRect = labelObject.GetComponent<RectTransform>();
labelRect.SetParent(toggleRect, false);
labelRect.anchorMin = Vector2.zero;
labelRect.anchorMax = Vector2.one;
labelRect.offsetMin = new Vector2(10f, 0f);
labelRect.offsetMax = new Vector2(-10f, 0f);
Text label = labelObject.GetComponent<Text>();
label.font = defaultFont;
label.fontSize = 16;
label.alignment = TextAnchor.MiddleCenter;
label.horizontalOverflow = HorizontalWrapMode.Overflow;
label.verticalOverflow = VerticalWrapMode.Overflow;
label.color = new Color(0.19607843f, 0.19607843f, 0.19607843f, 1f);
label.text = labelText;
return toggle;
}
private void RememberPrivatePartner(string partnerId)
{
if (string.IsNullOrWhiteSpace(partnerId))
{
return;
}
_lastPrivatePartnerId = partnerId.Trim();
PlayerPrefs.SetString(LastPrivatePartnerPrefKey, _lastPrivatePartnerId);
PlayerPrefs.Save();
}
private string ResolvePreferredPrivatePartnerId()
{
if (!string.IsNullOrWhiteSpace(_lastPrivatePartnerId))
{
return _lastPrivatePartnerId;
}
if (TryGetMostRecentPrivatePartnerId(out string recentPartnerId))
{
return recentPartnerId;
}
return string.Empty;
}
private bool TryGetMostRecentPrivatePartnerId(out string partnerId)
{
partnerId = string.Empty;
if (_service == null)
{
return false;
}
string bestPartner = string.Empty;
long bestTimestamp = long.MinValue;
foreach (SocialFriendEntry friend in _service.GetFriendsSnapshot())
{
string candidateId = friend != null ? friend.friend_steam_id : string.Empty;
if (string.IsNullOrWhiteSpace(candidateId))
{
continue;
}
IReadOnlyList<ArenaRoomChatMessage> messages = _service.GetPrivateChatMessages(candidateId);
if (messages == null || messages.Count == 0)
{
continue;
}
ArenaRoomChatMessage lastMessage = messages[messages.Count - 1];
long timestamp = ParseMessageUnixTimestamp(lastMessage != null ? lastMessage.created_at : string.Empty);
if (timestamp > bestTimestamp)
{
bestTimestamp = timestamp;
bestPartner = candidateId;
}
}
if (string.IsNullOrWhiteSpace(bestPartner))
{
return false;
}
partnerId = bestPartner;
return true;
}
private void NotifyActivePrivateConversationChanged()
{
string activePartnerId = isActiveAndEnabled && _currentMode == ChatChannelMode.Private
? _currentPrivatePartnerId ?? string.Empty
: string.Empty;
ActivePrivateConversationChanged?.Invoke(activePartnerId);
}
private int GetEffectiveCacheLimit()
{
return Mathf.Max(InitialHistoryLoadLimit, Mathf.Max(1, maxMessageHistory));