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