Update a ton of things

This commit is contained in:
Richard Feldman
2016-06-24 17:40:54 -07:00
parent 908ca61c77
commit c8e9a117f5
56 changed files with 1023 additions and 1466 deletions

View File

@@ -10,7 +10,7 @@ import Html.App
import Auth
import Http
import Task exposing (Task)
import Json.Decode exposing (Decoder, (:=))
import Json.Decode exposing (Decoder)
main : Program Never

View File

@@ -5,7 +5,7 @@ Getting Started
1. Install [Node.js](http://nodejs.org) 4.0.0 or higher
2. Add an [editor plugin](http://elm-lang.org/install#syntax-highlighting) for your editor of choice.
2. Add a plugin for your editor of choice: [Atom](https://atom.io/packages/language-elm), [Sublime Text](https://packagecontrol.io/packages/Elm%20Language%20Support), [VS Code](https://github.com/sbrink/vscode-elm), [Light Table](https://github.com/rundis/elm-light), [Vim](https://github.com/lambdatoast/elm.vim), [Emacs](https://github.com/jcollard/elm-mode), [Brackets](https://github.com/lepinay/elm-brackets)
3. Not required, but **highly** recommended: [install elm-format](https://github.com/avh4/elm-format#installation-) and integrate it into your editor so that it runs on save.

View File

@@ -16,7 +16,7 @@ model =
view model =
div [ class "content" ]
[ header []
[ -- TODO add the equivalent of <h1>ElmHub</h1> right before the tagline
[ -- TODO add the equivalent of <h1>ElmHub</h1> right before this <span>
span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ]
]
, ul [ class "results" ]

View File

@@ -4,19 +4,19 @@ Part 1
## Installation
```bash
elm package install
elm-package install
```
(Answer `y` at the prompt. In rare cases a known issue can cause the download
to fail; in that case, just run `elm package install` again.)
to fail; in that case, just run `elm-package install` again.)
## Building
```bash
elm live Main.elm --open -- --output=elm.js
elm-live Main.elm --open -- --output=elm.js
```
## References
* [html-to-elm](http://mbylstra.github.io/html-to-elm/) - paste in HTML, get elm-html code
* [elm-html documentation](http://package.elm-lang.org/packages/evancz/elm-html/4.0.2/)
* [record syntax](http://elm-lang.org/docs/syntax#records) (e.g. `{ foo = 1, bar = 2`)
* [record syntax](http://elm-lang.org/docs/syntax#records) (e.g. `{ foo = 1, bar = 2 }`)

View File

@@ -1,119 +1,100 @@
module ElmHub (..) where
module ElmHub exposing (..)
import Html exposing (..)
import Html.Attributes exposing (class, target, href, property)
import Html.Attributes exposing (class, target, href, property, defaultValue)
import Html.Events exposing (..)
import Html.Lazy exposing (..)
import Http
import Auth
import Task exposing (Task)
import Effects exposing (Effects)
import Json.Decode exposing (Decoder, (:=))
import Json.Encode
import Signal exposing (Address)
import Json.Decode exposing (Decoder)
import Dict exposing (Dict)
import SearchResult
searchFeed : String -> Task x Action
searchFeed : String -> Cmd Msg
searchFeed query =
let
-- See https://developer.github.com/v3/search/#example for how to customize!
url =
"https://api.github.com/search/repositories?access_token="
++ Auth.token
++ "&q="
++ query
++ "+language:elm&sort=stars&order=desc"
task =
Http.get responseDecoder url
|> Task.map SetResults
in
Task.onError task (\_ -> Task.succeed (SetResults []))
let
url =
"https://api.github.com/search/repositories?access_token="
++ Auth.token
++ "&q="
++ query
++ "+language:elm&sort=stars&order=desc"
in
Task.perform HandleSearchError HandleSearchResponse (Http.get responseDecoder url)
responseDecoder : Decoder (List SearchResult.Model)
responseDecoder =
-- TODO make use of SearchResult's decoder
Json.Decode.succeed []
-- TODO make use of SearchResult's decoder
Json.Decode.succeed []
type alias Model =
{ query : String
, results : Dict SearchResult.ResultId SearchResult.Model
}
{ query : String
, results : Dict SearchResult.ResultId SearchResult.Model
}
initialModel : Model
initialModel =
{ query = "tutorial"
, results = Dict.empty
}
{ query = "tutorial"
, results = Dict.empty
}
view : Address Action -> Model -> Html
view address model =
div
[ class "content" ]
[ header
[]
[ h1 [] [ text "ElmHub" ]
, span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ]
view : Model -> Html Msg
view model =
div [ class "content" ]
[ header []
[ h1 [] [ text "ElmHub" ]
, span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ]
]
, input [ class "search-query", onInput SetQuery, defaultValue model.query ] []
, button [ class "search-button", onClick Search ] [ text "Search" ]
, ul [ class "results" ] (viewSearchResults model.results)
]
, input [ class "search-query", onInput address SetQuery, defaultValue model.query ] []
, button [ class "search-button", onClick address Search ] [ text "Search" ]
, ul
[ class "results" ]
(viewSearchResults address model.results)
]
viewSearchResults : Address Action -> Dict SearchResult.ResultId SearchResult.Model -> List Html
viewSearchResults address results =
results
|> Dict.values
|> List.sortBy (.stars >> negate)
|> List.map (\_ -> div [] [ text "TODO replace this line with view logic from SearchResult" ])
viewSearchResults : Dict SearchResult.ResultId SearchResult.Model -> List (Html a)
viewSearchResults results =
results
|> Dict.values
|> List.sortBy (.stars >> negate)
|> List.map (\_ -> div [] [ text "TODO replace this line with view logic from SearchResult" ])
onInput address wrap =
on "input" targetValue (\val -> Signal.message address (wrap val))
type Msg
= Search
| SetQuery String
| DeleteById SearchResult.ResultId
| SetResults (List SearchResult.Model)
| HandleSearchResponse (List SearchResult.Model)
| HandleSearchError Http.Error
defaultValue str =
property "defaultValue" (Json.Encode.string str)
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Search ->
( model, searchFeed model.query )
SetQuery query ->
( { model | query = query }, Cmd.none )
type Action
= Search
| SetQuery String
| DeleteById SearchResult.ResultId
| SetResults (List SearchResult.Model)
SetResults results ->
let
resultsById : Dict SearchResult.ResultId SearchResult.Model
resultsById =
results
|> List.map (\result -> ( result.id, result ))
|> Dict.fromList
in
( { model | results = resultsById }, Cmd.none )
update : Action -> Model -> ( Model, Effects Action )
update action model =
case action of
Search ->
( model, Effects.task (searchFeed model.query) )
SetQuery query ->
( { model | query = query }, Effects.none )
SetResults results ->
let
resultsById : Dict SearchResult.ResultId SearchResult.Model
resultsById =
results
|> List.map (\result -> ( result.id, result ))
|> Dict.fromList
in
( { model | results = resultsById }, Effects.none )
DeleteById id ->
let
newModel =
{ model | results = Dict.remove id model.results }
in
( newModel, Effects.none )
DeleteById id ->
let
newModel =
{ model | results = Dict.remove id model.results }
in
( newModel, Cmd.none )

View File

@@ -1,27 +1,13 @@
module Main (..) where
module Main exposing (..)
import StartApp
import ElmHub exposing (..)
import Effects exposing (Effects)
import Task exposing (Task)
import Html exposing (Html)
main : Signal Html
main : Program Never
main =
app.html
app : StartApp.App Model
app =
StartApp.start
{ view = view
, update = update
, init = ( initialModel, Effects.task (searchFeed initialModel.query) )
, inputs = []
}
port tasks : Signal (Task Effects.Never ())
port tasks =
app.tasks
Html.App.program
{ view = view
, update = update
, init = ( initialModel, searchFeed initialModel.query )
, inputs = []
}

View File

@@ -4,14 +4,14 @@ Part 10
## Installation
```bash
elm package install
elm-package install
```
(Answer `y` at the prompt. In rare cases a known issue can cause the download
to fail; in that case, just run `elm package install` again.)
to fail; in that case, just run `elm-package install` again.)
## Building
```bash
elm live Main.elm --open -- --output=elm.js
elm-live Main.elm --open -- --output=elm.js
```

View File

@@ -1,45 +1,41 @@
module SearchResult (..) where
module SearchResult exposing (..)
import Html exposing (..)
import Html.Attributes exposing (class, target, href, property)
import Html.Events exposing (..)
import Json.Decode exposing (Decoder, (:=))
import Html.Attributes exposing (class, target, href, property, defaultValue)
import Json.Decode exposing (Decoder)
import Json.Decode.Pipeline exposing (..)
import Signal exposing (Address)
import Dict exposing (Dict)
type alias ResultId =
Int
Int
type alias Model =
{ id : ResultId
, name : String
, stars : Int
}
{ id : ResultId
, name : String
, stars : Int
}
decoder : Decoder Model
decoder =
decode Model
|> required "id" Json.Decode.int
|> required "full_name" Json.Decode.string
|> required "stargazers_count" Json.Decode.int
decode Model
|> required "id" Json.Decode.int
|> required "full_name" Json.Decode.string
|> required "stargazers_count" Json.Decode.int
view : Address a -> Model -> Html
view address result =
li
[]
[ span [ class "star-count" ] [ text (toString result.stars) ]
, a
[ href ("https://github.com/" ++ result.name)
, target "_blank"
view : Model -> Html a
view result =
li []
[ span [ class "star-count" ] [ text (toString result.stars) ]
, a
[ href ("https://github.com/" ++ result.name)
, target "_blank"
]
[ text result.name ]
, button
-- TODO onClick, send a delete action to the address
[ class "hide-result" ]
[ text "X" ]
]
[ text result.name ]
, button
-- TODO onClick, send a delete action to the address
[ class "hide-result" ]
[ text "X" ]
]

View File

@@ -10,11 +10,9 @@
"exposed-modules": [],
"dependencies": {
"NoRedInk/elm-decode-pipeline": "1.0.0 <= v < 2.0.0",
"elm-lang/core": "3.0.0 <= v < 4.0.0",
"evancz/elm-effects": "2.0.0 <= v < 3.0.0",
"evancz/elm-html": "4.0.0 <= v < 5.0.0",
"evancz/elm-http": "3.0.0 <= v < 4.0.0",
"evancz/start-app": "2.0.0 <= v < 3.0.0"
"elm-lang/core": "4.0.1 <= v < 5.0.0",
"elm-lang/html": "1.0.0 <= v < 2.0.0",
"evancz/elm-http": "3.0.1 <= v < 4.0.0"
},
"elm-version": "0.16.0 <= v < 0.17.0"
"elm-version": "0.17.0 <= v < 0.18.0"
}

View File

@@ -9,13 +9,10 @@
],
"exposed-modules": [],
"dependencies": {
"deadfoxygrandpa/elm-test": "3.1.1 <= v < 4.0.0",
"elm-lang/core": "3.0.0 <= v < 4.0.0",
"evancz/elm-effects": "2.0.0 <= v < 3.0.0",
"evancz/elm-html": "4.0.0 <= v < 5.0.0",
"evancz/elm-http": "3.0.0 <= v < 4.0.0",
"evancz/start-app": "2.0.0 <= v < 3.0.0",
"laszlopandy/elm-console": "1.0.3 <= v < 2.0.0"
"NoRedInk/elm-decode-pipeline": "1.0.0 <= v < 2.0.0",
"elm-lang/core": "4.0.1 <= v < 5.0.0",
"elm-lang/html": "1.0.0 <= v < 2.0.0",
"evancz/elm-http": "3.0.1 <= v < 4.0.0"
},
"elm-version": "0.16.0 <= v < 0.17.0"
"elm-version": "0.17.0 <= v < 0.18.0"
}

View File

@@ -1,4 +1,4 @@
module ElmHub (..) where
module ElmHub exposing (..)
import Html exposing (..)
import Html.Attributes exposing (..)
@@ -6,120 +6,101 @@ import Html.Events exposing (..)
import Http
import Auth
import Task exposing (Task)
import Effects exposing (Effects)
import Json.Decode exposing (Decoder, (:=))
import Json.Decode exposing (Decoder)
import Json.Encode
import Signal exposing (Address)
import Dict exposing (Dict)
import SearchResult
searchFeed : String -> Task x Action
searchFeed : String -> Cmd Msg
searchFeed query =
let
-- See https://developer.github.com/v3/search/#example for how to customize!
url =
"https://api.github.com/search/repositories?access_token="
++ Auth.token
++ "&q="
++ query
++ "+language:elm&sort=stars&order=desc"
task =
Http.get responseDecoder url
|> Task.map SetResults
in
Task.onError task (\_ -> Task.succeed (SetResults []))
let
url =
"https://api.github.com/search/repositories?access_token="
++ Auth.token
++ "&q="
++ query
++ "+language:elm&sort=stars&order=desc"
in
Task.perform HandleSearchError HandleSearchResponse (Http.get responseDecoder url)
responseDecoder : Decoder (List SearchResult.Model)
responseDecoder =
"items" := Json.Decode.list SearchResult.decoder
"items" := Json.Decode.list SearchResult.decoder
type alias Model =
{ query : String
, results : Dict SearchResult.ResultId SearchResult.Model
}
{ query : String
, results : Dict SearchResult.ResultId SearchResult.Model
}
initialModel : Model
initialModel =
{ query = "tutorial"
, results = Dict.empty
}
{ query = "tutorial"
, results = Dict.empty
}
view : Address Action -> Model -> Html
view address model =
div
[ class "content" ]
[ header
[]
[ h1 [] [ text "ElmHub" ]
, span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ]
view : Model -> Html Msg
view model =
div [ class "content" ]
[ header []
[ h1 [] [ text "ElmHub" ]
, span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ]
]
, input [ class "search-query", onInput SetQuery, defaultValue model.query ] []
, button [ class "search-button", onClick Search ] [ text "Search" ]
, ul [ class "results" ] (viewSearchResults model.results)
]
, input [ class "search-query", onInput address SetQuery, defaultValue model.query ] []
, button [ class "search-button", onClick address Search ] [ text "Search" ]
, ul
[ class "results" ]
(viewSearchResults address model.results)
]
viewSearchResults : Address Action -> Dict SearchResult.ResultId SearchResult.Model -> List Html
viewSearchResults address results =
results
|> Dict.values
|> List.sortBy (.stars >> negate)
|> filterResults
|> List.map (SearchResult.view address DeleteById)
viewSearchResults : Dict SearchResult.ResultId SearchResult.Model -> List (Html a)
viewSearchResults results =
results
|> Dict.values
|> List.sortBy (.stars >> negate)
|> filterResults
|> List.map (SearchResult.view address DeleteById)
filterResults : List SearchResult.Model -> List SearchResult.Model
filterResults results =
-- TODO filter out repos with 0 stars
-- using a case-expression rather than List.filter
results
-- TODO filter out repos with 0 stars
-- using a case-expression rather than List.filter
results
onInput address wrap =
on "input" targetValue (\val -> Signal.message address (wrap val))
type Msg
= Search
| SetQuery String
| DeleteById SearchResult.ResultId
| SetResults (List SearchResult.Model)
defaultValue str =
property "defaultValue" (Json.Encode.string str)
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Search ->
( model, searchFeed model.query )
SetQuery query ->
( { model | query = query }, Cmd.none )
type Action
= Search
| SetQuery String
| DeleteById SearchResult.ResultId
| SetResults (List SearchResult.Model)
SetResults results ->
let
resultsById : Dict SearchResult.ResultId SearchResult.Model
resultsById =
results
|> List.map (\result -> ( result.id, result ))
|> Dict.fromList
in
( { model | results = resultsById }, Cmd.none )
update : Action -> Model -> ( Model, Effects Action )
update action model =
case action of
Search ->
( model, Effects.task (searchFeed model.query) )
SetQuery query ->
( { model | query = query }, Effects.none )
SetResults results ->
let
resultsById : Dict SearchResult.ResultId SearchResult.Model
resultsById =
results
|> List.map (\result -> ( result.id, result ))
|> Dict.fromList
in
( { model | results = resultsById }, Effects.none )
DeleteById id ->
let
newModel =
{ model | results = Dict.remove id model.results }
in
( newModel, Effects.none )
DeleteById id ->
let
newModel =
{ model | results = Dict.remove id model.results }
in
( newModel, Cmd.none )

View File

@@ -1,27 +1,13 @@
module Main (..) where
module Main exposing (..)
import StartApp
import ElmHub exposing (..)
import Effects exposing (Effects)
import Task exposing (Task)
import Html exposing (Html)
main : Signal Html
main : Program Never
main =
app.html
app : StartApp.App Model
app =
StartApp.start
{ view = view
, update = update
, init = ( initialModel, Effects.task (searchFeed initialModel.query) )
, inputs = []
}
port tasks : Signal (Task Effects.Never ())
port tasks =
app.tasks
Html.App.program
{ view = view
, update = update
, init = ( initialModel, searchFeed initialModel.query )
, inputs = []
}

View File

@@ -4,14 +4,14 @@ Part 11
## Installation
```bash
elm package install
elm-package install
```
(Answer `y` at the prompt. In rare cases a known issue can cause the download
to fail; in that case, just run `elm package install` again.)
to fail; in that case, just run `elm-package install` again.)
## Building
```bash
elm live Main.elm --open -- --output=elm.js
elm-live Main.elm --open -- --output=elm.js
```

View File

@@ -1,50 +1,45 @@
module SearchResult (..) where
module SearchResult exposing (..)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Json.Decode exposing (Decoder, (:=))
import Signal exposing (Address)
import Json.Decode exposing (Decoder)
import Dict exposing (Dict)
type alias ResultId =
Int
Int
type alias Model =
{ id : ResultId
, name : String
, stars : Int
}
{ id : ResultId
, name : String
, stars : Int
}
decoder : Decoder Model
decoder =
Json.Decode.object3
Model
("id" := Json.Decode.int)
("full_name" := Json.Decode.string)
("stargazers_count" := Json.Decode.int)
Json.Decode.object3 Model
("id" := Json.Decode.int)
("full_name" := Json.Decode.string)
("stargazers_count" := Json.Decode.int)
view : Address a -> (Int -> a) -> Model -> Html
view address delete result =
li
[]
[ span [ class "star-count" ] [ text (toString result.stars) ]
, a
[ href
("https://github.com/"
++ (Debug.log
"TODO we should not see this when typing in the search box!"
result.name
)
)
, target "_blank"
li []
[ span [ class "star-count" ] [ text (toString result.stars) ]
, a
[ href
("https://github.com/"
++ (Debug.log "TODO we should not see this when typing in the search box!"
result.name
)
)
, target "_blank"
]
[ text result.name ]
, button [ class "hide-result", onClick (delete result.id) ]
[ text "X" ]
]
[ text result.name ]
, button
[ class "hide-result", onClick address (delete result.id) ]
[ text "X" ]
]

View File

@@ -10,11 +10,9 @@
"exposed-modules": [],
"dependencies": {
"NoRedInk/elm-decode-pipeline": "1.0.0 <= v < 2.0.0",
"elm-lang/core": "3.0.0 <= v < 4.0.0",
"evancz/elm-effects": "2.0.0 <= v < 3.0.0",
"evancz/elm-html": "4.0.0 <= v < 5.0.0",
"evancz/elm-http": "3.0.0 <= v < 4.0.0",
"evancz/start-app": "2.0.0 <= v < 3.0.0"
"elm-lang/core": "4.0.1 <= v < 5.0.0",
"elm-lang/html": "1.0.0 <= v < 2.0.0",
"evancz/elm-http": "3.0.1 <= v < 4.0.0"
},
"elm-version": "0.16.0 <= v < 0.17.0"
"elm-version": "0.17.0 <= v < 0.18.0"
}

View File

@@ -9,13 +9,10 @@
],
"exposed-modules": [],
"dependencies": {
"deadfoxygrandpa/elm-test": "3.1.1 <= v < 4.0.0",
"elm-lang/core": "3.0.0 <= v < 4.0.0",
"evancz/elm-effects": "2.0.0 <= v < 3.0.0",
"evancz/elm-html": "4.0.0 <= v < 5.0.0",
"evancz/elm-http": "3.0.0 <= v < 4.0.0",
"evancz/start-app": "2.0.0 <= v < 3.0.0",
"laszlopandy/elm-console": "1.0.3 <= v < 2.0.0"
"NoRedInk/elm-decode-pipeline": "1.0.0 <= v < 2.0.0",
"elm-lang/core": "4.0.1 <= v < 5.0.0",
"elm-lang/html": "1.0.0 <= v < 2.0.0",
"evancz/elm-http": "3.0.1 <= v < 4.0.0"
},
"elm-version": "0.16.0 <= v < 0.17.0"
"elm-version": "0.17.0 <= v < 0.18.0"
}

