From 8b282cb2042291cf902988f90e322e08780a1c74 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Sun, 27 Mar 2016 06:27:01 -0700 Subject: [PATCH] Update 9 --- stages/9/ElmHub.elm | 147 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 stages/9/ElmHub.elm diff --git a/stages/9/ElmHub.elm b/stages/9/ElmHub.elm new file mode 100644 index 0000000..d5eb24b --- /dev/null +++ b/stages/9/ElmHub.elm @@ -0,0 +1,147 @@ +module ElmHub (..) where + +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) +import Http +import Task exposing (Task) +import Effects exposing (Effects) +import Json.Decode exposing (Decoder, (:=)) +import Json.Encode +import Signal exposing (Address) +import Dict exposing (Dict) + + +searchFeed : String -> Task x Action +searchFeed query = + let + -- See https://developer.github.com/v3/search/#example for how to customize! + url = + "https://api.github.com/search/repositories?q=" + ++ query + ++ "+language:elm" + + task = + Http.get responseDecoder url + |> Task.map SetResults + in + Task.onError task (\_ -> Task.succeed (SetResults [])) + + +responseDecoder : Decoder (List SearchResult) +responseDecoder = + "items" := Json.Decode.list searchResultDecoder + + +searchResultDecoder : Decoder SearchResult +searchResultDecoder = + Json.Decode.object3 + SearchResult + ("id" := Json.Decode.int) + ("full_name" := Json.Decode.string) + ("stargazers_count" := Json.Decode.int) + + +type alias Model = + { query : String + , results : Dict ResultId SearchResult + } + + +type alias SearchResult = + { id : ResultId + , name : String + , stars : Int + } + + +type alias ResultId = + Int + + +initialModel : Model +initialModel = + { 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.”" ] + ] + , 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 = + results + |> Dict.values + |> List.sortBy (.stars >> negate) + |> List.map (viewSearchResult address) + + +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 + [] + [ 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) + + +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 ResultId SearchResult + 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 )