Bump part3 and part4

This commit is contained in:
Richard Feldman
2018-08-13 06:15:44 -04:00
parent 5d27ab52a7
commit 3002567065
58 changed files with 260 additions and 260 deletions

View File

@@ -1,16 +0,0 @@
# Part 3
To build everything, `cd` into this `part3/` directory and run:
```shell
elm make src/Main.elm --output=../server/public/elm.js
```
Then open [http://localhost:3000](http://localhost:3000) in your browser.
## Exercise
Resolve the TODOs in these files:
* `src/Page/Home.elm`
* `src/Page/Settings.elm`
* `src/Page/Article.elm`

View File

@@ -10,4 +10,7 @@ Then open [http://localhost:3000](http://localhost:3000) in your browser.
## Exercise ## Exercise
Open `src/Article/Feed.elm` in your editor and resolve the TODO there. Resolve the TODOs in these files:
* `src/Page/Home.elm`
* `src/Page/Settings.elm`
* `src/Page/Article.elm`

View File

@@ -110,50 +110,11 @@ viewArticles timeZone (Model { articles, sources, session }) =
FeedSources.selected sources FeedSources.selected sources
pagination = pagination =
viewPaginatedList articles (limit feedSource) PaginatedList.view ClickedFeedPage articles (limit feedSource)
in in
List.append articlesHtml [ pagination ] List.append articlesHtml [ pagination ]
{-| 👉 TODO Move this logic into PaginatedList.view and make it reusable,
so we can use it on other pages too!
💡 HINT: Make `PaginatedList.view` return `Html msg` instead of `Html Msg`. (The function will need to accept an extra argument for this to work.)
-}
viewPaginatedList : PaginatedList a -> Int -> Html Msg
viewPaginatedList paginatedList resultsPerPage =
let
totalPages =
ceiling (toFloat (PaginatedList.total paginatedList) / toFloat resultsPerPage)
activePage =
PaginatedList.page paginatedList
viewPageLink currentPage =
pageLink currentPage (currentPage == activePage)
in
if totalPages > 1 then
List.range 1 totalPages
|> List.map viewPageLink
|> ul [ class "pagination" ]
else
Html.text ""
pageLink : Int -> Bool -> Html Msg
pageLink targetPage isActive =
li [ classList [ ( "page-item", True ), ( "active", isActive ) ] ]
[ a
[ class "page-link"
, onClick (ClickedFeedPage targetPage)
-- The RealWorld CSS requires an href to work properly.
, href ""
]
[ text (String.fromInt targetPage) ]
]
viewPreview : Maybe Cred -> Time.Zone -> Article Preview -> Html Msg viewPreview : Maybe Cred -> Time.Zone -> Article Preview -> Html Msg
viewPreview maybeCred timeZone article = viewPreview maybeCred timeZone article =
let let

View File

@@ -84,19 +84,8 @@ view model =
[ viewBanner [ viewBanner
, div [ class "container page" ] , div [ class "container page" ]
[ div [ class "row" ] [ div [ class "row" ]
[ div [ class "col-md-9" ] <| [ div [ class "col-md-9" ]
case model.feed of (viewFeed model)
Loaded feed ->
viewFeed model.timeZone feed
Loading ->
[]
LoadingSlowly ->
[ Loading.icon ]
Failed ->
[ Loading.error "feed" ]
, div [ class "col-md-3" ] <| , div [ class "col-md-3" ] <|
case model.tags of case model.tags of
Loaded tags -> Loaded tags ->
@@ -130,11 +119,25 @@ viewBanner =
] ]
viewFeed : Time.Zone -> Feed.Model -> List (Html Msg) {-| 👉 TODO refactor this to accept narrower types than the entire Model.
viewFeed timeZone feed = 💡 HINT: It may end up with multiple arguments!
div [ class "feed-toggle" ] -}
[ Feed.viewFeedSources feed |> Html.map GotFeedMsg ] viewFeed : Model -> List (Html Msg)
:: (Feed.viewArticles timeZone feed |> List.map (Html.map GotFeedMsg)) viewFeed model =
case model.feed of
Loaded feed ->
div [ class "feed-toggle" ]
[ Feed.viewFeedSources feed |> Html.map GotFeedMsg ]
:: (Feed.viewArticles model.timeZone feed |> List.map (Html.map GotFeedMsg))
Loading ->
[]
LoadingSlowly ->
[ Loading.icon ]
Failed ->
[ Loading.error "feed" ]
viewTags : List Tag -> Html Msg viewTags : List Tag -> Html Msg

View File

@@ -98,6 +98,10 @@ type ValidForm
view : Model -> { title : String, content : Html Msg } view : Model -> { title : String, content : Html Msg }
view model = view model =
let
form =
viewForm model
in
{ title = "Settings" { title = "Settings"
, content = , content =
case Session.cred model.session of case Session.cred model.session of
@@ -109,7 +113,7 @@ view model =
[ h1 [ class "text-xs-center" ] [ text "Your Settings" ] [ h1 [ class "text-xs-center" ] [ text "Your Settings" ]
, ul [ class "error-messages" ] , ul [ class "error-messages" ]
(List.map viewProblem model.problems) (List.map viewProblem model.problems)
, viewForm cred model.form , form
] ]
] ]
] ]
@@ -120,62 +124,74 @@ view model =
} }
viewForm : Cred -> Form -> Html Msg {-| 👉 TODO refactor this to accept narrower types than the entire Model.
viewForm cred form = 💡 HINT: It may end up with multiple arguments!
Html.form [ onSubmit (SubmittedForm cred) ] -}
[ fieldset [] viewForm : Model -> Html Msg
[ fieldset [ class "form-group" ] viewForm model =
[ input let
[ class "form-control" form =
, placeholder "URL of profile picture" model.form
, value form.avatar in
, onInput EnteredAvatar case Session.cred model.session of
Nothing ->
text ""
Just cred ->
Html.form [ onSubmit (SubmittedForm cred) ]
[ fieldset []
[ fieldset [ class "form-group" ]
[ input
[ class "form-control"
, placeholder "URL of profile picture"
, value form.avatar
, onInput EnteredAvatar
]
[]
]
, fieldset [ class "form-group" ]
[ input
[ class "form-control form-control-lg"
, placeholder "Username"
, value form.username
, onInput EnteredUsername
]
[]
]
, fieldset [ class "form-group" ]
[ textarea
[ class "form-control form-control-lg"
, placeholder "Short bio about you"
, attribute "rows" "8"
, value form.bio
, onInput EnteredBio
]
[]
]
, fieldset [ class "form-group" ]
[ input
[ class "form-control form-control-lg"
, placeholder "Email"
, value form.email
, onInput EnteredEmail
]
[]
]
, fieldset [ class "form-group" ]
[ input
[ class "form-control form-control-lg"
, type_ "password"
, placeholder "Password"
, value form.password
, onInput EnteredPassword
]
[]
]
, button
[ class "btn btn-lg btn-primary pull-xs-right" ]
[ text "Update Settings" ]
] ]
[]
] ]
, fieldset [ class "form-group" ]
[ input
[ class "form-control form-control-lg"
, placeholder "Username"
, value form.username
, onInput EnteredUsername
]
[]
]
, fieldset [ class "form-group" ]
[ textarea
[ class "form-control form-control-lg"
, placeholder "Short bio about you"
, attribute "rows" "8"
, value form.bio
, onInput EnteredBio
]
[]
]
, fieldset [ class "form-group" ]
[ input
[ class "form-control form-control-lg"
, placeholder "Email"
, value form.email
, onInput EnteredEmail
]
[]
]
, fieldset [ class "form-group" ]
[ input
[ class "form-control form-control-lg"
, type_ "password"
, placeholder "Password"
, value form.password
, onInput EnteredPassword
]
[]
]
, button
[ class "btn btn-lg btn-primary pull-xs-right" ]
[ text "Update Settings" ]
]
]
viewProblem : Problem -> Html msg viewProblem : Problem -> Html msg

View File

@@ -1,4 +1,4 @@
module PaginatedList exposing (PaginatedList, fromList, map, mapPage, page, total, values) module PaginatedList exposing (PaginatedList, fromList, map, mapPage, page, total, values, view)
import Html exposing (Html, a, li, text, ul) import Html exposing (Html, a, li, text, ul)
import Html.Attributes exposing (class, classList, href) import Html.Attributes exposing (class, classList, href)
@@ -61,3 +61,38 @@ mapPage transform (PaginatedList info) =
-- VIEW -- VIEW
view : (Int -> msg) -> PaginatedList a -> Int -> Html msg
view toMsg list resultsPerPage =
let
totalPages =
ceiling (toFloat (total list) / toFloat resultsPerPage)
activePage =
page list
viewPageLink currentPage =
pageLink toMsg currentPage (currentPage == activePage)
in
if totalPages > 1 then
List.range 1 totalPages
|> List.map viewPageLink
|> ul [ class "pagination" ]
else
Html.text ""
pageLink : (Int -> msg) -> Int -> Bool -> Html msg
pageLink toMsg targetPage isActive =
li [ classList [ ( "page-item", True ), ( "active", isActive ) ] ]
[ a
[ class "page-link"
, onClick (toMsg targetPage)
-- The RealWorld CSS requires an href to work properly.
, href ""
]
[ text (String.fromInt targetPage) ]
]

13
advanced/part5/README.md Normal file
View File

@@ -0,0 +1,13 @@
# Part 5
To build everything, `cd` into this `part5/` directory and run:
```shell
elm make src/Main.elm --output=../server/public/elm.js
```
Then open [http://localhost:3000](http://localhost:3000) in your browser.
## Exercise
Open `src/Article/Feed.elm` in your editor and resolve the TODO there.

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 736 B

After

Width:  |  Height:  |  Size: 736 B

View File

Before

Width:  |  Height:  |  Size: 985 B

After

Width:  |  Height:  |  Size: 985 B

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

View File

@@ -110,11 +110,50 @@ viewArticles timeZone (Model { articles, sources, session }) =
FeedSources.selected sources FeedSources.selected sources
pagination = pagination =
PaginatedList.view ClickedFeedPage articles (limit feedSource) viewPaginatedList articles (limit feedSource)
in in
List.append articlesHtml [ pagination ] List.append articlesHtml [ pagination ]
{-| 👉 TODO Move this logic into PaginatedList.view and make it reusable,
so we can use it on other pages too!
💡 HINT: Make `PaginatedList.view` return `Html msg` instead of `Html Msg`. (The function will need to accept an extra argument for this to work.)
-}
viewPaginatedList : PaginatedList a -> Int -> Html Msg
viewPaginatedList paginatedList resultsPerPage =
let
totalPages =
ceiling (toFloat (PaginatedList.total paginatedList) / toFloat resultsPerPage)
activePage =
PaginatedList.page paginatedList
viewPageLink currentPage =
pageLink currentPage (currentPage == activePage)
in
if totalPages > 1 then
List.range 1 totalPages
|> List.map viewPageLink
|> ul [ class "pagination" ]
else
Html.text ""
pageLink : Int -> Bool -> Html Msg
pageLink targetPage isActive =
li [ classList [ ( "page-item", True ), ( "active", isActive ) ] ]
[ a
[ class "page-link"
, onClick (ClickedFeedPage targetPage)
-- The RealWorld CSS requires an href to work properly.
, href ""
]
[ text (String.fromInt targetPage) ]
]
viewPreview : Maybe Cred -> Time.Zone -> Article Preview -> Html Msg viewPreview : Maybe Cred -> Time.Zone -> Article Preview -> Html Msg
viewPreview maybeCred timeZone article = viewPreview maybeCred timeZone article =
let let

View File

@@ -84,8 +84,19 @@ view model =
[ viewBanner [ viewBanner
, div [ class "container page" ] , div [ class "container page" ]
[ div [ class "row" ] [ div [ class "row" ]
[ div [ class "col-md-9" ] [ div [ class "col-md-9" ] <|
(viewFeed model) case model.feed of
Loaded feed ->
viewFeed model.timeZone feed
Loading ->
[]
LoadingSlowly ->
[ Loading.icon ]
Failed ->
[ Loading.error "feed" ]
, div [ class "col-md-3" ] <| , div [ class "col-md-3" ] <|
case model.tags of case model.tags of
Loaded tags -> Loaded tags ->
@@ -119,25 +130,11 @@ viewBanner =
] ]
{-| 👉 TODO refactor this to accept narrower types than the entire Model. viewFeed : Time.Zone -> Feed.Model -> List (Html Msg)
💡 HINT: It may end up with multiple arguments! viewFeed timeZone feed =
-} div [ class "feed-toggle" ]
viewFeed : Model -> List (Html Msg) [ Feed.viewFeedSources feed |> Html.map GotFeedMsg ]
viewFeed model = :: (Feed.viewArticles timeZone feed |> List.map (Html.map GotFeedMsg))
case model.feed of
Loaded feed ->
div [ class "feed-toggle" ]
[ Feed.viewFeedSources feed |> Html.map GotFeedMsg ]
:: (Feed.viewArticles model.timeZone feed |> List.map (Html.map GotFeedMsg))
Loading ->
[]
LoadingSlowly ->
[ Loading.icon ]
Failed ->
[ Loading.error "feed" ]
viewTags : List Tag -> Html Msg viewTags : List Tag -> Html Msg

View File

@@ -98,10 +98,6 @@ type ValidForm
view : Model -> { title : String, content : Html Msg } view : Model -> { title : String, content : Html Msg }
view model = view model =
let
form =
viewForm model
in
{ title = "Settings" { title = "Settings"
, content = , content =
case Session.cred model.session of case Session.cred model.session of
@@ -113,7 +109,7 @@ view model =
[ h1 [ class "text-xs-center" ] [ text "Your Settings" ] [ h1 [ class "text-xs-center" ] [ text "Your Settings" ]
, ul [ class "error-messages" ] , ul [ class "error-messages" ]
(List.map viewProblem model.problems) (List.map viewProblem model.problems)
, form , viewForm cred model.form
] ]
] ]
] ]
@@ -124,74 +120,62 @@ view model =
} }
{-| 👉 TODO refactor this to accept narrower types than the entire Model. viewForm : Cred -> Form -> Html Msg
💡 HINT: It may end up with multiple arguments! viewForm cred form =
-} Html.form [ onSubmit (SubmittedForm cred) ]
viewForm : Model -> Html Msg [ fieldset []
viewForm model = [ fieldset [ class "form-group" ]
let [ input
form = [ class "form-control"
model.form , placeholder "URL of profile picture"
in , value form.avatar
case Session.cred model.session of , onInput EnteredAvatar
Nothing ->
text ""
Just cred ->
Html.form [ onSubmit (SubmittedForm cred) ]
[ fieldset []
[ fieldset [ class "form-group" ]
[ input
[ class "form-control"
, placeholder "URL of profile picture"
, value form.avatar
, onInput EnteredAvatar
]
[]
]
, fieldset [ class "form-group" ]
[ input
[ class "form-control form-control-lg"
, placeholder "Username"
, value form.username
, onInput EnteredUsername
]
[]
]
, fieldset [ class "form-group" ]
[ textarea
[ class "form-control form-control-lg"
, placeholder "Short bio about you"
, attribute "rows" "8"
, value form.bio
, onInput EnteredBio
]
[]
]
, fieldset [ class "form-group" ]
[ input
[ class "form-control form-control-lg"
, placeholder "Email"
, value form.email
, onInput EnteredEmail
]
[]
]
, fieldset [ class "form-group" ]
[ input
[ class "form-control form-control-lg"
, type_ "password"
, placeholder "Password"
, value form.password
, onInput EnteredPassword
]
[]
]
, button
[ class "btn btn-lg btn-primary pull-xs-right" ]
[ text "Update Settings" ]
] ]
[]
] ]
, fieldset [ class "form-group" ]
[ input
[ class "form-control form-control-lg"
, placeholder "Username"
, value form.username
, onInput EnteredUsername
]
[]
]
, fieldset [ class "form-group" ]
[ textarea
[ class "form-control form-control-lg"
, placeholder "Short bio about you"
, attribute "rows" "8"
, value form.bio
, onInput EnteredBio
]
[]
]
, fieldset [ class "form-group" ]
[ input
[ class "form-control form-control-lg"
, placeholder "Email"
, value form.email
, onInput EnteredEmail
]
[]
]
, fieldset [ class "form-group" ]
[ input
[ class "form-control form-control-lg"
, type_ "password"
, placeholder "Password"
, value form.password
, onInput EnteredPassword
]
[]
]
, button
[ class "btn btn-lg btn-primary pull-xs-right" ]
[ text "Update Settings" ]
]
]
viewProblem : Problem -> Html msg viewProblem : Problem -> Html msg

View File

@@ -1,4 +1,4 @@
module PaginatedList exposing (PaginatedList, fromList, map, mapPage, page, total, values, view) module PaginatedList exposing (PaginatedList, fromList, map, mapPage, page, total, values)
import Html exposing (Html, a, li, text, ul) import Html exposing (Html, a, li, text, ul)
import Html.Attributes exposing (class, classList, href) import Html.Attributes exposing (class, classList, href)
@@ -61,38 +61,3 @@ mapPage transform (PaginatedList info) =
-- VIEW -- VIEW
view : (Int -> msg) -> PaginatedList a -> Int -> Html msg
view toMsg list resultsPerPage =
let
totalPages =
ceiling (toFloat (total list) / toFloat resultsPerPage)
activePage =
page list
viewPageLink currentPage =
pageLink toMsg currentPage (currentPage == activePage)
in
if totalPages > 1 then
List.range 1 totalPages
|> List.map viewPageLink
|> ul [ class "pagination" ]
else
Html.text ""
pageLink : (Int -> msg) -> Int -> Bool -> Html msg
pageLink toMsg targetPage isActive =
li [ classList [ ( "page-item", True ), ( "active", isActive ) ] ]
[ a
[ class "page-link"
, onClick (toMsg targetPage)
-- The RealWorld CSS requires an href to work properly.
, href ""
]
[ text (String.fromInt targetPage) ]
]