Compare commits

..

37 Commits

Author SHA1 Message Date
dependabot[bot]
44f9de97aa Bump path-parse from 1.0.5 to 1.0.7 in /intro/server
Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.5 to 1.0.7.
- [Release notes](https://github.com/jbgutierrez/path-parse/releases)
- [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7)

---
updated-dependencies:
- dependency-name: path-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-11 15:39:33 +00:00
Richard Feldman
dfe4cc02d9 Clarify that the slides are Creative Commons too 2019-11-03 04:39:09 +01:00
Richard Feldman
331813a90b Fix intro/part4/elm.json 2019-11-01 11:58:04 +01:00
Richard Feldman
f6d6dc5ffc Update to 0.19.1 2019-11-01 08:12:13 +01:00
Richard Feldman
c1401c637f Merge pull request #6 from noahzgordon/patch-1
typo in README :)
2019-06-29 09:12:05 +02:00
Richard Feldman
d5d25f90f6 Fix HINT for filter 2019-06-29 09:09:15 +02:00
Richard Feldman
98901d3743 Fix elm-test version 2019-06-29 09:05:47 +02:00
Marc Grabanski
c9287a157c Update READMEs with links to courses 2018-11-27 14:23:28 -06:00
Richard Feldman
b2089add0d Update README.md 2018-11-27 10:11:50 -05:00
Richard Feldman
04ed226507 Link to 0.19 slides in TEACHING.md 2018-11-22 15:25:19 +01:00
Richard Feldman
da827954e5 Remind folks to install elm/browser 2018-11-22 12:19:44 +01:00
Noah Zachary Gordon
965184ca09 typo in README :) 2018-10-25 14:17:29 -04:00
Richard Feldman
1bcb581666 Merge pull request #3 from laniehei/master
Updates Getting Started Link to Correct Path
2018-10-20 16:50:13 -04:00
Lanie Hei
b5697d0765 Removes instructions to add elm-binary/fixes shell command
edits as per @rtfeldman's comments
2018-10-20 15:26:55 -05:00
Richard Feldman
e5124265f8 Merge pull request #2 from mkohler/fix_view_article_todo
Fix TODO that should refer to `viewArticle`.
2018-10-12 17:19:55 -04:00
Richard Feldman
81b51ab973 Merge pull request #1 from falonofthetower/update-part5-packages
Updates the dependencies in part5-9 to 0.19
2018-10-12 17:19:38 -04:00
Lanie Hei
3c6e9aeb07 Adds instructions to download elm binary
`elm make Main.elm --output elm.js` will not work without first downloading the binary
2018-10-04 17:45:48 -05:00
Lanie Hei
6142e8d685 Updates install of elm-format
ETARGET error when installing elm-format@rc, and no documentation discussing @rc in elm-format repo. Updated to reflect elm-format [installation instructions](https://github.com/avh4/elm-format#installation-)
2018-10-04 14:22:54 -05:00
Lanie Hei
b2b9cc5b76 Updates Getting Started Link to Correct Path 2018-10-04 11:26:40 -05:00
Mark Kohler
ab31fd08c0 Fix TODO that should refer to viewArticle. 2018-09-29 17:01:52 -07:00
Richard Feldman
3f9b3381c2 Update for public 0.19 release 2018-09-13 14:46:41 -04:00
Peter Karth
af7ed944f1 Part's 6 through 9 also require updating 2018-08-26 17:24:39 -04:00
Peter Karth
4035369af2 Updates the dependencies in part5 to 0.19
In attempting the `elm make` call in part received this error:

-- CORRUPT CACHE ---------------------------------------------------------------

I ran into an unknown package while exploring dependencies:

    NoRedInk/json-decode-pipeline

This suggests that your /Users/{UserName}/.elm directory has been corrupted.
Maybe some program is messing with it? It is just cached files, so you can
delete it and see if that fixes the issue.

The names of two of the modules appear to have changed for 0.19. This is
currently updated in the solutions branch but not the exercises branch.

Feel free to delete this if it doesn't fit into your workflow workflow.
It seemed nicest to open this as PR in case anyone else ran into the same
issue and is looking for answers.
2018-08-26 12:45:00 -04:00
Richard Feldman
850d4e6ff0 Update Article.elm 2018-08-16 13:00:13 -05:00
Richard Feldman
5b9cdfb3b3 Update README.md 2018-08-16 07:41:38 -05:00
Richard Feldman
2bd5aa8141 Update README.md 2018-08-16 07:39:48 -05:00
Richard Feldman
0a9fadcf87 Change comment style 2018-08-16 03:41:03 -05:00
Richard Feldman
4fcaad042a Fix typo 2018-08-16 03:40:54 -05:00
Richard Feldman
bb38d331b4 Fix intro/part9 2018-08-15 17:37:42 -05:00
Richard Feldman
0efe239ae4 Update Register.elm 2018-08-15 17:36:47 -05:00
Richard Feldman
69f3c478fb Update README.md 2018-08-15 17:36:01 -05:00
Richard Feldman
0288b3fcb7 Update README.md 2018-08-15 17:32:47 -05:00
Richard Feldman
33713b59b8 Update README.md 2018-08-15 11:31:57 -05:00
Richard Feldman
6c04669ec5 Update README.md 2018-08-15 11:31:30 -05:00
Richard Feldman
50aa6ee0d6 Update Main.elm 2018-08-15 10:36:42 -05:00
Richard Feldman
e7b7d0bdc3 Update README.md 2018-08-15 09:33:08 -05:00
Richard Feldman
9288011f22 Fix intro/part8 README 2018-08-15 08:21:30 -05:00
54 changed files with 3626 additions and 3403 deletions

View File

@@ -1,4 +1,4 @@
<i>This workshop is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>. The `server/` directories use [`moleculer-node-realworld-example`](https://github.com/gothinkster/moleculer-node-realworld-example-app), which has its own license. The JavaScript interop example uses [`localForage`](https://github.com/localForage/localForage), which is (c) 2013-2017 Mozilla, under the Apache License 2.0. The rest of the code is a variation on [`elm-spa-example`](https://github.com/rtfeldman/elm-spa-example/), an [MIT-licensed](https://github.com/rtfeldman/elm-spa-example/blob/master/LICENSE) implementation of the [`realworld`](https://github.com/gothinkster/realworld) front-end. Many thanks to the authors of these projects!</i>
<i>This workshop, as well as the slides that go with it (linked below), are all licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>. The `server/` directories use [`moleculer-node-realworld-example`](https://github.com/gothinkster/moleculer-node-realworld-example-app), which has its own license. The JavaScript interop example uses [`localForage`](https://github.com/localForage/localForage), which is (c) 2013-2017 Mozilla, under the Apache License 2.0. The rest of the code is a variation on [`elm-spa-example`](https://github.com/rtfeldman/elm-spa-example/), an [MIT-licensed](https://github.com/rtfeldman/elm-spa-example/blob/master/LICENSE) implementation of the [`realworld`](https://github.com/gothinkster/realworld) front-end. Many thanks to the authors of these projects!</i>
Getting Started
===============
@@ -14,7 +14,7 @@ Getting Started
> **Note:** Make sure not to run this command with `sudo`! If it gives you an `EACCESS` error, apply [**this fix**](https://docs.npmjs.com/getting-started/fixing-npm-permissions#option-two-change-npms-default-directory) and then re-run the command (still without `sudo`).
```shell
npm install -g elm elm-test@elm0.19.0 elm-format
npm install -g elm elm-test elm-format
```
5. Clone this repository
@@ -29,9 +29,9 @@ cd elm-0.19-workshop
6. Continue with either the [`intro`](https://github.com/rtfeldman/elm-0.19-workshop/blob/master/intro/README.md) or [`advanced`](https://github.com/rtfeldman/elm-0.19-workshop/blob/master/advanced/README.md) instructions, depending on which workshop you're doing!
Video Course of this Workshop
=======================
=============================
I recorded full-length videos for [Frontend Masters](https://frontendmasters.com/), in which I teach both of these workshops start to finish:
* [Introduction to Elm](https://frontendmasters.com/courses/intro-elm/) video course
* [Advanced Elm](https://frontendmasters.com/courses/advanced-elm/) video course
* [Introduction to Elm](https://frontendmasters.com/courses/intro-elm/) video course ([slides](https://docs.google.com/presentation/d/1LM_W2BRs_ItT-SPDe70C10cbwhGNHGQlJ1fVnAdnRIY/edit?usp=sharing)
* [Advanced Elm](https://frontendmasters.com/courses/advanced-elm/) video course ([slides](https://docs.google.com/presentation/d/1aFZBXs9kzlZww2JN6iDmrYiQaxKlCAz6a5zpt882GHk/edit?usp=sharing))

View File

@@ -15,7 +15,7 @@ some info about it that may be helpful in teaching this workshop.
## Slides
The [slides](https://docs.google.com/presentation/d/1sNx5k3_fHwJcgm9QEY1LsMH_TyF5SnnOSDKb8HvFsEU/edit?usp=sharing)
The [slides](https://docs.google.com/presentation/d/1LM_W2BRs_ItT-SPDe70C10cbwhGNHGQlJ1fVnAdnRIY/edit?usp=sharing)
I use with the workshop include speaker notes. If youve seen me give this
workshop before, you may notice that Im not following these notes very closely.

View File

@@ -43,7 +43,12 @@ If things arent working, the instructor will be happy to help!
## Links
* [Elm in Action](https://www.manning.com/books/elm-in-action?a_aid=elm_in_action&a_bid=b15edc5c), a book by [Richard Feldman](https://twitter.com/rtfeldman), creator of this workshop
* [Official Elm Guide](https://guide.elm-lang.org/) by [Evan Czaplicki](https://twitter.com/czaplic), creator of Elm
* [Elm Slack](http://elmlang.herokuapp.com/) - amazingly helpful chat community. People in [the `#beginners` channel](https://elmlang.slack.com/messages/C192T0Q1E/) are happy to answer questions!
* [Elm Discourse](https://discourse.elm-lang.org/) - for longer-form discussions.
* [The solutions to these exercises](https://github.com/rtfeldman/elm-0.19-workshop/tree/solutions/advanced)
* [Slides for the Frontend Masters workshop that goes with this repo](https://docs.google.com/presentation/d/1aFZBXs9kzlZww2JN6iDmrYiQaxKlCAz6a5zpt882GHk/edit?usp=sharing)
* [Advanced Elm Video Course](https://frontendmasters.com/courses/advanced-elm/) that goes with this repo
* [The Life of a File](https://www.youtube.com/watch?v=XpDsk374LDE) - Evan Czaplicki
* [The Importance of Ports](https://www.youtube.com/watch?v=P3pL85n9_5s) - Murphy Randle
* [Working with Maybe](https://www.youtube.com/watch?v=43eM4kNbb6c) - Joël Quenneville
* [Making Impossible States Impossible](https://www.youtube.com/watch?v=IcgmSRJHu_8) - Richard Feldman
* [Scaling Elm Apps](https://www.youtube.com/watch?v=DoA4Txr4GUs) - Richard Feldman
* [Make Data Structures](https://www.youtube.com/watch?v=x1FU3e0sT1I) - Richard Feldman

View File

@@ -3,7 +3,7 @@
"source-directories": [
"src"
],
"elm-version": "0.19.0",
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"NoRedInk/elm-json-decode-pipeline": "1.0.0",
@@ -32,4 +32,4 @@
"elm/random": "1.0.0"
}
}
}
}

View File

@@ -11,17 +11,18 @@ import Username exposing (Username)
-- TYPES
type Cred
= Cred Username String
type alias Cred =
{- 👉 TODO: Make Cred an opaque type, then fix the resulting compiler errors.
Afterwards, it should no longer be possible for any other module to access
this `token` value directly!
-- INFO
username : Cred -> Username
username (Cred uname _) =
uname
💡 HINT: Other modules still depend on being able to access the
`username` value. Expand this module's API to expose a new way for them
to access the `username` without also giving them access to `token`.
-}
{ username : Username
, token : String
}
@@ -40,14 +41,14 @@ decoder =
encodeToken : Cred -> Value
encodeToken (Cred _ token) =
Encode.string token
encodeToken cred =
Encode.string cred.token
addHeader : Cred -> RequestBuilder a -> RequestBuilder a
addHeader (Cred _ token) builder =
addHeader cred builder =
builder
|> withHeader "authorization" ("Token " ++ token)
|> withHeader "authorization" ("Token " ++ cred.token)
addHeaderIfAvailable : Maybe Cred -> RequestBuilder a -> RequestBuilder a

View File

@@ -3,7 +3,7 @@
"source-directories": [
"src"
],
"elm-version": "0.19.0",
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"NoRedInk/elm-json-decode-pipeline": "1.0.0",
@@ -32,4 +32,4 @@
"elm/random": "1.0.0"
}
}
}
}

View File

@@ -159,8 +159,8 @@ slug (Article internals _) =
body : Article Full -> Body
body (Article _ (Full bod)) =
bod
body _ =
"👉 TODO make this return the article's body"
@@ -181,8 +181,8 @@ mapAuthor transform (Article info extras) =
fromPreview : Body -> Article Preview -> Article Full
fromPreview bod (Article info _) =
Article info (Full bod)
fromPreview _ _ =
"👉 TODO convert from an Article Preview to an Article Full"
@@ -200,7 +200,7 @@ fullDecoder : Maybe Cred -> Decoder (Article Full)
fullDecoder maybeCred =
Decode.succeed Article
|> custom (internalsDecoder maybeCred)
|> required "body" (Decode.map Full Body.decoder)
|> required "body" "👉 TODO use `Body.decoder` (which is a `Decoder Body`) to decode the body into this Article Full"
internalsDecoder : Maybe Cred -> Decoder Internals

View File

@@ -3,7 +3,7 @@
"source-directories": [
"src"
],
"elm-version": "0.19.0",
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"NoRedInk/elm-json-decode-pipeline": "1.0.0",
@@ -32,4 +32,4 @@
"elm/random": "1.0.0"
}
}
}
}

View File

@@ -71,16 +71,21 @@ init session slug =
, article = Loading
}
, Cmd.batch
{- 👉 TODO: Oops! These are all `Task` values, not `Cmd` values!
Use `|> Task.attempt` and `|> Task.perform` to make this compile.
Relevant docs:
https://package.elm-lang.org/packages/elm/core/latest/Task#attempt
https://package.elm-lang.org/packages/elm/core/latest/Task#perform
-}
[ Article.fetch maybeCred slug
|> Http.toTask
|> Task.attempt CompletedLoadArticle
, Comment.list maybeCred slug
|> Http.toTask
|> Task.attempt CompletedLoadComments
, Time.here
|> Task.perform GotTimeZone
, Loading.slowThreshold
|> Task.perform (\_ -> PassedSlowLoadThreshold)
]
)

View File

@@ -3,7 +3,7 @@
"source-directories": [
"src"
],
"elm-version": "0.19.0",
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"NoRedInk/elm-json-decode-pipeline": "1.0.0",
@@ -32,4 +32,4 @@
"elm/random": "1.0.0"
}
}
}
}

View File

@@ -103,7 +103,7 @@ view model =
Loaded feed ->
[ div [ class "feed-toggle" ] <|
List.concat
[ [ viewTabs model.session model.feedTab ]
[ [ viewTabs model ]
, Feed.viewArticles model.timeZone feed
|> List.map (Html.map GotFeedMsg)
, [ Feed.viewPagination ClickedFeedPage feed ]
@@ -155,16 +155,21 @@ viewBanner =
-- TABS
viewTabs : Session -> FeedTab -> Html Msg
viewTabs session feedTab =
case feedTab of
{-| 👉 TODO: refactor this to accept narrower types than the entire Model.
💡 HINT: It may end up with multiple arguments!
-}
viewTabs : Model -> Html Msg
viewTabs model =
case model.feedTab of
YourFeed cred ->
Feed.viewTabs [] (yourFeed cred) [ globalFeed ]
GlobalFeed ->
let
otherTabs =
case Session.cred session of
case Session.cred model.session of
Just cred ->
[ yourFeed cred ]
@@ -176,7 +181,7 @@ viewTabs session feedTab =
TagFeed tag ->
let
otherTabs =
case Session.cred session of
case Session.cred model.session of
Just cred ->
[ yourFeed cred, globalFeed ]

View File

@@ -61,21 +61,24 @@ init session username =
let
maybeCred =
Session.cred session
model =
{ session = session
, timeZone = Time.utc
, errors = []
, feedTab = defaultFeedTab
, feedPage = 1
, author = Loading username
, feed = Loading username
}
in
( { session = session
, timeZone = Time.utc
, errors = []
, feedTab = defaultFeedTab
, feedPage = 1
, author = Loading username
, feed = Loading username
}
( model
, Cmd.batch
[ Author.fetch username maybeCred
|> Http.toTask
|> Task.mapError (Tuple.pair username)
|> Task.attempt CompletedAuthorLoad
, fetchFeed session username defaultFeedTab 1
, fetchFeed model defaultFeedTab 1
, Task.perform GotTimeZone Time.here
, Task.perform (\_ -> PassedSlowLoadThreshold) Loading.slowThreshold
]
@@ -107,11 +110,19 @@ defaultFeedTab =
-- HTTP
fetchFeed : Session -> Username -> FeedTab -> Int -> Cmd Msg
fetchFeed session username feedTabs page =
{-| 👉 TODO: refactor this to accept narrower types than the entire Model.
💡 HINT: It may end up with multiple arguments!
-}
fetchFeed : Model -> FeedTab -> Int -> Cmd Msg
fetchFeed model feedTabs page =
let
username =
currentUsername model
maybeCred =
Session.cred session
Session.cred model.session
( extraParamName, extraParamVal ) =
case feedTabs of
@@ -127,7 +138,7 @@ fetchFeed session username feedTabs page =
|> HttpBuilder.withQueryParam extraParamName extraParamVal
|> Cred.addHeaderIfAvailable maybeCred
|> PaginatedList.fromRequestBuilder articlesPerPage page
|> Task.map (Feed.init session)
|> Task.map (Feed.init model.session)
|> Task.mapError (Tuple.pair username)
|> Task.attempt CompletedFeedLoad
@@ -340,12 +351,12 @@ update msg model =
ClickedTab tab ->
( { model | feedTab = tab }
, fetchFeed model.session (currentUsername model) tab 1
, fetchFeed model tab 1
)
ClickedFeedPage page ->
( { model | feedPage = page }
, fetchFeed model.session (currentUsername model) model.feedTab page
, fetchFeed model model.feedTab page
)
CompletedFollowChange (Ok newAuthor) ->

View File

@@ -100,7 +100,7 @@ view : Model -> { title : String, content : Html Msg }
view model =
let
form =
viewForm (Session.cred model.session) model.form
viewForm model
in
{ title = "Settings"
, content =
@@ -124,9 +124,16 @@ view model =
}
viewForm : Maybe Cred -> Form -> Html Msg
viewForm maybeCred form =
case maybeCred of
{-| 👉 TODO refactor this to accept narrower types than the entire Model.
💡 HINT: It may end up with multiple arguments!
-}
viewForm : Model -> Html Msg
viewForm model =
let
form =
model.form
in
case Session.cred model.session of
Nothing ->
text ""

View File

@@ -1,15 +0,0 @@
{
"version": "1.0.0",
"summary": "helpful summary of your project, less than 80 characters",
"repository": "https://github.com/user/project.git",
"license": "BSD3",
"source-directories": [
"."
],
"exposed-modules": [],
"dependencies": {
"elm-lang/core": "5.1.1 <= v < 6.0.0",
"elm-lang/html": "2.0.0 <= v < 3.0.0"
},
"elm-version": "0.18.0 <= v < 0.19.0"
}

View File

@@ -3,7 +3,7 @@
"source-directories": [
"src"
],
"elm-version": "0.19.0",
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"NoRedInk/elm-json-decode-pipeline": "1.0.0",
@@ -32,4 +32,4 @@
"elm/random": "1.0.0"
}
}
}
}

View File

@@ -109,7 +109,7 @@ view model =
]
, Feed.viewArticles model.timeZone feed
|> List.map (Html.map GotFeedMsg)
, [ PaginatedList.view ClickedFeedPage (Feed.articles feed) ]
, [ viewPagination (Feed.articles feed) ]
]
]
@@ -155,6 +155,46 @@ viewBanner =
-- PAGINATION
{-| 👉 TODO: Relocate `viewPagination` into `PaginatedList.view` and make it reusable,
then refactor both Page.Home and Page.Profile to use it!
💡 HINT: Make `PaginatedList.view` return `Html msg` instead of `Html Msg`.
(You'll need to introduce at least one extra argument for this to work.)
-}
viewPagination : PaginatedList (Article Preview) -> Html Msg
viewPagination list =
let
viewPageLink currentPage =
pageLink currentPage (currentPage == page list)
in
if total list > 1 then
List.range 1 (total list)
|> 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) ]
]
-- TABS

View File

@@ -218,7 +218,7 @@ view model =
[ [ viewTabs model.feedTab ]
, Feed.viewArticles model.timeZone feed
|> List.map (Html.map GotFeedMsg)
, [ PaginatedList.view ClickedFeedPage (Feed.articles feed) ]
, [ viewPagination (Feed.articles feed) ]
]
]
]
@@ -246,6 +246,46 @@ view model =
-- PAGINATION
{-| 👉 TODO: Relocate `viewPagination` into `PaginatedList.view` and make it reusable,
then refactor both Page.Home and Page.Profile to use it!
💡 HINT: Make `PaginatedList.view` return `Html msg` instead of `Html Msg`.
(You'll need to introduce at least one extra argument for this to work.)
-}
viewPagination : PaginatedList (Article Preview) -> Html Msg
viewPagination list =
let
viewPageLink currentPage =
pageLink currentPage (currentPage == page list)
in
if total list > 1 then
List.range 1 (total list)
|> 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) ]
]
-- PAGE TITLE

