-
Notifications
You must be signed in to change notification settings - Fork 530
/
Main.elm
331 lines (245 loc) · 9.19 KB
/
Main.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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
module Main exposing (main)
import Api exposing (Cred)
import Article.Slug exposing (Slug)
import Avatar exposing (Avatar)
import Browser exposing (Document)
import Browser.Navigation as Nav
import Html exposing (..)
import Json.Decode as Decode exposing (Value)
import Page exposing (Page)
import Page.Article as Article
import Page.Article.Editor as Editor
import Page.Blank as Blank
import Page.Home as Home
import Page.Login as Login
import Page.NotFound as NotFound
import Page.Profile as Profile
import Page.Register as Register
import Page.Settings as Settings
import Route exposing (Route)
import Session exposing (Session)
import Task
import Time
import Url exposing (Url)
import Username exposing (Username)
import Viewer exposing (Viewer)
-- NOTE: Based on discussions around how asset management features
-- like code splitting and lazy loading have been shaping up, it's possible
-- that most of this file may become unnecessary in a future release of Elm.
-- Avoid putting things in this module unless there is no alternative!
-- See https://discourse.elm-lang.org/t/elm-spa-in-0-19/1800/2 for more.
type Model
= Redirect Session
| NotFound Session
| Home Home.Model
| Settings Settings.Model
| Login Login.Model
| Register Register.Model
| Profile Username Profile.Model
| Article Article.Model
| Editor (Maybe Slug) Editor.Model
-- MODEL
init : Maybe Viewer -> Url -> Nav.Key -> ( Model, Cmd Msg )
init maybeViewer url navKey =
changeRouteTo (Route.fromUrl url)
(Redirect (Session.fromViewer navKey maybeViewer))
-- VIEW
view : Model -> Document Msg
view model =
let
viewPage page toMsg config =
let
{ title, body } =
Page.view (Session.viewer (toSession model)) page config
in
{ title = title
, body = List.map (Html.map toMsg) body
}
in
case model of
Redirect _ ->
viewPage Page.Other (\_ -> Ignored) Blank.view
NotFound _ ->
viewPage Page.Other (\_ -> Ignored) NotFound.view
Settings settings ->
viewPage Page.Other GotSettingsMsg (Settings.view settings)
Home home ->
viewPage Page.Home GotHomeMsg (Home.view home)
Login login ->
viewPage Page.Other GotLoginMsg (Login.view login)
Register register ->
viewPage Page.Other GotRegisterMsg (Register.view register)
Profile username profile ->
viewPage (Page.Profile username) GotProfileMsg (Profile.view profile)
Article article ->
viewPage Page.Other GotArticleMsg (Article.view article)
Editor Nothing editor ->
viewPage Page.NewArticle GotEditorMsg (Editor.view editor)
Editor (Just _) editor ->
viewPage Page.Other GotEditorMsg (Editor.view editor)
-- UPDATE
type Msg
= Ignored
| ChangedUrl Url
| ClickedLink Browser.UrlRequest
| GotHomeMsg Home.Msg
| GotSettingsMsg Settings.Msg
| GotLoginMsg Login.Msg
| GotRegisterMsg Register.Msg
| GotProfileMsg Profile.Msg
| GotArticleMsg Article.Msg
| GotEditorMsg Editor.Msg
| GotSession Session
toSession : Model -> Session
toSession page =
case page of
Redirect session ->
session
NotFound session ->
session
Home home ->
Home.toSession home
Settings settings ->
Settings.toSession settings
Login login ->
Login.toSession login
Register register ->
Register.toSession register
Profile _ profile ->
Profile.toSession profile
Article article ->
Article.toSession article
Editor _ editor ->
Editor.toSession editor
changeRouteTo : Maybe Route -> Model -> ( Model, Cmd Msg )
changeRouteTo maybeRoute model =
let
session =
toSession model
in
case maybeRoute of
Nothing ->
( NotFound session, Cmd.none )
Just Route.Root ->
( model, Route.replaceUrl (Session.navKey session) Route.Home )
Just Route.Logout ->
( model, Api.logout )
Just Route.NewArticle ->
Editor.initNew session
|> updateWith (Editor Nothing) GotEditorMsg model
Just (Route.EditArticle slug) ->
Editor.initEdit session slug
|> updateWith (Editor (Just slug)) GotEditorMsg model
Just Route.Settings ->
Settings.init session
|> updateWith Settings GotSettingsMsg model
Just Route.Home ->
Home.init session
|> updateWith Home GotHomeMsg model
Just Route.Login ->
Login.init session
|> updateWith Login GotLoginMsg model
Just Route.Register ->
Register.init session
|> updateWith Register GotRegisterMsg model
Just (Route.Profile username) ->
Profile.init session username
|> updateWith (Profile username) GotProfileMsg model
Just (Route.Article slug) ->
Article.init session slug
|> updateWith Article GotArticleMsg model
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case ( msg, model ) of
( Ignored, _ ) ->
( model, Cmd.none )
( ClickedLink urlRequest, _ ) ->
case urlRequest of
Browser.Internal url ->
case url.fragment of
Nothing ->
-- If we got a link that didn't include a fragment,
-- it's from one of those (href "") attributes that
-- we have to include to make the RealWorld CSS work.
--
-- In an application doing path routing instead of
-- fragment-based routing, this entire
-- `case url.fragment of` expression this comment
-- is inside would be unnecessary.
( model, Cmd.none )
Just _ ->
( model
, Nav.pushUrl (Session.navKey (toSession model)) (Url.toString url)
)
Browser.External href ->
( model
, Nav.load href
)
( ChangedUrl url, _ ) ->
changeRouteTo (Route.fromUrl url) model
( GotSettingsMsg subMsg, Settings settings ) ->
Settings.update subMsg settings
|> updateWith Settings GotSettingsMsg model
( GotLoginMsg subMsg, Login login ) ->
Login.update subMsg login
|> updateWith Login GotLoginMsg model
( GotRegisterMsg subMsg, Register register ) ->
Register.update subMsg register
|> updateWith Register GotRegisterMsg model
( GotHomeMsg subMsg, Home home ) ->
Home.update subMsg home
|> updateWith Home GotHomeMsg model
( GotProfileMsg subMsg, Profile username profile ) ->
Profile.update subMsg profile
|> updateWith (Profile username) GotProfileMsg model
( GotArticleMsg subMsg, Article article ) ->
Article.update subMsg article
|> updateWith Article GotArticleMsg model
( GotEditorMsg subMsg, Editor slug editor ) ->
Editor.update subMsg editor
|> updateWith (Editor slug) GotEditorMsg model
( GotSession session, Redirect _ ) ->
( Redirect session
, Route.replaceUrl (Session.navKey session) Route.Home
)
( _, _ ) ->
-- Disregard messages that arrived for the wrong page.
( model, Cmd.none )
updateWith : (subModel -> Model) -> (subMsg -> Msg) -> Model -> ( subModel, Cmd subMsg ) -> ( Model, Cmd Msg )
updateWith toModel toMsg model ( subModel, subCmd ) =
( toModel subModel
, Cmd.map toMsg subCmd
)
-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions model =
case model of
NotFound _ ->
Sub.none
Redirect _ ->
Session.changes GotSession (Session.navKey (toSession model))
Settings settings ->
Sub.map GotSettingsMsg (Settings.subscriptions settings)
Home home ->
Sub.map GotHomeMsg (Home.subscriptions home)
Login login ->
Sub.map GotLoginMsg (Login.subscriptions login)
Register register ->
Sub.map GotRegisterMsg (Register.subscriptions register)
Profile _ profile ->
Sub.map GotProfileMsg (Profile.subscriptions profile)
Article article ->
Sub.map GotArticleMsg (Article.subscriptions article)
Editor _ editor ->
Sub.map GotEditorMsg (Editor.subscriptions editor)
-- MAIN
main : Program Value Model Msg
main =
Api.application Viewer.decoder
{ init = init
, onUrlChange = ChangedUrl
, onUrlRequest = ClickedLink
, subscriptions = subscriptions
, update = update
, view = view
}