elm-pages 3.0.0-beta.4 → 3.0.0-beta.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/README.md +10 -1
  2. package/adapter/netlify.js +207 -0
  3. package/codegen/{elm-pages-codegen.js → elm-pages-codegen.cjs} +2828 -2933
  4. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmi +0 -0
  5. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmo +0 -0
  6. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateDataTest.elmo +0 -0
  7. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
  8. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
  9. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
  10. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm.json +1 -1
  11. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1447 -342
  12. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Runner.elm.js +17004 -13817
  13. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
  14. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_supervisor.js +4 -4
  15. package/generator/dead-code-review/elm.json +9 -7
  16. package/generator/dead-code-review/src/Pages/Review/DeadCodeEliminateData.elm +59 -10
  17. package/generator/dead-code-review/tests/Pages/Review/DeadCodeEliminateDataTest.elm +52 -36
  18. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmi +0 -0
  19. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmo +0 -0
  20. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmi +0 -0
  21. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmo +0 -0
  22. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
  23. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
  24. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
  25. package/generator/review/elm-stuff/tests-0.19.1/elm.json +1 -1
  26. package/generator/review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1447 -342
  27. package/generator/review/elm-stuff/tests-0.19.1/js/Runner.elm.js +25025 -21739
  28. package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
  29. package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +4 -4
  30. package/generator/review/elm.json +10 -10
  31. package/generator/src/RouteBuilder.elm +121 -114
  32. package/generator/src/SharedTemplate.elm +8 -7
  33. package/generator/src/SiteConfig.elm +3 -2
  34. package/generator/src/basepath-middleware.js +3 -3
  35. package/generator/src/build.js +209 -92
  36. package/generator/src/cli.js +292 -88
  37. package/generator/src/codegen.js +29 -27
  38. package/generator/src/compatibility-key.js +3 -0
  39. package/generator/src/compile-elm.js +43 -26
  40. package/generator/src/config.js +39 -0
  41. package/generator/src/copy-dir.js +2 -2
  42. package/generator/src/dev-server.js +176 -138
  43. package/generator/src/dir-helpers.js +9 -26
  44. package/generator/src/elm-codegen.js +5 -4
  45. package/generator/src/elm-file-constants.js +2 -3
  46. package/generator/src/error-formatter.js +12 -11
  47. package/generator/src/file-helpers.js +3 -4
  48. package/generator/src/generate-template-module-connector.js +23 -23
  49. package/generator/src/init.js +9 -8
  50. package/generator/src/pre-render-html.js +39 -28
  51. package/generator/src/render-test.js +109 -0
  52. package/generator/src/render-worker.js +25 -28
  53. package/generator/src/render.js +321 -142
  54. package/generator/src/request-cache.js +265 -162
  55. package/generator/src/resolve-elm-module.js +64 -0
  56. package/generator/src/rewrite-client-elm-json.js +6 -5
  57. package/generator/src/rewrite-elm-json-help.js +56 -0
  58. package/generator/src/rewrite-elm-json.js +17 -7
  59. package/generator/src/route-codegen-helpers.js +16 -31
  60. package/generator/src/seo-renderer.js +12 -7
  61. package/generator/src/vite-utils.js +77 -0
  62. package/generator/static-code/elm-pages.js +10 -0
  63. package/generator/static-code/hmr.js +79 -13
  64. package/generator/template/app/Api.elm +6 -5
  65. package/generator/template/app/Effect.elm +123 -0
  66. package/generator/template/app/ErrorPage.elm +37 -6
  67. package/generator/template/app/Route/Index.elm +17 -10
  68. package/generator/template/app/Shared.elm +24 -47
  69. package/generator/template/app/Site.elm +19 -6
  70. package/generator/template/app/View.elm +1 -8
  71. package/generator/template/elm-tooling.json +0 -3
  72. package/generator/template/elm.json +32 -24
  73. package/generator/template/package.json +10 -4
  74. package/package.json +30 -27
  75. package/src/ApiRoute.elm +199 -61
  76. package/src/BackendTask/Custom.elm +325 -0
  77. package/src/BackendTask/Env.elm +90 -0
  78. package/src/{DataSource → BackendTask}/File.elm +171 -56
  79. package/src/{DataSource → BackendTask}/Glob.elm +136 -125
  80. package/src/BackendTask/Http.elm +679 -0
  81. package/src/{DataSource → BackendTask}/Internal/Glob.elm +1 -1
  82. package/src/BackendTask/Internal/Request.elm +69 -0
  83. package/src/BackendTask/Random.elm +79 -0
  84. package/src/BackendTask/Time.elm +47 -0
  85. package/src/BackendTask.elm +531 -0
  86. package/src/FatalError.elm +90 -0
  87. package/src/Head/Seo.elm +4 -4
  88. package/src/Head.elm +237 -7
  89. package/src/HtmlPrinter.elm +7 -3
  90. package/src/Internal/ApiRoute.elm +7 -5
  91. package/src/PageServerResponse.elm +6 -1
  92. package/src/Pages/ConcurrentSubmission.elm +127 -0
  93. package/src/Pages/Form.elm +340 -0
  94. package/src/Pages/FormData.elm +18 -0
  95. package/src/Pages/GeneratorProgramConfig.elm +15 -0
  96. package/src/Pages/Internal/FatalError.elm +5 -0
  97. package/src/Pages/Internal/Msg.elm +93 -0
  98. package/src/Pages/Internal/NotFoundReason.elm +4 -4
  99. package/src/Pages/Internal/Platform/Cli.elm +617 -768
  100. package/src/Pages/Internal/Platform/CompatibilityKey.elm +6 -0
  101. package/src/Pages/Internal/Platform/Effect.elm +1 -2
  102. package/src/Pages/Internal/Platform/GeneratorApplication.elm +379 -0
  103. package/src/Pages/Internal/Platform/StaticResponses.elm +65 -276
  104. package/src/Pages/Internal/Platform/ToJsPayload.elm +6 -9
  105. package/src/Pages/Internal/Platform.elm +359 -225
  106. package/src/Pages/Internal/ResponseSketch.elm +2 -2
  107. package/src/Pages/Internal/Script.elm +17 -0
  108. package/src/Pages/Internal/StaticHttpBody.elm +35 -1
  109. package/src/Pages/Manifest.elm +52 -11
  110. package/src/Pages/Navigation.elm +87 -0
  111. package/src/Pages/PageUrl.elm +26 -12
  112. package/src/Pages/ProgramConfig.elm +35 -23
  113. package/src/Pages/Script.elm +166 -0
  114. package/src/Pages/SiteConfig.elm +3 -2
  115. package/src/Pages/StaticHttp/Request.elm +2 -2
  116. package/src/Pages/StaticHttpRequest.elm +23 -99
  117. package/src/Pages/Url.elm +3 -3
  118. package/src/PagesMsg.elm +88 -0
  119. package/src/QueryParams.elm +21 -172
  120. package/src/RenderRequest.elm +7 -7
  121. package/src/RequestsAndPending.elm +37 -20
  122. package/src/Result/Extra.elm +26 -0
  123. package/src/Scaffold/Form.elm +569 -0
  124. package/src/Scaffold/Route.elm +1411 -0
  125. package/src/Server/Request.elm +74 -72
  126. package/src/Server/Session.elm +62 -42
  127. package/src/Server/SetCookie.elm +80 -32
  128. package/src/Stub.elm +53 -0
  129. package/src/Test/Html/Internal/ElmHtml/ToString.elm +8 -9
  130. package/src/{Path.elm → UrlPath.elm} +33 -36
  131. package/src/DataSource/Env.elm +0 -38
  132. package/src/DataSource/Http.elm +0 -446
  133. package/src/DataSource/Internal/Request.elm +0 -20
  134. package/src/DataSource/Port.elm +0 -90
  135. package/src/DataSource.elm +0 -538
  136. package/src/Form/Field.elm +0 -717
  137. package/src/Form/FieldStatus.elm +0 -36
  138. package/src/Form/FieldView.elm +0 -417
  139. package/src/Form/FormData.elm +0 -22
  140. package/src/Form/Validation.elm +0 -391
  141. package/src/Form/Value.elm +0 -118
  142. package/src/Form.elm +0 -1683
  143. package/src/FormDecoder.elm +0 -102
  144. package/src/Pages/FormState.elm +0 -256
  145. package/src/Pages/Generate.elm +0 -800
  146. package/src/Pages/Internal/Form.elm +0 -17
  147. package/src/Pages/Msg.elm +0 -79
  148. package/src/Pages/Transition.elm +0 -70
