262 lines
9.6 KiB
C#
262 lines
9.6 KiB
C#
using System;
|
||
using System.Threading.Tasks;
|
||
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
using Vanguard.Api;
|
||
using Vanguard.Auth;
|
||
using Vanguard.State;
|
||
|
||
namespace Vanguard.UI
|
||
{
|
||
/// <summary>Главный экран после входа: профиль, балансы и заглушки разделов.</summary>
|
||
public sealed class MetaView : IDisposable
|
||
{
|
||
private readonly AuthService _auth;
|
||
private readonly PlayerState _player;
|
||
private readonly Action _onLoggedOut;
|
||
private readonly Action _onCollection;
|
||
private readonly Action _onDecks;
|
||
private readonly Action _onStore;
|
||
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 onCollection,
|
||
Action onDecks,
|
||
Action onStore,
|
||
Action onPlay,
|
||
Action onOnboarding)
|
||
{
|
||
_auth = auth;
|
||
_player = player;
|
||
_onLoggedOut = onLoggedOut;
|
||
_onCollection = onCollection;
|
||
_onDecks = onDecks;
|
||
_onStore = onStore;
|
||
_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, "Коллекция", _onCollection);
|
||
AddAction(actions, "Колоды", _onDecks);
|
||
AddAction(actions, "Магазин", _onStore);
|
||
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<LayoutElement>();
|
||
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<LayoutElement>();
|
||
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();
|
||
}
|
||
}
|
||
}
|