packages feed

front-0.0.0.2: examples/todo/YesodTodo.hs

{-# LANGUAGE DeriveDataTypeable    #-}
{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NoImplicitPrelude     #-}
{-# LANGUAGE OverloadedStrings     #-}
{-# LANGUAGE QuasiQuotes           #-}
{-# LANGUAGE RecordWildCards       #-}
{-# LANGUAGE ScopedTypeVariables   #-}
{-# LANGUAGE TemplateHaskell       #-}
{-# LANGUAGE TypeFamilies          #-}

module YesodTodo where

import           Conduit
import           Control.Concurrent.STM.Lifted as STM
import           Control.Monad.Reader
import           Prelude                       hiding (interact)
import           System.Random                 (randomRIO)
import           Text.Blaze.Front.Renderer     (renderNewMarkup)
import           Yesod.Core
import           Yesod.Static
import           Yesod.WebSockets

import qualified Data.Text                     as T

import           Bridge
import           Todo
import           Web.Front.Broadcast

-- * App

data App = App
  { appModel   :: TVar Model
  , appChannel :: TChan (Out (Action Msg))
  , appStatic  :: Static
  }

mkYesod "App" [parseRoutes|
/ HomeR GET
/static      StaticR  Static  appStatic
|]

-- * API

instance Yesod App where
  defaultLayout widget = do
    pc <- widgetToPageContent [whamlet|^{widget}|]
    withUrlRenderer [hamlet|<html>
  <head>
    ^{pageHead pc}
    <script src="/static/bundle.js">
  <body>
    <div #root>^{pageBody pc}
|]

-- * Handler for /

getHomeR :: Handler Html
getHomeR = do
  modelTVar <- appModel <$> getYesod
  model <- STM.readTVarIO modelTVar
  webSockets $ webApp modelTVar
  defaultLayout $ do
    setTitle "TODO"
    toWidget $ renderNewMarkup $ renderModel model

-- * WebSockets Web application

webApp :: TVar Model -> WebSocketsT (HandlerFor App) ()
webApp tvar = do
  conn <- ask
  cid <- clientSession
  writeChan' <- appChannel <$> getYesod
  readChan' <- atomically $ do
    dupTChan writeChan'
  liftIO $ interact conn writeChan' readChan' tvar cid

-- * Helpers

clientSession :: MonadHandler m => m Int
clientSession = do
  res <- lookupSession "client"
  case res of
    Nothing -> do
      (rnd :: Int) <- liftIO $ randomRIO (0,200000)
      let clientid = T.pack . show $ rnd
      setSession "client" clientid
      return rnd
    Just cid -> return $ parseInt cid

-- * Main

main :: IO ()
main = do
  (App
    <$> STM.newTVarIO initModel
    <*> atomically newBroadcastTChan
    <*> staticDevel "./static")
  >>= warp 3000