@@ -20,34 +20,29 @@ import BuildError exposing (BuildError)
20
20
  import Bytes exposing (Bytes)
21
21
  import Bytes.Decode
22
22
  import Dict exposing (Dict)
23
- import Form.FormData exposing (FormData, Method(..))
24
- import FormDecoder
23
+ import Form
25
24
  import Html exposing (Html)
26
25
  import Html.Attributes as Attr
27
26
  import Http
28
27
  import Json.Decode as Decode
29
28
  import Json.Encode
29
+ import Pages.ConcurrentSubmission
30
30
  import Pages.ContentCache as ContentCache
31
31
  import Pages.Fetcher
32
32
  import Pages.Flags
33
- import Pages.FormState
33
+ import Pages.Internal.Msg
34
34
  import Pages.Internal.NotFoundReason exposing (NotFoundReason)
35
35
  import Pages.Internal.ResponseSketch as ResponseSketch exposing (ResponseSketch)
36
36
  import Pages.Internal.String as String
37
- import Pages.Msg
37
+ import Pages.Navigation
38
38
  import Pages.ProgramConfig exposing (ProgramConfig)
39
39
  import Pages.StaticHttpRequest as StaticHttpRequest
40
- import Pages.Transition
41
- import Path exposing (Path)
40
+ import PagesMsg exposing (PagesMsg)
42
41
  import QueryParams
43
42
  import Task
44
43
  import Time
45
44
  import Url exposing (Url)
46
-
47
-
48
- type Transition
49
- = Loading Int Path
50
- | Submitting FormData
45
+ import UrlPath exposing (UrlPath)
51
46
 
52
47
 
53
48
  {-| -}
@@ -58,7 +53,7 @@ type alias Program userModel userMsg pageData actionData sharedData errorPage =
58
53
  mainView :
59
54
  ProgramConfig userMsg userModel route pageData actionData sharedData effect (Msg userMsg pageData actionData sharedData errorPage) errorPage
60
55
  -> Model userModel pageData actionData sharedData
61
- -> { title : String, body : List (Html (Pages.Msg.Msg userMsg)) }
56
+ -> { title : String, body : List (Html (PagesMsg userMsg)) }
62
57
  mainView config model =
63
58
  case model.notFound of
64
59
  Just info ->
@@ -81,7 +76,7 @@ mainView config model =
81
76
  (config.view model.pageFormState
82
77
  (model.inFlightFetchers |> toFetcherState)
83
78
  (model.transition |> Maybe.map Tuple.second)
84
- { path = ContentCache.pathForUrl urls |> Path.join
79
+ { path = ContentCache.pathForUrl urls |> UrlPath.join
85
80
  , route = config.urlToRoute { currentUrl | path = model.currentPath }
86
81
  }
87
82
  Nothing
@@ -101,14 +96,14 @@ mainView config model =
101
96
 
102
97
  urlsToPagePath :
103
98
  { currentUrl : Url, basePath : List String }
104
- -> Path
99
+ -> UrlPath
105
100
  urlsToPagePath urls =
106
101
  urls.currentUrl.path
107
102
  |> String.chopForwardSlashes
108
103
  |> String.split "/"
109
104
  |> List.filter ((/=) "")
110
105
  |> List.drop (List.length urls.basePath)
111
- |> Path.join
106
+ |> UrlPath.join
112
107
 
113
108
 
114
109
  {-| -}
@@ -150,7 +145,7 @@ type alias Flags =
150
145
 
151
146
  type InitKind shared page actionData errorPage
152
147
  = OkPage shared page (Maybe actionData)
153
- | NotFound { reason : NotFoundReason, path : Path }
148
+ | NotFound { reason : NotFoundReason, path : UrlPath }
154
149
 
155
150
 
156
151
  {-| -}
@@ -204,7 +199,7 @@ init config flags url key =
204
199
  , basePath = config.basePath
205
200
  }
206
201
 
207
- pagePath : Path
202
+ pagePath : UrlPath
208
203
  pagePath =
209
204
  urlsToPagePath urls
210
205
 
@@ -230,7 +225,7 @@ init config flags url key =
230
225
  , host = url.host
231
226
  , port_ = url.port_
232
227
  , path = pagePath
233
- , query = url.query |> Maybe.map QueryParams.fromString
228
+ , query = url.query |> Maybe.map QueryParams.fromString |> Maybe.withDefault Dict.empty
234
229
  , fragment = url.fragment
235
230
  }
236
231
  }
@@ -274,7 +269,7 @@ init config flags url key =
274
269
  , url = url
275
270
  , currentPath = url.path
276
271
  , pageData = Err "Not found"
277
- , ariaNavigationAnnouncement = "Error" -- TODO use error page title for announcement?
272
+ , ariaNavigationAnnouncement = "Page Not Found" -- TODO use error page title for announcement?
278
273
  , userFlags = flags
279
274
  , notFound = Just info
280
275
  , transition = Nothing
@@ -313,16 +308,23 @@ init config flags url key =
313
308
  type Msg userMsg pageData actionData sharedData errorPage
314
309
  = LinkClicked Browser.UrlRequest
315
310
  | UrlChanged Url
316
- | UserMsg (Pages.Msg.Msg userMsg)
317
- | SetField { formId : String, name : String, value : String }
311
+ -- TODO rename to PagesMsg
312
+ | UserMsg (PagesMsg userMsg)
313
+ --| SetField { formId : String, name : String, value : String }
314
+ | FormMsg (Form.Msg (Msg userMsg pageData actionData sharedData errorPage))
318
315
  | UpdateCacheAndUrlNew Bool Url (Maybe userMsg) (Result Http.Error ( Url, ResponseSketch pageData actionData sharedData ))