View File

@@ -1,6 +1,5 @@
module PaginatedList exposing (PaginatedList, fromList, fromRequestBuilder, map, page, total, values, view)
module PaginatedList exposing (PaginatedList, fromList, fromRequestBuilder, map, page, total, values)
import Article exposing (Article, Preview)
import Html exposing (Html, a, li, text, ul)
import Html.Attributes exposing (class, classList, href)
import Html.Events exposing (onClick)
@@ -88,32 +87,3 @@ fromRequestBuilder resultsPerPage pageNumber builder =
-- VIEW
view : (Int -> msg) -> PaginatedList (Article Preview) -> Html msg
view toMsg list =
let
viewPageLink currentPage =
pageLink toMsg currentPage (currentPage == page list)
in
if total list > 1 then
List.range 1 (total list)
|> 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) ]
]

View File

@@ -3,7 +3,7 @@
"source-directories": [
"src"
],
"elm-version": "0.19.0",
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"NoRedInk/elm-json-decode-pipeline": "1.0.0",
@@ -32,4 +32,4 @@
"elm/random": "1.0.0"
}
}
}
}

View File

@@ -155,27 +155,46 @@ viewBanner =
-- TABS
{-| TODO: Have viewTabs render all the tabs, using `activeTab` as the
single source of truth for their state.
The specification for how the tabs work is:
1. If the user is logged in, render `yourFeed` as the first tab. Examples:
"Your Feed" "Global Feed"
"Your Feed" "Global Feed" "#dragons"
2. If the user is NOT logged in, do not render `yourFeed` at all. Examples:
"Global Feed"
"Global Feed" "#dragons"
3. If the active tab is a `TagFeed`, render that tab last. Show the tag it contains with a "#" in front.
"Global Feed" "#dragons"
"Your Feed" "Global Feed" "#dragons"
3. If the active tab is NOT a `TagFeed`, do not render a tag tab at all.
"Your Feed" "Global Feed"
"Global Feed"
💡 HINT: The 4 declarations after `viewTabs` may be helpful!
-}
viewTabs : Bool -> FeedTab -> Html Msg
viewTabs isLoggedIn activeTab =
ul [ class "nav nav-pills outline-active" ] <|
List.singleton <|
case activeTab of
YourFeed ->
tabBar [] yourFeed [ globalFeed ]
case activeTab of
YourFeed ->
[]
GlobalFeed ->
if isLoggedIn then
tabBar [ yourFeed ] globalFeed []
GlobalFeed ->
[]
else
tabBar [] globalFeed []
TagFeed tagName ->
if isLoggedIn then
tabBar [ yourFeed, globalFeed ] (tagFeed tagName) []
else
tabBar [ globalFeed ] (tagFeed tagName) []
TagFeed tagName ->
[]
tabBar :