View File

@@ -1,4 +1,4 @@
module ElmHub (..) where
module ElmHub exposing (..)
import Html exposing (..)
import Html.Attributes exposing (..)
@@ -7,133 +7,118 @@ import Html.Lazy exposing (..)
import Http
import Auth
import Task exposing (Task)
import Effects exposing (Effects)
import Json.Decode exposing (Decoder, (:=))
import Json.Decode exposing (Decoder)
import Json.Encode
import Signal exposing (Address)
import Dict exposing (Dict)
import SearchResult exposing (ResultId)
searchFeed : String -> Task x Action
searchFeed : String -> Task x Msg
searchFeed query =
let
-- See https://developer.github.com/v3/search/#example for how to customize!
url =
"https://api.github.com/search/repositories?access_token="
++ Auth.token
++ "&q="
++ query
++ "+language:elm&sort=stars&order=desc"
let
-- See https://developer.github.com/v3/search/#example for how to customize!
url =
"https://api.github.com/search/repositories?access_token="
++ Auth.token
++ "&q="
++ query
++ "+language:elm&sort=stars&order=desc"
task =
Http.get responseDecoder url
|> Task.map SetResults
in
Task.onError task (\_ -> Task.succeed (SetResults []))
task =
Http.get responseDecoder url
|> Task.map SetResults
in
Task.onError task (\_ -> Task.succeed (SetResults []))
responseDecoder : Decoder (List SearchResult.Model)
responseDecoder =
"items" := Json.Decode.list SearchResult.decoder
"items" := Json.Decode.list SearchResult.decoder
type alias Model =
{ query : String
, results : Dict SearchResult.ResultId SearchResult.Model
}
{ query : String
, results : Dict SearchResult.ResultId SearchResult.Model
}
initialModel : Model
initialModel =
{ query = "tutorial"
, results = Dict.empty
}
{ query = "tutorial"
, results = Dict.empty
}
view : Address Action -> Model -> Html
view address model =
div
[ class "content" ]
[ header
[]
[ h1 [] [ text "ElmHub" ]
, span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ]
view : Model -> Html Msg
view model =
div [ class "content" ]
[ header []
[ h1 [] [ text "ElmHub" ]
, span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ]
]
, input [ class "search-query", onInput SetQuery, defaultValue model.query ] []
, button [ class "search-button", onClick Search ] [ text "Search" ]
, ul [ class "results" ] (viewSearchResults model.results)
]
, input [ class "search-query", onInput address SetQuery, defaultValue model.query ] []
, button [ class "search-button", onClick address Search ] [ text "Search" ]
, ul
[ class "results" ]
(viewSearchResults address model.results)
]
viewSearchResults : Address Action -> Dict ResultId SearchResult.Model -> List Html
viewSearchResults address results =
results
|> Dict.values
|> List.sortBy (.stars >> negate)
|> List.map (viewSearchResult address)
viewSearchResults : Dict ResultId SearchResult.Model -> List Html
viewSearchResults results =
results
|> Dict.values
|> List.sortBy (.stars >> negate)
|> List.map (viewSearchResult address)
viewSearchResult : Address Action -> SearchResult.Model -> Html
viewSearchResult address result =
SearchResult.view
(Signal.forwardTo address (UpdateSearchResult result.id))
result
viewSearchResult : SearchResult.Model -> Html Msg
viewSearchResult result =
SearchResult.view (Signal.forwardTo address (UpdateSearchResult result.id))
result
onInput address wrap =
on "input" targetValue (\val -> Signal.message address (wrap val))
type Msg
= Search
| SetQuery String
| SetResults (List SearchResult.Model)
| UpdateSearchResult ResultId SearchResult.Msg
defaultValue str =
property "defaultValue" (Json.Encode.string str)
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Search ->
( model, searchFeed model.query )
SetQuery query ->
( { model | query = query }, Cmd.none )
type Action
= Search
| SetQuery String
| SetResults (List SearchResult.Model)
| UpdateSearchResult ResultId SearchResult.Action
update : Action -> Model -> ( Model, Effects Action )
update action model =
case action of
Search ->
( model, Effects.task (searchFeed model.query) )
SetQuery query ->
( { model | query = query }, Effects.none )
SetResults results ->
let
resultsById : Dict SearchResult.ResultId SearchResult.Model
resultsById =
results
|> List.map (\result -> ( result.id, result ))
|> Dict.fromList
in
( { model | results = resultsById }, Effects.none )
UpdateSearchResult id childAction ->
let
updated =
model.results
|> Dict.get id
|> Maybe.map (SearchResult.update childAction)
in
case updated of
Nothing ->
( model, Effects.none )
Just ( newChildModel, childEffects ) ->
SetResults results ->
let
effects =
Effects.map (UpdateSearchResult id) childEffects
newResults =
Dict.insert id newChildModel model.results
resultsById : Dict SearchResult.ResultId SearchResult.Model
resultsById =
results
|> List.map (\result -> ( result.id, result ))
|> Dict.fromList
in
( { model | results = newResults }, effects )
( { model | results = resultsById }, Cmd.none )
UpdateSearchResult id childMsg ->
let
updated =
model.results
|> Dict.get id
|> Maybe.map (SearchResult.update childMsg)
in
case updated of
Nothing ->
( model, Cmd.none )
Just ( newChildModel, childEffects ) ->
let
effects =
Effects.map (UpdateSearchResult id) childEffects
newResults =
Dict.insert id newChildModel model.results
in
( { model | results = newResults }, effects )

