frontend: render blog posts

This commit is contained in:
Cadey Ratio 2016-12-14 11:54:17 -08:00
parent 4a52f5006b
commit 81206835d5
6 changed files with 109 additions and 7 deletions

View File

@ -0,0 +1,9 @@
// Module App.BlogEntry
exports.mdify = function(id) {
var converter = new showdown.Converter()
elem = document.getElementById(id);
md = elem.innerHTML;
elem.innerHTML = converter.makeHtml(md);
return "done :)";
}

View File

@ -0,0 +1,79 @@
module App.BlogEntry where
import Control.Monad.Aff (attempt)
import DOM (DOM)
import Data.Argonaut (class DecodeJson, decodeJson, (.?))
import Data.Either (Either(..), either)
import Data.Maybe (Maybe(..))
import Network.HTTP.Affjax (AJAX, get)
import Prelude (bind, pure, show, ($), (<>), (<<<))
import Pux (EffModel, noEffects)
import Pux.Html (Html, div, h1, h2, p, text)
import Pux.Html.Attributes (id_)
data Action = RequestPost
| ReceivePost (Either String Post)
| RenderPost
type State =
{ status :: String
, hack :: String
, id :: Maybe Int
, post :: Post
, name :: String }
data Post = Post
{ title :: String
, body :: String
, date :: String }
instance decodeJsonPost :: DecodeJson Post where
decodeJson json = do
obj <- decodeJson json
title <- obj .? "title"
body <- obj .? "body"
date <- obj .? "date"
pure $ Post { title: title, body: body, date: date }
init :: State
init =
{ status: "Loading..."
, hack: ""
, post: Post
{ title: ""
, body: ""
, date: "" }
, name: ""
, id: Nothing }
update :: Action -> State -> EffModel State Action (ajax :: AJAX, dom :: DOM)
update (ReceivePost (Left err)) state =
noEffects $ state { id = Nothing, status = err }
update (ReceivePost (Right post)) state =
{ state: state { status = "", id = Just 1, post = post }
, effects: [ pure $ RenderPost ]
}
update RequestPost state =
{ state: state
, effects: [ do
res <- attempt $ get ("/api/blog/post?name=" <> state.name)
let decode r = decodeJson r.response :: Either String Post
let post = either (Left <<< show) decode res
pure $ ReceivePost post
]
}
update RenderPost state =
noEffects $ state { hack = mdify "blogpost" }
view :: State -> Html Action
view { id: id, status: status, post: (Post post) } =
case id of
Nothing -> div [] []
(Just _) ->
div []
[ h1 [] [ text status ]
, div []
[ p [ id_ "blogpost" ] [ text post.body ] ]
]
foreign import mdify :: String -> String

View File