View File

@@ -3,7 +3,7 @@
"source-directories": [
"src"
],
"elm-version": "0.19.0",
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"NoRedInk/elm-json-decode-pipeline": "1.0.0",
@@ -32,4 +32,4 @@
"elm/random": "1.0.0"
}
}
}
}

View File

@@ -206,14 +206,17 @@ toggleFollowButton txt extraClasses msgWhenClicked uname =
decoder : Maybe Cred -> Decoder Author
decoder maybeCred =
Decode.succeed Tuple.pair
{- 👉 TODO: Use this `Profile` and `Username` to decode an `Author`!
💡 HINT: `decoderHelp` will help here, but slightly altering its type may make things easier...
-}
Decode.succeed "..."
|> custom Profile.decoder
|> required "username" Username.decoder
|> Decode.andThen (decoderHelp maybeCred)
decoderHelp : Maybe Cred -> ( Profile, Username ) -> Decoder Author
decoderHelp maybeCred ( prof, uname ) =
decoderHelp : Maybe Cred -> Profile -> Username -> Decoder Author
decoderHelp maybeCred prof uname =
case maybeCred of
Nothing ->
-- If you're logged out, you can't be following anyone!

View File

@@ -24,18 +24,17 @@ view timeZone timestamp =
-}
iso8601Decoder : Decoder Time.Posix
iso8601Decoder =
Decode.string
|> Decode.andThen decoderHelp
{- 👉 TODO: Use the following function to decode this Time.Posix value:
decoderHelp : String -> Decoder Time.Posix
decoderHelp str =
case Iso8601.toTime str of
Ok time ->
Decode.succeed time
Iso8601.toTime : String -> Result (List DeadEnd) Time.Posix
Err _ ->
Decode.fail ("Invalid ISO-8601 timestamp: " ++ str)
NOTE: You can disregard the (List DeadEnd) here. No need to use it to complete this exercise!
💡 HINT: Decode.andThen will be useful here.
-}
"..."

