Update part1

This commit is contained in:
Richard Feldman
2018-08-13 05:11:14 -04:00
parent 493b5681d4
commit 1688e5ec38
19 changed files with 854 additions and 530 deletions

View File

@@ -19,7 +19,6 @@ import Route
import Session exposing (Session)
import Task exposing (Task)
import Time
import Validate exposing (Valid, Validator, fromValid, ifBlank, validate)
import Viewer exposing (Viewer)
import Viewer.Cred as Cred exposing (Cred)
@@ -38,14 +37,20 @@ type
Status
-- Edit Article
= Loading Slug
| LoadingSlowly Slug
| LoadingFailed Slug
| Saving Slug Form
| Editing Slug (List Error) Form
| Editing Slug (List Problem) Form
-- New Article
| EditingNew (List Error) Form
| EditingNew (List Problem) Form
| Creating Form
type Problem
= InvalidEntry ValidatedField String
| ServerError String
type alias Form =
{ title : String
, body : String
@@ -74,12 +79,15 @@ initEdit session slug =
( { session = session
, status = Loading slug
}
, Article.fetch (Session.cred session) slug
|> Http.toTask
-- If init fails, store the slug that failed in the msg, so we can
-- at least have it later to display the page's title properly!
|> Task.mapError (\httpError -> ( slug, httpError ))
|> Task.attempt CompletedArticleLoad
, Cmd.batch
[ Article.fetch (Session.cred session) slug
|> Http.toTask
-- If init fails, store the slug that failed in the msg, so we can
-- at least have it later to display the page's title properly!
|> Task.mapError (\httpError -> ( slug, httpError ))
|> Task.attempt CompletedArticleLoad
, Task.perform (\_ -> PassedSlowLoadThreshold) Loading.slowThreshold
]
)
@@ -106,12 +114,35 @@ view model =
}
viewProblems : List Problem -> Html msg
viewProblems problems =
ul [ class "error-messages" ]
(List.map viewProblem problems)
viewProblem : Problem -> Html msg
viewProblem problem =
let
errorMessage =
case problem of
InvalidEntry _ message ->
message
ServerError message ->
message
in
li [] [ text errorMessage ]
viewAuthenticated : Cred -> Model -> Html Msg
viewAuthenticated cred model =
let
formHtml =
case model.status of
Loading _ ->
[]
LoadingSlowly _ ->
[ Loading.icon ]
Saving slug form ->
@@ -120,17 +151,13 @@ viewAuthenticated cred model =
Creating form ->
[ viewForm cred form (newArticleSaveButton [ disabled True ]) ]
Editing slug errors form ->
[ errors
|> List.map (\( _, error ) -> li [] [ text error ])
|> ul [ class "error-messages" ]
Editing slug problems form ->
[ viewProblems problems
, viewForm cred form (editArticleSaveButton [])
]
EditingNew errors form ->
[ errors
|> List.map (\( _, error ) -> li [] [ text error ])
|> ul [ class "error-messages" ]
EditingNew problems form ->
[ viewProblems problems
, viewForm cred form (newArticleSaveButton [])
]
@@ -223,6 +250,7 @@ type Msg
| CompletedEdit (Result Http.Error (Article Full))
| CompletedArticleLoad (Result ( Slug, Http.Error ) (Article Full))
| GotSession Session
| PassedSlowLoadThreshold
update : Msg -> Model -> ( Model, Cmd Msg )
@@ -252,7 +280,7 @@ update msg model =
)
CompletedCreate (Err error) ->
( { model | status = savingError model.status }
( { model | status = savingError error model.status }
, Cmd.none
)
@@ -263,7 +291,7 @@ update msg model =
)
CompletedEdit (Err error) ->
( { model | status = savingError model.status }
( { model | status = savingError error model.status }
, Cmd.none
)
@@ -291,35 +319,51 @@ update msg model =
)
GotSession session ->
( { model | session = session }, Cmd.none )
( { model | session = session }
, Route.replaceUrl (Session.navKey session) Route.Home
)
PassedSlowLoadThreshold ->
let
-- If any data is still Loading, change it to LoadingSlowly
-- so `view` knows to render a spinner.
status =
case model.status of
Loading slug ->
LoadingSlowly slug
other ->
other
in
( { model | status = status }, Cmd.none )
save : Cred -> Status -> ( Status, Cmd Msg )
save cred status =
case status of
Editing slug _ fields ->
case validate formValidator fields of
Editing slug _ form ->
case validate form of
Ok validForm ->
( Saving slug fields
( Saving slug form
, edit slug validForm cred
|> Http.send CompletedEdit
)
Err errors ->
( Editing slug errors fields
Err problems ->
( Editing slug problems form
, Cmd.none
)
EditingNew _ fields ->
case validate formValidator fields of
EditingNew _ form ->
case validate form of
Ok validForm ->
( Creating fields
( Creating form
, create validForm cred
|> Http.send CompletedCreate
)
Err errors ->
( EditingNew errors fields
Err problems ->
( EditingNew problems form
, Cmd.none
)
@@ -333,18 +377,18 @@ save cred status =
( status, Cmd.none )
savingError : Status -> Status
savingError status =
savingError : Http.Error -> Status -> Status
savingError error status =
let
errors =
[ ( Server, "Error saving article" ) ]
problems =
[ ServerError "Error saving article" ]
in
case status of
Saving slug form ->
Editing slug errors form
Editing slug problems form
Creating form ->
EditingNew errors form
EditingNew problems form
_ ->
status
@@ -367,6 +411,9 @@ updateForm transform model =
Loading _ ->
model
LoadingSlowly _ ->
model
LoadingFailed _ ->
model
@@ -395,37 +442,85 @@ subscriptions model =
-- VALIDATION
-- FORM
type ErrorSource
= Server
| Title
{-| Marks that we've trimmed the form's fields, so we don't accidentally send
it to the server without having trimmed it!
-}
type TrimmedForm
= Trimmed Form
{-| When adding a variant here, add it to `fieldsToValidate` too!
-}
type ValidatedField
= Title
| Body
type alias Error =
( ErrorSource, String )
fieldsToValidate : List ValidatedField
fieldsToValidate =
[ Title
, Body
]
formValidator : Validator Error Form
formValidator =
Validate.all
[ ifBlank .title ( Title, "title can't be blank." )
, ifBlank .body ( Body, "body can't be blank." )
]
{-| Trim the form and validate its fields. If there are problems, report them!
-}
validate : Form -> Result (List Problem) TrimmedForm
validate form =
let
trimmedForm =
trimFields form
in
case List.concatMap (validateField trimmedForm) fieldsToValidate of
[] ->
Ok trimmedForm
problems ->
Err problems
validateField : TrimmedForm -> ValidatedField -> List Problem
validateField (Trimmed form) field =
List.map (InvalidEntry field) <|
case field of
Title ->
if String.isEmpty form.title then
[ "title can't be blank." ]
else
[]
Body ->
if String.isEmpty form.body then
[ "body can't be blank." ]
else
[]
{-| Don't trim while the user is typing! That would be super annoying.
Instead, trim only on submit.
-}
trimFields : Form -> TrimmedForm
trimFields form =
Trimmed
{ title = String.trim form.title
, body = String.trim form.body
, description = String.trim form.description
, tags = String.trim form.tags
}
-- HTTP
create : Valid Form -> Cred -> Http.Request (Article Full)
create validForm cred =
create : TrimmedForm -> Cred -> Http.Request (Article Full)
create (Trimmed form) cred =
let
form =
fromValid validForm
expect =
Article.fullDecoder (Just cred)
|> Decode.field "article"
@@ -459,12 +554,9 @@ tagsFromString str =
|> List.filter (not << String.isEmpty)
edit : Slug -> Valid Form -> Cred -> Http.Request (Article Full)
edit articleSlug validForm cred =
edit : Slug -> TrimmedForm -> Cred -> Http.Request (Article Full)
edit articleSlug (Trimmed form) cred =
let
form =
fromValid validForm
expect =
Article.fullDecoder (Just cred)
|> Decode.field "article"
@@ -510,6 +602,9 @@ getSlug status =
Loading slug ->
Just slug
LoadingSlowly slug ->
Just slug
LoadingFailed slug ->
Just slug