feat(task-0006): implement menu restart button and signal coordination
- Update MenuUIViewModel with IMenuRestartSignal dependency and Restart() method - Add RestartButton to MenuUIView with listener management in Initialize/Release - Connect MenuUIView click handler to ViewModel.Restart() callback - Fix race condition in MenuRestartSignal.RequestRestart() by nulling completion source first - Wrap MenuState.WaitAsync() in try-catch for proper view cleanup on cancellation - Update TASK-0006 status to Ready Выполнена задача TASK-0006: реализована кнопка Restart и координация сигналов - Обновлён MenuUIViewModel с зависимостью IMenuRestartSignal и методом Restart() - Добавлена кнопка RestartButton в MenuUIView с управлением слушателями в Initialize/Release - Подключен обработчик кликов MenuUIView к колбэку ViewModel.Restart() - Исправлено состояние гонки в MenuRestartSignal.RequestRestart() путем обнуления completion source - Обёрнут WaitAsync в MenuState в try-catch для корректной очистки view при отмене - Обновлён статус TASK-0006 до Ready
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
# TASK-0006: MenuState и Restart
|
||||
|
||||
## Статус
|
||||
|
||||
Ready
|
||||
|
||||
## Цель
|
||||
|
||||
Реализовать меню с кнопкой `Restart`, которое завершает `MenuState` и возвращает flow к загрузке.
|
||||
|
||||
@@ -381,6 +381,7 @@ GameObject:
|
||||
m_Component:
|
||||
- component: {fileID: 1000000302}
|
||||
- component: {fileID: 1000000303}
|
||||
- component: {fileID: 1000000304}
|
||||
m_Layer: 0
|
||||
m_Name: MenuView
|
||||
m_TagString: Untagged
|
||||
@@ -414,3 +415,48 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: b3333333333333333333333333333333, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
<RestartButton>k__BackingField: {fileID: 1000000304}
|
||||
--- !u!114 &1000000304
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1000000301}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Navigation:
|
||||
m_Mode: 3
|
||||
m_WrapAround: 0
|
||||
m_SelectOnUp: {fileID: 0}
|
||||
m_SelectOnDown: {fileID: 0}
|
||||
m_SelectOnLeft: {fileID: 0}
|
||||
m_SelectOnRight: {fileID: 0}
|
||||
m_Transition: 1
|
||||
m_Colors:
|
||||
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_HighlightedColor: {r: 0.88235295, g: 0.88235295, b: 0.88235295, a: 1}
|
||||
m_PressedColor: {r: 0.69803923, g: 0.69803923, b: 0.69803923, a: 1}
|
||||
m_SelectedColor: {r: 0.88235295, g: 0.88235295, b: 0.88235295, a: 1}
|
||||
m_DisabledColor: {r: 0.52156866, g: 0.52156866, b: 0.52156866, a: 0.5019608}
|
||||
m_ColorMultiplier: 1
|
||||
m_FadeDuration: 0.1
|
||||
m_SpriteState:
|
||||
m_HighlightedSprite: {fileID: 0}
|
||||
m_PressedSprite: {fileID: 0}
|
||||
m_SelectedSprite: {fileID: 0}
|
||||
m_DisabledSprite: {fileID: 0}
|
||||
m_AnimationTriggers:
|
||||
m_NormalTrigger: Normal
|
||||
m_HighlightedTrigger: Highlighted
|
||||
m_PressedTrigger: Pressed
|
||||
m_SelectedTrigger: Selected
|
||||
m_DisabledTrigger: Disabled
|
||||
m_Interactable: 1
|
||||
m_TargetGraphic: {fileID: 0}
|
||||
m_OnClick:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
|
||||
@@ -17,7 +17,9 @@ namespace QuizPleaseTest.Boot.Flow
|
||||
|
||||
public void RequestRestart()
|
||||
{
|
||||
_restartCompletionSource?.TrySetResult();
|
||||
UniTaskCompletionSource restartCompletionSource = _restartCompletionSource;
|
||||
_restartCompletionSource = null;
|
||||
restartCompletionSource?.TrySetResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using QuizPleaseTest.Boot.Flow;
|
||||
@@ -19,9 +20,18 @@ namespace QuizPleaseTest.Boot.States
|
||||
|
||||
public async UniTask EnterAsync(CancellationToken ct)
|
||||
{
|
||||
_view.Bind(new MenuUIViewModel());
|
||||
_view.Bind(new MenuUIViewModel(_restartSignal));
|
||||
_view.Initialize();
|
||||
await _restartSignal.WaitAsync(ct);
|
||||
|
||||
try
|
||||
{
|
||||
await _restartSignal.WaitAsync(ct);
|
||||
}
|
||||
catch (OperationCanceledException) when (ct.IsCancellationRequested)
|
||||
{
|
||||
_view.Release();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public UniTask ExitAsync(CancellationToken ct)
|
||||
|
||||
@@ -1,8 +1,39 @@
|
||||
using QuizPleaseTest.Common.UI;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace QuizPleaseTest.Boot.UI
|
||||
{
|
||||
public class MenuUIView : UIView<MenuUIViewModel>
|
||||
{
|
||||
[field: SerializeField] public Button RestartButton { get; private set; }
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
if (RestartButton == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RestartButton.onClick.RemoveListener(OnRestartClicked);
|
||||
RestartButton.onClick.AddListener(OnRestartClicked);
|
||||
}
|
||||
|
||||
public override void Release()
|
||||
{
|
||||
if (RestartButton != null)
|
||||
{
|
||||
RestartButton.onClick.RemoveListener(OnRestartClicked);
|
||||
}
|
||||
|
||||
base.Release();
|
||||
}
|
||||
|
||||
private void OnRestartClicked()
|
||||
{
|
||||
ViewModel.Restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,21 @@
|
||||
using System;
|
||||
using QuizPleaseTest.Boot.Flow;
|
||||
using QuizPleaseTest.Common.UI;
|
||||
|
||||
namespace QuizPleaseTest.Boot.UI
|
||||
{
|
||||
public class MenuUIViewModel : IUIViewModel
|
||||
{
|
||||
private readonly IMenuRestartSignal _restartSignal;
|
||||
|
||||
public MenuUIViewModel(IMenuRestartSignal restartSignal)
|
||||
{
|
||||
_restartSignal = restartSignal ?? throw new ArgumentNullException(nameof(restartSignal));
|
||||
}
|
||||
|
||||
public void Restart()
|
||||
{
|
||||
_restartSignal.RequestRestart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user