View File

@@ -1,15 +0,0 @@
{
"version": "1.0.0",
"summary": "helpful summary of your project, less than 80 characters",
"repository": "https://github.com/user/project.git",
"license": "BSD3",
"source-directories": [
"."
],
"exposed-modules": [],
"dependencies": {
"elm-lang/core": "5.1.1 <= v < 6.0.0",
"elm-lang/html": "2.0.0 <= v < 3.0.0"
},
"elm-version": "0.18.0 <= v < 0.19.0"
}

View File

@@ -3,7 +3,7 @@
"source-directories": [
"src"
],
"elm-version": "0.19.0",
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"NoRedInk/elm-json-decode-pipeline": "1.0.0",
@@ -32,4 +32,4 @@
"elm/random": "1.0.0"
}
}
}
}

View File

@@ -34,11 +34,14 @@ parser =
, Parser.map Login (s "login")
, Parser.map Logout (s "logout")
, Parser.map Profile (s "profile" </> Username.urlParser)
, Parser.map Settings (s "settings")
, Parser.map Register (s "settings")
, Parser.map Article (s "article" </> Slug.urlParser)
, Parser.map EditArticle (s "editor" </> Slug.urlParser)
, Parser.map NewArticle (s "editor")
-- 👉 TODO /settings → Settings
-- 👉 TODO /register → Register
-- 👉 TODO /article/[slug] → Article [slug]
-- 👉 TODO /editor → NewArticle
-- 👉 TODO /editor/[slug] → EditArticle [slug]
--
-- 💡 HINT: Article and EditArticle work similarly to how Profile works.
]