319
- | FetcherComplete Bool String Int (Result Http.Error ( Maybe userMsg, Maybe actionData ))
316
+ | FetcherComplete Bool String Int (Result Http.Error ( Maybe userMsg, ActionDataOrRedirect actionData ))
320
317
  | FetcherStarted String Int FormData Time.Posix
321
318
  | PageScrollComplete
322
319
  | HotReloadCompleteNew Bytes
323
320
  | ProcessFetchResponse Int (Result Http.Error ( Url, ResponseSketch pageData actionData sharedData )) (Result Http.Error ( Url, ResponseSketch pageData actionData sharedData ) -> Msg userMsg pageData actionData sharedData errorPage)
324
321
 
325
322
 
323
+ type ActionDataOrRedirect action
324
+ = ActionResponse (Maybe action)
325
+ | RedirectResponse String
326
+
327
+
326
328
  {-| -}
327
329
  type alias Model userModel pageData actionData sharedData =
328
330
  { key : Maybe Browser.Navigation.Key
@@ -337,12 +339,12 @@ type alias Model userModel pageData actionData sharedData =
337
339
  , sharedData : sharedData
338
340
  , actionData : Maybe actionData
339
341
  }
340
- , notFound : Maybe { reason : NotFoundReason, path : Path }
342
+ , notFound : Maybe { reason : NotFoundReason, path : UrlPath }
341
343
  , userFlags : Decode.Value
342
- , transition : Maybe ( Int, Pages.Transition.Transition )
344
+ , transition : Maybe ( Int, Pages.Navigation.Navigation )
343
345
  , nextTransitionKey : Int
344
- , inFlightFetchers : Dict String ( Int, Pages.Transition.FetcherState actionData )
345
- , pageFormState : Pages.FormState.PageFormState
346
+ , inFlightFetchers : Dict String ( Int, Pages.ConcurrentSubmission.ConcurrentSubmission actionData )
347
+ , pageFormState : Form.Model
346
348
  , pendingRedirect : Bool
347
349
  , pendingData : Maybe ( pageData, sharedData, Maybe actionData )
348
350
  }
@@ -361,6 +363,7 @@ type Effect userMsg pageData actionData sharedData userEffect errorPage
361
363
  | Batch (List (Effect userMsg pageData actionData sharedData userEffect errorPage))
362
364
  | UserCmd userEffect
363
365
  | CancelRequest Int
366
+ | RunCmd (Cmd (Msg userMsg pageData actionData sharedData errorPage))
364
367
 
365
368
 
366
369
  {-| -}
@@ -371,6 +374,18 @@ update :
371
374
  -> ( Model userModel pageData actionData sharedData, Effect userMsg pageData actionData sharedData userEffect errorPage )
372
375
  update config appMsg model =
373
376
  case appMsg of
377
+ FormMsg formMsg ->
378
+ let
379
+ -- TODO trigger formCmd
380
+ ( newModel, formCmd ) =
381
+ Form.update formMsg model.pageFormState
382
+ in
383
+ ( { model
384
+ | pageFormState = newModel
385
+ }
386
+ , RunCmd formCmd
387
+ )
388
+
374
389
  LinkClicked urlRequest ->
375
390
  case urlRequest of
376
391
  Browser.Internal url ->
@@ -396,11 +411,6 @@ update config appMsg model =
396
411
  , BrowserLoadUrl href
397
412
  )
398
413
 
399
- SetField info ->
400
- ( { model | pageFormState = Pages.FormState.setField info model.pageFormState }
401
- , NoEffect
402
- )
403
-
404
414
  UrlChanged url ->
405
415
  case model.pendingData of
406
416
  Just ( newPageData, newSharedData, newActionData ) ->
@@ -414,7 +424,7 @@ update config appMsg model =
414
424
  model
415
425
 
416
426
  Nothing ->
