{-# LANGUAGE TemplateHaskell, QuasiQuotes, TypeFamilies #-} {-# LANGUAGE OverloadedStrings #-} module Main where import Yesod import Yesod.Handler import Yesod.Helpers.Static import Control.Concurrent.STM import Control.Concurrent.STM.TChan import Control.Concurrent.STM.TVar import Control.Arrow ((***)) -- speaker and content data Message = Message String String type Handler yesod = GHandler yesod yesod -- all those TChans are dupes, so writing to any one writes to them all, but reading is separate data Chat = Chat { chatClients :: TVar [(Int, TChan Message)] , nextClient :: TVar Int , chatStatic :: Static } staticFiles "static" mkYesod "Chat" [$parseRoutes| / HomeR GET /check CheckR GET /post PostR GET /static StaticR Static chatStatic |] instance Yesod Chat where approot _ = "" defaultLayout widget = do content <- widgetToPageContent widget hamletToRepHtml [$hamlet| !!! %html %head %title $pageTitle.content$ %script!src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" %script!src=@StaticR.chat_js@ ^pageHead.content^ %body ^pageBody.content^ |] getHomeR :: Handler Chat RepHtml getHomeR = do Chat clients next _ <- getYesod client <- liftIO . atomically $ do c <- readTVar next writeTVar next (c+1) cs <- readTVar clients chan <- case cs of [] -> newTChan (_,x):_ -> dupTChan x writeTVar clients ((c,chan) : cs) return c defaultLayout $ do setTitle "Chat Page" addWidget [$hamlet| !!! %h1 Chat Example %form %textarea!cols=80!rows=20!name=chat %p %input!type=text!size=15!name=name#name %input!type=text!size=60!name=send#send %input!type=submit!value=Send %script var clientNumber = $show client$ |] getCheckR :: Handler Chat RepJson getCheckR = do liftIO $ putStrLn "Check" Chat clients _ _ <- getYesod client <- do c <- lookupGetParam "client" case c of Nothing -> invalidArgs ["No client value in Check request"] Just c' -> return $ read c' cs <- liftIO . atomically $ readTVar clients chan <- case lookup client cs of Nothing -> invalidArgs ["Bad client value"] Just ch -> return ch -- block until there's something there first <- liftIO . atomically $ readTChan chan let Message s c = first jsonToRepJson $ zipJson ["sender", "content"] [s,c] zipJson x y = jsonMap $ map (id *** jsonScalar) $ zip x y getPostR :: Handler Chat RepJson getPostR = do liftIO $ putStrLn "Post" Chat clients _ _ <- getYesod (sender,content) <- do s <- lookupGetParam "name" c <- lookupGetParam "send" case (s,c) of (Just s', Just c') -> return (s', c') _ -> invalidArgs ["Either name or send not provided."] liftIO . atomically $ do cs <- readTVar clients let chan = snd . head $ cs -- doesn't matter which one we use, they're all duplicates writeTChan chan (Message sender content) jsonToRepJson $ jsonScalar "success" main :: IO () main = do clients <- newTVarIO [] next <- newTVarIO 0 let static = fileLookupDir "static" typeByExt basicHandler 3000 $ Chat clients next static