View File

@@ -3,10 +3,10 @@
"source-directories": [
"src"
],
"elm-version": "0.19.0",
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"NoRedInk/json-decode-pipeline": "1.0.0",
"NoRedInk/elm-json-decode-pipeline": "1.0.0",
"elm/browser": "1.0.0",
"elm/core": "1.0.0",
"elm/html": "1.0.0",
@@ -16,7 +16,7 @@
"elm/url": "1.0.0",
"elm-explorations/markdown": "1.0.0",
"lukewestby/elm-http-builder": "6.0.0",
"rtfeldman/elm-iso8601": "1.0.1",
"rtfeldman/elm-iso8601-date-strings": "1.0.0",
"rtfeldman/elm-validate": "4.0.0"
},
"indirect": {

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
Introduction to Elm Workshop
============================
If you haven't already, follow the [Getting Started instructions](https://github.com/rtfeldman/elm-0.19-workshop/blob/master/intro/README.md
If you haven't already, follow the [Getting Started instructions](https://github.com/rtfeldman/elm-0.19-workshop#getting-started
) at the root of this repository, then continue here!
## Start the server
@@ -24,7 +24,7 @@ of the workshop, so **don't close it** until the workshop is over!
Leave the existing terminal running, and open a **second** terminal.
In the new termnal, `cd` into the `elm-0.19-workshop/intro/server/` directory again.
In the new terminal, `cd` into the `elm-0.19-workshop/intro/server/` directory again.
Then run this to build the Elm code for the first time:
@@ -43,6 +43,9 @@ If things arent working, the instructor will be happy to help!
## Links
* [The solutions to these exercises](https://github.com/rtfeldman/elm-0.19-workshop/tree/solutions/intro)
* [Slides for the Frontend Masters video course that goes with this repo](https://docs.google.com/presentation/d/1LM_W2BRs_ItT-SPDe70C10cbwhGNHGQlJ1fVnAdnRIY/edit?usp=sharing)
* [Introduction to Elm Video Course](https://frontendmasters.com/courses/intro-elm/) that goes along with this repo.
* [Elm in Action](https://www.manning.com/books/elm-in-action?a_aid=elm_in_action&a_bid=b15edc5c), a book by [Richard Feldman](https://twitter.com/rtfeldman), creator of this workshop
* [Official Elm Guide](https://guide.elm-lang.org/) by [Evan Czaplicki](https://twitter.com/czaplic), creator of Elm
* [Elm Slack](http://elmlang.herokuapp.com/) - amazingly helpful chat community. People in [the `#beginners` channel](https://elmlang.slack.com/messages/C192T0Q1E/) are happy to answer questions!

View File

@@ -5,11 +5,25 @@ import Html.Attributes exposing (..)
banner =
{- 👉 TODO: Add a logo and tagline to this banner, so its structure becomes:
<div class="banner">
<div class="container">
<h1 class="logo-font">conduit</h1>
<p>A place to share your knowledge.</p>
</div>
</div>
💡 HINT 1: the <div class="container"> above is an element with 2 child nodes.
💡 HINT 2: the <div class="feed-toggle"> below is an element with text.
-}
div [ class "banner" ]
[ div [ class "container" ]
[ h1 [ class "logo-font" ] [ text "conduit" ]
, p [] [ text "A place to share your knowledge." ]
]
[ text "👉 TODO: Put the <h1> here instead of this text, then add the <p> right after the <h1>" ]
]
@@ -19,7 +33,7 @@ feed =
main =
div [ class "home-page" ]
[ banner
[ div [] [ text "👉 TODO: Replace this <div> with the banner" ]
, div [ class "container page" ]
[ div [ class "row" ]
[ div [ class "col-md-9" ] [ feed ]

View File

@@ -3,7 +3,7 @@
"source-directories": [
"."
],
"elm-version": "0.19.0",
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"elm/core": "1.0.0",

View File

@@ -3,7 +3,7 @@
"source-directories": [
"src"
],
"elm-version": "0.19.0",
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"elm/core": "1.0.0",

View File

@@ -7,13 +7,18 @@ import Html.Attributes exposing (..)
viewTags tags =
let
renderedTags =
List.map viewTag tags
-- 👉 TODO: use `List.map` and `viewTag` to render the tags
[]
in
div [ class "tag-list" ] renderedTags
viewTag tagName =
button [ class "tag-pill tag-default" ] [ text tagName ]
{- 👉 TODO: render something like this:
<button class="tag-pill tag-default">tag name goes here</button>
-}
button [] []
main =
@@ -29,7 +34,9 @@ main =
, div [ class "col-md-3" ]
[ div [ class "sidebar" ]
[ p [] [ text "Popular Tags" ]
, viewTags tags
-- 👉 TODO: instead of passing [] to viewTags, pass the actual tags
, viewTags []
]
]
]

View File

@@ -3,18 +3,16 @@
"source-directories": [
"src"
],
"elm-version": "0.19.0",
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"elm/browser": "1.0.0",
"elm/core": "1.0.0",
"elm/html": "1.0.0"
},
"indirect": {
"elm/json": "1.0.0",
"elm/time": "1.0.0",
"elm/url": "1.0.0",
"elm/virtual-dom": "1.0.0"
"elm/url": "1.0.0"
}
},
"test-dependencies": {

View File

@@ -1,5 +1,8 @@
module Main exposing (main)
-- NOTE: Make sure to follow the instructions in the README for part3
-- to install the elm/browser package before running elm make!
--
-- FYI: 👇 You can see our new `Article` module in `src/Article.elm`
import Article
@@ -25,11 +28,15 @@ initialModel =
update msg model =
if msg.description == "ClickedTag" then
{ model | selectedTag = msg.data }
{- 👉 TODO: If `msg.description` is "ClickedTag", then
set the model's `selectedTag` field to be `msg.data`
else
model
💡 HINT: record update syntax looks like this:
{ model | foo = bar }
-}
model
@@ -38,8 +45,16 @@ update msg model =
view model =
let
{- 👉 TODO: Filter the articles down to only the ones
that include the currently selected tag.
💡 HINT: Replace `True` below with something involving
`List.member`, `article.tags`, and `model.selectedTag`
Docs for List.member: http://package.elm-lang.org/packages/elm-lang/core/latest/List#member
-}
articles =
List.filter (\article -> List.member model.selectedTag article.tags)
List.filter (\article -> True)
model.allArticles
feed =
@@ -89,7 +104,17 @@ viewTag selectedTagName tagName =
in
button
[ class ("tag-pill " ++ otherClass)
, onClick { description = "ClickedTag", data = tagName }
{- 👉 TODO: Add an `onClick` handler which sends a msg
that our `update` function above will use
to set the currently selected tag to `tagName`.
💡 HINT: It should look something like this:
, onClick { description = , data = }
👆 Don't forget to add a comma before `onClick`!
-}
]
[ text tagName ]

View File

@@ -1,15 +0,0 @@
{
"version": "1.0.0",
"summary": "helpful summary of your project, less than 80 characters",
"repository": "https://github.com/user/project.git",
"license": "BSD3",
"source-directories": [
"."
],
"exposed-modules": [],
"dependencies": {
"elm-lang/core": "5.1.1 <= v < 6.0.0",
"elm-lang/html": "2.0.0 <= v < 3.0.0"
},
"elm-version": "0.18.0 <= v < 0.19.0"
}

View File

@@ -3,7 +3,7 @@
"source-directories": [
"src"
],
"elm-version": "0.19.0",
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"elm/browser": "1.0.0",
@@ -21,4 +21,4 @@
"direct": {},
"indirect": {}
}
}
}

View File

@@ -14,20 +14,27 @@ import Html.Events exposing (onClick)
type alias Model =
{ tags : List String
, selectedTag : String
, allArticles : List Article
{- 👉 TODO: change this `allArticles` annotation to the following:
allArticles : List Article
💡 HINT: You'll need to move the existing annotation to a `type alias`.
-}
, allArticles :
List
{ title : String
, description : String
, body : String
, tags : List String
, slug : String
}
}
type alias Article =
{ title : String
, description : String
, body : String
, tags : List String
, slug : String
}
initialModel : Model
{-| 👉 TODO: Replace this comment with a type annotation for `initialModel`
-}
initialModel =
{ tags = Article.tags
, selectedTag = "elm"
@@ -45,7 +52,8 @@ type alias Msg =
}
update : Msg -> Model -> Model
{-| 👉 TODO: Replace this comment with a type annotation for `update`
-}
update msg model =
if msg.description == "ClickedTag" then
{ model | selectedTag = msg.data }
@@ -58,7 +66,8 @@ update msg model =
-- VIEW
view : Model -> Html Msg
{-| 👉 TODO: Replace this comment with a type annotation for `view`
-}
view model =
let
articles =
@@ -84,7 +93,8 @@ view model =
]
viewArticle : Article -> Html Msg
{-| 👉 TODO: Replace this comment with a type annotation for `viewArticle`
-}
viewArticle article =
div [ class "article-preview" ]
[ h1 [] [ text article.title ]
@@ -93,7 +103,8 @@ viewArticle article =
]
viewBanner : Html Msg
{-| 👉 TODO: Replace this comment with a type annotation for `viewBanner`
-}
viewBanner =
div [ class "banner" ]
[ div [ class "container" ]
@@ -103,7 +114,8 @@ viewBanner =
]
viewTag : String -> String -> Html Msg
{-| 👉 TODO: Replace this comment with a type annotation for `viewTag`
-}
viewTag selectedTagName tagName =
let
otherClass =

View File

@@ -3,7 +3,7 @@
"source-directories": [
"src"
],
"elm-version": "0.19.0",
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"NoRedInk/elm-json-decode-pipeline": "1.0.0",
@@ -32,4 +32,4 @@
"elm/random": "1.0.0"
}
}
}
}