417
- if model.url.path == url.path then
427
+ if model.url.path == url.path && model.url.query == url.query then
418
428
  ( { model
419
429
  | -- update the URL in case query params or fragment changed
420
430
  url = url
@@ -423,46 +433,57 @@ update config appMsg model =
423
433
  )
424
434
 
425
435
  else
426
- ( { model
427
- | url = url
428
- }
436
+ ( model
429
437
  , NoEffect
430
438
  )
431
439
  -- TODO is it reasonable to always re-fetch route data if you re-navigate to the current route? Might be a good
432
440
  -- parallel to the browser behavior
433
441
  |> startNewGetLoad url (UpdateCacheAndUrlNew True url Nothing)
434
442
 
435
- FetcherComplete forPageReload fetcherKey transitionId___ userMsgResult ->
443
+ FetcherComplete _ fetcherKey _ userMsgResult ->
436
444
  case userMsgResult of
437
- Ok ( userMsg, maybeFetcherDoneActionData ) ->
438
- ( { model
439
- | inFlightFetchers =
440
- model.inFlightFetchers
441
- |> Dict.update fetcherKey
442
- (Maybe.map
443
- (\( transitionId, fetcherState ) ->
444
- ( transitionId
445
- , { fetcherState
446
- | status =
447
- maybeFetcherDoneActionData
448
- |> Maybe.map Pages.Transition.FetcherReloading
449
- -- TODO remove this bad default, FetcherSubmitting is incorrect
450
- |> Maybe.withDefault Pages.Transition.FetcherSubmitting
451
- }
445
+ Ok ( userMsg, actionOrRedirect ) ->
446
+ case actionOrRedirect of
447
+ ActionResponse maybeFetcherDoneActionData ->
448
+ ( { model
449
+ | inFlightFetchers =
450
+ model.inFlightFetchers
451
+ |> Dict.update fetcherKey
452
+ (Maybe.map
453
+ (\( transitionId, fetcherState ) ->
454
+ ( transitionId
455
+ , { fetcherState
456
+ | status =
457
+ maybeFetcherDoneActionData
458
+ |> Maybe.map Pages.ConcurrentSubmission.Reloading
459
+ -- TODO remove this bad default, FetcherSubmitting is incorrect
460
+ |> Maybe.withDefault Pages.ConcurrentSubmission.Submitting
461
+ }
462
+ )
463
+ )
452
464
  )
453
- )
454
- )
455
- }
456
- , NoEffect
457
- )
458
- |> (case userMsg of
459
- Just justUserMsg ->
460
- performUserMsg justUserMsg config
461
-
462
- Nothing ->
463
- identity
464
- )
465
- |> startNewGetLoad (currentUrlWithPath model.url.path model) (UpdateCacheAndUrlNew False model.url Nothing)
465
+ }
466
+ , NoEffect
467
+ )
468
+ |> (case userMsg of
469
+ Just justUserMsg ->
470
+ performUserMsg justUserMsg config
471
+
472
+ Nothing ->
473
+ identity
474
+ )
475
+ |> startNewGetLoad (currentUrlWithPath model.url.path model) (UpdateCacheAndUrlNew False model.url Nothing)
476
+
477
+ RedirectResponse redirectTo ->
478
+ ( { model
479
+ | inFlightFetchers =
480
+ model.inFlightFetchers
481
+ |> Dict.remove fetcherKey
482
+ , pendingRedirect = True
483
+ }
484
+ , NoEffect
485
+ )
486
+ |> startNewGetLoad (currentUrlWithPath redirectTo model) (UpdateCacheAndUrlNew False model.url Nothing)
466
487
 
467
488
  Err _ ->
468
489
  -- TODO how to handle error?
@@ -480,74 +501,70 @@ update config appMsg model =
480
501
 
481
502
  UserMsg userMsg_ ->
482
503
  case userMsg_ of
483
- Pages.Msg.UserMsg userMsg ->
504
+ Pages.Internal.Msg.UserMsg userMsg ->
484
505
  ( model, NoEffect )
485
506
  |> performUserMsg userMsg config
486
507
 
487
- Pages.Msg.Submit fields ->
488
- ( { model
489
- | transition =
490
- Just
491
- ( -- TODO remove hardcoded number
492
- -1
493
- , Pages.Transition.Submitting fields
494
- )
495
- }
496
- , Submit fields
497
- )
498
-
499
- Pages.Msg.SubmitIfValid formId fields isValid ->
500
- if isValid then
501
- ( { model
502
- -- TODO should I setSubmitAttempted here, too?
503
- | transition =
504
- Just
505
- ( -- TODO remove hardcoded number
506
- -1
507
- , Pages.Transition.Submitting fields
508
- )
509
- }
510
- , Submit fields
511
- )
512
-
513
- else
514
- ( { model
515
- | pageFormState =
516
- model.pageFormState
517
- |> Pages.FormState.setSubmitAttempted formId
518
- }
519
- , NoEffect
520
- )
521
-
522
- Pages.Msg.SubmitFetcher fetcherKey fields isValid maybeUserMsg ->
523
- if isValid then
524
- -- TODO should I setSubmitAttempted here, too?
525
- ( { model | nextTransitionKey = model.nextTransitionKey + 1 }
526
- , SubmitFetcher fetcherKey model.nextTransitionKey fields
527
- )
528
- |> (case maybeUserMsg of
529
- Just justUserMsg ->
530
- performUserMsg justUserMsg config
508
+ Pages.Internal.Msg.Submit fields ->
509
+ if fields.valid then
510
+ let
511
+ payload : { fields : List ( String, String ), method : Form.Method, action : String, id : Maybe String }
512
+ payload =
513
+ { fields = fields.fields
514
+ , method = fields.method
515
+ , action = fields.action
516
+ , id = Just fields.id
517
+ }
518
+ in
519
+ if fields.useFetcher then
520
+ ( { model | nextTransitionKey = model.nextTransitionKey + 1 }
521
+ , SubmitFetcher fields.id model.nextTransitionKey payload
522
+ )
523
+ |> (case fields.msg of
524
+ Just justUserMsg ->
525
+ performUserMsg justUserMsg config
526
+
527
+ Nothing ->
528
+ identity
529
+ )
530
+
531
+ else
532
+ ( { model
533
+ -- TODO should I setSubmitAttempted here, too?
534
+ | transition =
535
+ Just
536
+ ( -- TODO remove hardcoded number
537
+ -1
538
+ , Pages.Navigation.Submitting payload
539
+ )
540
+ }
541
+ , Submit payload
542
+ )
543
+ |> (case fields.msg of
544
+ Just justUserMsg ->
545
+ performUserMsg justUserMsg config
531
546
 
532
- Nothing ->
533
- identity
534
- )
547
+ Nothing ->
548
+ identity
549
+ )
535
550
 
536
551
  else
537
- ( { model
538
- | pageFormState =
539
- model.pageFormState
540
- |> Pages.FormState.setSubmitAttempted fetcherKey
541
- }
542
- , NoEffect
543
- )
552
+ -- TODO should the user msg still be run if the form is invalid?
553
+ ( model, NoEffect )
544
554
 
545
- Pages.Msg.FormFieldEvent value ->
555
+ Pages.Internal.Msg.FormMsg formMsg ->
546
556
  -- TODO when init is called for a new page, also need to clear out client-side `pageFormState`
547
- ( { model | pageFormState = Pages.FormState.update value model.pageFormState }
548
- , NoEffect
557
+ let
558
+ ( formModel, formCmd ) =
559
+ Form.update formMsg model.pageFormState
560
+ in
561
+ ( { model | pageFormState = formModel }
562
+ , RunCmd (Cmd.map UserMsg formCmd)
549
563
  )
550
564
 
565
+ Pages.Internal.Msg.NoOp ->
566
+ ( model, NoEffect )
567
+
551
568
  UpdateCacheAndUrlNew scrollToTopWhenDone urlWithoutRedirectResolution maybeUserMsg updateResult ->
552
569
  -- TODO remove all fetchers that are in the state `FetcherReloading` here -- I think that's the right logic?
553
570
  case
@@ -605,27 +622,32 @@ update config appMsg model =
605
622
  , actionData = newActionData
606
623
  }
607
624
 
608
- ( userModel, _ ) =
625
+ ( userModel, userEffect ) =
609
626
  -- TODO if urlWithoutRedirectResolution is different from the url with redirect resolution, then
610
627
  -- instead of calling update, call pushUrl (I think?)
611
628
  -- TODO include user Cmd
612
- config.update model.pageFormState
613
- (model.inFlightFetchers |> toFetcherState)
614
- (model.transition |> Maybe.map Tuple.second)
615
- newSharedData
616
- newPageData
617
- model.key
618
- (config.onPageChange
619
- { protocol = model.url.protocol
620
- , host = model.url.host
621
- , port_ = model.url.port_
622
- , path = urlPathToPath urlWithoutRedirectResolution
623
- , query = urlWithoutRedirectResolution.query
624
- , fragment = urlWithoutRedirectResolution.fragment
625
- , metadata = config.urlToRoute urlWithoutRedirectResolution
626
- }
627
- )
628
- previousPageData.userModel
629
+ if stayingOnSamePath then
630
+ ( previousPageData.userModel, NoEffect )
631
+
632
+ else
633
+ config.update model.pageFormState
634
+ (model.inFlightFetchers |> toFetcherState)
635
+ (model.transition |> Maybe.map Tuple.second)
636
+ newSharedData
637
+ newPageData
638
+ model.key
639
+ (config.onPageChange
640
+ { protocol = model.url.protocol
641
+ , host = model.url.host
642
+ , port_ = model.url.port_
643
+ , path = urlPathToPath urlWithoutRedirectResolution
644
+ , query = urlWithoutRedirectResolution.query
645
+ , fragment = urlWithoutRedirectResolution.fragment
646
+ , metadata = config.urlToRoute urlWithoutRedirectResolution
647
+ }
648
+ )
649
+ previousPageData.userModel
650
+ |> Tuple.mapSecond UserCmd
629
651
 
630
652
  updatedModel : Model userModel pageData actionData sharedData
631
653
  updatedModel =
@@ -656,10 +678,13 @@ update config appMsg model =
656
678
  , currentPath = newUrl.path
657
679
  }
658
680
  , if not stayingOnSamePath && scrollToTopWhenDone then
659
- ScrollToTop
681
+ Batch
682
+ [ ScrollToTop
683
+ , userEffect
684
+ ]
660
685
 
661
686
  else
662
- NoEffect
687
+ userEffect
663
688
  )
664
689
  |> (case maybeUserMsg of
665
690
  Just userMsg ->
@@ -679,10 +704,10 @@ update config appMsg model =
679
704
  Err _ ->
680
705
  {-
681
706
  When there is an error loading the content.dat, we are either
682
- 1) in the dev server, and should show the relevant DataSource error for the page
707
+ 1) in the dev server, and should show the relevant BackendTask error for the page
683
708
  we're navigating to. This could be done more cleanly, but it's simplest to just
684
709
  do a fresh page load and use the code path for presenting an error for a fresh page.
685
- 2) In a production app. That means we had a successful build, so there were no DataSource failures,
710
+ 2) In a production app. That means we had a successful build, so there were no BackendTask failures,
686
711
  so the app must be stale (unless it's in some unexpected state from a bug). In the future,
687
712
  it probably makes sense to include some sort of hash of the app version we are fetching, match
688
713
  it with the current version that's running, and perform this logic when we see there is a mismatch.
@@ -745,7 +770,86 @@ update config appMsg model =
745
770
  _ ->
746
771
  ( model, NoEffect )
747
772
  )
