Flesh out part10

This commit is contained in:
Richard Feldman
2016-09-02 22:21:10 -07:00
parent 5fd5863b53
commit 6ec25f4f57
8 changed files with 221 additions and 86 deletions

View File

@@ -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

View File

@@ -1,7 +1,7 @@
module Main exposing (..) module Main exposing (..)
import Pages.Home import Page.Home
import Pages.Repository import Page.Repository
import Navigation import Navigation
import Page exposing (Page(..)) import Page exposing (Page(..))
import Tuple2 import Tuple2
@@ -11,13 +11,14 @@ import Html.App as Html
type Model type Model
= Home Pages.Home.Model = Home Page.Home.Model
| Repository Pages.Repository.Model | Repository Page.Repository.Model
| NotFound
type Msg type Msg
= HomeMsg Pages.Home.Msg = HomeMsg Page.Home.Msg
| RepositoryMsg Pages.Repository.Msg | RepositoryMsg Page.Repository.Msg
main : Program Never main : Program Never
@@ -35,17 +36,37 @@ subscriptions : Model -> Sub Msg
subscriptions model = subscriptions model =
case model of case model of
Home pageModel -> Home pageModel ->
Pages.Home.subscriptions pageModel -- TODO use Sub.map to translate from Page.Home.subscriptions
Page.Home.subscriptions pageModel
|> Sub.map HomeMsg |> Sub.map HomeMsg
Repository pageModel -> 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 Sub.none
init : Result String Page -> ( Model, Cmd Msg ) init : Result String Page -> ( Model, Cmd Msg )
init result = init result =
Home (fst Pages.Home.init) case result of
|> urlUpdate result 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 view : Model -> Html Msg
@@ -53,13 +74,18 @@ view model =
withHeader <| withHeader <|
case model of case model of
Home pageModel -> Home pageModel ->
Pages.Home.view pageModel -- TODO use Html.map to translate from Page.Home.view
Page.Home.view pageModel
|> Html.map HomeMsg |> Html.map HomeMsg
Repository pageModel -> Repository pageModel ->
Pages.Repository.view pageModel -- TODO use Html.map to translate from Page.Repository.view
Page.Repository.view pageModel
|> Html.map RepositoryMsg |> Html.map RepositoryMsg
NotFound ->
h1 [] [ text "Page Not Found" ]
withHeader : Html msg -> Html msg withHeader : Html msg -> Html msg
withHeader innerContent = withHeader innerContent =
@@ -76,14 +102,20 @@ update : Msg -> Model -> ( Model, Cmd Msg )
update msg model = update msg model =
case ( msg, model ) of case ( msg, model ) of
( HomeMsg pageMsg, Home pageModel ) -> ( 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) |> Tuple2.mapEach Home (Cmd.map HomeMsg)
( RepositoryMsg pageMsg, Repository pageModel ) -> ( 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) |> Tuple2.mapEach Repository (Cmd.map RepositoryMsg)
( _, _ ) -> _ ->
( model, Cmd.none ) ( model, Cmd.none )
@@ -91,15 +123,23 @@ urlUpdate : Result String Page -> Model -> ( Model, Cmd Msg )
urlUpdate result model = urlUpdate result model =
case result of case result of
Ok (Page.Home) -> 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) |> Tuple2.mapEach Home (Cmd.map HomeMsg)
Ok (Page.Repository id) -> Ok (Page.Repository repoOwner repoName) ->
Pages.Repository.init id -- 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) |> Tuple2.mapEach Repository (Cmd.map RepositoryMsg)
Ok NotFound -> Ok (Page.NotFound) ->
( model, Cmd.none ) ( NotFound, Cmd.none )
Err _ -> Err err ->
( model, Navigation.modifyUrl "/" ) ( NotFound, Cmd.none )

View File

@@ -7,7 +7,7 @@ import String
type Page type Page
= Home = Home
| Repository Int | Repository String String
| NotFound | NotFound
@@ -15,7 +15,7 @@ pageParser : Parser (Page -> a) a
pageParser = pageParser =
UrlParser.oneOf UrlParser.oneOf
[ format Home (s "") [ format Home (s "")
, format Repository (s "repositories" </> int) , format Repository (s "repositories" </> string </> string)
] ]

View File

@@ -1,14 +1,29 @@
port module Pages.Home exposing (..) port module Page.Home exposing (..)
import Html exposing (..) import Html exposing (..)
import Html.Attributes exposing (class, target, href, property, defaultValue) import Html.Attributes exposing (class, target, href, property, defaultValue)
import Html.Events exposing (..) import Html.Events exposing (..)
import Auth import Auth
import Json.Decode exposing (Decoder) import Json.Decode exposing (Decoder)
import ElmHub exposing (SearchResult) import Json.Decode.Pipeline exposing (decode, required)
import Navigation 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 : String -> String
getQueryString query = getQueryString query =
-- See https://developer.github.com/v3/search/#example for how to customize! -- See https://developer.github.com/v3/search/#example for how to customize!
@@ -21,7 +36,7 @@ getQueryString query =
responseDecoder : Decoder (List SearchResult) responseDecoder : Decoder (List SearchResult)
responseDecoder = responseDecoder =
Json.Decode.at [ "items" ] (Json.Decode.list ElmHub.searchResultDecoder) Json.Decode.at [ "items" ] (Json.Decode.list searchResultDecoder)
type alias Model = type alias Model =
@@ -70,7 +85,7 @@ viewSearchResult : SearchResult -> Html Msg
viewSearchResult result = viewSearchResult result =
li [] li []
[ span [ class "star-count" ] [ text (toString result.stars) ] [ span [ class "star-count" ] [ text (toString result.stars) ]
, a [ onClick (Visit ("/repositories/" ++ toString result.id)) ] , a [ onClick (Visit ("/repositories/" ++ result.name)) ]
[ text result.name ] [ text result.name ]
, button [ class "hide-result", onClick (DeleteById result.id) ] , button [ class "hide-result", onClick (DeleteById result.id) ]
[ text "X" ] [ text "X" ]

136
part10/Page/Repository.elm Normal file
View File

@@ -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 )

View File

@@ -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 )

View File

@@ -1,5 +1,5 @@
Part 8 Part 10
====== =======
The instructor will paste notes from the lesson, including code examples from 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). Q&A, in [this document](https://docs.google.com/document/d/1ApuSOk9DP0YsQrxhW7-WE8UOEAV4PPnLDDeqUOL2o5k/edit?usp=sharing).

View File

@@ -7,7 +7,7 @@
<script type="text/javascript" src="/github.js"></script> <script type="text/javascript" src="/github.js"></script>
<script type="text/javascript" src="/elm.js"></script> <script type="text/javascript" src="/elm.js"></script>
<link rel="stylesheet" href="/style.css"> <link rel="stylesheet" href="/style.css">
<link rel="icon" type="image/png" href="elm-hub.png"> <link rel="icon" type="image/png" href="/elm-hub.png">
</head> </head>
<body> <body>