258 lines
8.8 KiB
C#
258 lines
8.8 KiB
C#
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;
|
||
}
|
||
}
|
||
}
|