View File

@@ -86,7 +86,11 @@ viewForm form =
[ input
[ class "form-control form-control-lg"
, placeholder "Username"
, onInput EnteredUsername
{- 👉 TODO: when the user inputs a username, update it in the Model.
💡 HINT: Look at how the Email input below does this. 👇
-}
, value form.username
]
[]
@@ -137,7 +141,6 @@ type Msg
= SubmittedForm
| EnteredEmail String
| EnteredPassword String
| EnteredUsername String
| CompletedRegister (Result Http.Error Viewer)
| GotSession Session
@@ -151,9 +154,6 @@ update msg model =
EnteredPassword password ->
updateForm (\form -> { form | password = password }) model
EnteredUsername username ->
updateForm (\form -> { form | username = username }) model
SubmittedForm ->
case validate model.form of
Ok validForm ->

View File

@@ -3,7 +3,7 @@
"source-directories": [
"src"
],
"elm-version": "0.19.0",
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"NoRedInk/elm-json-decode-pipeline": "1.0.0",

View File

@@ -40,12 +40,12 @@ src (Avatar maybeUrl) =
resolveAvatarUrl : Maybe String -> String
resolveAvatarUrl maybeUrl =
case maybeUrl of
Just url ->
url
{- 👉 TODO #1 of 2: return the user's avatar from maybeUrl, if maybeUrl actually
contains one. If maybeUrl is Nothing, return this URL instead:
Nothing ->
"https://static.productionready.io/images/smiley-cyrus.jpg"
https://static.productionready.io/images/smiley-cyrus.jpg
-}
""
encode : Avatar -> Value

View File

@@ -572,7 +572,6 @@ toTagList tagString =
-}
String.split " " tagString
|> List.map String.trim
|> List.filter (\str -> str /= "")
edit : Slug -> TrimmedForm -> Cred -> Http.Request (Article Full)

