Skip to content

Instantly share code, notes, and snippets.

@imnutz
Forked from TheSeamau5/RedditHomePage.elm
Last active August 29, 2015 14:25
Show Gist options
  • Select an option

  • Save imnutz/bf826e7d1b0500f3d64f to your computer and use it in GitHub Desktop.

Select an option

Save imnutz/bf826e7d1b0500f3d64f to your computer and use it in GitHub Desktop.
Getting the Reddit Home Page using Elm Promises
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