View File

@@ -1,27 +1,13 @@
module Main (..) where
module Main exposing (..)
import StartApp
import ElmHub exposing (..)
import Effects exposing (Effects)
import Task exposing (Task)
import Html exposing (Html)
main : Signal Html
main : Program Never
main =
app.html
app : StartApp.App Model
app =
StartApp.start
{ view = view
, update = update
, init = ( initialModel, Effects.task (searchFeed initialModel.query) )
, inputs = []
}
port tasks : Signal (Task Effects.Never ())
port tasks =
app.tasks
Html.App.program
{ view = view
, update = update
, init = ( initialModel, searchFeed initialModel.query )
, inputs = []
}

View File

@@ -4,16 +4,16 @@ Part 12
## Installation
```bash
elm package install
elm-package install
```
(Answer `y` at the prompt. In rare cases a known issue can cause the download
to fail; in that case, just run `elm package install` again.)
to fail; in that case, just run `elm-package install` again.)
## Building
```bash
elm live Main.elm --open -- --output=elm.js
elm-live Main.elm --open -- --output=elm.js
```
## References

View File

@@ -1,63 +1,59 @@
module SearchResult (..) where
module SearchResult exposing (..)
import Html exposing (..)
import Html.Attributes exposing (class, target, href, property)
import Html.Attributes exposing (class, target, href, property, defaultValue)
import Html.Events exposing (..)
import Signal exposing (Address)
import Effects exposing (Effects)
import Json.Decode exposing (Decoder, (:=))
import Json.Decode exposing (Decoder)
import Json.Decode.Pipeline exposing (..)
type alias Model =
{ id : Int
, name : String
, stars : Int
, expanded : Bool
}
{ id : Int
, name : String
, stars : Int
, expanded : Bool
}
type alias ResultId =
Int
Int
type Action
= Expand
| Collapse
type Msg
= Expand
| Collapse
decoder : Decoder Model
decoder =
decode Model
|> required "id" Json.Decode.int
|> required "full_name" Json.Decode.string
|> required "stargazers_count" Json.Decode.int
|> hardcoded True
decode Model
|> required "id" Json.Decode.int
|> required "full_name" Json.Decode.string
|> required "stargazers_count" Json.Decode.int
|> hardcoded True
update : Action -> Model -> ( Model, Effects Action )
update action model =
-- TODO implement Expand and Collapse logic
( model, Effects.none )
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
-- TODO implement Expand and Collapse logic
( model, Cmd.none )
view : Address Action -> Model -> Html
view address model =
li
[]
<| if model.expanded then
[ span [ class "star-count" ] [ text (toString model.stars) ]
, a
[ href ("https://github.com/" ++ model.name), target "_blank" ]
[ text model.name ]
, button
-- TODO when the user clicks, send a Collapse action
[ class "hide-result" ]
[ text "X" ]
]
else
[ button
-- TODO when the user clicks, send an Expand action
[ class "expand-result" ]
[ text "Show" ]
]
view : Model -> Html Msg
view model =
li [] <|
if model.expanded then
[ span [ class "star-count" ] [ text (toString model.stars) ]
, a [ href ("https://github.com/" ++ model.name), target "_blank" ]
[ text model.name ]
, button
-- TODO when the user clicks, send a Collapse action
[ class "hide-result" ]
[ text "X" ]
]
else
[ button
-- TODO when the user clicks, send an Expand action
[ class "expand-result" ]
[ text "Show" ]
]

View File

@@ -10,11 +10,9 @@
"exposed-modules": [],
"dependencies": {
"NoRedInk/elm-decode-pipeline": "1.0.0 <= v < 2.0.0",
"elm-lang/core": "3.0.0 <= v < 4.0.0",
"evancz/elm-effects": "2.0.0 <= v < 3.0.0",
"evancz/elm-html": "4.0.0 <= v < 5.0.0",
"evancz/elm-http": "3.0.0 <= v < 4.0.0",
"evancz/start-app": "2.0.0 <= v < 3.0.0"
"elm-lang/core": "4.0.1 <= v < 5.0.0",
"elm-lang/html": "1.0.0 <= v < 2.0.0",
"evancz/elm-http": "3.0.1 <= v < 4.0.0"
},
"elm-version": "0.16.0 <= v < 0.17.0"
"elm-version": "0.17.0 <= v < 0.18.0"
}

View File

@@ -9,13 +9,10 @@
],
"exposed-modules": [],
"dependencies": {
"deadfoxygrandpa/elm-test": "3.1.1 <= v < 4.0.0",
"elm-lang/core": "3.0.0 <= v < 4.0.0",
"evancz/elm-effects": "2.0.0 <= v < 3.0.0",
"evancz/elm-html": "4.0.0 <= v < 5.0.0",
"evancz/elm-http": "3.0.0 <= v < 4.0.0",
"evancz/start-app": "2.0.0 <= v < 3.0.0",
"laszlopandy/elm-console": "1.0.3 <= v < 2.0.0"
"NoRedInk/elm-decode-pipeline": "1.0.0 <= v < 2.0.0",
"elm-lang/core": "4.0.1 <= v < 5.0.0",
"elm-lang/html": "1.0.0 <= v < 2.0.0",
"evancz/elm-http": "3.0.1 <= v < 4.0.0"
},
"elm-version": "0.16.0 <= v < 0.17.0"
"elm-version": "0.17.0 <= v < 0.18.0"
}

View File

@@ -1,4 +1,4 @@
module ElmHub (..) where
module ElmHub exposing (..)
import Html exposing (..)
import Html.Attributes exposing (..)
@@ -7,133 +7,118 @@ import Html.Lazy exposing (..)
import Http
import Auth
import Task exposing (Task)
import Effects exposing (Effects)
import Json.Decode exposing (Decoder, (:=))
import Json.Decode exposing (Decoder)
import Json.Encode
import Signal exposing (Address)
import Dict exposing (Dict)
import SearchResult exposing (ResultId)
searchFeed : String -> Task x Action
searchFeed : String -> Task x Msg
searchFeed query =
let
-- See https://developer.github.com/v3/search/#example for how to customize!
url =
"https://api.github.com/search/repositories?access_token="
++ Auth.token
++ "&q="
++ query
++ "+language:elm&sort=stars&order=desc"
let
-- See https://developer.github.com/v3/search/#example for how to customize!
url =
"https://api.github.com/search/repositories?access_token="
++ Auth.token
++ "&q="
++ query
++ "+language:elm&sort=stars&order=desc"
task =
Http.get responseDecoder url
|> Task.map SetResults
in
Task.onError task (\_ -> Task.succeed (SetResults []))
task =
Http.get responseDecoder url
|> Task.map SetResults
in
Task.onError task (\_ -> Task.succeed (SetResults []))
responseDecoder : Decoder (List SearchResult.Model)
responseDecoder =
"items" := Json.Decode.list SearchResult.decoder
"items" := Json.Decode.list SearchResult.decoder
type alias Model =
{ query : String
, results : Dict SearchResult.ResultId SearchResult.Model
}
{ query : String
, results : Dict SearchResult.ResultId SearchResult.Model
}
initialModel : Model
initialModel =
{ query = "tutorial"
, results = Dict.empty
}
{ query = "tutorial"
, results = Dict.empty
}
view : Address Action -> Model -> Html
view address model =
div
[ class "content" ]
[ header
[]
[ h1 [] [ text "ElmHub" ]
, span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ]
view : Model -> Html Msg
view model =
div [ class "content" ]
[ header []
[ h1 [] [ text "ElmHub" ]
, span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ]
]
, input [ class "search-query", onInput SetQuery, defaultValue model.query ] []
, button [ class "search-button", onClick Search ] [ text "Search" ]
, ul [ class "results" ] (viewSearchResults model.results)
]
, input [ class "search-query", onInput address SetQuery, defaultValue model.query ] []
, button [ class "search-button", onClick address Search ] [ text "Search" ]
, ul
[ class "results" ]
(viewSearchResults address model.results)
]
viewSearchResults : Address Action -> Dict ResultId SearchResult.Model -> List Html
viewSearchResults address results =
results
|> Dict.values
|> List.sortBy (.stars >> negate)
|> List.map (viewSearchResult address)
viewSearchResults : Dict ResultId SearchResult.Model -> List Html
viewSearchResults results =
results
|> Dict.values
|> List.sortBy (.stars >> negate)
|> List.map (viewSearchResult address)
viewSearchResult : Address Action -> SearchResult.Model -> Html
viewSearchResult address result =
SearchResult.view
(Signal.forwardTo address (UpdateSearchResult result.id))
result
viewSearchResult result =
SearchResult.view (Signal.forwardTo address (UpdateSearchResult result.id))
result
onInput address wrap =
on "input" targetValue (\val -> Signal.message address (wrap val))
type Msg
= Search
| SetQuery String
| SetResults (List SearchResult.Model)
| UpdateSearchResult ResultId SearchResult.Action
defaultValue str =
property "defaultValue" (Json.Encode.string str)
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Search ->
( model, searchFeed model.query )
SetQuery query ->
( { model | query = query }, Cmd.none )
type Action
= Search
| SetQuery String
| SetResults (List SearchResult.Model)
| UpdateSearchResult ResultId SearchResult.Action
update : Action -> Model -> ( Model, Effects Action )
update action model =
case action of
Search ->
( model, Effects.task (searchFeed model.query) )
SetQuery query ->
( { model | query = query }, Effects.none )
SetResults results ->
let
resultsById : Dict SearchResult.ResultId SearchResult.Model
resultsById =
results
|> List.map (\result -> ( result.id, result ))
|> Dict.fromList
in
( { model | results = resultsById }, Effects.none )
UpdateSearchResult id childAction ->
let
updated =
model.results
|> Dict.get id
|> Maybe.map (SearchResult.update childAction)
in
case updated of
Nothing ->
( model, Effects.none )
Just ( newChildModel, childEffects ) ->
SetResults results ->
let
effects =
Effects.map (UpdateSearchResult id) childEffects
newResults =
Dict.insert id newChildModel model.results
resultsById : Dict SearchResult.ResultId SearchResult.Model
resultsById =
results
|> List.map (\result -> ( result.id, result ))
|> Dict.fromList
in
( { model | results = newResults }, effects )
( { model | results = resultsById }, Cmd.none )
UpdateSearchResult id childAction ->
let
updated =
model.results
|> Dict.get id
|> Maybe.map (SearchResult.update childAction)
in
case updated of
Nothing ->
( model, Cmd.none )
Just ( newChildModel, childEffects ) ->
let
effects =
Effects.map (UpdateSearchResult id) childEffects
newResults =
Dict.insert id newChildModel model.results
in
( { model | results = newResults }, effects )

View File

@@ -1,27 +1,13 @@
module Main (..) where
module Main exposing (..)
import StartApp
import ElmHub exposing (..)
import Effects exposing (Effects)
import Task exposing (Task)
import Html exposing (Html)
main : Signal Html
main : Program Never
main =
app.html
app : StartApp.App Model
app =
StartApp.start
{ view = view
, update = update
, init = ( initialModel, Effects.task (searchFeed initialModel.query) )
, inputs = []
}
port tasks : Signal (Task Effects.Never ())
port tasks =
app.tasks
Html.App.program
{ view = view
, update = update
, init = ( initialModel, searchFeed initialModel.query )
, inputs = []
}

View File

@@ -4,16 +4,16 @@ Part 13
## Installation
```bash
elm package install
elm-package install
```
(Answer `y` at the prompt. In rare cases a known issue can cause the download
to fail; in that case, just run `elm package install` again.)
to fail; in that case, just run `elm-package install` again.)
## Building
```bash
elm live Main.elm --open -- --output=elm.js
elm-live Main.elm --open -- --output=elm.js
```
## Compiling CSS

View File

@@ -1,65 +1,59 @@
module SearchResult (..) where
module SearchResult exposing (..)
import Html exposing (..)
import Html.Attributes exposing (class, target, href, property)
import Html.Attributes exposing (class, target, href, property, defaultValue)
import Html.Events exposing (..)
import Signal exposing (Address)
import Effects exposing (Effects)
import Json.Decode exposing (Decoder, (:=))
import Json.Decode exposing (Decoder)
import Json.Decode.Pipeline exposing (..)
type alias Model =
{ id : Int
, name : String
, stars : Int
, expanded : Bool
}
{ id : Int
, name : String
, stars : Int
, expanded : Bool
}
type alias ResultId =
Int
Int
type Action
= Expand
| Collapse
type Msg
= Expand
| Collapse
decoder : Decoder Model
decoder =
decode Model
|> required "id" Json.Decode.int
|> required "full_name" Json.Decode.string
|> required "stargazers_count" Json.Decode.int
|> hardcoded True
decode Model
|> required "id" Json.Decode.int
|> required "full_name" Json.Decode.string
|> required "stargazers_count" Json.Decode.int
|> hardcoded True
update : Action -> Model -> ( Model, Effects Action )
update action model =
case action of
Expand ->
( { model | expanded = True }, Effects.none )
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Expand ->
( { model | expanded = True }, Cmd.none )
Collapse ->
( { model | expanded = False }, Effects.none )
Collapse ->
( { model | expanded = False }, Cmd.none )
view : Address Action -> Model -> Html
view address model =
li
[]
<| if model.expanded then
[ span [ class "star-count" ] [ text (toString model.stars) ]
, a
[ href ("https://github.com/" ++ model.name), target "_blank" ]
[ text model.name ]
, button
[ class "hide-result", onClick address Collapse ]
[ text "X" ]
]
else
[ button
[ class "expand-result", onClick address Expand ]
[ text "Show" ]
]
view : Model -> Html Msg
view model =
li []
<| if model.expanded then
[ span [ class "star-count" ] [ text (toString model.stars) ]
, a [ href ("https://github.com/" ++ model.name), target "_blank" ]
[ text model.name ]
, button [ class "hide-result", onClick Collapse ]
[ text "X" ]
]
else
[ button [ class "expand-result", onClick Expand ]
[ text "Show" ]
]

View File

@@ -10,11 +10,10 @@
"exposed-modules": [],
"dependencies": {
"NoRedInk/elm-decode-pipeline": "1.0.0 <= v < 2.0.0",
"elm-lang/core": "3.0.0 <= v < 4.0.0",
"evancz/elm-effects": "2.0.0 <= v < 3.0.0",
"evancz/elm-html": "4.0.0 <= v < 5.0.0",
"evancz/elm-http": "3.0.0 <= v < 4.0.0",
"evancz/start-app": "2.0.0 <= v < 3.0.0"
"elm-lang/core": "4.0.1 <= v < 5.0.0",
"elm-lang/html": "1.0.0 <= v < 2.0.0",
"rtfeldman/elm-css": "1.0.0 <= v < 2.0.0",
"evancz/elm-http": "3.0.1 <= v < 4.0.0"
},
"elm-version": "0.16.0 <= v < 0.17.0"
"elm-version": "0.17.0 <= v < 0.18.0"
}

View File

@@ -10,12 +10,10 @@
"exposed-modules": [],
"dependencies": {
"NoRedInk/elm-decode-pipeline": "1.0.0 <= v < 2.0.0",
"elm-lang/core": "3.0.0 <= v < 4.0.0",
"evancz/elm-effects": "2.0.0 <= v < 3.0.0",
"evancz/elm-html": "4.0.0 <= v < 5.0.0",
"evancz/elm-http": "3.0.0 <= v < 4.0.0",
"elm-lang/core": "4.0.1 <= v < 5.0.0",
"elm-lang/html": "1.0.0 <= v < 2.0.0",
"rtfeldman/elm-css": "1.0.0 <= v < 2.0.0",
"evancz/start-app": "2.0.0 <= v < 3.0.0"
"evancz/elm-http": "3.0.1 <= v < 4.0.0"
},
"elm-version": "0.16.0 <= v < 0.17.0"
"elm-version": "0.17.0 <= v < 0.18.0"
}

View File

@@ -9,13 +9,11 @@
],
"exposed-modules": [],
"dependencies": {
"deadfoxygrandpa/elm-test": "3.1.1 <= v < 4.0.0",
"elm-lang/core": "3.0.0 <= v < 4.0.0",
"evancz/elm-effects": "2.0.0 <= v < 3.0.0",
"evancz/elm-html": "4.0.0 <= v < 5.0.0",
"evancz/elm-http": "3.0.0 <= v < 4.0.0",
"evancz/start-app": "2.0.0 <= v < 3.0.0",
"laszlopandy/elm-console": "1.0.3 <= v < 2.0.0"
"NoRedInk/elm-decode-pipeline": "1.0.0 <= v < 2.0.0",
"elm-lang/core": "4.0.1 <= v < 5.0.0",
"elm-lang/html": "1.0.0 <= v < 2.0.0",
"rtfeldman/elm-css": "1.0.0 <= v < 2.0.0",
"evancz/elm-http": "3.0.1 <= v < 4.0.0"
},
"elm-version": "0.16.0 <= v < 0.17.0"
"elm-version": "0.17.0 <= v < 0.18.0"
}

