-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathElmishStore.fs
More file actions
197 lines (156 loc) · 5.74 KB
/
Copy pathElmishStore.fs
File metadata and controls
197 lines (156 loc) · 5.74 KB
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
namespace ElmishStore
open Elmish
open Fable.Core
open System.Collections.Generic
type ElmishStore<'model, 'msg> = {
GetModel: unit -> 'model
Dispatch: 'msg -> unit
Subscribe: UseSyncExternalStoreSubscribe
}
type private StoreState<'model, 'msg> = {
Store: ElmishStore<'model, 'msg>
SetTermination: bool -> unit
}
type private RuntimeState<'model, 'msg> = {
UniqueName: string
mutable State: 'model option
mutable FinalDispatch: Dispatch<'msg> option
mutable ShouldTerminate: bool
Subscribers: ResizeArray<unit -> unit>
mutable PreviousStore: StoreState<'model, 'msg> option
}
type ElmishProgramState<'arg, 'model, 'msg, 'view> = {
Program: Program<'arg, 'model, 'msg, 'view>
Run: Program<'arg, 'model, 'msg, 'view> -> 'arg -> ElmishStore<'model, 'msg>
}
module private Internal =
let mutable stores: Dictionary<string, obj> = Dictionary<string, obj>()
let tryGetStoreState (uniqueName: string) =
if stores.ContainsKey(uniqueName) then
Some(stores[uniqueName] |> unbox<StoreState<'model, 'msg>>)
else
None
module ElmishProgram =
let setup (uniqueName: string) (program: Program<'arg, 'model, 'msg, 'view>) =
let runtime = {
UniqueName = uniqueName
State = None
FinalDispatch = None
ShouldTerminate = false
Subscribers = ResizeArray()
PreviousStore = None
}
let dispatch msg =
match runtime.FinalDispatch with
| Some finalDispatch -> finalDispatch msg
| None -> failwith "You're using initial dispatch. That shouldn't happen."
let subscribe callback =
runtime.Subscribers.Add(callback)
fun () -> runtime.Subscribers.Remove(callback) |> ignore
let mapSetState setState model dispatch =
setState model dispatch
let oldModel = runtime.State
runtime.State <- Some model
runtime.FinalDispatch <- Some dispatch
match runtime.PreviousStore with
| Some previousStore ->
previousStore.SetTermination true
runtime.PreviousStore <- None
| None -> ()
// Skip re-renders if model hasn't changed
if not (obj.ReferenceEquals(model, oldModel)) then
runtime.Subscribers |> Seq.iter (fun callback -> callback ())
let mapInit userInit arg =
match runtime.PreviousStore with
| Some previousStore ->
let model = previousStore.Store.GetModel()
runtime.State <- Some model
model, Cmd.none
| None -> userInit arg
let mapTermination (predicate, terminate) =
let pred msg = predicate msg || runtime.ShouldTerminate
pred, terminate
let getState () =
match runtime.State with
| Some state -> state
| None -> failwith "State is not initialized. That shouldn't happen."
let store = {
GetModel = getState
Dispatch = dispatch
Subscribe = UseSyncExternalStoreSubscribe subscribe
}
let preparedProgram =
program
|> Program.map mapInit id id mapSetState id mapTermination
let run (program: Program<'arg, 'model, 'msg, 'view>) (arg: 'arg) =
runtime.PreviousStore <- Internal.tryGetStoreState runtime.UniqueName
runtime.ShouldTerminate <- false
let setTermination should = runtime.ShouldTerminate <- should
program
|> Program.runWith arg
let storeState = {
Store = store
SetTermination = setTermination
}
Internal.stores[runtime.UniqueName] <- box storeState
store
{
Program = preparedProgram
Run = run
}
let mapProgram
(mapper: Program<'arg, 'model, 'msg, 'view> -> Program<'arg, 'model, 'msg, 'view>)
(elmishProgram: ElmishProgramState<'arg, 'model, 'msg, 'view>)
=
{
Program = mapper elmishProgram.Program
Run = fun program arg -> elmishProgram.Run program arg
}
let runWith (arg: 'arg) (elmishProgram: ElmishProgramState<'arg, 'model, 'msg, 'view>) =
elmishProgram.Run elmishProgram.Program arg
let inline run (elmishProgram: ElmishProgramState<unit, 'model, 'msg, 'view>) =
runWith () elmishProgram
let createStoreWith (uniqueName: string) (arg: 'arg) (program: Program<'arg, 'model, 'msg, 'view>) =
program
|> setup uniqueName
|> runWith arg
let inline createStore (uniqueName: string) (program: Program<unit, 'model, 'msg, 'view>) =
createStoreWith uniqueName () program
module ElmishStore =
let createStoreWith (uniqueName: string) (arg: 'arg) (program: Program<'arg, 'model, 'msg, 'view>) =
ElmishProgram.createStoreWith uniqueName arg program
let inline createStore (uniqueName: string) (program: Program<unit, 'model, 'msg, 'view>) : ElmishStore<'model, 'msg> =
ElmishProgram.createStore uniqueName program
[<Erase>]
type ElmishStore =
static member Create
(
program: Program<'arg, 'model, 'msg, unit>,
arg: 'arg,
uniqueName: string
) : ElmishStore<'model, 'msg> =
ElmishStore.createStoreWith uniqueName arg program
static member inline Create(program: Program<unit, 'model, 'msg, unit>, uniqueName: string) =
ElmishStore.Create(program, (), uniqueName)
static member inline Create
(
init: 'arg -> 'model * Cmd<'msg>,
update: 'msg -> 'model -> 'model * Cmd<'msg>,
arg: 'arg,
uniqueName: string
) =
ElmishStore.Create((Program.mkProgram init update (fun _ _ -> ())), arg, uniqueName)
static member inline Create
(
init: unit -> 'model * Cmd<'msg>,
update: 'msg -> 'model -> 'model * Cmd<'msg>,
uniqueName: string
) =
ElmishStore.Create(Program.mkProgram init update (fun _ _ -> ()), uniqueName)
static member inline Create
(
init: 'model * Cmd<'msg>,
update: 'msg -> 'model -> 'model * Cmd<'msg>,
uniqueName: string
) =
ElmishStore.Create(Program.mkProgram (fun () -> init) update (fun _ _ -> ()), uniqueName)