748
- |> Result.withDefault ( model, NoEffect )
773
+ |> Result.withDefault
774
+ (let
775
+ pageDataResult : Maybe (InitKind sharedData pageData actionData errorPage)
776
+ pageDataResult =
777
+ case Bytes.Decode.decode config.decodeResponse pageDataBytes of
778
+ Just (ResponseSketch.RenderPage _ _) ->
779
+ Nothing
780
+
781
+ Just (ResponseSketch.HotUpdate pageData shared actionData) ->
782
+ OkPage shared pageData actionData
783
+ |> Just
784
+
785
+ Just (ResponseSketch.NotFound notFound) ->
786
+ NotFound notFound
787
+ |> Just
788
+
789
+ _ ->
790
+ Nothing
791
+ in
792
+ case pageDataResult of
793
+ Just (OkPage sharedData pageData actionData) ->
794
+ let
795
+ urls : { currentUrl : Url, basePath : List String }
796
+ urls =
797
+ { currentUrl = model.url
798
+ , basePath = config.basePath
799
+ }
800
+
801
+ pagePath : UrlPath
802
+ pagePath =
803
+ urlsToPagePath urls
804
+
805
+ userFlags : Pages.Flags.Flags
806
+ userFlags =
807
+ model.userFlags
808
+ |> Decode.decodeValue
809
+ (Decode.field "userFlags" Decode.value)
810
+ |> Result.withDefault Json.Encode.null
811
+ |> Pages.Flags.BrowserFlags
812
+
813
+ ( userModel, userCmd ) =
814
+ Just
815
+ { path =
816
+ { path = pagePath
817
+ , query = model.url.query
818
+ , fragment = model.url.fragment
819
+ }
820
+ , metadata = config.urlToRoute model.url
821
+ , pageUrl =
822
+ Just
823
+ { protocol = model.url.protocol
824
+ , host = model.url.host
825
+ , port_ = model.url.port_
826
+ , path = pagePath
827
+ , query = model.url.query |> Maybe.map QueryParams.fromString |> Maybe.withDefault Dict.empty
828
+ , fragment = model.url.fragment
829
+ }
830
+ }
831
+ |> config.init userFlags sharedData pageData actionData
832
+
833
+ cmd : Effect userMsg pageData actionData sharedData userEffect errorPage
834
+ cmd =
835
+ UserCmd userCmd
836
+ in
837
+ ( { model
838
+ | pageData =
839
+ Ok
840
+ { userModel = userModel
841
+ , sharedData = sharedData
842
+ , pageData = pageData
843
+ , actionData = actionData
844
+ }
845
+ , notFound = Nothing
846
+ }
847
+ , cmd
848
+ )
849
+
850
+ _ ->
851
+ ( model, NoEffect )
852
+ )
749
853
 
750
854
  FetcherStarted fetcherKey transitionId fetcherData initiatedAt ->
751
855
  ( { model
@@ -754,7 +858,7 @@ update config appMsg model =
754
858
  |> Dict.insert fetcherKey
755
859
  ( transitionId
756
860
  , { payload = fetcherData
757
- , status = Pages.Transition.FetcherSubmitting
861
+ , status = Pages.ConcurrentSubmission.Submitting
758
862
  , initiatedAt = initiatedAt
759
863
  }
760
864
  )
@@ -763,10 +867,10 @@ update config appMsg model =
763
867
  )
764
868
 
765
869
 
766
- toFetcherState : Dict String ( Int, Pages.Transition.FetcherState actionData ) -> Dict String (Pages.Transition.FetcherState actionData)
870
+ toFetcherState : Dict String ( Int, Pages.ConcurrentSubmission.ConcurrentSubmission actionData ) -> Dict String (Pages.ConcurrentSubmission.ConcurrentSubmission actionData)
767
871
  toFetcherState inFlightFetchers =
768
872
  inFlightFetchers
769
- |> Dict.map (\_ ( index, fetcherState ) -> fetcherState)
873
+ |> Dict.map (\_ ( _, fetcherState ) -> fetcherState)
770
874
 
771
875
 
772
876
  performUserMsg :
@@ -800,6 +904,9 @@ perform config model effect =
800
904
  NoEffect ->
801
905
  Cmd.none
802
906
 
907
+ RunCmd cmd ->
908
+ cmd
909
+
803
910
  Batch effects ->
804
911
  effects
805
912
  |> List.map (perform config model)
@@ -828,10 +935,10 @@ perform config model effect =
828
935
  |> Maybe.withDefault Cmd.none
829
936
 
830
937
  FetchPageData transitionKey maybeRequestInfo url toMsg ->
831
- fetchRouteData True transitionKey toMsg config url maybeRequestInfo
938
+ fetchRouteData transitionKey toMsg config url maybeRequestInfo
832
939
 
833
940
  Submit fields ->
834
- if fields.method == Get then
941
+ if fields.method == Form.Get then
835
942
  model.key
836
943
  |> Maybe.map (\key -> Browser.Navigation.pushUrl key (appendFormQueryParams fields))
837
944
  |> Maybe.withDefault Cmd.none
@@ -843,7 +950,7 @@ perform config model effect =
843
950
  -- TODO add optional path parameter to Submit variant to allow submitting to other routes
844
951
  model.url
845
952
  in
846
- fetchRouteData False -1 (UpdateCacheAndUrlNew False model.url Nothing) config urlToSubmitTo (Just fields)
953
+ fetchRouteData -1 (UpdateCacheAndUrlNew False model.url Nothing) config urlToSubmitTo (Just fields)
847
954
 
848
955
  SubmitFetcher fetcherKey transitionId formData ->
849
956
  startFetcher2 config False fetcherKey transitionId formData model
@@ -863,7 +970,7 @@ perform config model effect =
863
970
  |> config.perform
864
971
  { fetchRouteData =
865
972
  \fetchInfo ->
866
- fetchRouteData False
973
+ fetchRouteData
867
974
  -1
868
975
  (prepare fetchInfo.toMsg)
869
976
  config
@@ -873,15 +980,19 @@ perform config model effect =
873
980
  ---- TODO map the Msg with the wrapper type (like in the PR branch)
874
981
  , submit =
875
982
  \fetchInfo ->
876
- fetchRouteData False -1 (prepare fetchInfo.toMsg) config (fetchInfo.values.action |> Url.fromString |> Maybe.withDefault model.url) (Just fetchInfo.values)
983
+ fetchRouteData -1 (prepare fetchInfo.toMsg) config (fetchInfo.values.action |> Url.fromString |> Maybe.withDefault model.url) (Just fetchInfo.values)
877
984
  , runFetcher =
878
985
  \(Pages.Fetcher.Fetcher options) ->
879
986
  -- TODO need to get the fetcherId here
880
987
  -- TODO need to increment and pass in the transitionId
881
988
  startFetcher "TODO" -1 options model
882
- , fromPageMsg = Pages.Msg.UserMsg >> UserMsg
989
+ , fromPageMsg = Pages.Internal.Msg.UserMsg >> UserMsg
883
990
  , key = key
884
- , setField = \info -> Task.succeed (SetField info) |> Task.perform identity
991
+ , setField =
992
+ \_ ->
993
+ --Task.succeed (SetField info) |> Task.perform identity
994
+ -- TODO
995
+ Cmd.none
885
996
  }
886
997
 
887
998
  Nothing ->
@@ -896,21 +1007,12 @@ startFetcher fetcherKey transitionId options model =
896
1007
  let
897
1008
  encodedBody : String
898
1009
  encodedBody =
899
- FormDecoder.encodeFormData
900
- { fields = options.fields
901
-
902
- -- TODO remove hardcoding
903
- , action = ""
1010
+ encodeFormData options.fields
904
1011
 
905
- -- TODO remove hardcoding
906
- , method = Post
907
- , id = Nothing
908
- }
909
-
910
- formData : { method : Method, action : String, fields : List ( String, String ), id : Maybe String }
1012
+ formData : { method : Form.Method, action : String, fields : List ( String, String ), id : Maybe String }
911
1013
  formData =
912
1014
  { -- TODO remove hardcoding
913
- method = Get
1015
+ method = Form.Get
914
1016
 
915
1017
  -- TODO pass FormData directly
916
1018
  , action = options.url |> Maybe.withDefault model.url.path
@@ -927,10 +1029,10 @@ startFetcher fetcherKey transitionId options model =
927
1029
  Http.expectBytesResponse (FetcherComplete False fetcherKey model.nextTransitionKey)
928
1030
  (\bytes ->
929
1031
  case bytes of
930
- Http.GoodStatus_ metadata bytesBody ->
1032
+ Http.GoodStatus_ _ bytesBody ->
931
1033
  ( options.decoder (Ok bytesBody)
932
1034
  |> Just
933
- , Nothing
1035
+ , ActionResponse Nothing
934
1036
  )
935
1037
  |> Ok
936
1038
 
@@ -943,13 +1045,13 @@ startFetcher fetcherKey transitionId options model =
943
1045
  Http.NetworkError_ ->
944
1046
  Err <| Http.NetworkError
945
1047
 
946
- Http.BadStatus_ metadata body ->
1048
+ Http.BadStatus_ metadata _ ->
947
1049
  Err <| Http.BadStatus metadata.statusCode
948
1050
  )
949
1051
  , tracker = Nothing
950
1052
  , body = Http.stringBody "application/x-www-form-urlencoded" encodedBody
951
1053
  , headers = options.headers |> List.map (\( name, value ) -> Http.header name value)
952
- , url = options.url |> Maybe.withDefault (Path.join [ model.url.path, "content.dat" ] |> Path.toAbsolute)
1054
+ , url = options.url |> Maybe.withDefault (UrlPath.join [ model.url.path, "content.dat" ] |> UrlPath.toAbsolute)
953
1055
  , method = "POST"
954
1056
  , timeout = Nothing
955
1057
  }
@@ -968,13 +1070,13 @@ startFetcher2 config fromPageReload fetcherKey transitionId formData model =
968
1070
  let
969
1071
  encodedBody : String
970
1072
  encodedBody =
971
- FormDecoder.encodeFormData formData
1073
+ encodeFormData formData.fields
972
1074
  in
973
1075
  -- TODO make sure that `actionData` isn't updated in Model for fetchers
974
1076
  Cmd.batch
975
1077
  [ cancelStaleFetchers model
976
1078
  , case Dict.get fetcherKey model.inFlightFetchers of
977
- Just ( inFlightId, inFlightFetcher ) ->
1079
+ Just ( inFlightId, _ ) ->
978
1080
  Http.cancel (String.fromInt inFlightId)
979
1081
 
980
1082
  Nothing ->
@@ -985,22 +1087,26 @@ startFetcher2 config fromPageReload fetcherKey transitionId formData model =
985
1087
  Http.expectBytesResponse (FetcherComplete fromPageReload fetcherKey model.nextTransitionKey)
986
1088
  (\bytes ->
987
1089
  case bytes of
988
- Http.GoodStatus_ metadata bytesBody ->
1090
+ Http.GoodStatus_ _ bytesBody ->
989
1091
  let
990
- decodedAction : Maybe actionData
1092
+ decodedAction : ActionDataOrRedirect actionData
991
1093
  decodedAction =
992
1094
  case Bytes.Decode.decode config.decodeResponse bytesBody of
1095
+ -- @@@
1096
+ Just (ResponseSketch.Redirect redirectTo) ->
1097
+ RedirectResponse redirectTo
1098
+
993
1099
  Just (ResponseSketch.RenderPage _ maybeAction) ->
994
- maybeAction
1100
+ ActionResponse maybeAction
995
1101
 
996
- Just (ResponseSketch.HotUpdate pageData shared maybeAction) ->
997
- maybeAction
1102
+ Just (ResponseSketch.HotUpdate _ _ maybeAction) ->
1103
+ ActionResponse maybeAction
998
1104
 
999
- Just (ResponseSketch.NotFound notFound) ->
1000
- Nothing
1105
+ Just (ResponseSketch.NotFound _) ->
1106
+ ActionResponse Nothing
1001
1107
 
1002
1108
  _ ->
1003
- Nothing
1109
+ ActionResponse Nothing
1004
1110
  in
1005
1111
  -- TODO maybe have an optional way to pass the bytes through?
1006
1112
  Ok ( Nothing, decodedAction )
@@ -1014,7 +1120,7 @@ startFetcher2 config fromPageReload fetcherKey transitionId formData model =
1014
1120
  Http.NetworkError_ ->
1015
1121
  Err <| Http.NetworkError
1016
1122
 
1017
- Http.BadStatus_ metadata body ->
1123
+ Http.BadStatus_ metadata _ ->
1018
1124
  Err <| Http.BadStatus metadata.statusCode
1019
1125
  )
1020
1126
  , tracker = Just (String.fromInt transitionId)
@@ -1024,8 +1130,8 @@ startFetcher2 config fromPageReload fetcherKey transitionId formData model =
1024
1130
  , headers = []
1025
1131
 
1026
1132
  -- TODO use formData.method to do either query params or POST body
1027
- , url = formData.action |> Url.fromString |> Maybe.map (\{ path } -> Path.join [ path, "content.dat" ] |> Path.toAbsolute) |> Maybe.withDefault "/"
1028
- , method = formData.method |> FormDecoder.methodToString
1133
+ , url = formData.action |> Url.fromString |> Maybe.map (\{ path } -> UrlPath.join [ path, "content.dat" ] |> UrlPath.toAbsolute) |> Maybe.withDefault "/"
1134
+ , method = formData.method |> methodToString
1029
1135
  , timeout = Nothing
1030
1136
  }
1031
1137
  ]
@@ -1036,16 +1142,16 @@ cancelStaleFetchers model =
1036
1142
  model.inFlightFetchers
1037
1143
  |> Dict.toList
1038
1144
  |> List.filterMap
1039
- (\( fetcherKey, ( id, fetcher ) ) ->
1145
+ (\( _, ( id, fetcher ) ) ->
1040
1146
  case fetcher.status of
1041
- Pages.Transition.FetcherReloading _ ->
1147
+ Pages.ConcurrentSubmission.Reloading _ ->
1042
1148
  Http.cancel (String.fromInt id)
1043
1149
  |> Just
1044
1150
 
1045
- Pages.Transition.FetcherSubmitting ->
1151
+ Pages.ConcurrentSubmission.Submitting ->
1046
1152
  Nothing
1047
1153
 
1048
- Pages.Transition.FetcherComplete _ ->
1154
+ Pages.ConcurrentSubmission.Complete _ ->
1049
1155
  Nothing
1050
1156
  )
1051
1157
  |> Cmd.batch
@@ -1059,10 +1165,10 @@ appendFormQueryParams fields =
1059
1165
  |> Maybe.withDefault "/"
1060
1166
  )
1061
1167
  ++ (case fields.method of
1062
- Get ->
1063
- "?" ++ FormDecoder.encodeFormData fields
1168
+ Form.Get ->
1169
+ "?" ++ encodeFormData fields.fields
1064
1170
 
1065
- Post ->
1171
+ Form.Post ->
1066
1172
  ""
1067
1173
  )
1068
1174
 
@@ -1102,9 +1208,9 @@ application config =
1102
1208
  in
1103
1209
  Sub.batch
1104
1210
  [ config.subscriptions (model.url |> config.urlToRoute)
1105
- (urls.currentUrl |> config.urlToRoute |> config.routeToPath |> Path.join)
1211
+ (urls.currentUrl |> config.urlToRoute |> config.routeToPath |> UrlPath.join)
1106
1212
  pageData.userModel
1107
- |> Sub.map (Pages.Msg.UserMsg >> UserMsg)
1213
+ |> Sub.map (Pages.Internal.Msg.UserMsg >> UserMsg)
1108
1214
  , config.hotReloadData
1109
1215
  |> Sub.map HotReloadCompleteNew
1110
1216
  ]
@@ -1151,20 +1257,19 @@ withUserMsg config userMsg ( model, effect ) =
1151
1257
  ( model, effect )
1152
1258
 
1153
1259
 
1154
- urlPathToPath : Url -> Path
1260
+ urlPathToPath : Url -> UrlPath
1155
1261
  urlPathToPath urls =
1156
- urls.path |> Path.fromString
1262
+ urls.path |> UrlPath.fromString
1157
1263
 
1158
1264
 
1159
1265
  fetchRouteData :
1160
- Bool
1161
- -> Int
1266
+ Int
1162
1267
  -> (Result Http.Error ( Url, ResponseSketch pageData actionData sharedData ) -> Msg userMsg pageData actionData sharedData errorPage)
1163
1268
  -> ProgramConfig userMsg userModel route pageData actionData sharedData effect (Msg userMsg pageData actionData sharedData errorPage) errorPage
1164
1269
  -> Url
1165
1270
  -> Maybe FormData
1166
1271
  -> Cmd (Msg userMsg pageData actionData sharedData errorPage)
1167
- fetchRouteData forPageDataReload transitionKey toMsg config url details =
1272
+ fetchRouteData transitionKey toMsg config url details =
1168
1273
  {-
1169
1274
  TODO:
1170
1275
  - [X] `toMsg` needs a parameter for the callback Msg so it can pass it on if there is a Redirect response
@@ -1178,14 +1283,14 @@ fetchRouteData forPageDataReload transitionKey toMsg config url details =
1178
1283
 
1179
1284
  -}
1180
1285
  let
1181
- formMethod : Method
1286
+ formMethod : Form.Method
1182
1287
  formMethod =
1183
1288
  details
1184
1289
  |> Maybe.map .method
1185
- |> Maybe.withDefault Get
1290
+ |> Maybe.withDefault Form.Get
1186
1291
  in
1187
1292
  Http.request