View File

@@ -50,14 +50,19 @@ model =
}
elmHubHeader : Html a
elmHubHeader =
header []
[ h1 [] [ text "ElmHub" ]
, span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ]
]
{-| TODO add a type annotation to this function
-}
view model =
div [ class "content" ]
[ header []
[ h1 [] [ text "ElmHub" ]
, span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ]
]
[ elmHubHeader
, ul [ class "results" ]
[{- TODO use model.results and viewSearchResults to display results -}]
]

View File

@@ -4,16 +4,16 @@ Part 2
## Installation
```bash
elm package install
elm-package install
```
(Answer `y` at the prompt. In rare cases a known issue can cause the download
to fail; in that case, just run `elm package install` again.)
to fail; in that case, just run `elm-package install` again.)
## Building
```bash
elm live Main.elm --open -- --output=elm.js
elm-live Main.elm --open -- --output=elm.js
```
## References

View File

@@ -3,6 +3,7 @@ module Main exposing (..)
import Html exposing (..)
import Html.App
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)
type alias Model =
@@ -50,19 +51,27 @@ initialModel =
}
view : Model -> Html Msg
view model =
div [ class "content" ]
[ header []
[ h1 [] [ text "ElmHub" ]
, span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ]
]
, ul [ class "results" ]
(List.map viewSearchResult model.results)
elmHubHeader : Html a
elmHubHeader =
header []
[ h1 [] [ text "ElmHub" ]
, span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ]
]
viewSearchResult : SearchResult -> Html Msg
{-| TODO revise this type annotation once we add our onClick handler
-}
view : Model -> Html a
view model =
div [ class "content" ]
[ elmHubHeader
, ul [ class "results" ] (List.map viewSearchResult model.results)
]
{-| TODO revise this type annotation once we add our onClick handler
-}
viewSearchResult : SearchResult -> Html a
viewSearchResult result =
li []
[ span [ class "star-count" ] [ text (toString result.stars) ]

View File

@@ -4,16 +4,16 @@ Part 3
## Installation
```bash
elm package install
elm-package install
```
(Answer `y` at the prompt. In rare cases a known issue can cause the download
to fail; in that case, just run `elm package install` again.)
to fail; in that case, just run `elm-package install` again.)
## Building
```bash
elm live Main.elm --open -- --output=elm.js
elm-live Main.elm --open -- --output=elm.js
```
## References

View File

@@ -3,6 +3,7 @@ module Main exposing (..)
import Html exposing (..)
import Html.App
import Html.Attributes exposing (..)
import Html.Events exposing (onClick, onInput)
type alias Model =
@@ -63,13 +64,12 @@ view model =
]
, input
[ class "search-query"
-- TODO when we receive onInput, set the query in the model
-- TODO onInput, set the query in the model
, defaultValue model.query
]
[]
, button [ class "search-button" ] [ text "Search" ]
, ul [ class "results" ]
(List.map viewSearchResult model.results)
, ul [ class "results" ] (List.map viewSearchResult model.results)
]

View File

@@ -4,16 +4,16 @@ Part 4
## Installation
```bash
elm package install
elm-package install
```
(Answer `y` at the prompt. In rare cases a known issue can cause the download
to fail; in that case, just run `elm package install` again.)
to fail; in that case, just run `elm-package install` again.)
## Building
```bash
elm live Main.elm --open -- --output=elm.js
elm-live Main.elm --open -- --output=elm.js
```
## References

View File

@@ -69,7 +69,7 @@ responseDecoder =
searchResultDecoder : Decoder SearchResult
searchResultDecoder =
-- See https://developer.github.com/v3/search/#example
-- TODO replace these `hardcoded` with calls to `require`
-- TODO replace these calls to `hardcoded` with calls to `require`
decode SearchResult
|> hardcoded 0
|> hardcoded ""

View File

@@ -4,14 +4,14 @@ Part 5
## Installation
```bash
elm package install
elm-package install
```
(Answer `y` at the prompt. In rare cases a known issue can cause the download
to fail; in that case, just run `elm package install` again.)
to fail; in that case, just run `elm-package install` again.)
## Building
```bash
elm live Main.elm --open -- --output=elm.js
elm-live Main.elm --open -- --output=elm.js
```

View File

@@ -1,200 +1,152 @@
module ElmHub (..) where
module ElmHub exposing (..)
import Auth
import Html exposing (..)
import Html.Attributes exposing (class, target, href, property)
import Html.Attributes exposing (class, target, href, property, defaultValue)
import Html.Events exposing (..)
import Http
import Task exposing (Task)
import Effects exposing (Effects)
import Json.Decode exposing (Decoder, (:=))
import Json.Decode exposing (Decoder)
import Json.Decode.Pipeline exposing (..)
import Json.Encode
import Signal exposing (Address)
searchFeed : String -> Effects Action
searchFeed : String -> Cmd Msg
searchFeed query =
let
url =
"https://api.github.com/search/repositories?access_token="
++ Auth.token
++ "&q="
++ query
++ "+language:elm&sort=stars&order=desc"
let
url =
"https://api.github.com/search/repositories?access_token="
++ Auth.token
++ "&q="
++ query
++ "+language:elm&sort=stars&order=desc"
-- TODO define task as:
--
-- task = performAction argument1 argument2 argument3
--
-- Use these "ingredients" to give `performAction` the arguments it needs:
--
-- Http.get
-- url
-- responseDecoder
-- HandleSearchResponse
-- HandleSearchError
--
-- Hint: http://package.elm-lang.org/packages/evancz/elm-http/3.0.0/Http#get
task =
"TODO performAction ..."
in
-- TODO replace this `Effects.none` with a call to:
--
-- Effects.task task
Effects.none
{-| Note: this will be a standard function in the next release of Elm.
Example:
type Action =
HandleResponse String | HandleError Http.Error
performAction
(\responseString -> HandleResponse responseString)
(\httpError -> HandleError httpError)
(Http.getString "https://google.com?q=something")
-}
performAction : (a -> b) -> (y -> b) -> Task y a -> Task x b
performAction successToAction errorToAction task =
let
successTask =
Task.map successToAction task
in
Task.onError successTask (\err -> Task.succeed (errorToAction err))
-- TODO use these ingredients to give Task.perform the arguments it needs:
--
-- Http.get
-- url
-- responseDecoder
-- HandleSearchResponse
-- HandleSearchError
--
-- Hint: http://package.elm-lang.org/packages/evancz/elm-http/3.0.0/Http#get
task =
"TODO call Task.perform ..."
in
-- TODO replace this Cmd.none with a call to Task.perform
Cmd.none
responseDecoder : Decoder (List SearchResult)
responseDecoder =
"items" := Json.Decode.list searchResultDecoder
Json.Decode.at [ "items" ] (Json.Decode.list searchResultDecoder)
searchResultDecoder : Decoder SearchResult
searchResultDecoder =
decode SearchResult
|> required "id" Json.Decode.int
|> required "full_name" Json.Decode.string
|> required "stargazers_count" Json.Decode.int
decode SearchResult
|> required "id" Json.Decode.int
|> required "full_name" Json.Decode.string
|> required "stargazers_count" Json.Decode.int
type alias Model =
{ query : String
, results : List SearchResult
, errorMessage : Maybe String
}
{ query : String
, results : List SearchResult
, errorMessage : Maybe String
}
type alias SearchResult =
{ id : ResultId
, name : String
, stars : Int
}
{ id : ResultId
, name : String
, stars : Int
}
type alias ResultId =
Int
Int
initialModel : Model
initialModel =
{ query = "tutorial"
, results = []
, errorMessage = Nothing
}
{ query = "tutorial"
, results = []
, errorMessage = Nothing
}
view : Address Action -> Model -> Html
view address model =
div
[ class "content" ]
[ header
[]
[ h1 [] [ text "ElmHub" ]
, span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ]
view : Model -> Html Msg
view model =
div [ class "content" ]
[ header []
[ h1 [] [ text "ElmHub" ]
, span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ]
]
, input [ class "search-query", onInput SetQuery, defaultValue model.query ] []
, button [ class "search-button", onClick Search ] [ text "Search" ]
, viewErrorMessage model.errorMessage
, ul [ class "results" ] (List.map viewSearchResult model.results)
]
, input [ class "search-query", onInput address SetQuery, defaultValue model.query ] []
, button [ class "search-button", onClick address Search ] [ text "Search" ]
, viewErrorMessage model.errorMessage
, ul
[ class "results" ]
(List.map (viewSearchResult address) model.results)
]
viewErrorMessage : Maybe String -> Html
viewErrorMessage : Maybe String -> Html a a
viewErrorMessage errorMessage =
case errorMessage of
Just message ->
div [ class "error" ] [ text message ]
case errorMessage of
Just message ->
div [ class "error" ] [ text message ]
Nothing ->
text ""
Nothing ->
text ""
onInput address wrap =
on "input" targetValue (\val -> Signal.message address (wrap val))
viewSearchResult : SearchResult -> Html Msg
viewSearchResult result =
li []
[ span [ class "star-count" ] [ text (toString result.stars) ]
, a [ href ("https://github.com/" ++ result.name), target "_blank" ]
[ text result.name ]
, button [ class "hide-result", onClick (DeleteById result.id) ]
[ text "X" ]
]
defaultValue str =
property "defaultValue" (Json.Encode.string str)
type Msg
= Search
| SetQuery String
| DeleteById ResultId
| HandleSearchResponse (List SearchResult)
| HandleSearchError Http.Error
viewSearchResult : Address Action -> SearchResult -> Html
viewSearchResult address result =
li
[]
[ span [ class "star-count" ] [ text (toString result.stars) ]
, a
[ href ("https://github.com/" ++ result.name), target "_blank" ]
[ text result.name ]
, button
[ class "hide-result", onClick address (DeleteById result.id) ]
[ text "X" ]
]
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Search ->
( model, searchFeed model.query )
HandleSearchResponse results ->
( { model | results = results }, Cmd.none )
type Action
= Search
| SetQuery String
| DeleteById ResultId
| HandleSearchResponse (List SearchResult)
| HandleSearchError Http.Error
HandleSearchError error ->
-- TODO if decoding failed, store the message in model.errorMessage
--
-- Hint 1: look for "decode" in the documentation for this union type:
-- http://package.elm-lang.org/packages/evancz/elm-http/3.0.1/Http#Error
--
-- Hint 2: to check if this is working, break responseDecoder
-- by changing "stargazers_count" to "description"
( model, Cmd.none )
SetQuery query ->
( { model | query = query }, Cmd.none )
update : Action -> Model -> ( Model, Effects Action )
update action model =
case action of
Search ->
( model, searchFeed model.query )
DeleteById idToHide ->
let
newResults =
model.results
|> List.filter (\{ id } -> id /= idToHide)
HandleSearchResponse results ->
( { model | results = results }, Effects.none )
HandleSearchError error ->
-- TODO if decoding failed, store the message in model.errorMessage
--
-- Hint 1: look for "decode" in the documentation for this union type:
-- http://package.elm-lang.org/packages/evancz/elm-http/3.0.0/Http#Error
--
-- Hint 2: to check if this is working, break responseDecoder
-- by changing "stargazers_count" to "description"
( model, Effects.none )
SetQuery query ->
( { model | query = query }, Effects.none )
DeleteById idToHide ->
let
newResults =
model.results
|> List.filter (\{ id } -> id /= idToHide)
newModel =
{ model | results = newResults }
in
( newModel, Effects.none )
newModel =
{ model | results = newResults }
in
( newModel, Cmd.none )

