packages feed

ema-0.12.0.0: src/Ema/Server/WebSocket/Options.hs

{-# LANGUAGE QuasiQuotes #-}

module Ema.Server.WebSocket.Options where

import Control.Monad.Logger (
  LogLevel (LevelDebug),
  LoggingT,
  logWithoutLoc,
 )
import Data.Default (Default (def))
import Ema.Route.Class (IsRoute (RouteModel))
import Ema.Server.Common (wsClientJSShim)
import NeatInterpolation (text)
import Network.WebSockets qualified as WS

{- | A handler takes a websocket connection and the current model and then watches
   for websocket messages. It must return a new route to watch (after that, the
   returned route's HTML will be sent back to the client).

  Note that this is usually a long-running thread that waits for the client's
  messages. But you can also use it to implement custom server actions, by handling
  the incoming websocket messages or other IO events in any way you like.

  Also note that whenever the model is updated, the handler action will be
  stopped and then restarted with the new model as argument.
-}
newtype EmaWsHandler r = EmaWsHandler
  { unEmaWsHandler :: WS.Connection -> RouteModel r -> LoggingT IO Text
  }

instance Default (EmaWsHandler r) where
  def = EmaWsHandler $ \conn _model -> do
    msg :: Text <- liftIO $ WS.receiveData conn
    log LevelDebug $ "<~~ " <> show msg
    pure msg
    where
      log lvl (t :: Text) = logWithoutLoc "ema.ws" lvl t

data EmaWebSocketOptions r = EmaWebSocketOptions
  { emaWebSocketClientShim :: LByteString
  , emaWebSocketServerHandler :: EmaWsHandler r
  }

instance Default (EmaWebSocketOptions r) where
  def =
    EmaWebSocketOptions wsClientJS def

-- Browser-side JavaScript code for interacting with the Haskell server
wsClientJS :: LByteString
wsClientJS =
  encodeUtf8
    [text|
        <script src="https://unpkg.com/idiomorph@0.4.0"></script>

        <script type="module">
        ${wsClientJSShim}
        
        window.onpageshow = function () { init(false) };
        </script>
    |]