diff --git a/part10/ElmHub.elm b/part10/ElmHub.elm deleted file mode 100644 index 6275b46..0000000 --- a/part10/ElmHub.elm +++ /dev/null @@ -1,19 +0,0 @@ -module ElmHub exposing (..) - -import Json.Decode exposing (Decoder) -import Json.Decode.Pipeline exposing (..) - - -type alias SearchResult = - { id : Int - , name : String - , stars : Int - } - - -searchResultDecoder : Decoder SearchResult -searchResultDecoder = - decode SearchResult - |> required "id" Json.Decode.int - |> required "full_name" Json.Decode.string - |> required "stargazers_count" Json.Decode.int diff --git a/part10/Main.elm b/part10/Main.elm index a5576be..cb7af06 100644 --- a/part10/Main.elm +++ b/part10/Main.elm @@ -1,7 +1,7 @@ module Main exposing (..) -import Pages.Home -import Pages.Repository +import Page.Home +import Page.Repository import Navigation import Page exposing (Page(..)) import Tuple2 @@ -11,13 +11,14 @@ import Html.App as Html type Model - = Home Pages.Home.Model - | Repository Pages.Repository.Model + = Home Page.Home.Model + | Repository Page.Repository.Model + | NotFound type Msg - = HomeMsg Pages.Home.Msg - | RepositoryMsg Pages.Repository.Msg + = HomeMsg Page.Home.Msg + | RepositoryMsg Page.Repository.Msg main : Program Never @@ -35,17 +36,37 @@ subscriptions : Model -> Sub Msg subscriptions model = case model of Home pageModel -> - Pages.Home.subscriptions pageModel + -- TODO use Sub.map to translate from Page.Home.subscriptions + Page.Home.subscriptions pageModel |> Sub.map HomeMsg Repository pageModel -> + -- Repository has no subscriptions, so there's nothing to translate! + Sub.none + + NotFound -> + -- NotFound has no subscriptions, so there's nothing to translate! Sub.none init : Result String Page -> ( Model, Cmd Msg ) init result = - Home (fst Pages.Home.init) - |> urlUpdate result + case result of + Ok (Page.Home) -> + -- TODO use Html.map to translate from Page.Home.view + Page.Home.init + |> Tuple2.mapEach Home (Cmd.map HomeMsg) + + Ok (Page.Repository repoOwner repoName) -> + -- TODO use Html.map to translate from Page.Repository.view + Page.Repository.init repoOwner repoName + |> Tuple2.mapEach Repository (Cmd.map RepositoryMsg) + + Ok (Page.NotFound) -> + ( NotFound, Cmd.none ) + + Err err -> + ( NotFound, Cmd.none ) view : Model -> Html Msg @@ -53,13 +74,18 @@ view model = withHeader <| case model of Home pageModel -> - Pages.Home.view pageModel + -- TODO use Html.map to translate from Page.Home.view + Page.Home.view pageModel |> Html.map HomeMsg Repository pageModel -> - Pages.Repository.view pageModel + -- TODO use Html.map to translate from Page.Repository.view + Page.Repository.view pageModel |> Html.map RepositoryMsg + NotFound -> + h1 [] [ text "Page Not Found" ] + withHeader : Html msg -> Html msg withHeader innerContent = @@ -76,14 +102,20 @@ update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case ( msg, model ) of ( HomeMsg pageMsg, Home pageModel ) -> - Pages.Home.update pageMsg pageModel + -- TODO use Tuple2.mapEach and (Cmd.map HomeMsg) + -- to translate from Page.Home.update + -- + -- mapEach : (a -> newA) -> (b -> newB) -> ( a, b ) -> ( newA, newB ) + Page.Home.update pageMsg pageModel |> Tuple2.mapEach Home (Cmd.map HomeMsg) ( RepositoryMsg pageMsg, Repository pageModel ) -> - Pages.Repository.update pageMsg pageModel + -- TODO use Tuple2.mapEach and (Cmd.map RepositoryMsg) + -- to translate from Page.Repository.update + Page.Repository.update pageMsg pageModel |> Tuple2.mapEach Repository (Cmd.map RepositoryMsg) - ( _, _ ) -> + _ -> ( model, Cmd.none ) @@ -91,15 +123,23 @@ urlUpdate : Result String Page -> Model -> ( Model, Cmd Msg ) urlUpdate result model = case result of Ok (Page.Home) -> - Pages.Home.init + -- TODO use Tuple2.mapEach and (Cmd.map HomeMsg) + -- to translate from Page.Home.init + -- + -- mapEach : (a -> newA) -> (b -> newB) -> ( a, b ) -> ( newA, newB ) + Page.Home.init |> Tuple2.mapEach Home (Cmd.map HomeMsg) - Ok (Page.Repository id) -> - Pages.Repository.init id + Ok (Page.Repository repoOwner repoName) -> + -- TODO use Tuple2.mapEach and (Cmd.map RepositoryMsg) + -- to translate from Page.Repository.init + -- + -- HINT: Page.Repository.init is a function that takes 2 arguments. + Page.Repository.init repoOwner repoName |> Tuple2.mapEach Repository (Cmd.map RepositoryMsg) - Ok NotFound -> - ( model, Cmd.none ) + Ok (Page.NotFound) -> + ( NotFound, Cmd.none ) - Err _ -> - ( model, Navigation.modifyUrl "/" ) + Err err -> + ( NotFound, Cmd.none ) diff --git a/part10/Page.elm b/part10/Page.elm index 8b17668..0ed0b1f 100644 --- a/part10/Page.elm +++ b/part10/Page.elm @@ -7,7 +7,7 @@ import String type Page = Home - | Repository Int + | Repository String String | NotFound @@ -15,7 +15,7 @@ pageParser : Parser (Page -> a) a pageParser = UrlParser.oneOf [ format Home (s "") - , format Repository (s "repositories" int) + , format Repository (s "repositories" string string) ] diff --git a/part10/Pages/Home.elm b/part10/Page/Home.elm similarity index 86% rename from part10/Pages/Home.elm rename to part10/Page/Home.elm index 827d921..4392ae7 100644 --- a/part10/Pages/Home.elm +++ b/part10/Page/Home.elm @@ -1,14 +1,29 @@ -port module Pages.Home exposing (..) +port module Page.Home exposing (..) import Html exposing (..) import Html.Attributes exposing (class, target, href, property, defaultValue) import Html.Events exposing (..) import Auth import Json.Decode exposing (Decoder) -import ElmHub exposing (SearchResult) +import Json.Decode.Pipeline exposing (decode, required) import Navigation +type alias SearchResult = + { id : Int + , name : String + , stars : Int + } + + +searchResultDecoder : Decoder SearchResult +searchResultDecoder = + decode SearchResult + |> required "id" Json.Decode.int + |> required "full_name" Json.Decode.string + |> required "stargazers_count" Json.Decode.int + + getQueryString : String -> String getQueryString query = -- See https://developer.github.com/v3/search/#example for how to customize! @@ -21,7 +36,7 @@ getQueryString query = responseDecoder : Decoder (List SearchResult) responseDecoder = - Json.Decode.at [ "items" ] (Json.Decode.list ElmHub.searchResultDecoder) + Json.Decode.at [ "items" ] (Json.Decode.list searchResultDecoder) type alias Model = @@ -70,7 +85,7 @@ viewSearchResult : SearchResult -> Html Msg viewSearchResult result = li [] [ span [ class "star-count" ] [ text (toString result.stars) ] - , a [ onClick (Visit ("/repositories/" ++ toString result.id)) ] + , a [ onClick (Visit ("/repositories/" ++ result.name)) ] [ text result.name ] , button [ class "hide-result", onClick (DeleteById result.id) ] [ text "X" ] diff --git a/part10/Page/Repository.elm b/part10/Page/Repository.elm new file mode 100644 index 0000000..6e02cd1 --- /dev/null +++ b/part10/Page/Repository.elm @@ -0,0 +1,136 @@ +module Page.Repository exposing (..) + +import Html exposing (..) +import Html.Attributes exposing (class, target, href, property, defaultValue, src) +import Auth +import Http +import Task +import Json.Decode exposing (Decoder, int, string, list) +import Json.Decode.Pipeline exposing (decode, required) + + +type alias Model = + { repoOwner : String + , repoName : String + , repository : Maybe Repository + } + + +type alias Repository = + { id : Int + , issues : Int + , forks : Int + , watchers : Int + , owner : User + , description : String + } + + +type alias User = + { id : Int + , username : String + , avatarUrl : String + , profileUrl : String + } + + +userDecoder : Decoder User +userDecoder = + decode User + |> required "id" int + |> required "login" string + |> required "avatar_url" string + |> required "url" string + + +repoDecoder : Decoder Repository +repoDecoder = + decode Repository + |> required "id" int + |> required "open_issues_count" int + |> required "forks" int + |> required "watchers" int + |> required "owner" userDecoder + |> required "description" string + + +init : String -> String -> ( Model, Cmd Msg ) +init repoOwner repoName = + ( { repoOwner = repoOwner + , repoName = repoName + , repository = Nothing + } + , getRepoInfo repoOwner repoName + ) + + +view : Model -> Html Msg +view model = + let + ownerUrl = + "https://github.com/" ++ model.repoOwner + + repoUrl = + ownerUrl ++ "/" ++ model.repoName + + details = + model.repository + |> Maybe.map viewDetails + |> Maybe.withDefault (text "") + in + div [] + [ h1 [] + [ a [ href repoUrl ] [ text model.repoName ] ] + , details + ] + + +viewDetails : Repository -> Html Msg +viewDetails repo = + div [] + [ p [] [ text repo.description ] + , h2 [] + [ a [ href repo.owner.profileUrl ] + [ img [ class "profile-photo", src repo.owner.avatarUrl ] [] + , text repo.owner.username + ] + ] + , table [] + [ tbody [] + [ tr [] [ th [] [ text "issues" ], td [] [ text (toString repo.issues) ] ] + , tr [] [ th [] [ text "forks" ], td [] [ text (toString repo.forks) ] ] + , tr [] [ th [] [ text "watchers" ], td [] [ text (toString repo.watchers) ] ] + ] + ] + ] + + +type Msg + = HandleRepoError Http.Error + | HandleRepoResponse Repository + + +getRepoInfo : String -> String -> Cmd Msg +getRepoInfo repoOwner repoName = + let + url = + "https://api.github.com/repos/" + ++ repoOwner + ++ "/" + ++ repoName + ++ "?access_token=" + ++ Auth.token + |> Debug.log "getRepoInfo" + in + Http.get repoDecoder url + |> Task.perform HandleRepoError HandleRepoResponse + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + HandleRepoError err -> + ( model, Cmd.none ) + + HandleRepoResponse repository -> + ( { model | repository = Just repository }, Cmd.none ) diff --git a/part10/Pages/Repository.elm b/part10/Pages/Repository.elm deleted file mode 100644 index 075ee09..0000000 --- a/part10/Pages/Repository.elm +++ /dev/null @@ -1,37 +0,0 @@ -module Pages.Repository exposing (..) - -import Html exposing (..) -import Html.Attributes exposing (class, target, href, property, defaultValue) -import ElmHub exposing (SearchResult) - - -type alias Model = - SearchResult - - -init : Int -> ( Model, Cmd Msg ) -init id = - ( { id = id - , name = "" - , stars = id - } - , Cmd.none - ) - - -view : Model -> Html Msg -view model = - div [] - [ div [] [ text ("repo" ++ toString model.stars) ] - ] - - -type Msg - = NoOp - - -update : Msg -> Model -> ( Model, Cmd Msg ) -update msg model = - case msg of - NoOp -> - ( model, Cmd.none ) diff --git a/part10/README.md b/part10/README.md index 5dbc3c7..97faaca 100644 --- a/part10/README.md +++ b/part10/README.md @@ -1,5 +1,5 @@ -Part 8 -====== +Part 10 +======= The instructor will paste notes from the lesson, including code examples from Q&A, in [this document](https://docs.google.com/document/d/1ApuSOk9DP0YsQrxhW7-WE8UOEAV4PPnLDDeqUOL2o5k/edit?usp=sharing). diff --git a/part10/index.html b/part10/index.html index 67af708..0e6f8e9 100644 --- a/part10/index.html +++ b/part10/index.html @@ -7,7 +7,7 @@ - +