Monadic WS
This commit is contained in:
parent
3dc0135b48
commit
01df3f1068
|
@ -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
|
|
@ -7,7 +7,6 @@ module WebServer (runWebServer) where
|
||||||
|
|
||||||
import ClassyPrelude hiding (decodeUtf8)
|
import ClassyPrelude hiding (decodeUtf8)
|
||||||
import Control.Monad.Except
|
import Control.Monad.Except
|
||||||
import Data.Text.Encoding
|
|
||||||
import Network.HTTP.Types
|
import Network.HTTP.Types
|
||||||
import Network.Wai
|
import Network.Wai
|
||||||
import Network.Wai.Handler.Warp (run)
|
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 ReaderT env (ExceptTApp ResponseReceived)
|
||||||
|
|
||||||
-- via ExceptT ResponseReceived (ReaderT env IO)
|
|
||||||
|
|
||||||
getRequestPath ::
|
getRequestPath ::
|
||||||
( MonadIO m,
|
( MonadIO m,
|
||||||
HasWebEnv env,
|
HasWebEnv env,
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
{-# LANGUAGE DerivingVia #-}
|
{-# LANGUAGE DerivingVia #-}
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
{-# LANGUAGE UndecidableInstances #-}
|
{-# LANGUAGE UndecidableInstances #-}
|
||||||
|
|
||||||
module WebSocket (runWebSocketServer) where
|
module WebSocket (runWebSocketServer) where
|
||||||
|
|
||||||
import BroadcastUserData (broadcastUserData)
|
import BroadcastUserData (broadcastUserData)
|
||||||
import ClassyPrelude
|
import ClassyPrelude
|
||||||
|
import Data.Aeson
|
||||||
import Data.UUID (UUID)
|
import Data.UUID (UUID)
|
||||||
import Data.UUID.V4 (nextRandom)
|
import Data.UUID.V4 (nextRandom)
|
||||||
import Network.WebSockets qualified as WS
|
import Network.WebSockets qualified as WS
|
||||||
|
@ -13,8 +15,15 @@ import Types.AppTypes
|
||||||
( Env (..),
|
( Env (..),
|
||||||
HasConnectedClientState (getConnectedClientState),
|
HasConnectedClientState (getConnectedClientState),
|
||||||
)
|
)
|
||||||
import Types.ConnectionState (Client (..), ConnectedClients)
|
import Types.ConnectionState
|
||||||
|
( Client (..),
|
||||||
|
ConnectedClients,
|
||||||
|
)
|
||||||
import Types.RoomsState (HasRoomsState (..))
|
import Types.RoomsState (HasRoomsState (..))
|
||||||
|
import Types.WebSocketMessages.WebSocketMessages
|
||||||
|
( SetClientInfo (displayName),
|
||||||
|
WebSocketMessage (..),
|
||||||
|
)
|
||||||
|
|
||||||
data WSEnv = WSEnv
|
data WSEnv = WSEnv
|
||||||
{ appEnv :: Env,
|
{ appEnv :: Env,
|
||||||
|
@ -81,6 +90,21 @@ joinRoom = do
|
||||||
let s' = updateClient clientId (\c -> c {joinedRoom = True}) s
|
let s' = updateClient clientId (\c -> c {joinedRoom = True}) s
|
||||||
in return 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 :: UUID -> ConnectedClients -> ConnectedClients
|
||||||
removeClient toRemove = filter ((/= toRemove) . uuid)
|
removeClient toRemove = filter ((/= toRemove) . uuid)
|
||||||
|
|
||||||
|
@ -121,11 +145,11 @@ newClient ::
|
||||||
HasWSEnv env,
|
HasWSEnv env,
|
||||||
MonadReader env m
|
MonadReader env m
|
||||||
) =>
|
) =>
|
||||||
Text ->
|
SetClientInfo ->
|
||||||
m Client
|
m Client
|
||||||
newClient name = do
|
newClient clientInfo = do
|
||||||
env <- ask
|
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}
|
newtype WSApp env a = WSApp {unWSApp :: env -> IO a}
|
||||||
deriving
|
deriving
|
||||||
|
@ -138,25 +162,54 @@ newtype WSApp env a = WSApp {unWSApp :: env -> IO a}
|
||||||
)
|
)
|
||||||
via ReaderT env IO
|
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 ::
|
wsApp ::
|
||||||
( HasWSEnv env,
|
( HasWSEnv env,
|
||||||
HasConnectedClientState env,
|
HasConnectedClientState env,
|
||||||
HasRoomsState env,
|
HasRoomsState env,
|
||||||
MonadReader env m,
|
MonadReader env m,
|
||||||
MonadUnliftIO m
|
MonadUnliftIO m,
|
||||||
|
MonadWebSocket m
|
||||||
) =>
|
) =>
|
||||||
m ()
|
m ()
|
||||||
wsApp = do
|
wsApp = do
|
||||||
msg <- getMessage
|
msg <- getTypedWSMessage
|
||||||
putStrLn msg
|
print msg
|
||||||
client <- newClient msg
|
client <- newClient msg
|
||||||
addWSClient client
|
addWSClient client
|
||||||
broadcastUserData
|
broadcastUserData
|
||||||
flip finally disconnectWsClient $ forever $ do
|
flip finally disconnectWsClient $ forever $ do
|
||||||
currentMsg <- getMessage
|
handleWSAction
|
||||||
joinRoom
|
|
||||||
broadcastUserData
|
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 ::
|
getMessage ::
|
||||||
( HasWSEnv env,
|
( HasWSEnv env,
|
||||||
|
@ -167,3 +220,14 @@ getMessage ::
|
||||||
getMessage = do
|
getMessage = do
|
||||||
conn' <- getConn <$> ask
|
conn' <- getConn <$> ask
|
||||||
liftIO $ WS.receiveData conn'
|
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
|
||||||
|
|
Loading…
Reference in New Issue