Fix stale player-state subscriptions after scene changes
CI / test (push) Has been cancelled
Details
CI / test (push) Has been cancelled
Details
This commit is contained in:
parent
797e45a527
commit
e0ee7cdaa7
|
|
@ -19,5 +19,10 @@ namespace Vanguard.App
|
||||||
Resolve<PlayerState>(),
|
Resolve<PlayerState>(),
|
||||||
() => _ = Flow.LoadLobbyAsync());
|
() => _ = Flow.LoadLobbyAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
_view?.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,5 +21,10 @@ namespace Vanguard.App
|
||||||
() => _ = Flow.LoadLobbyAsync(),
|
() => _ = Flow.LoadLobbyAsync(),
|
||||||
deck => _ = Flow.OpenLobbyWithQueueAsync(deck));
|
deck => _ = Flow.OpenLobbyWithQueueAsync(deck));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
_view?.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -111,5 +111,11 @@ namespace Vanguard.App
|
||||||
_queue.Hide();
|
_queue.Hide();
|
||||||
_store.Hide();
|
_store.Hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
_metaView?.Dispose();
|
||||||
|
_store?.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,12 @@ using Vanguard.State;
|
||||||
namespace Vanguard.UI
|
namespace Vanguard.UI
|
||||||
{
|
{
|
||||||
/// <summary>Коллекция игрока и разбор карт в пыль.</summary>
|
/// <summary>Коллекция игрока и разбор карт в пыль.</summary>
|
||||||
public sealed class CollectionView
|
public sealed class CollectionView : IDisposable
|
||||||
{
|
{
|
||||||
private readonly MetaService _meta;
|
private readonly MetaService _meta;
|
||||||
private readonly PlayerState _player;
|
private readonly PlayerState _player;
|
||||||
private readonly Action _onBack;
|
private readonly Action _onBack;
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
private readonly RectTransform _root;
|
private readonly RectTransform _root;
|
||||||
private readonly Text _balances;
|
private readonly Text _balances;
|
||||||
|
|
@ -106,6 +107,15 @@ namespace Vanguard.UI
|
||||||
_confirm.gameObject.SetActive(false);
|
_confirm.gameObject.SetActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
_player.Changed -= OnPlayerChanged;
|
||||||
|
}
|
||||||
|
|
||||||
private RectTransform BuildConfirm(Transform parent)
|
private RectTransform BuildConfirm(Transform parent)
|
||||||
{
|
{
|
||||||
var overlay = UiKit.CreatePanel(parent, "DismantleConfirm", new Color(0f, 0f, 0f, 0.72f));
|
var overlay = UiKit.CreatePanel(parent, "DismantleConfirm", new Color(0f, 0f, 0f, 0.72f));
|
||||||
|
|
@ -145,6 +155,9 @@ namespace Vanguard.UI
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var collection = await _meta.GetCollectionAsync();
|
var collection = await _meta.GetCollectionAsync();
|
||||||
|
if (_disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
Rebuild(collection.Items ?? new List<CollectionItemDto>());
|
Rebuild(collection.Items ?? new List<CollectionItemDto>());
|
||||||
SetStatus(
|
SetStatus(
|
||||||
collection.Items == null || collection.Items.Count == 0
|
collection.Items == null || collection.Items.Count == 0
|
||||||
|
|
@ -286,12 +299,18 @@ namespace Vanguard.UI
|
||||||
|
|
||||||
private void OnPlayerChanged()
|
private void OnPlayerChanged()
|
||||||
{
|
{
|
||||||
|
if (_disposed || _balances == null)
|
||||||
|
return;
|
||||||
|
|
||||||
_balances.text = "Gold " + _player.Gold + " · Пыль " + _player.Dust
|
_balances.text = "Gold " + _player.Gold + " · Пыль " + _player.Dust
|
||||||
+ " · Токены " + _player.Tokens;
|
+ " · Токены " + _player.Tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetStatus(string text, Color color)
|
private void SetStatus(string text, Color color)
|
||||||
{
|
{
|
||||||
|
if (_disposed || _status == null)
|
||||||
|
return;
|
||||||
|
|
||||||
_status.text = text ?? string.Empty;
|
_status.text = text ?? string.Empty;
|
||||||
_status.color = color;
|
_status.color = color;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,13 @@ using Vanguard.State;
|
||||||
namespace Vanguard.UI
|
namespace Vanguard.UI
|
||||||
{
|
{
|
||||||
/// <summary>Список колод и быстрый сборщик стартовой колоды из триал-деки.</summary>
|
/// <summary>Список колод и быстрый сборщик стартовой колоды из триал-деки.</summary>
|
||||||
public sealed class DecksView
|
public sealed class DecksView : IDisposable
|
||||||
{
|
{
|
||||||
private readonly MetaService _meta;
|
private readonly MetaService _meta;
|
||||||
private readonly PlayerState _player;
|
private readonly PlayerState _player;
|
||||||
private readonly Action _onBack;
|
private readonly Action _onBack;
|
||||||
private readonly Action<DeckDto> _onPlay;
|
private readonly Action<DeckDto> _onPlay;
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
private readonly RectTransform _root;
|
private readonly RectTransform _root;
|
||||||
private RectTransform _content;
|
private RectTransform _content;
|
||||||
|
|
@ -122,6 +123,15 @@ namespace Vanguard.UI
|
||||||
_confirm.gameObject.SetActive(false);
|
_confirm.gameObject.SetActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
_player.Changed -= OnPlayerChanged;
|
||||||
|
}
|
||||||
|
|
||||||
private RectTransform BuildConfirm(Transform parent)
|
private RectTransform BuildConfirm(Transform parent)
|
||||||
{
|
{
|
||||||
var overlay = UiKit.CreatePanel(parent, "DeleteDeckConfirm", new Color(0f, 0f, 0f, 0.72f));
|
var overlay = UiKit.CreatePanel(parent, "DeleteDeckConfirm", new Color(0f, 0f, 0f, 0.72f));
|
||||||
|
|
@ -161,6 +171,9 @@ namespace Vanguard.UI
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var decks = await _meta.GetDecksAsync();
|
var decks = await _meta.GetDecksAsync();
|
||||||
|
if (_disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
Rebuild(decks);
|
Rebuild(decks);
|
||||||
SetStatus(
|
SetStatus(
|
||||||
decks.Count == 0 ? "Колод пока нет." : "Колод: " + decks.Count + ".",
|
decks.Count == 0 ? "Колод пока нет." : "Колод: " + decks.Count + ".",
|
||||||
|
|
@ -324,12 +337,18 @@ namespace Vanguard.UI
|
||||||
|
|
||||||
private void OnPlayerChanged()
|
private void OnPlayerChanged()
|
||||||
{
|
{
|
||||||
|
if (_disposed || _balances == null)
|
||||||
|
return;
|
||||||
|
|
||||||
_balances.text = "Gold " + _player.Gold + " · Пыль " + _player.Dust
|
_balances.text = "Gold " + _player.Gold + " · Пыль " + _player.Dust
|
||||||
+ " · Токены " + _player.Tokens;
|
+ " · Токены " + _player.Tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetStatus(string text, Color color)
|
private void SetStatus(string text, Color color)
|
||||||
{
|
{
|
||||||
|
if (_disposed || _status == null)
|
||||||
|
return;
|
||||||
|
|
||||||
_status.text = text ?? string.Empty;
|
_status.text = text ?? string.Empty;
|
||||||
_status.color = color;
|
_status.color = color;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -229,6 +229,10 @@ namespace Vanguard.UI
|
||||||
|
|
||||||
private void SetBusy(bool busy, string status)
|
private void SetBusy(bool busy, string status)
|
||||||
{
|
{
|
||||||
|
if (_loginButton == null || _registerButton == null || _serverUrl == null
|
||||||
|
|| _userName == null || _password == null)
|
||||||
|
return;
|
||||||
|
|
||||||
_busy = busy;
|
_busy = busy;
|
||||||
_loginButton.interactable = !busy;
|
_loginButton.interactable = !busy;
|
||||||
_registerButton.interactable = !busy;
|
_registerButton.interactable = !busy;
|
||||||
|
|
@ -242,6 +246,9 @@ namespace Vanguard.UI
|
||||||
|
|
||||||
private void SetStatus(string text, Color color)
|
private void SetStatus(string text, Color color)
|
||||||
{
|
{
|
||||||
|
if (_status == null)
|
||||||
|
return;
|
||||||
|
|
||||||
_status.text = text ?? string.Empty;
|
_status.text = text ?? string.Empty;
|
||||||
_status.color = color;
|
_status.color = color;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ using Vanguard.State;
|
||||||
namespace Vanguard.UI
|
namespace Vanguard.UI
|
||||||
{
|
{
|
||||||
/// <summary>Главный экран после входа: профиль, балансы и заглушки разделов.</summary>
|
/// <summary>Главный экран после входа: профиль, балансы и заглушки разделов.</summary>
|
||||||
public sealed class MetaView
|
public sealed class MetaView : IDisposable
|
||||||
{
|
{
|
||||||
private readonly AuthService _auth;
|
private readonly AuthService _auth;
|
||||||
private readonly PlayerState _player;
|
private readonly PlayerState _player;
|
||||||
|
|
@ -19,6 +19,7 @@ namespace Vanguard.UI
|
||||||
private readonly Action _onStore;
|
private readonly Action _onStore;
|
||||||
private readonly Action _onPlay;
|
private readonly Action _onPlay;
|
||||||
private readonly Action _onOnboarding;
|
private readonly Action _onOnboarding;
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
private readonly RectTransform _root;
|
private readonly RectTransform _root;
|
||||||
private readonly Text _playerName;
|
private readonly Text _playerName;
|
||||||
|
|
@ -144,6 +145,15 @@ namespace Vanguard.UI
|
||||||
|
|
||||||
public void Hide() => UiKit.SetActive(_root, false);
|
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)
|
private static Text AddStat(Transform parent, string caption)
|
||||||
{
|
{
|
||||||
var card = UiKit.CreatePanel(parent, "Stat_" + caption, new Color(0.09f, 0.11f, 0.15f, 1f));
|
var card = UiKit.CreatePanel(parent, "Stat_" + caption, new Color(0.09f, 0.11f, 0.15f, 1f));
|
||||||
|
|
@ -183,11 +193,15 @@ namespace Vanguard.UI
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _auth.RefreshMeAsync();
|
await _auth.RefreshMeAsync();
|
||||||
|
if (_disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
SetStatus("Профиль обновлён.", new Color(0.45f, 0.85f, 0.55f));
|
SetStatus("Профиль обновлён.", new Color(0.45f, 0.85f, 0.55f));
|
||||||
}
|
}
|
||||||
catch (ApiException exception) when (exception.StatusCode == 401)
|
catch (ApiException exception) when (exception.StatusCode == 401)
|
||||||
{
|
{
|
||||||
_onLoggedOut();
|
if (!_disposed)
|
||||||
|
_onLoggedOut();
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
|
|
@ -202,6 +216,8 @@ namespace Vanguard.UI
|
||||||
|
|
||||||
private void UpdateTexts()
|
private void UpdateTexts()
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
return;
|
||||||
_playerName.text = string.IsNullOrEmpty(_player.UserName)
|
_playerName.text = string.IsNullOrEmpty(_player.UserName)
|
||||||
? "…"
|
? "…"
|
||||||
: _player.UserName;
|
: _player.UserName;
|
||||||
|
|
@ -224,6 +240,9 @@ namespace Vanguard.UI
|
||||||
|
|
||||||
private void SetStatus(string text, Color color)
|
private void SetStatus(string text, Color color)
|
||||||
{
|
{
|
||||||
|
if (_disposed || _status == null)
|
||||||
|
return;
|
||||||
|
|
||||||
_status.text = text ?? string.Empty;
|
_status.text = text ?? string.Empty;
|
||||||
_status.color = color;
|
_status.color = color;
|
||||||
}
|
}
|
||||||
|
|
@ -232,6 +251,9 @@ namespace Vanguard.UI
|
||||||
{
|
{
|
||||||
_logout.interactable = false;
|
_logout.interactable = false;
|
||||||
await _auth.LogoutAsync();
|
await _auth.LogoutAsync();
|
||||||
|
if (_disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
_logout.interactable = true;
|
_logout.interactable = true;
|
||||||
_onLoggedOut();
|
_onLoggedOut();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,13 @@ using Vanguard.State;
|
||||||
namespace Vanguard.UI
|
namespace Vanguard.UI
|
||||||
{
|
{
|
||||||
/// <summary>Магазин в Lobby: бустеры и вторая триал-дека.</summary>
|
/// <summary>Магазин в Lobby: бустеры и вторая триал-дека.</summary>
|
||||||
public sealed class StoreView
|
public sealed class StoreView : IDisposable
|
||||||
{
|
{
|
||||||
private readonly MetaService _meta;
|
private readonly MetaService _meta;
|
||||||
private readonly AuthService _auth;
|
private readonly AuthService _auth;
|
||||||
private readonly PlayerState _player;
|
private readonly PlayerState _player;
|
||||||
private readonly Action _onBack;
|
private readonly Action _onBack;
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
private readonly RectTransform _root;
|
private readonly RectTransform _root;
|
||||||
private readonly Text _balances;
|
private readonly Text _balances;
|
||||||
|
|
@ -147,6 +148,15 @@ namespace Vanguard.UI
|
||||||
UiKit.SetActive(_root, false);
|
UiKit.SetActive(_root, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
_player.Changed -= OnPlayerChanged;
|
||||||
|
}
|
||||||
|
|
||||||
public void Tick()
|
public void Tick()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
@ -164,6 +174,9 @@ namespace Vanguard.UI
|
||||||
{
|
{
|
||||||
await _auth.EnsureAccessAsync();
|
await _auth.EnsureAccessAsync();
|
||||||
_store = await _meta.GetStoreAsync();
|
_store = await _meta.GetStoreAsync();
|
||||||
|
if (_disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
RebuildTrials(_store.TrialDecks);
|
RebuildTrials(_store.TrialDecks);
|
||||||
_goldButton.GetComponentInChildren<Text>().text =
|
_goldButton.GetComponentInChildren<Text>().text =
|
||||||
"Купить за " + _store.BoosterPriceGold + " Gold";
|
"Купить за " + _store.BoosterPriceGold + " Gold";
|
||||||
|
|
@ -319,6 +332,9 @@ namespace Vanguard.UI
|
||||||
private async Task BuyBoosterAsync(string payment)
|
private async Task BuyBoosterAsync(string payment)
|
||||||
{
|
{
|
||||||
var result = await _meta.BuyBoosterAsync(payment);
|
var result = await _meta.BuyBoosterAsync(payment);
|
||||||
|
if (_disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
SetStatus("Бустер открыт!", new Color(0.45f, 0.85f, 0.55f));
|
SetStatus("Бустер открыт!", new Color(0.45f, 0.85f, 0.55f));
|
||||||
ShowResult(result);
|
ShowResult(result);
|
||||||
}
|
}
|
||||||
|
|
@ -326,6 +342,9 @@ namespace Vanguard.UI
|
||||||
private async Task BuyTrialAsync(StoreTrialDeckDto deck)
|
private async Task BuyTrialAsync(StoreTrialDeckDto deck)
|
||||||
{
|
{
|
||||||
await _meta.BuyTrialDeckAsync(deck.Id);
|
await _meta.BuyTrialDeckAsync(deck.Id);
|
||||||
|
if (_disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
SetStatus("«" + deck.Name + "» добавлена в коллекцию.", new Color(0.45f, 0.85f, 0.55f));
|
SetStatus("«" + deck.Name + "» добавлена в коллекцию.", new Color(0.45f, 0.85f, 0.55f));
|
||||||
await LoadAsync();
|
await LoadAsync();
|
||||||
}
|
}
|
||||||
|
|
@ -437,6 +456,9 @@ namespace Vanguard.UI
|
||||||
|
|
||||||
private void SetButtons(bool interactable)
|
private void SetButtons(bool interactable)
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
if (_goldButton != null)
|
if (_goldButton != null)
|
||||||
_goldButton.interactable = interactable;
|
_goldButton.interactable = interactable;
|
||||||
if (_tokenButton != null)
|
if (_tokenButton != null)
|
||||||
|
|
@ -445,12 +467,18 @@ namespace Vanguard.UI
|
||||||
|
|
||||||
private void OnPlayerChanged()
|
private void OnPlayerChanged()
|
||||||
{
|
{
|
||||||
|
if (_disposed || _balances == null)
|
||||||
|
return;
|
||||||
|
|
||||||
_balances.text = "Gold " + _player.Gold + " · Пыль " + _player.Dust
|
_balances.text = "Gold " + _player.Gold + " · Пыль " + _player.Dust
|
||||||
+ " · Токены " + _player.Tokens;
|
+ " · Токены " + _player.Tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetStatus(string text, Color color)
|
private void SetStatus(string text, Color color)
|
||||||
{
|
{
|
||||||
|
if (_disposed || _status == null)
|
||||||
|
return;
|
||||||
|
|
||||||
_status.text = text ?? string.Empty;
|
_status.text = text ?? string.Empty;
|
||||||
_status.color = color;
|
_status.color = color;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ using NUnit.Framework;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
using Vanguard.Api;
|
using Vanguard.Api;
|
||||||
|
using Vanguard.Api.Contracts;
|
||||||
using Vanguard.App;
|
using Vanguard.App;
|
||||||
using Vanguard.Auth;
|
using Vanguard.Auth;
|
||||||
using Vanguard.Meta;
|
using Vanguard.Meta;
|
||||||
|
|
@ -55,6 +56,19 @@ namespace Vanguard.Tests
|
||||||
Assert.IsTrue(Exists("QueueScreen"));
|
Assert.IsTrue(Exists("QueueScreen"));
|
||||||
Assert.IsTrue(Exists("BattleScreen"));
|
Assert.IsTrue(Exists("BattleScreen"));
|
||||||
|
|
||||||
|
// После смены сцены старые views не должны реагировать на PlayerState.
|
||||||
|
lobby.Dispose();
|
||||||
|
collection.Dispose();
|
||||||
|
decks.Dispose();
|
||||||
|
store.Dispose();
|
||||||
|
player.Apply(new MeResponse
|
||||||
|
{
|
||||||
|
UserName = "after-dispose",
|
||||||
|
Gold = 10,
|
||||||
|
Dust = 2,
|
||||||
|
Tokens = 1,
|
||||||
|
});
|
||||||
|
|
||||||
Cleanup();
|
Cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue