elm-pages 3.0.0-beta.9 → 3.0.0
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.
- package/README.md +2 -2
- package/adapter/netlify.js +207 -0
- package/codegen/{elm-pages-codegen.js → elm-pages-codegen.cjs} +2731 -2939
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmi +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmo +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateDataTest.elmo +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm.json +1 -1
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1527 -422
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Runner.elm.js +16840 -13653
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_supervisor.js +4 -4
- package/generator/dead-code-review/elm.json +9 -7
- package/generator/dead-code-review/src/Pages/Review/DeadCodeEliminateData.elm +59 -10
- package/generator/dead-code-review/tests/Pages/Review/DeadCodeEliminateDataTest.elm +52 -36
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmi +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmo +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmi +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmo +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm.json +1 -1
- package/generator/review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1527 -422
- package/generator/review/elm-stuff/tests-0.19.1/js/Runner.elm.js +25118 -21832
- package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
- package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +4 -4
- package/generator/review/elm.json +10 -10
- package/generator/src/RouteBuilder.elm +93 -128
- package/generator/src/SharedTemplate.elm +8 -7
- package/generator/src/SiteConfig.elm +3 -2
- package/generator/src/basepath-middleware.js +3 -3
- package/generator/src/build.js +143 -59
- package/generator/src/cli.js +292 -88
- package/generator/src/codegen.js +29 -27
- package/generator/src/compatibility-key.js +3 -0
- package/generator/src/compile-elm.js +43 -26
- package/generator/src/config.js +2 -4
- package/generator/src/copy-dir.js +2 -2
- package/generator/src/dev-server.js +160 -102
- package/generator/src/dir-helpers.js +9 -26
- package/generator/src/elm-codegen.js +5 -4
- package/generator/src/elm-file-constants.js +2 -3
- package/generator/src/error-formatter.js +12 -11
- package/generator/src/file-helpers.js +3 -4
- package/generator/src/generate-template-module-connector.js +23 -23
- package/generator/src/init.js +9 -8
- package/generator/src/pre-render-html.js +10 -13
- package/generator/src/render-test.js +109 -0
- package/generator/src/render-worker.js +25 -28
- package/generator/src/render.js +320 -143
- package/generator/src/request-cache.js +265 -162
- package/generator/src/resolve-elm-module.js +64 -0
- package/generator/src/rewrite-client-elm-json.js +6 -5
- package/generator/src/rewrite-elm-json-help.js +56 -0
- package/generator/src/rewrite-elm-json.js +17 -7
- package/generator/src/route-codegen-helpers.js +16 -31
- package/generator/src/seo-renderer.js +1 -3
- package/generator/src/vite-utils.js +1 -2
- package/generator/static-code/elm-pages.js +10 -0
- package/generator/static-code/hmr.js +79 -13
- package/generator/template/app/Api.elm +3 -2
- package/generator/template/app/Effect.elm +155 -0
- package/generator/template/app/ErrorPage.elm +49 -6
- package/generator/template/app/Route/Blog/Slug_.elm +86 -0
- package/generator/template/app/Route/Greet.elm +107 -0
- package/generator/template/app/Route/Hello.elm +119 -0
- package/generator/template/app/Route/Index.elm +26 -25
- package/generator/template/app/Shared.elm +38 -39
- package/generator/template/app/Site.elm +4 -7
- package/generator/template/app/View.elm +9 -8
- package/generator/template/codegen/elm.codegen.json +18 -0
- package/generator/template/custom-backend-task.ts +3 -0
- package/generator/template/elm-pages.config.mjs +13 -0
- package/generator/template/elm-tooling.json +0 -3
- package/generator/template/elm.json +25 -20
- package/generator/template/index.ts +1 -2
- package/generator/template/netlify.toml +4 -1
- package/generator/template/package.json +10 -4
- package/generator/template/script/.elm-pages/compiled-ports/custom-backend-task.mjs +7 -0
- package/generator/template/script/custom-backend-task.ts +3 -0
- package/generator/template/script/elm.json +61 -0
- package/generator/template/script/src/AddRoute.elm +312 -0
- package/generator/template/script/src/Stars.elm +42 -0
- package/package.json +30 -27
- package/src/ApiRoute.elm +249 -82
- package/src/BackendTask/Custom.elm +325 -0
- package/src/BackendTask/Env.elm +90 -0
- package/src/{DataSource → BackendTask}/File.elm +171 -56
- package/src/{DataSource → BackendTask}/Glob.elm +136 -125
- package/src/BackendTask/Http.elm +679 -0
- package/src/{DataSource → BackendTask}/Internal/Glob.elm +1 -1
- package/src/BackendTask/Internal/Request.elm +69 -0
- package/src/BackendTask/Random.elm +79 -0
- package/src/BackendTask/Time.elm +47 -0
- package/src/BackendTask.elm +531 -0
- package/src/FatalError.elm +90 -0
- package/src/FormData.elm +21 -18
- package/src/Head/Seo.elm +4 -4
- package/src/Head.elm +112 -8
- package/src/Internal/ApiRoute.elm +7 -5
- package/src/Internal/Request.elm +84 -4
- package/src/PageServerResponse.elm +6 -1
- package/src/Pages/ConcurrentSubmission.elm +127 -0
- package/src/Pages/Form.elm +340 -0
- package/src/Pages/FormData.elm +19 -0
- package/src/Pages/GeneratorProgramConfig.elm +15 -0
- package/src/Pages/Internal/FatalError.elm +5 -0
- package/src/Pages/Internal/Msg.elm +93 -0
- package/src/Pages/Internal/NotFoundReason.elm +4 -4
- package/src/Pages/Internal/Platform/Cli.elm +586 -768
- package/src/Pages/Internal/Platform/CompatibilityKey.elm +1 -1
- package/src/Pages/Internal/Platform/Effect.elm +1 -2
- package/src/Pages/Internal/Platform/GeneratorApplication.elm +379 -0
- package/src/Pages/Internal/Platform/StaticResponses.elm +65 -276
- package/src/Pages/Internal/Platform/ToJsPayload.elm +6 -9
- package/src/Pages/Internal/Platform.elm +330 -203
- package/src/Pages/Internal/ResponseSketch.elm +2 -2
- package/src/Pages/Internal/Script.elm +17 -0
- package/src/Pages/Internal/StaticHttpBody.elm +35 -1
- package/src/Pages/Manifest.elm +52 -11
- package/src/Pages/Navigation.elm +85 -0
- package/src/Pages/PageUrl.elm +26 -12
- package/src/Pages/ProgramConfig.elm +32 -22
- package/src/Pages/Script.elm +166 -0
- package/src/Pages/SiteConfig.elm +3 -2
- package/src/Pages/StaticHttp/Request.elm +2 -2
- package/src/Pages/StaticHttpRequest.elm +23 -99
- package/src/Pages/Url.elm +3 -3
- package/src/PagesMsg.elm +88 -0
- package/src/QueryParams.elm +21 -172
- package/src/RenderRequest.elm +7 -7
- package/src/RequestsAndPending.elm +37 -20
- package/src/Result/Extra.elm +26 -0
- package/src/Scaffold/Form.elm +569 -0
- package/src/Scaffold/Route.elm +1431 -0
- package/src/Server/Request.elm +476 -1001
- package/src/Server/Response.elm +130 -36
- package/src/Server/Session.elm +181 -111
- package/src/Server/SetCookie.elm +80 -32
- package/src/Stub.elm +53 -0
- package/src/Test/Html/Internal/ElmHtml/ToString.elm +8 -9
- package/src/{Path.elm → UrlPath.elm} +33 -36
- package/generator/template/public/images/icon-png.png +0 -0
- package/src/DataSource/Env.elm +0 -38
- package/src/DataSource/Http.elm +0 -446
- package/src/DataSource/Internal/Request.elm +0 -20
- package/src/DataSource/Port.elm +0 -90
- package/src/DataSource.elm +0 -546
- package/src/Form/Field.elm +0 -717
- package/src/Form/FieldStatus.elm +0 -36
- package/src/Form/FieldView.elm +0 -417
- package/src/Form/FormData.elm +0 -22
- package/src/Form/Validation.elm +0 -391
- package/src/Form/Value.elm +0 -118
- package/src/Form.elm +0 -1683
- package/src/FormDecoder.elm +0 -102
- package/src/Pages/FormState.elm +0 -256
- package/src/Pages/Generate.elm +0 -1242
- package/src/Pages/Internal/Form.elm +0 -17
- package/src/Pages/Internal/Platform/Cli.elm.bak +0 -1276
- package/src/Pages/Msg.elm +0 -79
- package/src/Pages/Transition.elm +0 -70
package/src/Head.elm
CHANGED
|
@@ -9,14 +9,118 @@ module Head exposing
|
|
|
9
9
|
, toJson, canonicalLink
|
|
10
10
|
)
|
|
11
11
|
|
|
12
|
-
{-| This module contains
|
|
13
|
-
|
|
14
|
-
when
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
{-| This module contains functions for building up
|
|
13
|
+
tags with metadata that will be rendered into the page's `<head>` tag
|
|
14
|
+
when your page is pre-rendered (or server-rendered, in the case of your server-rendered Route Modules). See also [`Head.Seo`](Head-Seo),
|
|
15
|
+
which has some helper functions for defining OpenGraph and Twitter tags.
|
|
16
|
+
|
|
17
|
+
One of the unique benefits of using `elm-pages` is that all of your routes (both pre-rendered and server-rendered) fully
|
|
18
|
+
render the HTML of your page. That includes the full initial `view` (with the BackendTask resolved, and the `Model` from `init`).
|
|
19
|
+
The HTML response also includes all of the `Head` tags, which are defined in two places:
|
|
20
|
+
|
|
21
|
+
1. `app/Site.elm` - there is a `head` definition in `Site.elm` where you define global head tags that will be included on every rendered page.
|
|
22
|
+
|
|
23
|
+
2. In each Route Module - there is a `head` function where you have access to both the resolved `BackendTask` and the `RouteParams` for the page and can return head tags based on that.
|
|
24
|
+
|
|
25
|
+
Here is a common set of global head tags that we can define in `Site.elm`:
|
|
26
|
+
|
|
27
|
+
module Site exposing (canonicalUrl, config)
|
|
28
|
+
|
|
29
|
+
import BackendTask exposing (BackendTask)
|
|
30
|
+
import Head
|
|
31
|
+
import MimeType
|
|
32
|
+
import SiteConfig exposing (SiteConfig)
|
|
33
|
+
|
|
34
|
+
config : SiteConfig
|
|
35
|
+
config =
|
|
36
|
+
{ canonicalUrl = "<https://elm-pages.com">
|
|
37
|
+
, head = head
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
head : BackendTask (List Head.Tag)
|
|
41
|
+
head =
|
|
42
|
+
[ Head.metaName "viewport" (Head.raw "width=device-width,initial-scale=1")
|
|
43
|
+
, Head.metaName "mobile-web-app-capable" (Head.raw "yes")
|
|
44
|
+
, Head.metaName "theme-color" (Head.raw "#ffffff")
|
|
45
|
+
, Head.metaName "apple-mobile-web-app-capable" (Head.raw "yes")
|
|
46
|
+
, Head.metaName "apple-mobile-web-app-status-bar-style" (Head.raw "black-translucent")
|
|
47
|
+
, Head.icon [ ( 32, 32 ) ] MimeType.Png (cloudinaryIcon MimeType.Png 32)
|
|
48
|
+
, Head.icon [ ( 16, 16 ) ] MimeType.Png (cloudinaryIcon MimeType.Png 16)
|
|
49
|
+
, Head.appleTouchIcon (Just 180) (cloudinaryIcon MimeType.Png 180)
|
|
50
|
+
, Head.appleTouchIcon (Just 192) (cloudinaryIcon MimeType.Png 192)
|
|
51
|
+
]
|
|
52
|
+
|> BackendTask.succeed
|
|
53
|
+
|
|
54
|
+
And here is a `head` function for a Route Module for a blog post. Note that we have access to our `BackendTask` Data and
|
|
55
|
+
are using it to populate article metadata like the article's image, publish date, etc.
|
|
56
|
+
|
|
57
|
+
import Article
|
|
58
|
+
import BackendTask
|
|
59
|
+
import Date
|
|
60
|
+
import Head
|
|
61
|
+
import Head.Seo
|
|
62
|
+
import Path
|
|
63
|
+
import Route exposing (Route)
|
|
64
|
+
import RouteBuilder exposing (App, StatelessRoute)
|
|
65
|
+
|
|
66
|
+
type alias RouteParams =
|
|
67
|
+
{ slug : String }
|
|
68
|
+
|
|
69
|
+
type alias Data =
|
|
70
|
+
{ metadata : ArticleMetadata
|
|
71
|
+
, body : List Markdown.Block.Block
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
route : StatelessRoute RouteParams Data ActionData
|
|
75
|
+
route =
|
|
76
|
+
RouteBuilder.preRender
|
|
77
|
+
{ data = data
|
|
78
|
+
, head = head
|
|
79
|
+
, pages = pages
|
|
80
|
+
}
|
|
81
|
+
|> RouteBuilder.buildNoState { view = view }
|
|
82
|
+
|
|
83
|
+
head :
|
|
84
|
+
App Data ActionData RouteParams
|
|
85
|
+
-> List Head.Tag
|
|
86
|
+
head static =
|
|
87
|
+
let
|
|
88
|
+
metadata =
|
|
89
|
+
static.data.metadata
|
|
90
|
+
in
|
|
91
|
+
Head.Seo.summaryLarge
|
|
92
|
+
{ canonicalUrlOverride = Nothing
|
|
93
|
+
, siteName = "elm-pages"
|
|
94
|
+
, image =
|
|
95
|
+
{ url = metadata.image
|
|
96
|
+
, alt = metadata.description
|
|
97
|
+
, dimensions = Nothing
|
|
98
|
+
, mimeType = Nothing
|
|
99
|
+
}
|
|
100
|
+
, description = metadata.description
|
|
101
|
+
, locale = Nothing
|
|
102
|
+
, title = metadata.title
|
|
103
|
+
}
|
|
104
|
+
|> Head.Seo.article
|
|
105
|
+
{ tags = []
|
|
106
|
+
, section = Nothing
|
|
107
|
+
, publishedTime = Just (DateOrDateTime.Date metadata.published)
|
|
108
|
+
, modifiedTime = Nothing
|
|
109
|
+
, expirationTime = Nothing
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
## Why is pre-rendered HTML important? Does it still matter for SEO?
|
|
114
|
+
|
|
115
|
+
Many search engines are able to execute JavaScript now. However, not all are, and even with crawlers like Google, there
|
|
116
|
+
is a longer lead time for your pages to be indexed when you have HTML with a blank page that is only visible after the JavaScript executes.
|
|
117
|
+
|
|
118
|
+
But most importantly, many tools that unfurl links will not execute JavaScript at all, but rather simply do a simple pass to parse your `<head>` tags.
|
|
119
|
+
It is not viable or reliable to add `<head>` tags for metadata on the client-side, it must be present in the initial HTML payload. Otherwise you may not
|
|
120
|
+
get unfurling preview content when you share a link to your site on Slack, Twitter, etc.
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
## Building up Head Tags
|
|
20
124
|
|
|
21
125
|
@docs Tag, metaName, metaProperty, metaRedirect
|
|
22
126
|
@docs rssLink, sitemapLink, rootLanguage, manifestLink
|
|
@@ -8,8 +8,10 @@ module Internal.ApiRoute exposing
|
|
|
8
8
|
, withRoutes
|
|
9
9
|
)
|
|
10
10
|
|
|
11
|
-
import
|
|
11
|
+
import BackendTask exposing (BackendTask)
|
|
12
|
+
import FatalError exposing (FatalError)
|
|
12
13
|
import Head
|
|
14
|
+
import Json.Decode
|
|
13
15
|
import Pattern exposing (Pattern)
|
|
14
16
|
import Regex exposing (Regex)
|
|
15
17
|
|
|
@@ -44,12 +46,12 @@ tryMatchDone path (ApiRoute handler) =
|
|
|
44
46
|
type ApiRoute response
|
|
45
47
|
= ApiRoute
|
|
46
48
|
{ regex : Regex
|
|
47
|
-
, matchesToResponse : String ->
|
|
48
|
-
, buildTimeRoutes :
|
|
49
|
-
, handleRoute : String ->
|
|
49
|
+
, matchesToResponse : Json.Decode.Value -> String -> BackendTask FatalError (Maybe response)
|
|
50
|
+
, buildTimeRoutes : BackendTask FatalError (List String)
|
|
51
|
+
, handleRoute : String -> BackendTask FatalError Bool
|
|
50
52
|
, pattern : Pattern
|
|
51
53
|
, kind : String
|
|
52
|
-
, globalHeadTags : Maybe (
|
|
54
|
+
, globalHeadTags : Maybe (BackendTask FatalError (List Head.Tag))
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
|
package/src/Internal/Request.elm
CHANGED
|
@@ -1,7 +1,87 @@
|
|
|
1
|
-
module Internal.Request exposing (
|
|
1
|
+
module Internal.Request exposing (Request(..), RequestRecord, fakeRequest, toRequest)
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import CookieParser
|
|
4
|
+
import Dict exposing (Dict)
|
|
5
|
+
import Json.Decode as Decode
|
|
6
|
+
import Time
|
|
4
7
|
|
|
5
8
|
|
|
6
|
-
type
|
|
7
|
-
=
|
|
9
|
+
type Request
|
|
10
|
+
= Request RequestRecord
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
type alias RequestRecord =
|
|
14
|
+
{ time : Time.Posix
|
|
15
|
+
, method : String
|
|
16
|
+
, body : Maybe String
|
|
17
|
+
, rawUrl : String
|
|
18
|
+
, rawHeaders : Dict String String
|
|
19
|
+
, cookies : Dict String String
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
toRequest : Decode.Value -> Request
|
|
24
|
+
toRequest value =
|
|
25
|
+
Decode.decodeValue requestDecoder value
|
|
26
|
+
|> Result.map Request
|
|
27
|
+
|> Result.withDefault fakeRequest
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
fakeRequest : Request
|
|
31
|
+
fakeRequest =
|
|
32
|
+
Request
|
|
33
|
+
{ time = Time.millisToPosix 0
|
|
34
|
+
, method = "ERROR"
|
|
35
|
+
, body = Just "ERROR"
|
|
36
|
+
, rawUrl = "ERROR"
|
|
37
|
+
, rawHeaders = Dict.empty
|
|
38
|
+
, cookies = Dict.empty
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
requestDecoder : Decode.Decoder RequestRecord
|
|
43
|
+
requestDecoder =
|
|
44
|
+
Decode.succeed RequestRecord
|
|
45
|
+
|> andMap
|
|
46
|
+
(Decode.field "requestTime"
|
|
47
|
+
(Decode.int |> Decode.map Time.millisToPosix)
|
|
48
|
+
)
|
|
49
|
+
|> andMap (Decode.field "method" Decode.string)
|
|
50
|
+
|> andMap (Decode.field "body" (Decode.nullable Decode.string))
|
|
51
|
+
|> andMap
|
|
52
|
+
(Decode.string
|
|
53
|
+
|> Decode.field "rawUrl"
|
|
54
|
+
)
|
|
55
|
+
|> andMap (Decode.field "headers" (Decode.dict Decode.string))
|
|
56
|
+
|> andMap
|
|
57
|
+
(Decode.field "headers"
|
|
58
|
+
(optionalField "cookie" Decode.string
|
|
59
|
+
|> Decode.map
|
|
60
|
+
(Maybe.map CookieParser.parse
|
|
61
|
+
>> Maybe.withDefault Dict.empty
|
|
62
|
+
)
|
|
63
|
+
)
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
andMap : Decode.Decoder a -> Decode.Decoder (a -> b) -> Decode.Decoder b
|
|
68
|
+
andMap =
|
|
69
|
+
Decode.map2 (|>)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
optionalField : String -> Decode.Decoder a -> Decode.Decoder (Maybe a)
|
|
73
|
+
optionalField fieldName decoder_ =
|
|
74
|
+
let
|
|
75
|
+
finishDecoding : Decode.Value -> Decode.Decoder (Maybe a)
|
|
76
|
+
finishDecoding json =
|
|
77
|
+
case Decode.decodeValue (Decode.field fieldName Decode.value) json of
|
|
78
|
+
Ok _ ->
|
|
79
|
+
-- The field is present, so run the decoder on it.
|
|
80
|
+
Decode.map Just (Decode.field fieldName decoder_)
|
|
81
|
+
|
|
82
|
+
Err _ ->
|
|
83
|
+
-- The field was missing, which is fine!
|
|
84
|
+
Decode.succeed Nothing
|
|
85
|
+
in
|
|
86
|
+
Decode.value
|
|
87
|
+
|> Decode.andThen finishDecoding
|
|
@@ -15,7 +15,12 @@ type PageServerResponse data error
|
|
|
15
15
|
| ErrorPage error { headers : List ( String, String ) }
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
toRedirect :
|
|
18
|
+
toRedirect :
|
|
19
|
+
{ response
|
|
20
|
+
| statusCode : Int
|
|
21
|
+
, headers : List ( String, String )
|
|
22
|
+
}
|
|
23
|
+
-> Maybe { statusCode : Int, location : String }
|
|
19
24
|
toRedirect response =
|
|
20
25
|
response.headers
|
|
21
26
|
|> Dict.fromList
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
module Pages.ConcurrentSubmission exposing
|
|
2
|
+
( ConcurrentSubmission, Status(..)
|
|
3
|
+
, map
|
|
4
|
+
)
|
|
5
|
+
|
|
6
|
+
{-| When you render a `Form` with the [`Pages.Form.withConcurrent`](Pages-Form#withConcurrent) `Option`, the state of in-flight and completed submissions will be available
|
|
7
|
+
from your `Route` module through `app.concurrentSubmissions` as a `Dict String (ConcurrentSubmission (Maybe Action))`.
|
|
8
|
+
|
|
9
|
+
You can use this state to declaratively derive Pending UI or Optimistic UI from your pending submissions (without managing the state in your `Model`, since `elm-pages`
|
|
10
|
+
manages form submission state for you).
|
|
11
|
+
|
|
12
|
+
You can [see the full-stack TodoMVC example](https://github.com/dillonkearns/elm-pages/blob/master/examples/todos/app/Route/Visibility__.elm) for a complete example of deriving Pending UI state from `app.concurrentSubmissions`.
|
|
13
|
+
|
|
14
|
+
For example, this how the TodoMVC example derives the list of new items that are being created (but are still pending).
|
|
15
|
+
|
|
16
|
+
view :
|
|
17
|
+
App Data ActionData RouteParams
|
|
18
|
+
-> Shared.Model
|
|
19
|
+
-> Model
|
|
20
|
+
-> View (PagesMsg Msg)
|
|
21
|
+
view app shared model =
|
|
22
|
+
let
|
|
23
|
+
pendingActions : List Action
|
|
24
|
+
pendingActions =
|
|
25
|
+
app.concurrentSubmissions
|
|
26
|
+
|> Dict.values
|
|
27
|
+
|> List.filterMap
|
|
28
|
+
(\{ status, payload } ->
|
|
29
|
+
case status of
|
|
30
|
+
Pages.ConcurrentSubmission.Complete _ ->
|
|
31
|
+
Nothing
|
|
32
|
+
|
|
33
|
+
_ ->
|
|
34
|
+
allForms
|
|
35
|
+
|> Form.Handler.run payload.fields
|
|
36
|
+
|> Form.toResult
|
|
37
|
+
|> Result.toMaybe
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
newPendingItems : List Entry
|
|
41
|
+
newPendingItems =
|
|
42
|
+
pendingActions
|
|
43
|
+
|> List.filterMap
|
|
44
|
+
(\submission ->
|
|
45
|
+
case submission of
|
|
46
|
+
Add description ->
|
|
47
|
+
Just
|
|
48
|
+
{ description = description
|
|
49
|
+
, completed = False
|
|
50
|
+
, id = ""
|
|
51
|
+
, isPending = True
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
_ ->
|
|
55
|
+
-- `newPendingItems` only cares about pending Add actions. Other values will use
|
|
56
|
+
-- pending submissions for other types of Actions.
|
|
57
|
+
Nothing
|
|
58
|
+
)
|
|
59
|
+
in
|
|
60
|
+
itemsView app newPendingItems
|
|
61
|
+
|
|
62
|
+
allForms : Form.Handler.Handler String Action
|
|
63
|
+
allForms =
|
|
64
|
+
|> Form.Handler.init Add addItemForm
|
|
65
|
+
-- |> Form.Handler.with ...
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
type Action
|
|
69
|
+
= UpdateEntry ( String, String )
|
|
70
|
+
| Add String
|
|
71
|
+
| Delete String
|
|
72
|
+
| DeleteComplete
|
|
73
|
+
| Check ( Bool, String )
|
|
74
|
+
| CheckAll Bool
|
|
75
|
+
|
|
76
|
+
@docs ConcurrentSubmission, Status
|
|
77
|
+
|
|
78
|
+
@docs map
|
|
79
|
+
|
|
80
|
+
-}
|
|
81
|
+
|
|
82
|
+
import Pages.FormData exposing (FormData)
|
|
83
|
+
import Time
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
{-| -}
|
|
87
|
+
type alias ConcurrentSubmission actionData =
|
|
88
|
+
{ status : Status actionData
|
|
89
|
+
, payload : FormData
|
|
90
|
+
, initiatedAt : Time.Posix
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
{-| The status of a `ConcurrentSubmission`.
|
|
95
|
+
|
|
96
|
+
- `Submitting` - The submission is in-flight.
|
|
97
|
+
- `Reloading` - The submission has completed, and the page is now reloading the `Route`'s `data` to reflect the new state. The `actionData` holds any data returned from the `Route`'s `action`.
|
|
98
|
+
- `Complete` - The submission has completed, and the `Route`'s `data` has since reloaded so the state reflects the refreshed state after completing this specific form submission. The `actionData` holds any data returned from the `Route`'s `action`.
|
|
99
|
+
|
|
100
|
+
-}
|
|
101
|
+
type Status actionData
|
|
102
|
+
= Submitting
|
|
103
|
+
| Reloading actionData
|
|
104
|
+
| Complete actionData
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
{-| `map` a `ConcurrentSubmission`. Not needed for most high-level cases since this state is managed by the `elm-pages` framework for you.
|
|
108
|
+
-}
|
|
109
|
+
map : (a -> b) -> ConcurrentSubmission a -> ConcurrentSubmission b
|
|
110
|
+
map mapFn fetcherState =
|
|
111
|
+
{ status = mapStatus mapFn fetcherState.status
|
|
112
|
+
, payload = fetcherState.payload
|
|
113
|
+
, initiatedAt = fetcherState.initiatedAt
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
mapStatus : (a -> b) -> Status a -> Status b
|
|
118
|
+
mapStatus mapFn fetcherSubmitStatus =
|
|
119
|
+
case fetcherSubmitStatus of
|
|
120
|
+
Submitting ->
|
|
121
|
+
Submitting
|
|
122
|
+
|
|
123
|
+
Reloading value ->
|
|
124
|
+
Reloading (mapFn value)
|
|
125
|
+
|
|
126
|
+
Complete value ->
|
|
127
|
+
Complete (mapFn value)
|