View File

@@ -3,7 +3,7 @@
"source-directories": [
"src"
],
"elm-version": "0.19.0",
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"NoRedInk/elm-json-decode-pipeline": "1.0.0",

View File

@@ -182,12 +182,26 @@ type alias Metadata =
metadataDecoder : Decoder Metadata
metadataDecoder =
{- 👉 TODO: replace the calls to `hardcoded` with calls to `required`
in order to decode these fields:
--- "description" -------> description : String
--- "title" -------------> title : String
--- "tagList" -----------> tags : List String
--- "favorited" ---------> favorited : Bool
--- "favoritesCount" ----> favoritesCount : Int
Once this is done, the articles in the feed should look normal again.
💡 HINT: Order matters! These must be decoded in the same order
as the order of the fields in `type alias Metadata` above.
-}
Decode.succeed Metadata
|> required "description" string
|> required "title" string
|> required "tagList" (list string)
|> required "favorited" bool
|> required "favoritesCount" int
|> hardcoded "(needs decoding!)"
|> hardcoded "(needs decoding!)"
|> hardcoded []
|> hardcoded False
|> hardcoded 0
|> required "createdAt" Timestamp.iso8601Decoder

View File

@@ -12,6 +12,6 @@ Then open `http://localhost:3000` in your browser.
## Exercise
We need to make login work. Currently it doesn't actually send a HTTP request to the server.
We need to make signup work again. Currently it doesn't actually send a HTTP request to the server.
We'll fix this by editing `src/Page/Login.elm` and resolving the TODOs there.
We'll fix this by editing `src/Page/Register.elm` and resolving the TODOs there.

View File

@@ -3,7 +3,7 @@
"source-directories": [
"src"
],
"elm-version": "0.19.0",
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"NoRedInk/elm-json-decode-pipeline": "1.0.0",

View File

@@ -155,13 +155,34 @@ update msg model =
responseDecoder =
Decode.field "user" Viewer.decoder
{- 👉 TODO: Create a Http.Request value that represents
a POST request to "/api/users"
💡 HINT 1: Documentation for `Http.post` is here:
http://package.elm-lang.org/packages/elm-lang/http/1.0.0/Http#post
💡 HINT 2: Look at the values defined above in this
let-expression. What are their types? What are the types the
`Http.post` function is looking for?
-}
request : Http.Request Viewer
request =
Http.post "/api/users" requestBody responseDecoder
Debug.todo "Call Http.post to represent a POST to /api/users"
{- 👉 TODO: Use Http.send to turn the request we just defined
into a Cmd for `update` to execute.
💡 HINT 1: Documentation for `Http.send` is here:
http://package.elm-lang.org/packages/elm-lang/http/1.0.0/Http#send
💡 HINT 2: The `CompletedRegister` variant defined in `type Msg`
will be useful here!
-}
cmd : Cmd Msg
cmd =
Http.send CompletedRegister request
Cmd.none
in
( { model | problems = [] }, cmd )

View File

