Monadic WS

This commit is contained in:
qvalentin 2023-02-18 16:57:20 +01:00
parent 3dc0135b48
commit 01df3f1068
3 changed files with 114 additions and 13 deletions

View File

@ -0,0 +1,40 @@
{-# LANGUAGE DeriveGeneric #-}
module Types.WebSocketMessages.WebSocketMessages
( WebSocketMessage (..),
SetClientInfo (..),
JoinRoom (..),
)
where
import ClassyPrelude
import Data.Aeson
( FromJSON (parseJSON),
Options (sumEncoding),
SumEncoding (..),
decode,
defaultOptions,
genericParseJSON,
withObject,
(.:),
)
data WebSocketMessage = ClientInfoMessage SetClientInfo | JoinRoomMessage JoinRoom
deriving (Generic)
instance FromJSON WebSocketMessage where
parseJSON = genericParseJSON defaultOptions {sumEncoding = UntaggedValue}
data SetClientInfo = SetClientInfo
{ displayName :: Text
}
deriving (Generic, Show)
instance FromJSON SetClientInfo
data JoinRoom = JoinRoom
{ roomName :: Text
}
deriving (Generic, Show)
instance FromJSON JoinRoom

View File

@ -7,7 +7,6 @@ module WebServer (runWebServer) where
import ClassyPrelude hiding (decodeUtf8)
import Control.Monad.Except
import Data.Text.Encoding
import Network.HTTP.Types
import Network.Wai
import Network.Wai.Handler.Warp (run)
@ -44,8 +43,6 @@ newtype WebApp env a = WebApp {unWebApp :: env -> IO (Either ResponseReceived a)
)
via ReaderT env (ExceptTApp ResponseReceived)
-- via ExceptT ResponseReceived (ReaderT env IO)
getRequestPath ::
( MonadIO m,
HasWebEnv env,

View File

@ -1,11 +1,13 @@
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
module WebSocket (runWebSocketServer) where
import BroadcastUserData (broadcastUserData)
import ClassyPrelude
import Data.Aeson
import Data.UUID (UUID)
import Data.UUID.V4 (nextRandom)
import Network.WebSockets qualified as WS
@ -13,8 +15,15 @@ import Types.AppTypes
( Env (..),
HasConnectedClientState (getConnectedClientState),
)
import Types.ConnectionState (Client (..), ConnectedClients)
import Types.ConnectionState
( Client (..),
ConnectedClients,
)
import Types.RoomsState (HasRoomsState (..))
import Types.WebSocketMessages.WebSocketMessages
( SetClientInfo (displayName),
WebSocketMessage (..),
)
data WSEnv = WSEnv
{ appEnv :: Env,
@ -81,6 +90,21 @@ joinRoom = do
let s' = updateClient clientId (\c -> c {joinedRoom = True}) s
in return s'
updateClientName ::
( HasWSEnv env,
HasConnectedClientState env,
MonadReader env m,
MonadUnliftIO m
) =>
SetClientInfo ->
m ()
updateClientName clientInfo = do
clientId <- getClientId <$> ask
state <- getConnectedClientState <$> ask
modifyMVar_ state $ \s ->
let s' = updateClient clientId (\c -> c {name = displayName clientInfo}) s
in return s'
removeClient :: UUID -> ConnectedClients -> ConnectedClients
removeClient toRemove = filter ((/= toRemove) . uuid)
@ -121,11 +145,11 @@ newClient ::
HasWSEnv env,
MonadReader env m
) =>
Text ->
SetClientInfo ->
m Client
newClient name = do
newClient clientInfo = do
env <- ask
return $ Client {uuid = getClientId env, name = name, conn = getConn env, joinedRoom = False}
return $ Client {uuid = getClientId env, name = displayName clientInfo, conn = getConn env, joinedRoom = False}
newtype WSApp env a = WSApp {unWSApp :: env -> IO a}
deriving
@ -138,25 +162,54 @@ newtype WSApp env a = WSApp {unWSApp :: env -> IO a}
)
via ReaderT env IO
class Monad m => MonadWebSocket m where
getTypedWSMessage :: FromJSON a => m a
instance MonadWebSocket (WSApp WSEnv) where
getTypedWSMessage = do
msg <- getMessage
case eitherDecodeStrict $ encodeUtf8 msg of
Right a -> return a
Left err -> do
sendMessage $ "Bad message: " <> pack err
getTypedWSMessage
wsApp ::
( HasWSEnv env,
HasConnectedClientState env,
HasRoomsState env,
MonadReader env m,
MonadUnliftIO m
MonadUnliftIO m,
MonadWebSocket m
) =>
m ()
wsApp = do
msg <- getMessage
putStrLn msg
msg <- getTypedWSMessage
print msg
client <- newClient msg
addWSClient client
broadcastUserData
flip finally disconnectWsClient $ forever $ do
currentMsg <- getMessage
joinRoom
handleWSAction
broadcastUserData
putStrLn currentMsg
handleWSAction ::
( HasWSEnv env,
HasConnectedClientState env,
HasRoomsState env,
MonadReader env m,
MonadUnliftIO m,
MonadWebSocket m
) =>
m ()
handleWSAction = do
msg <- getTypedWSMessage
case msg of
JoinRoomMessage _ -> do
joinRoom
return ()
ClientInfoMessage clientInfo -> do
updateClientName clientInfo
getMessage ::
( HasWSEnv env,
@ -167,3 +220,14 @@ getMessage ::
getMessage = do
conn' <- getConn <$> ask
liftIO $ WS.receiveData conn'
sendMessage ::
( HasWSEnv env,
MonadIO m,
MonadReader env m
) =>
Text ->
m ()
sendMessage msg = do
conn' <- getConn <$> ask
liftIO $ WS.sendTextData conn' msg