diff --git a/KoogleApp/Components/Controls/NumberPanel.razor b/KoogleApp/Components/Controls/NumberPanel.razor index 21c7157..6ebd19b 100644 --- a/KoogleApp/Components/Controls/NumberPanel.razor +++ b/KoogleApp/Components/Controls/NumberPanel.razor @@ -2,6 +2,7 @@ @using KoogleApp.Model.EventMessages @using KoogleApp.Services @using KoogleApp.Store.Game +@using KoogleApp.Store.Game.ThrowPanel @inherits FluxorComponent diff --git a/KoogleApp/Components/Controls/PinPanel.razor b/KoogleApp/Components/Controls/PinPanel.razor index 7e47568..41bc505 100644 --- a/KoogleApp/Components/Controls/PinPanel.razor +++ b/KoogleApp/Components/Controls/PinPanel.razor @@ -3,6 +3,7 @@ @using KoogleApp.Model.EventMessages @using KoogleApp.Services @using KoogleApp.Store.Game +@using KoogleApp.Store.Game.ThrowPanel @inherits FluxorComponent diff --git a/KoogleApp/Components/Controls/ThrowPanel.razor b/KoogleApp/Components/Controls/ThrowPanel.razor index c2c4f4f..250bfb7 100644 --- a/KoogleApp/Components/Controls/ThrowPanel.razor +++ b/KoogleApp/Components/Controls/ThrowPanel.razor @@ -1,10 +1,11 @@ @using KoogleApp.Model @using KoogleApp.Store.Game +@using KoogleApp.Store.Game.ThrowPanel @inherits FluxorComponent @inject IState ThrowPanelState - +@inject IDispatcher Dispatcher @if (ThrowPanelState.Value.IsStated) { @@ -60,6 +61,16 @@ // public ThrowPanelState? PanelState { get; set; } + protected override void OnAfterRender(bool firstRender) + { + if (firstRender) + { + Dispatcher.Dispatch(new LoadStateFromSessionAction()); + } + + base.OnAfterRender(firstRender); + } + private async Task OnNumberPinStateChanged(ThrowPanelState state) { await OnStateChanged.InvokeAsync(state); diff --git a/KoogleApp/Components/Pages/Game.razor b/KoogleApp/Components/Pages/Game.razor index 2ad5c70..2757b45 100644 --- a/KoogleApp/Components/Pages/Game.razor +++ b/KoogleApp/Components/Pages/Game.razor @@ -8,6 +8,7 @@ @using KoogleApp.Model.EventMessages @using KoogleApp.Model.Framework @using KoogleApp.Store.Game +@using KoogleApp.Store.Game.ThrowPanel @inherits FluxorComponent @@ -255,7 +256,7 @@ Dispatcher.Dispatch(action); // _throwPanelState = ThrowPanelState.Create(); - // await BroadcastThrowPanelState(_throwPanelState); + // await BroadcastThrowPanelStateAction(_throwPanelState); // await InvokeAsync(StateHasChanged); } @@ -274,10 +275,10 @@ // private async Task OnThrowPanelStateChanged(ThrowPanelState state) // { - // await BroadcastThrowPanelState(state); + // await BroadcastThrowPanelStateAction(state); // } - // private async Task BroadcastThrowPanelState(ThrowPanelState state) + // private async Task BroadcastThrowPanelStateAction(ThrowPanelState state) // { // if (hubConnection is not null) // { diff --git a/KoogleApp/Hub/SharedModelHub.cs b/KoogleApp/Hub/SharedModelHub.cs index 19d5f8b..5c6c769 100644 --- a/KoogleApp/Hub/SharedModelHub.cs +++ b/KoogleApp/Hub/SharedModelHub.cs @@ -1,12 +1,12 @@ using Fluxor; using KoogleApp.Model; using KoogleApp.Services; -using KoogleApp.Store.Game; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR.Client; using Microsoft.EntityFrameworkCore.Metadata.Internal; using System.Diagnostics; +using KoogleApp.Store.Game.ThrowPanel; namespace KoogleApp.Hub { @@ -63,9 +63,9 @@ namespace KoogleApp.Hub try { if (_hubConnection is not null) - { - await _hubConnection.SendAsync("BroadcastThrowPanelState", action.State); - } + { + await _hubConnection.SendAsync("BroadcastThrowPanelState", action.State); + } //await Clients.Others.SendAsync("ReceiveThrowPanelState", new ThrowPanelState()); diff --git a/KoogleApp/Model/ThrowPanelState.cs b/KoogleApp/Model/ThrowPanelState.cs new file mode 100644 index 0000000..50a2433 --- /dev/null +++ b/KoogleApp/Model/ThrowPanelState.cs @@ -0,0 +1,27 @@ +using System.Runtime.InteropServices; +using Fluxor; +using Microsoft.AspNetCore.Mvc; +using Microsoft.IdentityModel.Tokens; + +namespace KoogleApp.Model +{ + [FeatureState] + public record ThrowPanelState(bool IsStated, bool BellValue, + bool Pin1Value, bool Pin2Value, bool Pin3Value, bool Pin4Value, bool Pin5Value, bool Pin6Value, bool Pin7Value, bool Pin8Value, bool Pin9Value, + bool Pin1Disabled, bool Pin2Disabled, bool Pin3Disabled, bool Pin4Disabled, bool Pin5Disabled, bool Pin6Disabled, bool Pin7Disabled, bool Pin8Disabled, bool Pin9Disabled + ) + { + // Required for creating initial state + public ThrowPanelState() : this(BellValue:false, IsStated:false, + Pin1Value:false, Pin2Value:false, Pin3Value: false, Pin4Value: false, Pin5Value: false, Pin6Value: false, Pin7Value: false, Pin8Value: false, Pin9Value: false, + Pin1Disabled:false, Pin2Disabled:false, Pin3Disabled: false, Pin4Disabled: false, Pin5Disabled: false, Pin6Disabled: false, Pin7Disabled: false, Pin8Disabled: false, Pin9Disabled: false + ) + { } + } + + + // Actions + + + +} \ No newline at end of file diff --git a/KoogleApp/Services/GameStatusDataService.cs b/KoogleApp/Services/GameStatusDataService.cs index 7a03152..5e24a5a 100644 --- a/KoogleApp/Services/GameStatusDataService.cs +++ b/KoogleApp/Services/GameStatusDataService.cs @@ -22,7 +22,7 @@ namespace KoogleApp.Services private GameStatusSnapshot _currentData; private readonly Stack _undoStack; private readonly Stack _redoStack; - private readonly object _lock = new object(); + private readonly Lock _lock = new Lock(); private readonly string _dataFilePath = "appdata.json"; private const int MaxUndoSteps = 50; diff --git a/KoogleApp/Services/SessionStorage.cs b/KoogleApp/Services/SessionStorage.cs index f0d956d..ac1fc79 100644 --- a/KoogleApp/Services/SessionStorage.cs +++ b/KoogleApp/Services/SessionStorage.cs @@ -15,5 +15,16 @@ namespace KoogleApp.Services { await sessionStorage.SetAsync("selectedPlayer", record); } + + public async Task GetThrowPanelStateAsync() + { + var result = await sessionStorage.GetAsync("throwPanelState"); + return result.Success ? result.Value : null; + } + + public async Task SetThrowPanelStateAsync(ThrowPanelState state) + { + await sessionStorage.SetAsync("throwPanelState", state); + } } } diff --git a/KoogleApp/Store/Game/ThrowPanel/Actions.cs b/KoogleApp/Store/Game/ThrowPanel/Actions.cs new file mode 100644 index 0000000..bd42d7f --- /dev/null +++ b/KoogleApp/Store/Game/ThrowPanel/Actions.cs @@ -0,0 +1,24 @@ +using KoogleApp.Model; + +namespace KoogleApp.Store.Game.ThrowPanel +{ + public record TogglePinValueAction(bool IsOn, int PinNumber); + + public record StartAction(ThrowPanelState State); + + public record ConnectToHubAction(); + + public record ToggleAllPinsAction(); + + public record ToggleBellAction(); + + public record UpdatePinStateByNumber(int Number); + + public record ReceiveStateFromServer(ThrowPanelState State); + + public record LoadStateFromSessionAction(); + + public record StateLoadedAction(ThrowPanelState? State); + + public record BroadcastThrowPanelStateAction(ThrowPanelState State); +} diff --git a/KoogleApp/Store/Game/ThrowPanel/Effects.cs b/KoogleApp/Store/Game/ThrowPanel/Effects.cs new file mode 100644 index 0000000..5860ee9 --- /dev/null +++ b/KoogleApp/Store/Game/ThrowPanel/Effects.cs @@ -0,0 +1,103 @@ +using Fluxor; +using KoogleApp.Hub; +using KoogleApp.Model; +using KoogleApp.Services; +using System.Text.Json; + +namespace KoogleApp.Store.Game.ThrowPanel +{ + public class ThrowPanelEffects : IDisposable + { + private readonly ILogger _logger; + private readonly HubConnectionService _sharedModelHub; + private readonly IState _state; + private readonly SessionStorage _sessionStorage; + private readonly string _dataFilePath = "ThrowPanelState.json"; + + public ThrowPanelEffects( + ILogger logger, + HubConnectionService sharedModelHub, IState state, + SessionStorage sessionStorage) + { + _logger = logger; + _sharedModelHub = sharedModelHub; + _state = state; + _sessionStorage = sessionStorage; + } + + public void Dispose() + { + //_sharedModelHub?.Dispose(); + } + + //[EffectMethod(typeof(StartAction))] + [EffectMethod] + public async Task HandleStartAction(StartAction action, IDispatcher dispatcher) + { + dispatcher.Dispatch(new BroadcastThrowPanelStateAction(_state.Value)); + } + + [EffectMethod] + public async Task HandleConnectToHubAction(ConnectToHubAction action, IDispatcher dispatcher) + { + try + { + await _sharedModelHub.ConnectToHub(dispatcher); + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to connect to hub"); + //dispatcher.Dispatch(new HubConnectionFailedAction(ex.Message)); + } + } + + [EffectMethod] + public async Task HandelLoadStateAction(LoadStateFromSessionAction stateFromSessionAction, IDispatcher dispatcher) + { + var state = await _sessionStorage.GetThrowPanelStateAsync(); + + if (state == null) + { + try + { + if (File.Exists(_dataFilePath)) + { + var json = await File.ReadAllTextAsync(_dataFilePath); + state = JsonSerializer.Deserialize(json); + } + } + catch (Exception e) + { + // TODO error handling + } + } + + if (state != null) + { + dispatcher.Dispatch(new StateLoadedAction(state)); + } + + } + + [EffectMethod] + public async Task HandelBroadcastThrowPanelStateAction(BroadcastThrowPanelStateAction action, IDispatcher dispatcher) + { + await _sessionStorage.SetThrowPanelStateAsync(action.State); + + try + { + var json = JsonSerializer.Serialize(action.State, new JsonSerializerOptions + { + WriteIndented = true + }); + await File.WriteAllTextAsync(_dataFilePath, json); + } + catch (Exception ex) + { + // TODO error handling + } + + await _sharedModelHub.HandleStartAction(new StartAction(_state.Value), dispatcher); + } + } +} diff --git a/KoogleApp/Store/Game/ThrowPanelState.cs b/KoogleApp/Store/Game/ThrowPanel/Reducers.cs similarity index 74% rename from KoogleApp/Store/Game/ThrowPanelState.cs rename to KoogleApp/Store/Game/ThrowPanel/Reducers.cs index 4f7b744..8cc977b 100644 --- a/KoogleApp/Store/Game/ThrowPanelState.cs +++ b/KoogleApp/Store/Game/ThrowPanel/Reducers.cs @@ -1,40 +1,8 @@ -using System.Runtime.InteropServices; -using Fluxor; -using Microsoft.AspNetCore.Mvc; -using Microsoft.IdentityModel.Tokens; +using Fluxor; +using KoogleApp.Model; -namespace KoogleApp.Store.Game +namespace KoogleApp.Store.Game.ThrowPanel { - [FeatureState] - public record ThrowPanelState(bool IsStated, bool BellValue, - bool Pin1Value, bool Pin2Value, bool Pin3Value, bool Pin4Value, bool Pin5Value, bool Pin6Value, bool Pin7Value, bool Pin8Value, bool Pin9Value, - bool Pin1Disabled, bool Pin2Disabled, bool Pin3Disabled, bool Pin4Disabled, bool Pin5Disabled, bool Pin6Disabled, bool Pin7Disabled, bool Pin8Disabled, bool Pin9Disabled - ) - { - // Required for creating initial state - public ThrowPanelState() : this(BellValue:false, IsStated:false, - Pin1Value:false, Pin2Value:false, Pin3Value: false, Pin4Value: false, Pin5Value: false, Pin6Value: false, Pin7Value: false, Pin8Value: false, Pin9Value: false, - Pin1Disabled:false, Pin2Disabled:false, Pin3Disabled: false, Pin4Disabled: false, Pin5Disabled: false, Pin6Disabled: false, Pin7Disabled: false, Pin8Disabled: false, Pin9Disabled: false - ) - { } - } - - - // Actions - public record TogglePinValueAction(bool IsOn, int PinNumber); - - public record StartAction(ThrowPanelState State); - - public record ConnectToHubAction(); - - public record ToggleAllPinsAction(); - - public record ToggleBellAction(); - - public record UpdatePinStateByNumber(int Number); - - public record ReceiveStateFromServer(ThrowPanelState State); - // Reducer public static class ThrowPanelStateReducer { @@ -148,5 +116,11 @@ namespace KoogleApp.Store.Game { return state with { BellValue = !state.BellValue }; } + + [ReducerMethod] + public static ThrowPanelState OnLoadState(ThrowPanelState state, StateLoadedAction action) + { + return action.State; + } } -} \ No newline at end of file +} diff --git a/KoogleApp/Store/Game/ThrowPanelEffects.cs b/KoogleApp/Store/Game/ThrowPanelEffects.cs deleted file mode 100644 index 69c71c1..0000000 --- a/KoogleApp/Store/Game/ThrowPanelEffects.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Fluxor; -using KoogleApp.Hub; -using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.SignalR.Client; - -namespace KoogleApp.Store.Game -{ - public class ThrowPanelEffects : IDisposable - { - private readonly ILogger _logger; - private readonly HubConnectionService _sharedModelHub; - private readonly IState _state; - - public ThrowPanelEffects( - ILogger logger, - HubConnectionService sharedModelHub, IState state) - { - _logger = logger; - _sharedModelHub = sharedModelHub; - _state = state; - } - - public void Dispose() - { - //_sharedModelHub?.Dispose(); - } - - //[EffectMethod(typeof(StartAction))] - [EffectMethod] - public async Task HandleStartAction(StartAction action, IDispatcher dispatcher) - { - await _sharedModelHub.HandleStartAction(new StartAction(_state.Value), dispatcher); - } - - [EffectMethod] - public async Task HandleConnectToHubAction(ConnectToHubAction action, IDispatcher dispatcher) - { - try - { - await _sharedModelHub.ConnectToHub(dispatcher); - } - catch (Exception ex) - { - _logger.LogError(ex, "Failed to connect to hub"); - //dispatcher.Dispatch(new HubConnectionFailedAction(ex.Message)); - } - } - } -} diff --git a/KoogleApp/ThrowPanelState.json b/KoogleApp/ThrowPanelState.json new file mode 100644 index 0000000..0679a84 --- /dev/null +++ b/KoogleApp/ThrowPanelState.json @@ -0,0 +1,22 @@ +{ + "IsStated": true, + "BellValue": false, + "Pin1Value": false, + "Pin2Value": false, + "Pin3Value": false, + "Pin4Value": false, + "Pin5Value": false, + "Pin6Value": false, + "Pin7Value": false, + "Pin8Value": false, + "Pin9Value": false, + "Pin1Disabled": false, + "Pin2Disabled": false, + "Pin3Disabled": false, + "Pin4Disabled": false, + "Pin5Disabled": false, + "Pin6Disabled": false, + "Pin7Disabled": false, + "Pin8Disabled": false, + "Pin9Disabled": false +} \ No newline at end of file