VanguardReMaster/client/Assets/Vanguard/Runtime/UI/OnboardingView.cs

258 lines
8.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
using Vanguard.Api;
using Vanguard.Api.Contracts;
using Vanguard.Auth;
using Vanguard.Meta;
namespace Vanguard.UI
{
/// <summary>Первый вход: выбор одной из двух бесплатных триал-дек.</summary>
public sealed class OnboardingView
{
private readonly AuthService _auth;
private readonly MetaService _meta;
private readonly Action _onCompleted;
private readonly Action _onLoggedOut;
private readonly RectTransform _root;
private readonly RectTransform _deckRow;
private readonly Text _status;
private readonly Button _retry;
private readonly Button _logout;
private bool _loading;
public OnboardingView(
Transform parent,
AuthService auth,
MetaService meta,
Action onCompleted,
Action onLoggedOut)
{
_auth = auth;
_meta = meta;
_onCompleted = onCompleted;
_onLoggedOut = onLoggedOut;
_root = UiKit.CreatePanel(parent, "OnboardingScreen", new Color(0.035f, 0.045f, 0.065f, 1f));
var card = UiKit.CreateCenteredCard(
_root,
"OnboardingCard",
new Vector2(1320f, 900f),
new Color(0.07f, 0.085f, 0.115f, 1f));
UiKit.AddVerticalLayout(card, 34, 12);
var title = UiKit.CreateText(
card, "Title", "Стартовый набор", 46, Color.white);
title.fontStyle = FontStyle.Bold;
UiKit.SetPreferredHeight(title, 58f);
var subtitle = UiKit.CreateText(
card,
"Subtitle",
"Выберите одну триал-деку. 50 карт попадут в коллекцию, плюс 5 токенов на бустеры.",
20,
new Color(0.68f, 0.74f, 0.88f));
UiKit.SetPreferredHeight(subtitle, 54f);
_deckRow = UiKit.CreatePanel(
card,
"DeckRow",
new Color(0.05f, 0.06f, 0.085f, 1f));
UiKit.SetPreferredHeight(_deckRow, 500f);
UiKit.AddHorizontalLayout(_deckRow, 16, 18);
_status = UiKit.CreateText(
card, "Status", string.Empty, 18,
new Color(0.68f, 0.74f, 0.88f),
TextAnchor.MiddleCenter);
UiKit.SetPreferredHeight(_status, 38f);
_retry = UiKit.CreateButton(card, "Retry", "Повторить загрузку", OnRetryClicked, 18);
UiKit.SetPreferredHeight(_retry, 42f);
_retry.gameObject.SetActive(false);
_logout = UiKit.CreateButton(card, "Logout", "Выйти из аккаунта", OnLogoutClicked, 18);
UiKit.SetPreferredHeight(_logout, 42f);
_root.gameObject.SetActive(false);
}
public void Show()
{
UiKit.SetActive(_root, true);
_ = LoadAsync();
}
public void Hide() => UiKit.SetActive(_root, false);
private async Task LoadAsync()
{
if (_loading)
return;
_loading = true;
_retry.gameObject.SetActive(false);
_logout.interactable = false;
ClearDeckRow();
SetStatus("Загружаем триал-деки…", new Color(0.68f, 0.74f, 0.88f));
try
{
var store = await _meta.GetStoreAsync();
BuildDecks(store);
SetStatus("Выберите деку — выбор делается один раз.", new Color(0.68f, 0.74f, 0.88f));
}
catch (ApiException exception)
{
SetStatus(exception.Message, new Color(1f, 0.55f, 0.45f));
_retry.gameObject.SetActive(true);
}
catch (Exception exception)
{
Debug.LogException(exception);
SetStatus("Не удалось загрузить триал-деки. Сервер недоступен?", new Color(1f, 0.55f, 0.45f));
_retry.gameObject.SetActive(true);
}
finally
{
_loading = false;
_logout.interactable = true;
}
}
private void BuildDecks(StoreResponse store)
{
if (store.TrialDecks == null || store.TrialDecks.Count == 0)
{
SetStatus("Сервер не вернул ни одной триал-деки.", new Color(1f, 0.55f, 0.45f));
return;
}
foreach (var deck in store.TrialDecks)
BuildDeck(deck);
}
private void BuildDeck(StoreTrialDeckDto deck)
{
var card = UiKit.CreatePanel(
_deckRow,
"Trial_" + deck.Id,
new Color(0.09f, 0.11f, 0.15f, 1f));
var element = card.gameObject.AddComponent<LayoutElement>();
element.flexibleWidth = 1f;
element.preferredHeight = 460f;
UiKit.AddVerticalLayout(card, 24, 10);
var name = UiKit.CreateText(card, "Name", deck.Name, 30, Color.white, TextAnchor.MiddleLeft);
name.fontStyle = FontStyle.Bold;
UiKit.SetPreferredHeight(name, 42f);
var clan = UiKit.CreateText(
card, "Clan", "Клан: " + (string.IsNullOrEmpty(deck.Clan) ? "—" : deck.Clan),
20, new Color(0.68f, 0.74f, 0.88f), TextAnchor.MiddleLeft);
UiKit.SetPreferredHeight(clan, 30f);
var count = UiKit.CreateText(
card, "Count", deck.CardCount + " карт · " + deck.Cards.Count + " уникальных",
18, new Color(0.60f, 0.67f, 0.80f), TextAnchor.MiddleLeft);
UiKit.SetPreferredHeight(count, 28f);
var preview = UiKit.CreateText(
card,
"Preview",
BuildPreview(deck),
16,
new Color(0.52f, 0.60f, 0.74f),
TextAnchor.UpperLeft);
preview.horizontalOverflow = HorizontalWrapMode.Wrap;
UiKit.SetPreferredHeight(preview, 150f, 1f);
if (deck.Owned)
{
var owned = UiKit.CreateText(
card, "Owned", "Уже в коллекции", 18,
new Color(0.45f, 0.85f, 0.55f));
UiKit.SetPreferredHeight(owned, 28f);
}
var choose = UiKit.CreateButton(
card,
"Choose",
deck.Owned ? "Дека уже получена" : "Выбрать бесплатно",
() => OnChooseClicked(deck),
21);
choose.interactable = !deck.Owned;
UiKit.SetPreferredHeight(choose, 54f);
}
private static string BuildPreview(StoreTrialDeckDto deck)
{
if (deck.Cards == null || deck.Cards.Count == 0)
return "Состав появится после выбора.";
var lines = deck.Cards
.Take(8)
.Select(card => "• " + card.PrintingId + " ×" + card.Count);
return string.Join("\n", lines);
}
private async void OnChooseClicked(StoreTrialDeckDto deck)
{
if (_loading)
return;
_loading = true;
_retry.gameObject.SetActive(false);
SetStatus("Активируем " + deck.Name + "…", new Color(0.68f, 0.74f, 0.88f));
try
{
await _meta.OnboardAsync(deck.Id);
_onCompleted();
}
catch (ApiException exception)
{
SetStatus(exception.Message, new Color(1f, 0.55f, 0.45f));
}
catch (Exception exception)
{
Debug.LogException(exception);
SetStatus("Не удалось завершить онбординг.", new Color(1f, 0.55f, 0.45f));
}
finally
{
_loading = false;
}
}
private void OnRetryClicked()
{
_ = LoadAsync();
}
private async void OnLogoutClicked()
{
_logout.interactable = false;
await _auth.LogoutAsync();
_onLoggedOut();
}
private void ClearDeckRow()
{
for (var i = _deckRow.childCount - 1; i >= 0; i--)
UnityEngine.Object.Destroy(_deckRow.GetChild(i).gameObject);
}
private void SetStatus(string text, Color color)
{
_status.text = text ?? string.Empty;
_status.color = color;
}
}
}