using System; using System.Threading.Tasks; using UnityEngine; using UnityEngine.UI; using Vanguard.Api; using Vanguard.Auth; using Vanguard.State; namespace Vanguard.UI { /// Главный экран после входа: профиль, балансы и заглушки разделов. public sealed class MetaView : IDisposable { private readonly AuthService _auth; private readonly PlayerState _player; private readonly Action _onLoggedOut; private readonly Action _onDecks; private readonly Action _onStore; private readonly Action _onHistory; private readonly Action _onPlay; private readonly Action _onOnboarding; private bool _disposed; private readonly RectTransform _root; private readonly Text _playerName; private readonly Text _onboarding; private readonly Text _gold; private readonly Text _dust; private readonly Text _tokens; private readonly Text _ranked; private readonly Text _casual; private readonly Text _record; private readonly Text _status; private readonly Button _logout; private readonly Button _onboardingButton; private bool _refreshing; public MetaView( Transform parent, AuthService auth, PlayerState player, Action onLoggedOut, Action onDecks, Action onStore, Action onHistory, Action onPlay, Action onOnboarding) { _auth = auth; _player = player; _onLoggedOut = onLoggedOut; _onDecks = onDecks; _onStore = onStore; _onHistory = onHistory; _onPlay = onPlay; _onOnboarding = onOnboarding; _root = UiKit.CreatePanel(parent, "MetaScreen", new Color(0.035f, 0.045f, 0.065f, 1f)); var top = UiKit.CreatePanel(_root, "TopBar", new Color(0.07f, 0.085f, 0.115f, 1f)); var topRect = top; topRect.anchorMin = new Vector2(0f, 1f); topRect.anchorMax = new Vector2(1f, 1f); topRect.pivot = new Vector2(0.5f, 1f); topRect.anchoredPosition = Vector2.zero; topRect.sizeDelta = new Vector2(0f, 92f); UiKit.AddHorizontalLayout(top, 22, 16); var brand = UiKit.CreateText( top, "Brand", "VANGUARD", 34, new Color(0.86f, 0.90f, 1f), TextAnchor.MiddleLeft); brand.fontStyle = FontStyle.Bold; UiKit.SetPreferredHeight(brand, 48f, 0f); _playerName = UiKit.CreateText( top, "PlayerName", "…", 20, new Color(0.70f, 0.76f, 0.90f), TextAnchor.MiddleLeft); UiKit.SetPreferredHeight(_playerName, 40f); _logout = UiKit.CreateButton(top, "Logout", "Выйти", OnLogoutClicked, 18); UiKit.SetPreferredHeight(_logout, 42f, 0f); var content = UiKit.CreatePanel(_root, "Content", new Color(0.035f, 0.045f, 0.065f, 0f)); var contentRect = content; contentRect.anchorMin = Vector2.zero; contentRect.anchorMax = Vector2.one; contentRect.offsetMin = new Vector2(48f, 36f); contentRect.offsetMax = new Vector2(-48f, -116f); UiKit.AddVerticalLayout(contentRect, 12, 16); var welcome = UiKit.CreateText( content, "Welcome", "Личный кабинет", 38, Color.white, TextAnchor.MiddleLeft); UiKit.SetPreferredHeight(welcome, 56f); _onboarding = UiKit.CreateText( content, "Onboarding", string.Empty, 18, new Color(1f, 0.76f, 0.38f), TextAnchor.MiddleLeft); UiKit.SetPreferredHeight(_onboarding, 28f); var stats = UiKit.CreatePanel(content, "Stats", new Color(0.07f, 0.085f, 0.115f, 1f)); UiKit.SetPreferredHeight(stats, 130f); UiKit.AddHorizontalLayout(stats, 18, 14); _gold = AddStat(stats, "Gold"); _dust = AddStat(stats, "Пыль"); _tokens = AddStat(stats, "Токены"); _ranked = AddStat(stats, "Ranked MMR"); _casual = AddStat(stats, "Casual MMR"); var actions = UiKit.CreatePanel(content, "Actions", new Color(0.07f, 0.085f, 0.115f, 1f)); UiKit.SetPreferredHeight(actions, 96f); UiKit.AddHorizontalLayout(actions, 18, 14); AddAction(actions, "Коллекция", _onDecks); AddAction(actions, "Магазин", _onStore); AddAction(actions, "История", _onHistory); AddAction(actions, "В бой", _onPlay); var lower = UiKit.CreatePanel(content, "Lower", new Color(0.07f, 0.085f, 0.115f, 1f)); UiKit.SetPreferredHeight(lower, 220f, 1f); UiKit.AddVerticalLayout(lower, 20, 8); _record = UiKit.CreateText( lower, "Record", string.Empty, 20, new Color(0.72f, 0.78f, 0.92f), TextAnchor.MiddleLeft); UiKit.SetPreferredHeight(_record, 28f); _status = UiKit.CreateText( lower, "Status", string.Empty, 18, new Color(0.62f, 0.68f, 0.82f), TextAnchor.UpperLeft); UiKit.SetPreferredHeight(_status, 70f); _onboardingButton = UiKit.CreateButton( lower, "OnboardingButton", "Выбрать триал-деку", () => _onOnboarding(), 20); UiKit.SetPreferredHeight(_onboardingButton, 46f); _player.Changed += UpdateTexts; _root.gameObject.SetActive(false); } public void Show() { UiKit.SetActive(_root, true); UpdateTexts(); _ = RefreshAsync(); } public void Hide() => UiKit.SetActive(_root, false); public void Dispose() { if (_disposed) return; _disposed = true; _player.Changed -= UpdateTexts; } private static Text AddStat(Transform parent, string caption) { var card = UiKit.CreatePanel(parent, "Stat_" + caption, new Color(0.09f, 0.11f, 0.15f, 1f)); UiKit.AddVerticalLayout(card, 12, 4); var element = card.gameObject.AddComponent(); element.flexibleWidth = 1f; element.preferredHeight = 100f; var label = UiKit.CreateText( card, "Caption", caption, 16, new Color(0.58f, 0.65f, 0.78f)); UiKit.SetPreferredHeight(label, 22f); var value = UiKit.CreateText( card, "Value", "—", 30, Color.white); value.fontStyle = FontStyle.Bold; UiKit.SetPreferredHeight(value, 40f); return value; } private static Button AddAction(Transform parent, string caption, Action onClick) { var button = UiKit.CreateButton(parent, "Action_" + caption, caption, () => onClick(), 20); var element = button.gameObject.AddComponent(); element.flexibleWidth = 1f; element.preferredHeight = 56f; return button; } private async Task RefreshAsync() { if (_refreshing) return; _refreshing = true; SetStatus("Обновляем профиль…", new Color(0.62f, 0.68f, 0.82f)); try { await _auth.RefreshMeAsync(); if (_disposed) return; SetStatus("Профиль обновлён.", new Color(0.45f, 0.85f, 0.55f)); } catch (ApiException exception) when (exception.StatusCode == 401) { if (!_disposed) _onLoggedOut(); } catch (Exception exception) { Debug.LogException(exception); SetStatus("Не удалось обновить профиль. Сервер недоступен?", new Color(1f, 0.60f, 0.45f)); } finally { _refreshing = false; } } private void UpdateTexts() { if (_disposed) return; _playerName.text = string.IsNullOrEmpty(_player.UserName) ? "…" : _player.UserName; _gold.text = _player.Gold.ToString(); _dust.text = _player.Dust.ToString(); _tokens.text = _player.Tokens.ToString(); _ranked.text = _player.RankedMmr.ToString(); _casual.text = _player.CasualMmr.ToString(); _record.text = "Матчей: " + _player.Matches + " · побед: " + _player.Wins + " · XP: " + _player.Xp; _onboarding.text = _player.OnboardingCompleted ? "Онбординг завершён: " + (_player.SelectedTrialDeckId ?? "—") : "Онбординг не завершён: выберите стартовую триал-деку."; _onboardingButton.gameObject.SetActive(!_player.OnboardingCompleted); } private void SetStatus(string text, Color color) { if (_disposed || _status == null) return; _status.text = text ?? string.Empty; _status.color = color; } private async void OnLogoutClicked() { _logout.interactable = false; await _auth.LogoutAsync(); if (_disposed) return; _logout.interactable = true; _onLoggedOut(); } } }