Last active
August 29, 2015 14:25
-
-
Save codedmart/de6b8c988787391e0e28 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
| {-# LANGUAGE OverloadedStrings #-} | |
| {-# LANGUAGE FlexibleContexts #-} | |
| module Lib.Chat (eioServer, ServerState (..)) where | |
| import Prelude hiding (mapM_) | |
| import Control.Monad.IO.Class (liftIO) | |
| import Data.Aeson ((.=)) | |
| import Data.Foldable (mapM_) | |
| import qualified Control.Concurrent.STM as STM | |
| import qualified Data.Aeson as Aeson | |
| import qualified Data.Text as Text | |
| import qualified Network.SocketIO as SocketIO | |
| import Database.RethinkDB (next, fromDatum) | |
| import Database.RethinkDB.Datum (resultToMaybe, Datum) | |
| import Control.Concurrent | |
| import Control.Exception | |
| import qualified Model.User as User | |
| import Model.Types.User (SecureUser) | |
| data AddUser = AddUser Text.Text | |
| instance Aeson.FromJSON AddUser where | |
| parseJSON = Aeson.withText "AddUser" $ pure . AddUser | |
| data NumConnected = NumConnected !Int | |
| instance Aeson.ToJSON NumConnected where | |
| toJSON (NumConnected n) = Aeson.object [ "numUsers" .= n] | |
| data NewMessage = NewMessage Text.Text | |
| instance Aeson.FromJSON NewMessage where | |
| parseJSON = Aeson.withText "NewMessage" $ pure . NewMessage | |
| data Said = Said Text.Text Text.Text | |
| instance Aeson.ToJSON Said where | |
| toJSON (Said username message) = Aeson.object | |
| [ "username" .= username | |
| , "message" .= message | |
| ] | |
| data UserName = UserName Text.Text | |
| instance Aeson.ToJSON UserName where | |
| toJSON (UserName un) = Aeson.object [ "username" .= un ] | |
| data UserJoined = UserJoined Text.Text Int | |
| instance Aeson.ToJSON UserJoined where | |
| toJSON (UserJoined un n) = Aeson.object | |
| [ "username" .= un | |
| , "numUsers" .= n | |
| ] | |
| -------------------------------------------------------------------------------- | |
| data ServerState = ServerState { ssNConnected :: STM.TVar Int } | |
| eioServer state pool = do | |
| userNameMVar <- liftIO STM.newEmptyTMVarIO | |
| let forUserName m = liftIO (STM.atomically (STM.tryReadTMVar userNameMVar)) >>= mapM_ m | |
| -- Disregard the chat example. | |
| -- I would like to get changes here and emit them everytime there are changes | |
| -- I just don't want to have to call next cursor everytime if possible. | |
| cursor <- run fakeH $ table "users" # changes :: IO (Cursor Datum) | |
| -- emit changes somehow ie: | |
| -- SocketIO.on "changes" (Changes changes) | |
| SocketIO.on "new message" $ \(NewMessage message) -> | |
| forUserName $ \userName -> | |
| SocketIO.broadcast "new message" (Said userName message) | |
| SocketIO.on "add user" $ \(AddUser userName) -> do | |
| n <- liftIO $ STM.atomically $ do | |
| n <- (+ 1) <$> STM.readTVar (ssNConnected state) | |
| STM.putTMVar userNameMVar userName | |
| STM.writeTVar (ssNConnected state) n | |
| return n | |
| SocketIO.emit "login" (NumConnected n) | |
| SocketIO.broadcast "user joined" (UserJoined userName n) | |
| SocketIO.appendDisconnectHandler $ do | |
| (n, mUserName) <- liftIO $ STM.atomically $ do | |
| n <- (+ (-1)) <$> STM.readTVar (ssNConnected state) | |
| mUserName <- STM.tryReadTMVar userNameMVar | |
| STM.writeTVar (ssNConnected state) n | |
| return (n, mUserName) | |
| case mUserName of | |
| Nothing -> return () | |
| Just userName -> | |
| SocketIO.broadcast "user left" (UserJoined userName n) | |
| SocketIO.on "typing" $ | |
| forUserName $ \userName -> | |
| SocketIO.broadcast "typing" (UserName userName) | |
| SocketIO.on "stop typing" $ | |
| forUserName $ \userName -> | |
| SocketIO.broadcast "stop typing" (UserName userName) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment