-
-
Save imnutz/bf826e7d1b0500f3d64f to your computer and use it in GitHub Desktop.
Getting the Reddit Home Page using Elm Promises
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Html exposing (..) | |
| import Html.Events exposing (..) | |
| import Html.Attributes exposing (href, style, src) | |
| import Task exposing (Task) | |
| import Http exposing (Error) | |
| import JavaScript.Decode exposing (..) | |
| import List as L | |
| -------------- | |
| -- HELPER CODE | |
| -------------- | |
| -- Operator from Clojure | |
| (>!) : a -> Stream.Input a -> Stream.Message | |
| (>!) a input' = Stream.message input'.address a | |
| -- Javascript Decoding Helper | |
| andMap : Decoder (a -> b) -> Decoder a -> Decoder b | |
| andMap = object2 (<|) | |
| -- Status type | |
| type Status | |
| = Initial | |
| | Loading | |
| | Ready | |
| | Failed | |
| -------- | |
| -- MODEL | |
| -------- | |
| -- The entire model for the app | |
| type alias Model = | |
| { status : Status | |
| , posts : List Post | |
| } | |
| type alias Post = | |
| { url : String | |
| , title : String | |
| , score : Int | |
| } | |
| -- The initial model | |
| initialModel : Model | |
| initialModel = | |
| { status = Initial | |
| , posts = [] | |
| } | |
| ------------------ | |
| -- Reddit Homepage | |
| ------------------ | |
| redditHomeUrl : String | |
| redditHomeUrl = | |
| "https://www.reddit.com/.json" | |
| ---------------------------- | |
| -- JSON Parsing and Decoding | |
| ---------------------------- | |
| {-| Reddit JSON data appears as : | |
| { | |
| "data": { | |
| "children": [ | |
| { | |
| "data": { | |
| "url": "www.catsareawesome.org", | |
| "title": "Cats are amazing", | |
| "score": 100 | |
| } | |
| }, | |
| { | |
| "data": { | |
| "url": "www.dogsareawesome.org", | |
| "title": "Dogs are amazing", | |
| "score": 90 | |
| } | |
| } | |
| ] | |
| } | |
| } | |
| -} | |
| type alias RedditJson = | |
| { data : RedditJsonData } | |
| redditJsonDecoder : Decoder RedditJson | |
| redditJsonDecoder = RedditJson | |
| `map` ("data" := redditJsonDataDecoder) | |
| type alias RedditJsonData = | |
| { children : List RedditJsonPost } | |
| redditJsonDataDecoder : Decoder RedditJsonData | |
| redditJsonDataDecoder = RedditJsonData | |
| `map` ("children" := list redditJsonPostDecoder) | |
| type alias RedditJsonPost = | |
| { data : RedditJsonPostData } | |
| redditJsonPostDecoder : Decoder RedditJsonPost | |
| redditJsonPostDecoder = RedditJsonPost | |
| `map` ("data" := redditJsonPostDataDecoder) | |
| type alias RedditJsonPostData = | |
| { url : String | |
| , title : String | |
| , score : Int | |
| } | |
| redditJsonPostDataDecoder : Decoder RedditJsonPostData | |
| redditJsonPostDataDecoder = RedditJsonPostData | |
| `map` ("url" := string) | |
| `andMap` ("title" := string) | |
| `andMap` ("score" := int) | |
| -------------------------- | |
| -- Convert Posts from JSON | |
| -------------------------- | |
| postsFromJson : RedditJson -> List Post | |
| postsFromJson json = | |
| let | |
| convertChild : RedditJsonPost -> Post | |
| convertChild child = | |
| { url = child.data.url | |
| , title = child.data.title | |
| , score = child.data.score | |
| } | |
| in | |
| List.map convertChild json.data.children | |
| --------- | |
| -- INPUTS | |
| --------- | |
| -- The url(s) requested by the user | |
| input urlRequests : Stream.Input String | |
| -- The results from requesting the url | |
| input results : Stream.Stream (Result Error RedditJson) | |
| input results from | |
| Stream.map requestRedditPage urlRequests.stream | |
| -------- | |
| -- TASKS | |
| -------- | |
| requestRedditPage : String -> Task Error RedditJson | |
| requestRedditPage url = | |
| Http.get redditJsonDecoder url | |
| ---------- | |
| -- ACTIONS | |
| ---------- | |
| type Action | |
| = Load | |
| | SetPosts (List Post) | |
| | Fail | |
| resultToAction : Result Error RedditJson -> Action | |
| resultToAction result = case result of | |
| Err _ -> Fail | |
| Ok json -> SetPosts (postsFromJson json) | |
| actions : Stream Action | |
| actions = | |
| Stream.map resultToAction results | |
| --------- | |
| -- UPDATE | |
| --------- | |
| update : Action -> Model -> Model | |
| update action model = case action of | |
| Load -> | |
| { model | status <- Loading } | |
| Fail -> | |
| { model | status <- Failed } | |
| SetPosts posts -> | |
| { model | status <- Ready | |
| , posts <- posts | |
| } | |
| ------- | |
| -- VIEW | |
| ------- | |
| view : Model -> Html | |
| view model = case model.status of | |
| Initial -> viewInitialPage | |
| Loading -> viewLoadingPage | |
| Ready -> viewPage model.posts | |
| Failed -> viewFailedPage | |
| viewInitialPage : Html | |
| viewInitialPage = | |
| button | |
| [ onClick (redditHomeUrl >! urlRequests) ] | |
| [ text "Get Reddit Front Page" ] | |
| viewLoadingPage : Html | |
| viewLoadingPage = | |
| text "Loading Reddit...." | |
| viewFailedPage : Html | |
| viewFailedPage = | |
| section [] | |
| [ p [] [ text "Oh noes! Request went bad!" ] | |
| , p [] [ text "Care to try again?" ] | |
| , viewInitialPage | |
| ] | |
| viewPage : List Post -> Html | |
| viewPage posts = | |
| div [] | |
| [ pageHeader | |
| , ul [] (List.map viewPost posts) | |
| ] | |
| viewPost : Post -> Html | |
| viewPost post = | |
| let | |
| scoreStyle = style | |
| [("height", "50px") | |
| ,("flex", "1") | |
| ,("align-self", "center") | |
| ] | |
| itemStyle = style | |
| [("display", "flex") | |
| ,("margin-bottom", "10px") | |
| ] | |
| linkStyle = style | |
| [("flex", "8") | |
| ,("align-self", "flex-start") | |
| ] | |
| in | |
| li [ itemStyle ] | |
| [ div [ scoreStyle ] | |
| [ text (toString post.score) ] | |
| , | |
| a | |
| [ linkStyle | |
| , href post.url | |
| ] | |
| [ div [] [text post.title] ] | |
| ] | |
| pageHeader : Html | |
| pageHeader = | |
| let | |
| headerStyle = style | |
| [ ("display", "flex") | |
| , ("align-items", "center") | |
| ] | |
| textStyle = style | |
| [ ("flex", "8") ] | |
| in | |
| header [ headerStyle ] | |
| [ elmLogo | |
| , div [ textStyle ] [ text "Reddit Client in Elm" ] | |
| ] | |
| -- The Glorious Elm logo! | |
| elmLogo : Html | |
| elmLogo = | |
| let | |
| logoStyle = style | |
| [("height", "50px") | |
| ,("width", "50px") | |
| ,("min-height", "40px") | |
| ,("min-width", "40px") | |
| ,("flex", "1") | |
| ] | |
| in | |
| img | |
| [ src "http://elm-lang.org/logo.svg" | |
| , logoStyle | |
| ][] | |
| ------- | |
| -- MAIN | |
| ------- | |
| main = | |
| Varying.map view | |
| (Stream.fold update initialModel actions) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment