-
Notifications
You must be signed in to change notification settings - Fork 0
/
App.elm
190 lines (145 loc) · 5.08 KB
/
App.elm
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
module App where
import AppTypes exposing (..)
import Account.AccountService as AccountService
import Account.AccountServiceTypes as AccountServiceTypes
import Language.LanguageService as LanguageService
import Language.LanguageTypes as LanguageTypes
import RouteHash exposing (HashUpdate)
import Components.FocusTypes as FocusTypes
import Components.FocusUI as FocusUI
import Html exposing (Html, div)
import Signal exposing (Signal, Mailbox, filter, mailbox, filterMap, merge, foldp, dropRepeats, sampleOn)
import Signal.Extra exposing (foldp', passiveMap2)
import Task exposing (Task, andThen, onError)
import Task.Util exposing (batch)
{- Collect the wiring for submodules -}
accountModule =
superModule
{ modelTag = .account
, modelUpdater = \a b -> {b | account <- a}
, actionTag = AccountAction
, submodule = AccountService.submodule
}
focusModule =
superModule
{ modelTag = .focus
, modelUpdater = \a b -> {b | focus <- a}
, actionTag = FocusAction
, submodule = FocusUI.submodule
}
languageModule =
superModule
{ modelTag = .language
, modelUpdater = \a b -> {b | language <- a}
, actionTag = LanguageAction
, submodule = LanguageService.submodule
}
{-| The initial model.
We collect the initialization of the model from the various modules that 'own'
the parts of the model.
-}
initialModel : Model
initialModel =
{ language = languageModule.initialModel
, account = accountModule.initialModel
, focus = focusModule.initialModel
}
{-| The actions our app can perform
We don't have any top-level actions ... we simply dispatch to one
module or another.
-}
type Action
= AccountAction AccountServiceTypes.Action
| FocusAction FocusTypes.Action
| LanguageAction LanguageTypes.Action
{-| The merger of all the action signals defined by various modules. -}
actions : Signal Action
actions =
Signal.mergeMany
[ .actions focusModule
, .actions accountModule
, .actions languageModule
]
{-| The update function.
Just dispatch to the various models that can handle actions.
-}
update : Action -> Model -> Model
update action model =
case action of
AccountAction subaction ->
accountModule.update subaction model
FocusAction subaction ->
focusModule.update subaction model
LanguageAction subaction ->
languageModule.update subaction model
_ ->
model
{-| Like update, except returns an additional task to perform in response to
the action (rather than an updated model).
Of coure, the task returned may be a sequence, or composed with various
`andThen` tasks.
An alternative is to let have the `update` function return a tuple of model and
task. However, I think it's nice to keep the two things separate.
Originally, I didn't provide access to the model here -- all the needed
information had to be in the action itself. However, this turned out to be too
limiting for some cases, so now I provide it. Of course, the access to the
model is essentially read-only. However, if the reaction needs to change the
model, then it can simply include a Task that sends the appropriate message.
-}
reaction : Action -> Model -> Maybe (Task () ())
reaction action model =
model |>
case action of
FocusAction subaction ->
.reaction focusModule subaction
AccountAction subaction ->
.reaction accountModule subaction
LanguageAction subaction ->
.reaction languageModule subaction
_ ->
always Nothing
{-| A task to perform when the app starts.
-}
initialTask : Task () ()
initialTask =
batch <|
List.filterMap identity
[ .initialTask accountModule
, .initialTask focusModule
, .initialTask languageModule
]
{-| Actually executes the reaction tasks.
Note the default is the initialTask, so that the initialTask gets executed.
-}
port reactions : Signal (Task () ())
port reactions =
Signal.Extra.filter
initialTask
(Signal.map2 reaction actions models)
{-| A signal of the changing model over time.
Note that we use foldp' from Signal.Extra to work around a characteristic
of the standard foldp. That characteristic is that the standard foldp
does nothing with the initial value of the Signal that you supply to it.
This was causing problems at one point.
-}
models : Signal Model
models =
let
initialUpdate = (flip update) initialModel
in
foldp' update initialUpdate actions
{-| Turns our model into Html. -}
view : Model -> Html
view = FocusUI.view
{-| The main method, which generates a signal of Html to display. -}
main : Signal Html
main = Signal.map view models
port routeTasks : Signal (Task () ())
port routeTasks =
RouteHash.start
{ prefix = RouteHash.defaultPrefix
, models = models
, delta2update = FocusUI.delta2path
, address = FocusTypes.address
, location2action = FocusUI.route
}