Last active
September 23, 2016 03:16
-
-
Save aratama/fa9fd5eca5573fc2e6dd to your computer and use it in GitHub Desktop.
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
module Main where | |
import Prelude (Unit(), unit, pure, ($), (<$>), (<#>), map, bind, show, void, const, (++), (==)) | |
import Data.Maybe (Maybe(..), fromMaybe) | |
import Data.Either (either) | |
import Data.Array (updateAt, take, drop, findIndex, head, tail, replicate) | |
import Data.Traversable (for) | |
import Data.Functor ((<$)) | |
import Control.Monad.Aff (Aff(), runAff) | |
import Control.Monad.Eff (Eff()) | |
import Control.Monad.Eff.Exception (throwException) | |
import Halogen (HalogenEffects(), ComponentDSL(), Natural(), ComponentHTML(), Component(), action, runUI, component, set, get, liftEff', liftH, HTML()) | |
import Halogen.Util (appendToBody, onLoad) | |
import Halogen.HTML.Core (className) | |
import Halogen.HTML.Indexed (text, div, span, button, img, a) | |
import Halogen.HTML.Events.Indexed (onClick, input_) | |
import Halogen.HTML.Properties.Indexed (src, href, class_) | |
import Network.HTTP.Affjax (AJAX(), AffjaxResponse()) | |
import Network.HTTP.Affjax (get) as Affjax | |
import Data.Argonaut.Core (Json(), toArray, toObject) | |
import Data.Argonaut.Combinators ((.?)) | |
import Control.Monad.Eff.Random (RANDOM(), randomInt) | |
import DOM.File.Types (Blob()) | |
type State = { display :: Array (Maybe User), users :: Array User } | |
type User = { login :: String, avatar_url :: String, html_url :: String } | |
data Query a = Refresh a | Close String a | |
type Effects = HalogenEffects (ajax :: AJAX, random :: RANDOM) | |
numberOfUsers :: Int | |
numberOfUsers = 3 | |
parse :: Json -> Maybe (Array User) | |
parse json = do | |
xs <- toArray json | |
for xs \json -> do | |
m <- toObject json | |
either (const Nothing) Just do | |
login <- m .? "login" | |
avatar_url <- m .? "avatar_url" | |
html_url <- m .? "html_url" | |
pure { login, avatar_url, html_url } | |
ui :: Component State Query (Aff Effects) | |
ui = component render eval | |
where | |
render :: State -> ComponentHTML Query | |
render s = div [class_ (className "outer")] [ | |
div [class_ (className "title")] [ | |
span [] [text "Who to Follow - "], | |
button [class_ (className "refresh"), onClick (input_ Refresh)] [text "Refresh"] | |
], | |
div [class_ (className "users")] $ map renderUser s.display | |
] | |
renderUser :: forall p. Maybe User -> HTML p Query | |
renderUser user = div [class_ (className "user")] case user of | |
Nothing -> [] | |
Just user -> [ | |
img [src user.avatar_url, class_ (className "avator")], | |
a [class_ (className "name"), href user.html_url] [text user.login], | |
button [class_ (className "close"), onClick $ input_ (Close user.login)] [text "X"] | |
] | |
eval :: Natural Query (ComponentDSL State Query (Aff Effects)) | |
eval (Close login next) = next <$ do | |
state <- get | |
fromMaybe (pure unit) do | |
index <- findIndex (\u -> (u <#> _.login) == Just login) state.display | |
users <- tail state.users | |
user <- head state.users | |
blank <- updateAt index Nothing state.display | |
display <- updateAt index (Just user) state.display | |
pure do | |
set { display: blank, users } | |
_ :: AffjaxResponse Blob <- liftH $ Affjax.get user.avatar_url | |
set { display, users } | |
eval (Refresh next) = next <$ do | |
set { display: replicate numberOfUsers Nothing, users: [] } -- まず一覧をクリアする | |
randomOffset <- liftEff' $ randomInt 0 500 -- それが終わったら0-500の間の乱数を1個生成する | |
res <- liftH $ Affjax.get ("https://api.github.com/users?since=" ++ show randomOffset) -- 次にgetでAPIを叩く | |
let parsed = fromMaybe [] (parse res.response) -- レスポンスが返ってきら、そのJSONをパース | |
let display = Just <$> take numberOfUsers parsed -- 上から3人を取り出す | |
let users = drop numberOfUsers parsed -- 残りの人を取り出す | |
set { display, users } -- 状態を設定する | |
main :: Eff Effects Unit | |
main = runAff throwException pure $ void do | |
app <- runUI ui { display: [], users : [] } -- アプリケーションを作成する | |
onLoad $ appendToBody app.node -- 作成が終わったら、次に要素をページに追加する | |
app.driver $ action Refresh -- それが終わったら、`Refresh`アクションを投げてユーザ一覧を更新する |
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
<script src="index.js"></script> | |
<style> | |
.outer { | |
margin: 10px; | |
border: solid 1px black; | |
padding: 10px; | |
border-radius: 4px; | |
width: 250px; | |
} | |
.title { | |
font-size: large; | |
color: grey; | |
font-weight: bold; | |
margin: 8px; | |
} | |
.refreash { | |
font-size: small; | |
border: none; | |
background-color: transparent; | |
color: rgba(0, 0, 0, 0.6); | |
} | |
.users { | |
display: flex; | |
flex-direction: column; | |
} | |
.user { | |
height: 50px; | |
display: flex; | |
} | |
img.avator { | |
width: 40px; | |
height: 40px; | |
border-radius: 20px; | |
flex-grow: 0; | |
border: none; | |
} | |
.name { | |
flex-grow: 1.0; | |
padding: 10px; | |
} | |
.close { | |
font-size: small; | |
border: none; | |
background-color: transparent; | |
color: rgba(0, 0, 0, 0.6); | |
flex-grow: 0; | |
} | |
</style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment