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")]
|
@attribute [Authorize(Policy = "ClubViewer")]
|
||||||
|
|
||||||
@inherits Fluxor.Blazor.Web.Components.FluxorComponent
|
@inherits Fluxor.Blazor.Web.Components.FluxorComponent
|
||||||
|
@implements IDisposable
|
||||||
|
|
||||||
@using Fluxor
|
@using Fluxor
|
||||||
@using Koogle.Application.DTOs
|
@using Koogle.Application.DTOs
|
||||||
|
|
@ -9,12 +10,14 @@
|
||||||
@using Koogle.Web.Store.DayState
|
@using Koogle.Web.Store.DayState
|
||||||
@using Koogle.Web.Store.GameState
|
@using Koogle.Web.Store.GameState
|
||||||
@using Koogle.Web.Store.PersonState
|
@using Koogle.Web.Store.PersonState
|
||||||
|
@using Koogle.Web.Store.TimerState
|
||||||
@using Koogle.Web.Components.Game
|
@using Koogle.Web.Components.Game
|
||||||
@using Microsoft.AspNetCore.Authorization
|
@using Microsoft.AspNetCore.Authorization
|
||||||
|
|
||||||
@inject IState<DayState> DayState
|
@inject IState<DayState> DayState
|
||||||
@inject IState<GameState> GameState
|
@inject IState<GameState> GameState
|
||||||
@inject IState<PersonState> PersonState
|
@inject IState<PersonState> PersonState
|
||||||
|
@inject IState<TimerState> TimerState
|
||||||
@inject IDispatcher Dispatcher
|
@inject IDispatcher Dispatcher
|
||||||
@inject ISnackbar Snackbar
|
@inject ISnackbar Snackbar
|
||||||
@inject IDialogService DialogService
|
@inject IDialogService DialogService
|
||||||
|
|
@ -426,6 +429,12 @@ else
|
||||||
|
|
||||||
<!-- Tab 3: Tafel (Game Board) -->
|
<!-- Tab 3: Tafel (Game Board) -->
|
||||||
<MudTabPanel Text="Tafel" Icon="@Icons.Material.Filled.TableChart" Disabled="@(!GameState.Value.IsGameActive)">
|
<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 />
|
<GameBoardPanel />
|
||||||
</MudTabPanel>
|
</MudTabPanel>
|
||||||
</MudTabs>
|
</MudTabs>
|
||||||
|
|
@ -835,9 +844,43 @@ else
|
||||||
Snackbar.Add("Spieler-Auswahl noch nicht implementiert", Severity.Info);
|
Snackbar.Add("Spieler-Auswahl noch nicht implementiert", Severity.Info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const int TimerDurationSeconds = 3;
|
||||||
|
|
||||||
private Task HandleThrowCompleted(ThrowResult result)
|
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;
|
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