1188
- { method = details |> Maybe.map (.method >> FormDecoder.methodToString) |> Maybe.withDefault "GET"
1293
+ { method = details |> Maybe.map (.method >> methodToString) |> Maybe.withDefault "GET"
1189
1294
  , headers = []
1190
1295
  , url =
1191
1296
  "/"
@@ -1203,34 +1308,34 @@ fetchRouteData forPageDataReload transitionKey toMsg config url details =
1203
1308
  |> String.join "/"
1204
1309
  )
1205
1310
  ++ (case formMethod of
1206
- Post ->
1311
+ Form.Post ->
1207
1312
  "/"
1208
1313
 
1209
- Get ->
1314
+ Form.Get ->
1210
1315
  details
1211
- |> Maybe.map FormDecoder.encodeFormData
1316
+ |> Maybe.map (.fields >> encodeFormData)
1212
1317
  |> Maybe.map (\encoded -> "?" ++ encoded)
1213
1318
  |> Maybe.withDefault ""
1214
1319
  )
1215
1320
  ++ (case formMethod of
1216
1321
  -- TODO extract this to something unit testable
1217
1322
  -- TODO make states mutually exclusive for submissions and direct URL requests (shouldn't be possible to append two query param strings)
1218
- Post ->
1323
+ Form.Post ->
1219
1324
  ""
1220
1325
 
1221
- Get ->
1326
+ Form.Get ->
1222
1327
  url.query
1223
1328
  |> Maybe.map (\encoded -> "?" ++ encoded)
1224
1329
  |> Maybe.withDefault ""
1225
1330
  )
1226
1331
  , body =
1227
1332
  case formMethod of
1228
- Post ->
1333
+ Form.Post ->
1229
1334
  let
1230
1335
  urlEncodedFields : Maybe String
1231
1336
  urlEncodedFields =
1232
1337
  details
1233
- |> Maybe.map FormDecoder.encodeFormData
1338
+ |> Maybe.map (.fields >> encodeFormData)
1234
1339
  in
1235
1340
  urlEncodedFields
1236
1341
  |> Maybe.map (\encoded -> Http.stringBody "application/x-www-form-urlencoded" encoded)
@@ -1251,7 +1356,7 @@ fetchRouteData forPageDataReload transitionKey toMsg config url details =
1251
1356
  Http.NetworkError_ ->
1252
1357
  Err Http.NetworkError
1253
1358
 
1254
- Http.BadStatus_ metadata body ->
1359
+ Http.BadStatus_ _ body ->
1255
1360
  body
1256
1361
  |> Bytes.Decode.decode config.decodeResponse
1257
1362
  |> Result.fromMaybe "Decoding error"
@@ -1303,7 +1408,7 @@ startNewGetLoad urlToGet toMsg ( model, effect ) =
1303
1408
  cancelIfStale : Effect userMsg pageData actionData sharedData userEffect errorPage
1304
1409
  cancelIfStale =
1305
1410
  case model.transition of
1306
- Just ( transitionKey, Pages.Transition.Loading path loadingKind ) ->
1411
+ Just ( transitionKey, Pages.Navigation.Loading _ _ ) ->
1307
1412
  CancelRequest transitionKey
1308
1413
 
1309
1414
  _ ->
@@ -1314,22 +1419,22 @@ startNewGetLoad urlToGet toMsg ( model, effect ) =
1314
1419
  , transition =
1315
1420
  ( model.nextTransitionKey
1316
1421
  , case model.transition of
1317
- Just ( transitionKey, Pages.Transition.LoadAfterSubmit submitData _ _ ) ->
1318
- Pages.Transition.LoadAfterSubmit
1422
+ Just ( _, Pages.Navigation.LoadAfterSubmit submitData _ _ ) ->
1423
+ Pages.Navigation.LoadAfterSubmit
1319
1424
  submitData
1320
- (urlToGet.path |> Path.fromString)
1321
- Pages.Transition.Load
1425
+ (urlToGet.path |> UrlPath.fromString)
1426
+ Pages.Navigation.Load
1322
1427
 
1323
- Just ( transitionKey, Pages.Transition.Submitting submitData ) ->
1324
- Pages.Transition.LoadAfterSubmit
1428
+ Just ( _, Pages.Navigation.Submitting submitData ) ->
1429
+ Pages.Navigation.LoadAfterSubmit
1325
1430
  submitData
1326
- (urlToGet.path |> Path.fromString)
1327
- Pages.Transition.Load
1431
+ (urlToGet.path |> UrlPath.fromString)
1432
+ Pages.Navigation.Load
1328
1433
 
1329
1434
  _ ->
1330
- Pages.Transition.Loading
1331
- (urlToGet.path |> Path.fromString)
1332
- Pages.Transition.Load
1435
+ Pages.Navigation.Loading
1436
+ (urlToGet.path |> UrlPath.fromString)
1437
+ Pages.Navigation.Load
1333
1438
  )
1334
1439
  |> Just
1335
1440
  }
@@ -1355,9 +1460,9 @@ clearLoadingFetchersAfterDataLoad completedTransitionId model =
1355
1460
  -- TODO fetchers are never removed from the list. Need to decide how and when to remove them.
1356
1461
  --(fetcherState.status /= Pages.Transition.FetcherReloading) || (transitionId > completedTransitionId)
1357
1462
  case ( transitionId > completedTransitionId, fetcherState.status ) of
1358
- ( False, Pages.Transition.FetcherReloading actionData ) ->
1463
+ ( False, Pages.ConcurrentSubmission.Reloading actionData ) ->
1359
1464
  ( transitionId
1360
- , { fetcherState | status = Pages.Transition.FetcherComplete actionData }
1465
+ , { fetcherState | status = Pages.ConcurrentSubmission.Complete actionData }
1361
1466
  )
1362
1467
 
1363
1468
  _ ->
@@ -1392,6 +1497,7 @@ loadDataAndUpdateUrl ( newPageData, newSharedData, newActionData ) maybeUserMsg
1392
1497
  , actionData = newActionData
1393
1498
  }
1394
1499
 
1500
+ -- TODO use userEffect here?
1395
1501
  ( userModel, _ ) =
1396
1502
  -- TODO if urlWithoutRedirectResolution is different from the url with redirect resolution, then
1397
1503
  -- instead of calling update, call pushUrl (I think?)
@@ -1467,10 +1573,10 @@ loadDataAndUpdateUrl ( newPageData, newSharedData, newActionData ) maybeUserMsg
1467
1573
  Err _ ->
1468
1574
  {-
1469
1575
  When there is an error loading the content.dat, we are either
1470
- 1) in the dev server, and should show the relevant DataSource error for the page
1576
+ 1) in the dev server, and should show the relevant BackendTask error for the page
1471
1577
  we're navigating to. This could be done more cleanly, but it's simplest to just
1472
1578
  do a fresh page load and use the code path for presenting an error for a fresh page.
1473
- 2) In a production app. That means we had a successful build, so there were no DataSource failures,
1579
+ 2) In a production app. That means we had a successful build, so there were no BackendTask failures,
1474
1580
  so the app must be stale (unless it's in some unexpected state from a bug). In the future,
1475
1581
  it probably makes sense to include some sort of hash of the app version we are fetching, match
1476
1582
  it with the current version that's running, and perform this logic when we see there is a mismatch.
@@ -1483,3 +1589,31 @@ loadDataAndUpdateUrl ( newPageData, newSharedData, newActionData ) maybeUserMsg
1483
1589
  |> Url.toString
1484
1590
  |> BrowserLoadUrl
1485
1591
  )
1592
+
1593
+
1594
+ methodToString : Form.Method -> String
1595
+ methodToString method =
1596
+ case method of
1597
+ Form.Get ->
1598
+ "GET"
1599
+
1600
+ Form.Post ->
1601
+ "POST"
1602
+
1603
+
1604
+ encodeFormData : List ( String, String ) -> String
1605
+ encodeFormData fields =
1606
+ fields
1607
+ |> List.map
1608
+ (\( name, value ) ->
1609
+ Url.percentEncode name ++ "=" ++ Url.percentEncode value
1610
+ )
1611
+ |> String.join "&"
1612
+
1613
+
1614
+ type alias FormData =
1615
+ { fields : List ( String, String )
1616
+ , method : Form.Method
1617
+ , action : String
1618
+ , id : Maybe String
1619
+ }