add ThrowTimer
This commit is contained in:
parent
5db650f2b2
commit
c33a6f9d91
|
|
@ -0,0 +1,40 @@
|
|||
@using Koogle.Web.Store.TimerState
|
||||
|
||||
@inherits Fluxor.Blazor.Web.Components.FluxorComponent
|
||||
|
||||
@inject IState<TimerState> TimerState
|
||||
@inject IDispatcher Dispatcher
|
||||
|
||||
@if (TimerState.Value.IsRunning)
|
||||
{
|
||||
<MudButton OnClick="AbortTimer"
|
||||
Variant="Variant.Filled"
|
||||
Color="Color.Primary"
|
||||
Size="Size.Large"
|
||||
Class="throw-timer-button">
|
||||
@TimerState.Value.RemainingSeconds
|
||||
</MudButton>
|
||||
}
|
||||
|
||||
<style>
|
||||
.throw-timer-button {
|
||||
font-size: 2rem;
|
||||
font-weight: bold;
|
||||
min-width: 80px;
|
||||
min-height: 60px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@code {
|
||||
/// <summary>
|
||||
/// Callback when timer is aborted by user click.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public EventCallback OnTimerAborted { get; set; }
|
||||
|
||||
private async Task AbortTimer()
|
||||
{
|
||||
Dispatcher.Dispatch(new StopTimerAction());
|
||||
await OnTimerAborted.InvokeAsync();
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
@attribute [Authorize(Policy = "ClubViewer")]
|
||||
|
||||
@inherits Fluxor.Blazor.Web.Components.FluxorComponent
|
||||
@implements IDisposable
|
||||
|
||||
@using Fluxor
|
||||
@using Koogle.Application.DTOs
|
||||
|
|
@ -9,12 +10,14 @@
|
|||
@using Koogle.Web.Store.DayState
|
||||
@using Koogle.Web.Store.GameState
|
||||
@using Koogle.Web.Store.PersonState
|
||||
@using Koogle.Web.Store.TimerState
|
||||
@using Koogle.Web.Components.Game
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
|
||||
@inject IState<DayState> DayState
|
||||
@inject IState<GameState> GameState
|
||||
@inject IState<PersonState> PersonState
|
||||
@inject IState<TimerState> TimerState
|
||||
@inject IDispatcher Dispatcher
|
||||
@inject ISnackbar Snackbar
|
||||
@inject IDialogService DialogService
|
||||
|
|
@ -426,6 +429,12 @@ else
|
|||
|
||||
<!-- Tab 3: Tafel (Game Board) -->
|
||||
<MudTabPanel Text="Tafel" Icon="@Icons.Material.Filled.TableChart" Disabled="@(!GameState.Value.IsGameActive)">
|
||||
@if (TimerState.Value.IsRunning)
|
||||
{
|
||||
<div class="d-flex justify-center mb-4">
|
||||
<ThrowTimer OnTimerAborted="HandleTimerAborted" />
|
||||
</div>
|
||||
}
|
||||
<GameBoardPanel />
|
||||
</MudTabPanel>
|
||||
</MudTabs>
|
||||
|
|
@ -835,9 +844,43 @@ else
|
|||
Snackbar.Add("Spieler-Auswahl noch nicht implementiert", Severity.Info);
|
||||
}
|
||||
|
||||
private const int TimerDurationSeconds = 3;
|
||||
|
||||
private Task HandleThrowCompleted(ThrowResult result)
|
||||
{
|
||||
// Throw was completed - game logic will handle state updates
|
||||
// Switch to Tafel tab and start timer
|
||||
_activeTabIndex = 2;
|
||||
Dispatcher.Dispatch(new StartTimerAction(TimerDurationSeconds));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void HandleTimerAborted()
|
||||
{
|
||||
// User clicked timer button - switch back to Eingabe tab
|
||||
_activeTabIndex = 1;
|
||||
}
|
||||
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
base.OnAfterRender(firstRender);
|
||||
if (firstRender)
|
||||
{
|
||||
TimerState.StateChanged += OnTimerStateChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTimerStateChanged(object? sender, EventArgs e)
|
||||
{
|
||||
// When timer stops (completed naturally), switch back to Eingabe tab
|
||||
if (!TimerState.Value.IsRunning && _activeTabIndex == 2)
|
||||
{
|
||||
_activeTabIndex = 1;
|
||||
InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
TimerState.StateChanged -= OnTimerStateChanged;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
namespace Koogle.Web.Store.TimerState;
|
||||
|
||||
/// <summary>
|
||||
/// Action to start the throw timer.
|
||||
/// </summary>
|
||||
public record StartTimerAction(int Seconds);
|
||||
|
||||
/// <summary>
|
||||
/// Action dispatched when timer completes naturally.
|
||||
/// </summary>
|
||||
public record TimerCompletedAction;
|
||||
|
||||
/// <summary>
|
||||
/// Action to stop the timer manually (user click).
|
||||
/// </summary>
|
||||
public record StopTimerAction;
|
||||
|
||||
/// <summary>
|
||||
/// Action dispatched every second while timer is running.
|
||||
/// </summary>
|
||||
public record TickAction;
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
using Fluxor;
|
||||
|
||||
namespace Koogle.Web.Store.TimerState;
|
||||
|
||||
/// <summary>
|
||||
/// Effects for handling timer async operations.
|
||||
/// </summary>
|
||||
public class TimerEffects(ILogger<TimerEffects> logger)
|
||||
{
|
||||
private CancellationTokenSource? _timerCancellation;
|
||||
|
||||
[EffectMethod]
|
||||
public async Task HandleStartTimer(StartTimerAction action, IDispatcher dispatcher)
|
||||
{
|
||||
logger.LogInformation("Timer started for {Seconds} seconds", action.Seconds);
|
||||
|
||||
// Cancel previous timer if running
|
||||
if (_timerCancellation != null)
|
||||
{
|
||||
await _timerCancellation.CancelAsync();
|
||||
}
|
||||
|
||||
_timerCancellation = new CancellationTokenSource();
|
||||
|
||||
try
|
||||
{
|
||||
for (var i = 0; i < action.Seconds; i++)
|
||||
{
|
||||
var token = _timerCancellation?.Token ?? CancellationToken.None;
|
||||
await Task.Delay(1000, token);
|
||||
dispatcher.Dispatch(new TickAction());
|
||||
}
|
||||
|
||||
// Timer completed naturally
|
||||
dispatcher.Dispatch(new TimerCompletedAction());
|
||||
logger.LogInformation("Timer completed");
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
logger.LogInformation("Timer was cancelled");
|
||||
}
|
||||
}
|
||||
|
||||
[EffectMethod]
|
||||
public async Task HandleStopTimer(StopTimerAction action, IDispatcher dispatcher)
|
||||
{
|
||||
if (_timerCancellation is { IsCancellationRequested: false })
|
||||
{
|
||||
logger.LogInformation("Timer stopped manually");
|
||||
await _timerCancellation.CancelAsync();
|
||||
_timerCancellation = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
using Fluxor;
|
||||
|
||||
namespace Koogle.Web.Store.TimerState;
|
||||
|
||||
public static class TimerReducers
|
||||
{
|
||||
[ReducerMethod]
|
||||
public static TimerState OnStartTimer(TimerState state, StartTimerAction action)
|
||||
{
|
||||
return state with
|
||||
{
|
||||
RemainingSeconds = action.Seconds,
|
||||
IsRunning = true
|
||||
};
|
||||
}
|
||||
|
||||
[ReducerMethod]
|
||||
public static TimerState OnTick(TimerState state, TickAction action)
|
||||
{
|
||||
if (!state.IsRunning || state.RemainingSeconds <= 0)
|
||||
return state;
|
||||
|
||||
return state with
|
||||
{
|
||||
RemainingSeconds = state.RemainingSeconds - 1,
|
||||
IsRunning = state.RemainingSeconds - 1 > 0
|
||||
};
|
||||
}
|
||||
|
||||
[ReducerMethod]
|
||||
public static TimerState OnTimerCompleted(TimerState state, TimerCompletedAction action)
|
||||
{
|
||||
return state with
|
||||
{
|
||||
IsRunning = false,
|
||||
RemainingSeconds = 0
|
||||
};
|
||||
}
|
||||
|
||||
[ReducerMethod]
|
||||
public static TimerState OnStopTimer(TimerState state, StopTimerAction action)
|
||||
{
|
||||
return state with
|
||||
{
|
||||
IsRunning = false,
|
||||
RemainingSeconds = 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
using Fluxor;
|
||||
|
||||
namespace Koogle.Web.Store.TimerState;
|
||||
|
||||
/// <summary>
|
||||
/// Fluxor state for throw timer management.
|
||||
/// </summary>
|
||||
[FeatureState]
|
||||
public record TimerState(int RemainingSeconds, bool IsRunning)
|
||||
{
|
||||
public TimerState() : this(0, false)
|
||||
{
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue