/
Store.cs
154 lines (124 loc) · 4.9 KB
/
Store.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Blazor.Services;
namespace BlazorRedux
{
public class Store<TState, TAction> : IDisposable
{
private readonly TState _initialState;
private readonly Reducer<TState, TAction> _rootReducer;
private readonly ReduxOptions<TState> _options;
private IUriHelper _uriHelper;
private string _currentLocation;
private bool _timeTraveling;
private readonly object _syncRoot = new object();
public TState State { get; private set; }
public IList<HistoricEntry<TState, object>> History { get; }
public event EventHandler Change;
public Store(TState initialState, Reducer<TState, TAction> rootReducer, ReduxOptions<TState> options)
{
_initialState = initialState;
_rootReducer = rootReducer;
_options = options;
State = initialState;
DevToolsInterop.Reset += OnDevToolsReset;
DevToolsInterop.TimeTravel += OnDevToolsTimeTravel;
DevToolsInterop.Log("initial", _options.StateSerializer(State));
History = new List<HistoricEntry<TState, object>>
{
new HistoricEntry<TState, object>(State)
};
}
internal void Init(IUriHelper uriHelper)
{
if (_uriHelper != null || uriHelper == null) return;
lock (_syncRoot)
{
_uriHelper = uriHelper;
_uriHelper.OnLocationChanged += OnLocationChanged;
}
// TODO: Queue up any other actions, and let this apply to the initial state.
DispatchLocation(new NewLocationAction { Location = _uriHelper.GetAbsoluteUri() });
Console.WriteLine("Redux store initialized.");
}
public void Dispose()
{
if (_uriHelper != null)
_uriHelper.OnLocationChanged -= OnLocationChanged;
DevToolsInterop.Reset -= OnDevToolsReset;
DevToolsInterop.TimeTravel -= OnDevToolsTimeTravel;
}
private void OnLocationChanged(object sender, string newAbsoluteUri)
{
if (_timeTraveling) return;
if (newAbsoluteUri == _currentLocation) return;
lock (_syncRoot)
{
_currentLocation = newAbsoluteUri;
}
DispatchLocation(new NewLocationAction { Location = newAbsoluteUri });
}
private void OnDevToolsReset(object sender, EventArgs e)
{
var state = _initialState;
TimeTravel(state);
}
private void OnDevToolsTimeTravel(object sender, StringEventArgs e)
{
var state = _options.StateDeserializer(e.String);
_timeTraveling = true;
TimeTravel(state);
_timeTraveling = false;
}
private void OnChange(EventArgs e)
{
var handler = Change;
handler?.Invoke(this, e);
var getLocation = _options.GetLocation;
if (getLocation == null) return;
var newLocation = getLocation(State);
if (newLocation == _currentLocation || newLocation == null) return;
lock (_syncRoot)
{
_currentLocation = newLocation;
}
_uriHelper.NavigateTo(newLocation);
}
public void Dispatch(TAction action)
{
lock (_syncRoot)
{
State = _rootReducer(State, action);
DevToolsInterop.Log(action.ToString(), _options.StateSerializer(State));
History.Add(new HistoricEntry<TState, object>(State, action));
}
OnChange(null);
}
void DispatchLocation(NewLocationAction locationAction)
{
var locationReducer = _options.LocationReducer;
if (locationReducer == null && locationAction is TAction)
{
// Just use the RootReducer unless the user has configured a LocationReducer
var genericAction = (TAction)(object)locationAction;
Dispatch(genericAction);
}
if (locationReducer == null) return;
lock (_syncRoot)
{
State = locationReducer(State, locationAction);
DevToolsInterop.Log(locationAction.ToString(), _options.StateSerializer(State));
History.Add(new HistoricEntry<TState, object>(State, locationAction));
}
OnChange(null);
}
public void TimeTravel(TState state)
{
lock (_syncRoot)
{
State = state;
}
OnChange(null);
}
}
}