@ -39,15 +39,15 @@ instance decodeJsonPost :: DecodeJson Post where
init :: State init :: State
init = init =
{ posts: [] { posts: []
, status: "Loading..." } , status: "" }
update :: Action -> State -> EffModel State Action (ajax :: AJAX, dom :: DOM) update :: Action -> State -> EffModel State Action (ajax :: AJAX, dom :: DOM)
update (ReceivePosts (Left err)) state = update (ReceivePosts (Left err)) state =
noEffects $ state { status = ("error: " <> err) } noEffects $ state { status = ("error: " <> err) }
update (ReceivePosts (Right posts)) state = update (ReceivePosts (Right posts)) state =
noEffects $ state { posts = posts, status = "Posts" } noEffects $ state { posts = posts, status = "" }
update RequestPosts state = update RequestPosts state =
{ state: state { status = "Fetching posts..." } { state: state { status = "Loading..." }
, effects: [ do , effects: [ do
res <- attempt $ get "/api/blog/posts" res <- attempt $ get "/api/blog/posts"
let decode r = decodeJson r.response :: Either String Posts let decode r = decodeJson r.response :: Either String Posts
@ -79,5 +79,6 @@ view :: State -> Html Action
view state = view state =
div div
[] []
[ h1 [] [ text state.status ] [ h1 [] [ text "Posts" ]
, p [] [ text state.status ]
, div [ className "row" ] $ map post state.posts ] , div [ className "row" ] $ map post state.posts ]

View File

@ -1,8 +1,10 @@
module App.Layout where module App.Layout where
import App.BlogEntry as BlogEntry
import App.BlogIndex as BlogIndex import App.BlogIndex as BlogIndex
import App.Counter as Counter import App.Counter as Counter
import App.Routes (Route(..)) import App.Routes (Route(..))
import Control.Monad.RWS (state)
import DOM (DOM) import DOM (DOM)
import Network.HTTP.Affjax (AJAX) import Network.HTTP.Affjax (AJAX)
import Prelude (($), (#), map, pure) import Prelude (($), (#), map, pure)
@ -14,29 +16,37 @@ import Pux.Router (link)
data Action data Action
= Child (Counter.Action) = Child (Counter.Action)
| BIChild (BlogIndex.Action) | BIChild (BlogIndex.Action)
| BEChild (BlogEntry.Action)
| PageView Route | PageView Route
type State = type State =
{ route :: Route { route :: Route
, count :: Counter.State , count :: Counter.State
, bistate :: BlogIndex.State } , bistate :: BlogIndex.State
, bestate :: BlogEntry.State }
init :: State init :: State
init = init =
{ route: NotFound { route: NotFound
, count: Counter.init , count: Counter.init
, bistate: BlogIndex.init } , bistate: BlogIndex.init
, bestate: BlogEntry.init }
update :: Action -> State -> EffModel State Action (ajax :: AJAX, dom :: DOM) update :: Action -> State -> EffModel State Action (ajax :: AJAX, dom :: DOM)
update (PageView route) state = routeEffects route $ state { route = route } update (PageView route) state = routeEffects route $ state { route = route }
update (BIChild action) state = BlogIndex.update action state.bistate update (BIChild action) state = BlogIndex.update action state.bistate
# mapState (state { bistate = _ }) # mapState (state { bistate = _ })
# mapEffects BIChild # mapEffects BIChild
update (BEChild action) state = BlogEntry.update action state.bestate
# mapState (state { bestate = _ })
# mapEffects BEChild
update (Child action) state = noEffects $ state { count = Counter.update action state.count } update (Child action) state = noEffects $ state { count = Counter.update action state.count }
routeEffects :: Route -> State -> EffModel State Action (dom :: DOM, ajax :: AJAX) routeEffects :: Route -> State -> EffModel State Action (dom :: DOM, ajax :: AJAX)
routeEffects BlogIndex state = { state: state routeEffects BlogIndex state = { state: state
, effects: [ pure BlogIndex.RequestPosts ] } # mapEffects BIChild , effects: [ pure BlogIndex.RequestPosts ] } # mapEffects BIChild
routeEffects (BlogPost page) state = { state: state { bestate = BlogEntry.init { name = page } }
, effects: [ pure BlogEntry.RequestPost ] } # mapEffects BEChild
routeEffects _ state = noEffects $ state routeEffects _ state = noEffects $ state
view :: State -> Html Action view :: State -> Html Action
@ -71,4 +81,5 @@ page NotFound _ = h1 [] [ text "not found" ]
page Home state = map Child $ Counter.view state.count page Home state = map Child $ Counter.view state.count
page Resume state = h1 [] [ text "Christine Dodrill" ] page Resume state = h1 [] [ text "Christine Dodrill" ]
page BlogIndex state = map BIChild $ BlogIndex.view state.bistate page BlogIndex state = map BIChild $ BlogIndex.view state.bistate
page (BlogPost _) state = map BEChild $ BlogEntry.view state.bestate
page _ _ = h1 [] [ text "not implemented yet" ] page _ _ = h1 [] [ text "not implemented yet" ]

View File

@ -7,7 +7,7 @@ import Control.Monad.Eff (Eff)
import DOM (DOM) import DOM (DOM)
import Network.HTTP.Affjax (AJAX) import Network.HTTP.Affjax (AJAX)
import Prelude (bind, pure) import Prelude (bind, pure)
import Pux (App, Config, CoreEffects, fromSimple, renderToDOM, start) import Pux (App, Config, CoreEffects, renderToDOM, start)
import Pux.Devtool (Action, start) as Pux.Devtool import Pux.Devtool (Action, start) as Pux.Devtool
import Pux.Router (sampleUrl) import Pux.Router (sampleUrl)
import Signal ((~>)) import Signal ((~>))

View File

@ -19,3 +19,5 @@ match url = fromMaybe NotFound $ router url $
Home <$ end Home <$ end
<|> <|>
BlogIndex <$ lit "blog" <* end BlogIndex <$ lit "blog" <* end
<|>
BlogPost <$> (lit "blog" *> str) <* end