View File

@@ -1,27 +1,13 @@
module Main (..) where
module Main exposing (..)
import StartApp
import ElmHub exposing (..)
import Effects exposing (Effects)
import Task exposing (Task)
import Html exposing (Html)
main : Signal Html
main : Program Never
main =
app.html
app : StartApp.App Model
app =
StartApp.start
{ view = view
, update = update
, init = ( initialModel, searchFeed initialModel.query )
, inputs = []
}
port tasks : Signal (Task Effects.Never ())
port tasks =
app.tasks
Html.App.program
{ view = view
, update = update
, init = ( initialModel, searchFeed initialModel.query )
, inputs = []
}

View File

@@ -4,16 +4,16 @@ Part 6
## Installation
```bash
elm package install
elm-package install
```
(Answer `y` at the prompt. In rare cases a known issue can cause the download
to fail; in that case, just run `elm package install` again.)
to fail; in that case, just run `elm-package install` again.)
## Building
```bash
elm live Main.elm --open -- --output=elm.js
elm-live Main.elm --open -- --output=elm.js
```
## References

View File

@@ -10,11 +10,9 @@
"exposed-modules": [],
"dependencies": {
"NoRedInk/elm-decode-pipeline": "1.0.0 <= v < 2.0.0",
"elm-lang/core": "3.0.0 <= v < 4.0.0",
"evancz/elm-effects": "2.0.0 <= v < 3.0.0",
"evancz/elm-html": "4.0.0 <= v < 5.0.0",
"evancz/elm-http": "3.0.0 <= v < 4.0.0",
"evancz/start-app": "2.0.0 <= v < 3.0.0"
"elm-lang/core": "4.0.1 <= v < 5.0.0",
"elm-lang/html": "1.0.0 <= v < 2.0.0",
"evancz/elm-http": "3.0.1 <= v < 4.0.0"
},
"elm-version": "0.16.0 <= v < 0.17.0"
"elm-version": "0.17.0 <= v < 0.18.0"
}

View File

@@ -1,189 +1,141 @@
module ElmHub (..) where
module ElmHub exposing (..)
import Auth
import Html exposing (..)
import Html.Attributes exposing (class, target, href, property)
import Html.Attributes exposing (class, target, href, property, defaultValue)
import Html.Events exposing (..)
import Http
import Task exposing (Task)
import Effects exposing (Effects)
import Json.Decode exposing (Decoder, (:=))
import Json.Decode exposing (Decoder)
import Json.Decode.Pipeline exposing (..)
import Json.Encode
import Signal exposing (Address)
searchFeed : String -> Effects Action
searchFeed : String -> Cmd Msg
searchFeed query =
let
url =
"https://api.github.com/search/repositories?access_token="
++ Auth.token
++ "&q="
++ query
++ "+language:elm&sort=stars&order=desc"
task =
performAction
(\response -> HandleSearchResponse response)
(\error -> HandleSearchError error)
(Http.get responseDecoder url)
in
Effects.task task
let
url =
"https://api.github.com/search/repositories?access_token="
++ Auth.token
++ "&q="
++ query
++ "+language:elm&sort=stars&order=desc"
in
Task.perform HandleSearchError HandleSearchResponse (Http.get responseDecoder url)
responseDecoder : Decoder (List SearchResult)
responseDecoder =
"items" := Json.Decode.list searchResultDecoder
Json.Decode.at [ "items" ] (Json.Decode.list searchResultDecoder)
searchResultDecoder : Decoder SearchResult
searchResultDecoder =
decode SearchResult
|> required "id" Json.Decode.int
|> required "full_name" Json.Decode.string
|> required "stargazers_count" Json.Decode.int
{-| Note: this will be a standard function in the next release of Elm.
Example:
type Action =
HandleResponse String | HandleError Http.Error
performAction
(\responseString -> HandleResponse responseString)
(\httpError -> HandleError httpError)
(Http.getString "https://google.com?q=something")
-}
performAction : (a -> b) -> (y -> b) -> Task y a -> Task x b
performAction successToAction errorToAction task =
let
successTask =
Task.map successToAction task
in
Task.onError successTask (\err -> Task.succeed (errorToAction err))
decode SearchResult
|> required "id" Json.Decode.int
|> required "full_name" Json.Decode.string
|> required "stargazers_count" Json.Decode.int
type alias Model =
{ query : String
, results : List SearchResult
, errorMessage : Maybe String
}
{ query : String
, results : List SearchResult
, errorMessage : Maybe String
}
type alias SearchResult =
{ id : ResultId
, name : String
, stars : Int
}
{ id : ResultId
, name : String
, stars : Int
}
type alias ResultId =
Int
Int
initialModel : Model
initialModel =
{ query = "tutorial"
, results = []
, errorMessage = Nothing
}
{ query = "tutorial"
, results = []
, errorMessage = Nothing
}
view : Address Action -> Model -> Html
view address model =
div
[ class "content" ]
[ header
[]
[ h1 [] [ text "ElmHub" ]
, span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ]
view : Model -> Html Msg
view model =
div [ class "content" ]
[ header []
[ h1 [] [ text "ElmHub" ]
, span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ]
]
, input [ class "search-query", onInput SetQuery, defaultValue model.query ] []
, button [ class "search-button", onClick Search ] [ text "Search" ]
, viewErrorMessage model.errorMessage
, ul [ class "results" ] (List.map viewSearchResult model.results)
]
, input [ class "search-query", onInput address SetQuery, defaultValue model.query ] []
, button [ class "search-button", onClick address Search ] [ text "Search" ]
, viewErrorMessage model.errorMessage
, ul
[ class "results" ]
(List.map (viewSearchResult address) model.results)
]
viewErrorMessage : Maybe String -> Html
viewErrorMessage : Maybe String -> Html a
viewErrorMessage errorMessage =
case errorMessage of
Just message ->
div [ class "error" ] [ text message ]
case errorMessage of
Just message ->
div [ class "error" ] [ text message ]
Nothing ->
text ""
Nothing ->
text ""
onInput address wrap =
on "input" targetValue (\val -> Signal.message address (wrap val))
viewSearchResult : SearchResult -> Html Msg
viewSearchResult result =
li []
[ span [ class "star-count" ] [ text (toString result.stars) ]
, a [ href ("https://github.com/" ++ result.name), target "_blank" ]
[ text result.name ]
, button [ class "hide-result", onClick (DeleteById result.id) ]
[ text "X" ]
]
defaultValue str =
property "defaultValue" (Json.Encode.string str)
type Msg
= Search
| SetQuery String
| DeleteById ResultId
| HandleSearchResponse (List SearchResult)
| HandleSearchError Http.Error
viewSearchResult : Address Action -> SearchResult -> Html
viewSearchResult address result =
li
[]
[ span [ class "star-count" ] [ text (toString result.stars) ]
, a
[ href ("https://github.com/" ++ result.name), target "_blank" ]
[ text result.name ]
, button
[ class "hide-result", onClick address (DeleteById result.id) ]
[ text "X" ]
]
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Search ->
( model, searchFeed model.query )
HandleSearchResponse results ->
( { model | results = results }, Cmd.none )
type Action
= Search
| SetQuery String
| DeleteById ResultId
| HandleSearchResponse (List SearchResult)
| HandleSearchError Http.Error
HandleSearchError error ->
let
errorMessage =
case error of
Http.UnexpectedPayload message ->
Just message
_ ->
Nothing
in
( { model | errorMessage = errorMessage }, Cmd.none )
update : Action -> Model -> ( Model, Effects Action )
update action model =
case action of
Search ->
( model, searchFeed model.query )
SetQuery query ->
( { model | query = query }, Cmd.none )
HandleSearchResponse results ->
( { model | results = results }, Effects.none )
DeleteById idToHide ->
let
newResults =
model.results
|> List.filter (\{ id } -> id /= idToHide)
HandleSearchError error ->
let
errorMessage =
case error of
Http.UnexpectedPayload message ->
Just message
_ ->
Nothing
in
( { model | errorMessage = errorMessage }, Effects.none )
SetQuery query ->
( { model | query = query }, Effects.none )
DeleteById idToHide ->
let
newResults =
model.results
|> List.filter (\{ id } -> id /= idToHide)
newModel =
{ model | results = newResults }
in
( newModel, Effects.none )
newModel =
{ model | results = newResults }
in
( newModel, Cmd.none )

View File

@@ -1,27 +1,13 @@
module Main (..) where
module Main exposing (..)
import StartApp
import ElmHub exposing (..)
import Effects exposing (Effects)
import Task exposing (Task)
import Html exposing (Html)
main : Signal Html
main : Program Never
main =
app.html
app : StartApp.App Model
app =
StartApp.start
{ view = view
, update = update
, init = ( initialModel, searchFeed initialModel.query )
, inputs = []
}
port tasks : Signal (Task Effects.Never ())
port tasks =
app.tasks
Html.App.program
{ view = view
, update = update
, init = ( initialModel, searchFeed initialModel.query )
, inputs = []
}

View File

@@ -4,23 +4,23 @@ Part 7
## Installation
```bash
elm package install
elm-package install
```
(Answer `y` at the prompt. In rare cases a known issue can cause the download
to fail; in that case, just run `elm package install` again.)
to fail; in that case, just run `elm-package install` again.)
## Building
```bash
elm live Main.elm --open -- --output=elm.js
elm-live Main.elm --open -- --output=elm.js
```
## Running Tests
```bash
cd test
elm package install
elm-package install
elm test TestRunner.elm
```

View File

@@ -10,11 +10,9 @@
"exposed-modules": [],
"dependencies": {
"NoRedInk/elm-decode-pipeline": "1.0.0 <= v < 2.0.0",
"elm-lang/core": "3.0.0 <= v < 4.0.0",
"evancz/elm-effects": "2.0.0 <= v < 3.0.0",
"evancz/elm-html": "4.0.0 <= v < 5.0.0",
"evancz/elm-http": "3.0.0 <= v < 4.0.0",
"evancz/start-app": "2.0.0 <= v < 3.0.0"
"elm-lang/core": "4.0.1 <= v < 5.0.0",
"elm-lang/html": "1.0.0 <= v < 2.0.0",
"evancz/elm-http": "3.0.1 <= v < 4.0.0"
},
"elm-version": "0.16.0 <= v < 0.17.0"
"elm-version": "0.17.0 <= v < 0.18.0"
}

View File

@@ -9,14 +9,10 @@
],
"exposed-modules": [],
"dependencies": {
"deadfoxygrandpa/elm-test": "3.1.1 <= v < 4.0.0",
"NoRedInk/elm-decode-pipeline": "1.0.0 <= v < 2.0.0",
"elm-lang/core": "3.0.0 <= v < 4.0.0",
"evancz/elm-effects": "2.0.0 <= v < 3.0.0",
"evancz/elm-html": "4.0.0 <= v < 5.0.0",
"evancz/elm-http": "3.0.0 <= v < 4.0.0",
"evancz/start-app": "2.0.0 <= v < 3.0.0",
"laszlopandy/elm-console": "1.0.3 <= v < 2.0.0"
"elm-lang/core": "4.0.1 <= v < 5.0.0",
"elm-lang/html": "1.0.0 <= v < 2.0.0",
"evancz/elm-http": "3.0.1 <= v < 4.0.0"
},
"elm-version": "0.16.0 <= v < 0.17.0"
"elm-version": "0.17.0 <= v < 0.18.0"
}

View File

@@ -1,187 +1,142 @@
module ElmHub (..) where
module ElmHub exposing (..)
import Html exposing (..)
import Html.Attributes exposing (class, target, href, property)
import Html.Attributes exposing (class, target, href, property, defaultValue)
import Html.Events exposing (..)
import Http
import Auth
import Task exposing (Task)
import Effects exposing (Effects)
import Json.Decode exposing (Decoder, (:=))
import Json.Decode exposing (Decoder)
import Json.Decode.Pipeline exposing (..)
import Json.Encode
import Signal exposing (Address)
searchFeed : Address String -> String -> Effects Action
searchFeed address query =
let
getQueryUrl : String -> String
getQueryUrl query =
-- See https://developer.github.com/v3/search/#example for how to customize!
url =
"https://api.github.com/search/repositories?access_token="
"https://api.github.com/search/repositories?access_token="
++ Auth.token
++ "&q="
++ query
++ "+language:elm&sort=stars&order=desc"
-- These only talk to JavaScript ports now. They never result in Actions
-- actually do any actions themselves.
task =
performAction
(\_ -> DoNothing)
(\_ -> DoNothing)
(Signal.send address query)
in
Effects.task task
responseDecoder : Decoder (List SearchResult)
responseDecoder =
"items" := Json.Decode.list searchResultDecoder
Json.Decode.at [ "items" ] (Json.Decode.list searchResultDecoder)
searchResultDecoder : Decoder SearchResult
searchResultDecoder =
decode SearchResult
|> required "id" Json.Decode.int
|> required "full_name" Json.Decode.string
|> required "stargazers_count" Json.Decode.int
{-| Note: this will be a standard function in the next release of Elm.
Example:
type Action =
HandleResponse String | HandleError Http.Error
performAction
(\responseString -> HandleResponse responseString)
(\httpError -> HandleError httpError)
(Http.getString "https://google.com?q=something")
-}
performAction : (a -> b) -> (y -> b) -> Task y a -> Task x b
performAction successToAction errorToAction task =
let
successTask =
Task.map successToAction task
in
Task.onError successTask (\err -> Task.succeed (errorToAction err))
decode SearchResult
|> required "id" Json.Decode.int
|> required "full_name" Json.Decode.string
|> required "stargazers_count" Json.Decode.int
type alias Model =
{ query : String
, results : List SearchResult
, errorMessage : Maybe String
}
{ query : String
, results : List SearchResult
, errorMessage : Maybe String
}
type alias SearchResult =
{ id : ResultId
, name : String
, stars : Int
}
{ id : ResultId
, name : String
, stars : Int
}
type alias ResultId =
Int
Int
initialModel : Model
initialModel =
{ query = "tutorial"
, results = []
, errorMessage = Nothing
}
{ query = "tutorial"
, results = []
, errorMessage = Nothing
}
view : Address Action -> Model -> Html
view address model =
div
[ class "content" ]
[ header
[]
[ h1 [] [ text "ElmHub" ]
, span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ]
view : Model -> Html Msg
view model =
div [ class "content" ]
[ header []
[ h1 [] [ text "ElmHub" ]
, span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ]
]
, input [ class "search-query", onInput SetQuery, defaultValue model.query ] []
, button [ class "search-button", onClick Search ] [ text "Search" ]
, viewErrorMessage model.errorMessage
, ul [ class "results" ] (List.map viewSearchResult model.results)
]
, input [ class "search-query", onInput address SetQuery, defaultValue model.query ] []
, button [ class "search-button", onClick address Search ] [ text "Search" ]
, viewErrorMessage model.errorMessage
, ul
[ class "results" ]
(List.map (viewSearchResult address) model.results)
]
viewErrorMessage : Maybe String -> Html
viewErrorMessage : Maybe String -> Html a
viewErrorMessage errorMessage =
case errorMessage of
Just message ->
div [ class "error" ] [ text message ]
case errorMessage of
Just message ->
div [ class "error" ] [ text message ]
Nothing ->
text ""
Nothing ->
text ""
onInput address wrap =
on "input" targetValue (\val -> Signal.message address (wrap val))
viewSearchResult : SearchResult -> Html Msg
viewSearchResult result =
li []
[ span [ class "star-count" ] [ text (toString result.stars) ]
, a [ href ("https://github.com/" ++ result.name), target "_blank" ]
[ text result.name ]
, button [ class "hide-result", onClick (DeleteById result.id) ]
[ text "X" ]
]
defaultValue str =
property "defaultValue" (Json.Encode.string str)
type Msg
= Search
| SetQuery String
| DeleteById ResultId
| SetResults (List SearchResult)
| SetErrorMessage (Maybe String)
| DoNothing
viewSearchResult : Address Action -> SearchResult -> Html
viewSearchResult address result =
li
[]
[ span [ class "star-count" ] [ text (toString result.stars) ]
, a
[ href ("https://github.com/" ++ result.name), target "_blank" ]
[ text result.name ]
, button
[ class "hide-result", onClick address (DeleteById result.id) ]
[ text "X" ]
]
update : (String -> Cmd Msg) -> Msg -> Model -> ( Model, Cmd Msg )
update searchFeed msg model =
case msg of
Search ->
( model, searchFeed (getQueryUrl model.query) )
SetQuery query ->
( { model | query = query }, Cmd.none )
SetResults results ->
( { model | results = results }, Cmd.none )
SetErrorMessage errorMessage ->
( { model | errorMessage = errorMessage }, Cmd.none )
DeleteById idToHide ->
let
newResults =
model.results
|> List.filter (\{ id } -> id /= idToHide)
newModel =
{ model | results = newResults }
in
( newModel, Cmd.none )
DoNothing ->
( model, Cmd.none )
type Action
= Search
| SetQuery String
| DeleteById ResultId
| SetResults (List SearchResult)
| SetErrorMessage (Maybe String)
| DoNothing
update : Address String -> Action -> Model -> ( Model, Effects Action )
update searchAddress action model =
case action of
Search ->
( model, searchFeed searchAddress model.query )
SetQuery query ->
( { model | query = query }, Effects.none )
SetResults results ->
( { model | results = results }, Effects.none )
SetErrorMessage errorMessage ->
( { model | errorMessage = errorMessage }, Effects.none )
DeleteById idToHide ->
let
newResults =
model.results
|> List.filter (\{ id } -> id /= idToHide)
newModel =
{ model | results = newResults }
in
( newModel, Effects.none )
DoNothing ->
( model, Effects.none )
decodeGithubResponse : Json.Encode.Value -> Msg
decodeGithubResponse value =
-- TODO use Json.Decode.DecodeValue to decode the response into an Action.
--
-- Hint: look at ElmHub.elm, specifically the definition of Action and
-- the deefinition of responseDecoder
SetErrorMessage (Just "TODO decode the response!")

View File

@@ -1,57 +1,31 @@
module Main (..) where
port module Main exposing (..)
import StartApp
import ElmHub exposing (..)
import Effects exposing (Effects)
import Task exposing (Task)
import Html exposing (Html)
import Signal
import Json.Encode
import Json.Decode
import Html.App
import Json.Decode exposing (Value)
main : Signal Html
main : Program Never
main =
app.html
Html.App.program
{ view = view
, update = update githubSearch
, init = ( initialModel, githubSearch (getQueryUrl initialModel.query) )
, subscriptions = \_ -> githubResponse decodeResponse
}
app : StartApp.App Model
app =
StartApp.start
{ view = view
, update = update search.address
, init = ( initialModel, searchFeed search.address initialModel.query )
, inputs = [ responseActions ]
}
decodeResponse : Json.Decode.Value -> Msg
decodeResponse json =
case Json.Decode.decodeValue responseDecoder json of
Err err ->
SetErrorMessage (Just err)
Ok results ->
SetResults results
port tasks : Signal (Task Effects.Never ())
port tasks =
app.tasks
port githubSearch : String -> Cmd msg
search : Signal.Mailbox String
search =
Signal.mailbox ""
port githubSearch : Signal String
port githubSearch =
search.signal
responseActions : Signal Action
responseActions =
Signal.map decodeGithubResponse githubResponse
decodeGithubResponse : Json.Encode.Value -> Action
decodeGithubResponse value =
-- TODO use Json.Decode.DecodeValue to decode the response into an Action.
--
-- Hint: look at ElmHub.elm, specifically the definition of Action and
-- the deefinition of responseDecoder
SetErrorMessage (Just "TODO decode the response!")
port githubResponse : Signal Json.Encode.Value
port githubResponse : (Json.Decode.Value -> msg) -> Sub msg

View File

@@ -4,22 +4,22 @@ Part 8
## Installation
```bash
elm package install
elm-package install
```
(Answer `y` at the prompt. In rare cases a known issue can cause the download
to fail; in that case, just run `elm package install` again.)
to fail; in that case, just run `elm-package install` again.)
## Building
```bash
elm live Main.elm --open -- --output=elm.js
elm-live Main.elm --open -- --output=elm.js
```
## Running Tests
```bash
cd test
elm package install
elm-package install
elm test TestRunner.elm
```

View File

@@ -10,11 +10,9 @@
"exposed-modules": [],
"dependencies": {
"NoRedInk/elm-decode-pipeline": "1.0.0 <= v < 2.0.0",
"elm-lang/core": "3.0.0 <= v < 4.0.0",
"evancz/elm-effects": "2.0.0 <= v < 3.0.0",
"evancz/elm-html": "4.0.0 <= v < 5.0.0",
"evancz/elm-http": "3.0.0 <= v < 4.0.0",
"evancz/start-app": "2.0.0 <= v < 3.0.0"
"elm-lang/core": "4.0.1 <= v < 5.0.0",
"elm-lang/html": "1.0.0 <= v < 2.0.0",
"evancz/elm-http": "3.0.1 <= v < 4.0.0"
},
"elm-version": "0.16.0 <= v < 0.17.0"
"elm-version": "0.17.0 <= v < 0.18.0"
}

View File

@@ -9,14 +9,10 @@
],
"exposed-modules": [],
"dependencies": {
"deadfoxygrandpa/elm-test": "3.1.1 <= v < 4.0.0",
"NoRedInk/elm-decode-pipeline": "1.0.0 <= v < 2.0.0",
"elm-lang/core": "3.0.0 <= v < 4.0.0",
"evancz/elm-effects": "2.0.0 <= v < 3.0.0",
"evancz/elm-html": "4.0.0 <= v < 5.0.0",
"evancz/elm-http": "3.0.0 <= v < 4.0.0",
"evancz/start-app": "2.0.0 <= v < 3.0.0",
"laszlopandy/elm-console": "1.0.3 <= v < 2.0.0"
"elm-lang/core": "4.0.1 <= v < 5.0.0",
"elm-lang/html": "1.0.0 <= v < 2.0.0",
"evancz/elm-http": "3.0.1 <= v < 4.0.0"
},
"elm-version": "0.16.0 <= v < 0.17.0"
"elm-version": "0.17.0 <= v < 0.18.0"
}

View File

@@ -1,182 +1,126 @@
module ElmHub (..) where
module ElmHub exposing (..)
import Html exposing (..)
import Html.Attributes exposing (class, target, href, property)
import Html.Attributes exposing (class, target, href, property, defaultValue)
import Html.Events exposing (..)
import Http
import Auth
import Task exposing (Task)
import Effects exposing (Effects)
import Json.Decode exposing (Decoder, (:=))
import Json.Decode exposing (Decoder)
import Json.Decode.Pipeline exposing (..)
import Json.Encode
import Signal exposing (Address)
import Dict exposing (Dict)
searchFeed : Address String -> String -> Effects Action
searchFeed address query =
let
getQueryUrl : String -> String
getQueryUrl query =
-- See https://developer.github.com/v3/search/#example for how to customize!
url =
"https://api.github.com/search/repositories?access_token="
"https://api.github.com/search/repositories?access_token="
++ Auth.token
++ "&q="
++ query
++ "+language:elm&sort=stars&order=desc"
-- These only talk to JavaScript ports now. They never result in Actions
-- actually do any actions themselves.
task =
performAction
(\_ -> DoNothing)
(\_ -> DoNothing)
(Signal.send address query)
in
Effects.task task
responseDecoder : Decoder (List SearchResult)
responseDecoder =
"items" := Json.Decode.list searchResultDecoder
Json.Decode.at [ "items" ] (Json.Decode.list searchResultDecoder)
searchResultDecoder : Decoder SearchResult
searchResultDecoder =
decode SearchResult
|> required "id" Json.Decode.int
|> required "full_name" Json.Decode.string
|> required "stargazers_count" Json.Decode.int
{-| Note: this will be a standard function in the next release of Elm.
Example:
type Action =
HandleResponse String | HandleError Http.Error
performAction
(\responseString -> HandleResponse responseString)
(\httpError -> HandleError httpError)
(Http.getString "https://google.com?q=something")
-}
performAction : (a -> b) -> (y -> b) -> Task y a -> Task x b
performAction successToAction errorToAction task =
let
successTask =
Task.map successToAction task
in
Task.onError successTask (\err -> Task.succeed (errorToAction err))
decode SearchResult
|> required "id" Json.Decode.int
|> required "full_name" Json.Decode.string
|> required "stargazers_count" Json.Decode.int
type alias Model =
{ query : String
, results : Dict ResultId SearchResult
, errorMessage : Maybe String
}
{ query : String
, results : Dict ResultId SearchResult
, errorMessage : Maybe String
}
type alias SearchResult =
{ id : ResultId
, name : String
, stars : Int
}
{ id : ResultId
, name : String
, stars : Int
}
type alias ResultId =
Int
Int
initialModel : Model
initialModel =
{ query = "tutorial"
, results = Dict.empty
, errorMessage = Nothing
}
{ query = "tutorial"
, results = Dict.empty
, errorMessage = Nothing
}
view : Address Action -> Model -> Html
view address model =
div
[ class "content" ]
[ header
[]
[ h1 [] [ text "ElmHub" ]
, span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ]
view : Model -> Html Msg
view model =
div [ class "content" ]
[ header []
[ h1 [] [ text "ElmHub" ]
, span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ]
]
, input [ class "search-query", onInput SetQuery, defaultValue model.query ] []
, button [ class "search-button", onClick Search ] [ text "Search" ]
, ul [ class "results" ] (viewSearchResults model.results)
]
, input [ class "search-query", onInput address SetQuery, defaultValue model.query ] []
, button [ class "search-button", onClick address Search ] [ text "Search" ]
, ul
[ class "results" ]
(viewSearchResults address model.results)
]
viewSearchResults : Address Action -> Dict ResultId SearchResult -> List Html
viewSearchResults address results =
-- TODO sort by star count and render
[]
onInput address wrap =
on "input" targetValue (\val -> Signal.message address (wrap val))
defaultValue str =
property "defaultValue" (Json.Encode.string str)
viewSearchResult : Address Action -> SearchResult -> Html
viewSearchResult address result =
li
viewSearchResults : Dict ResultId SearchResult -> List (Html Msg)
viewSearchResults results =
-- TODO sort by star count and render
[]
[ span [ class "star-count" ] [ text (toString result.stars) ]
, a
[ href ("https://github.com/" ++ result.name), target "_blank" ]
[ text result.name ]
, button
[ class "hide-result", onClick address (DeleteById result.id) ]
[ text "X" ]
]
type Action
= Search
| SetQuery String
| DeleteById ResultId
| SetResults (List SearchResult)
| SetErrorMessage (Maybe String)
| DoNothing
viewSearchResult : SearchResult -> Html Msg
viewSearchResult result =
li []
[ span [ class "star-count" ] [ text (toString result.stars) ]
, a [ href ("https://github.com/" ++ result.name), target "_blank" ]
[ text result.name ]
, button [ class "hide-result", onClick (DeleteById result.id) ]
[ text "X" ]
]
update : Address String -> Action -> Model -> ( Model, Effects Action )
update searchAddress action model =
case action of
Search ->
( model, searchFeed searchAddress model.query )
type Msg
= Search
| SetQuery String
| DeleteById ResultId
| SetResults (List SearchResult)
| SetErrorMessage (Maybe String)
| DoNothing
SetQuery query ->
( { model | query = query }, Effects.none )
SetResults results ->
let
resultsById : Dict ResultId SearchResult
resultsById =
-- TODO convert results list into a Dict
Dict.empty
in
( { model | results = resultsById }, Effects.none )
update : (String -> Cmd Msg) -> Msg -> Model -> ( Model, Cmd Msg )
update searchFeed msg model =
case msg of
Search ->
( model, searchFeed (getQueryUrl model.query) )
DeleteById id ->
-- TODO delete the result with the given id
( model, Effects.none )
SetQuery query ->
( { model | query = query }, Cmd.none )
SetErrorMessage errorMessage ->
( { model | errorMessage = errorMessage }, Effects.none )
SetResults results ->
let
resultsById : Dict ResultId SearchResult
resultsById =
-- TODO convert results list into a Dict
Dict.empty
in
( { model | results = resultsById }, Cmd.none )
DoNothing ->
( model, Effects.none )
DeleteById id ->
-- TODO delete the result with the given id
( model, Cmd.none )
SetErrorMessage errorMessage ->
( { model | errorMessage = errorMessage }, Cmd.none )
DoNothing ->
( model, Cmd.none )

View File

@@ -1,58 +1,31 @@
module Main (..) where
port module Main exposing (..)
import StartApp
import ElmHub exposing (..)
import Effects exposing (Effects)
import Task exposing (Task)
import Html exposing (Html)
import Signal
import Json.Encode
import Json.Decode
import Html.App
import Json.Decode exposing (Value)
main : Signal Html
main : Program Never
main =
app.html
Html.App.program
{ view = view
, update = update githubSearch
, init = ( initialModel, githubSearch (getQueryUrl initialModel.query) )
, subscriptions = \_ -> githubResponse decodeResponse
}
app : StartApp.App Model
app =
StartApp.start
{ view = view
, update = update search.address
, init = ( initialModel, searchFeed search.address initialModel.query )
, inputs = [ responseActions ]
}
decodeResponse : Json.Decode.Value -> Msg
decodeResponse json =
case Json.Decode.decodeValue responseDecoder json of
Err err ->
SetErrorMessage (Just err)
Ok results ->
SetResults results
port tasks : Signal (Task Effects.Never ())
port tasks =
app.tasks
port githubSearch : String -> Cmd msg
search : Signal.Mailbox String
search =
Signal.mailbox ""
port githubSearch : Signal String
port githubSearch =
search.signal
responseActions : Signal Action
responseActions =
Signal.map decodeGithubResponse githubResponse
decodeGithubResponse : Json.Encode.Value -> Action
decodeGithubResponse value =
case Json.Decode.decodeValue responseDecoder value of
Ok results ->
SetResults results
Err message ->
SetErrorMessage (Just message)
port githubResponse : Signal Json.Encode.Value
port githubResponse : (Json.Decode.Value -> msg) -> Sub msg

View File

@@ -4,14 +4,14 @@ Part 9
## Installation
```bash
elm package install
elm-package install
```
(Answer `y` at the prompt. In rare cases a known issue can cause the download
to fail; in that case, just run `elm package install` again.)
to fail; in that case, just run `elm-package install` again.)
## Building
```bash
elm live Main.elm --open -- --output=elm.js
elm-live Main.elm --open -- --output=elm.js
```

View File

@@ -10,11 +10,9 @@
"exposed-modules": [],
"dependencies": {
"NoRedInk/elm-decode-pipeline": "1.0.0 <= v < 2.0.0",
"elm-lang/core": "3.0.0 <= v < 4.0.0",
"evancz/elm-effects": "2.0.0 <= v < 3.0.0",
"evancz/elm-html": "4.0.0 <= v < 5.0.0",
"evancz/elm-http": "3.0.0 <= v < 4.0.0",
"evancz/start-app": "2.0.0 <= v < 3.0.0"
"elm-lang/core": "4.0.1 <= v < 5.0.0",
"elm-lang/html": "1.0.0 <= v < 2.0.0",
"evancz/elm-http": "3.0.1 <= v < 4.0.0"
},
"elm-version": "0.16.0 <= v < 0.17.0"
"elm-version": "0.17.0 <= v < 0.18.0"
}