@@ -1,8 +1,8 @@
# Part 8
# Part 9
Once again, we'll be building `src/Main.elm`, but editing a different file.
To build everything, `cd` into the `part8/` directory and run8
To build everything, `cd` into the `part9/` directory and run:
```shell
elm make src/Main.elm --output ../server/public/elm.js

View File

@@ -3,10 +3,10 @@
"source-directories": [
"src"
],
"elm-version": "0.19.0",
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"NoRedInk/json-decode-pipeline": "1.0.0",
"NoRedInk/elm-json-decode-pipeline": "1.0.0",
"elm/browser": "1.0.0",
"elm/core": "1.0.0",
"elm/html": "1.0.0",
@@ -16,7 +16,7 @@
"elm/url": "1.0.0",
"elm-explorations/markdown": "1.0.0",
"lukewestby/elm-http-builder": "6.0.0",
"rtfeldman/elm-iso8601": "1.0.1"
"rtfeldman/elm-iso8601-date-strings": "1.0.0"
},
"indirect": {
"elm/parser": "1.0.0",

View File

@@ -40,7 +40,7 @@ import Html.Attributes exposing (class)
import Html.Events exposing (stopPropagationOn)
import Http
import HttpBuilder exposing (RequestBuilder, withBody, withExpect, withQueryParams)
import Json.Decode as Decode exposing (Decoder, bool, int, list, string)
import Json.Decode as Decode exposing (Decoder)
import Json.Decode.Pipeline exposing (custom, hardcoded, required)
import Json.Encode as Encode
import Markdown
@@ -83,6 +83,47 @@ type Article a
= Article Internals a
{-| Metadata about the article - its title, description, and so on.
Importantly, this module's public API exposes a way to read this metadata, but
not to alter it. This is read-only information!
If we find ourselves using any particular piece of metadata often,
for example `title`, we could expose a convenience function like this:
Article.title : Article a -> String
If you like, it's totally reasonable to expose a function like that for every one
of these fields!
(Okay, to be completely honest, exposing one function per field is how I prefer
to do it, and that's how I originally wrote this module. However, I'm aware that
this code base has become a common reference point for beginners, and I think it
is _extremely important_ that slapping some "getters and setters" on a record
does not become a habit for anyone who is getting started with Elm. The whole
point of making the Article type opaque is to create guarantees through
_selectively choosing boundaries_ around it. If you aren't selective about
where those boundaries are, and instead expose a "getter and setter" for every
field in the record, the result is an API with no more guarantees than if you'd
exposed the entire record directly! It is so important to me that beginners not
fall into the terrible "getters and setters" trap that I've exposed this
Metadata record instead of exposing a single function for each of its fields,
as I did originally. This record is not a bad way to do it, by any means,
but if this seems at odds with <https://youtu.be/x1FU3e0sT1I> - now you know why!
See commit c2640ae3abd60262cdaafe6adee3f41d84cd85c3 for how it looked before.
)
-}
type alias Metadata =
{ description : String
, title : String
, tags : List String
, createdAt : Time.Posix
, favorited : Bool
, favoritesCount : Int
}
type alias Internals =
{ slug : Slug
, author : Author
@@ -170,39 +211,15 @@ internalsDecoder maybeCred =
|> custom metadataDecoder
type alias Metadata =
{ description : String
, title : String
, tags : List String
, favorited : Bool
, favoritesCount : Int
, createdAt : Time.Posix
}
metadataDecoder : Decoder Metadata
metadataDecoder =
{- 👉 TODO: replace the calls to `hardcoded` with calls to `required`
in order to decode these fields:
--- "description" -------> description : String
--- "title" -------------> title : String
--- "tagList" -----------> tags : List String
--- "favorited" ---------> favorited : Bool
--- "favoritesCount" ----> favoritesCount : Int
Once this is done, the articles in the feed should look normal again.
💡 HINT: Order matters! These must be decoded in the same order
as the order of the fields in `type alias Metadata` above.
-}
Decode.succeed Metadata
|> hardcoded "(needs decoding!)"
|> hardcoded "(needs decoding!)"
|> hardcoded []
|> hardcoded False
|> hardcoded 0
|> required "description" (Decode.map (Maybe.withDefault "") (Decode.nullable Decode.string))
|> required "title" Decode.string
|> required "tagList" (Decode.list Decode.string)
|> required "createdAt" Timestamp.iso8601Decoder
|> required "favorited" Decode.bool
|> required "favoritesCount" Decode.int

View File

@@ -72,7 +72,7 @@ login newViewer =
Viewer.encode newViewer
|> Encode.encode 0
|> Just
|> storeSession
|> sendSessionToJavaScript
@@ -81,10 +81,24 @@ login newViewer =
logout : Cmd msg
logout =
storeSession Nothing
sendSessionToJavaScript Nothing
port storeSession : Maybe String -> Cmd msg
{-| 👉 TODO 1 of 2: Replace this do-nothing function with a port that sends the
authentication token to JavaScript.
💡 HINT 1: When you convert it to a port, the port's name _must_ match
the name JavaScript expects in `intro/server/public/index.html`.
That name is not `sendSessionToJavaScript`, so you will need to
rename it to match what JS expects!
💡 HINT 2: After you rename it, some code in this file will break because
it was depending on the old name. Follow the compiler errors to fix them!
-}
sendSessionToJavaScript : Maybe String -> Cmd msg
sendSessionToJavaScript maybeAuthenticationToken =
Cmd.none
@@ -93,10 +107,24 @@ port storeSession : Maybe String -> Cmd msg
changes : (Session -> msg) -> Nav.Key -> Sub msg
changes toMsg key =
onSessionChange (\val -> toMsg (decode key val))
receiveSessionFromJavaScript (\val -> toMsg (decode key val))
port onSessionChange : (Value -> msg) -> Sub msg
{-| 👉 TODO 2 of 2: Replace this do-nothing function with a port that receives the
authentication token from JavaScript.
💡 HINT 1: When you convert it to a port, the port's name _must_ match
the name JavaScript expects in `intro/server/public/index.html`.
That name is not `receiveSessionFromJavaScript`, so you will need to
rename it to match what JS expects!
💡 HINT 2: After you rename it, some code in this file will break because
it was depending on the old name. Follow the compiler errors to fix them!
-}
receiveSessionFromJavaScript : (Value -> msg) -> Sub msg
receiveSessionFromJavaScript toMsg =
Sub.none
decode : Nav.Key -> Value -> Session

View File

@@ -3,10 +3,10 @@
"source-directories": [
"src"
],
"elm-version": "0.19.0",
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"NoRedInk/json-decode-pipeline": "1.0.0",
"NoRedInk/elm-json-decode-pipeline": "1.0.0",
"elm/browser": "1.0.0",
"elm/core": "1.0.0",
"elm/html": "1.0.0",
@@ -16,7 +16,7 @@
"elm/url": "1.0.0",
"elm-explorations/markdown": "1.0.0",
"lukewestby/elm-http-builder": "6.0.0",
"rtfeldman/elm-iso8601": "1.0.1",
"rtfeldman/elm-iso8601-date-strings": "1.0.0",
"rtfeldman/elm-validate": "4.0.0"
},
"indirect": {

File diff suppressed because it is too large Load Diff