{-# LANGUAGE TemplateHaskell #-}
{-|
Module: IHP.ServerSideComponent.ControllerFunctions
Copyright: (c) digitally induced GmbH, 2021
-}
module IHP.ServerSideComponent.ControllerFunctions where

import IHP.Prelude
import IHP.ControllerPrelude
import IHP.ServerSideComponent.Types as SSC

import qualified Network.WebSockets as WebSocket
import qualified Text.Blaze.Html.Renderer.Text as Blaze

import qualified Data.Aeson as Aeson
import qualified Data.Aeson.TH as Aeson

import IHP.ServerSideComponent.HtmlParser
import IHP.ServerSideComponent.HtmlDiff

$(Aeson.deriveJSON Aeson.defaultOptions { sumEncoding = defaultTaggedObject { tagFieldName = "type" }} ''Attribute)
$(Aeson.deriveJSON Aeson.defaultOptions { sumEncoding = defaultTaggedObject { tagFieldName = "type" }} ''AttributeOperation)
$(Aeson.deriveJSON Aeson.defaultOptions { sumEncoding = defaultTaggedObject { tagFieldName = "type" }} ''Node)
$(Aeson.deriveJSON Aeson.defaultOptions { sumEncoding = defaultTaggedObject { tagFieldName = "type" }} ''NodeOperation)

setState :: (?instanceRef :: IORef (ComponentInstance state), ?connection :: WebSocket.Connection, Component state action, ?context :: ControllerContext) => state -> IO ()
setState :: forall state action.
(?instanceRef::IORef (ComponentInstance state),
 ?connection::Connection, Component state action,
 ?context::ControllerContext) =>
state -> IO ()
setState state
state = do
    state
oldState <- (.state) (ComponentInstance state -> state)
-> IO (ComponentInstance state) -> IO state
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IORef (ComponentInstance state) -> IO (ComponentInstance state)
forall a. IORef a -> IO a
readIORef ?instanceRef::IORef (ComponentInstance state)
IORef (ComponentInstance state)
?instanceRef
    let oldHtml :: Text
oldHtml = state
oldState
            state -> (state -> Html) -> Html
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> state -> Html
state -> Html
forall state action. Component state action => state -> Html
SSC.render
            Html -> (Html -> Text) -> Text
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> Html -> Text
Blaze.renderHtml
            Text -> (Text -> Text) -> Text
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> Text -> Text
forall a b. ConvertibleStrings a b => a -> b
cs
    let newHtml :: Text
newHtml = state
state
            state -> (state -> Html) -> Html
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> state -> Html
state -> Html
forall state action. Component state action => state -> Html
SSC.render
            Html -> (Html -> Text) -> Text
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> Html -> Text
Blaze.renderHtml
            Text -> (Text -> Text) -> Text
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> Text -> Text
forall a b. ConvertibleStrings a b => a -> b
cs

    IORef (ComponentInstance state)
-> (ComponentInstance state -> ComponentInstance state) -> IO ()
forall a. IORef a -> (a -> a) -> IO ()
modifyIORef' ?instanceRef::IORef (ComponentInstance state)
IORef (ComponentInstance state)
?instanceRef (\ComponentInstance state
componentInstance -> ComponentInstance state
componentInstance { state
state :: state
$sel:state:ComponentInstance :: state
state })
    
    case Text -> Text -> Either (ParseErrorBundle Text Void) [NodeOperation]
diffHtml Text
oldHtml Text
newHtml of
        Left ParseErrorBundle Text Void
error -> Text -> IO ()
putStrLn (ParseErrorBundle Text Void -> Text
forall a. Show a => a -> Text
tshow ParseErrorBundle Text Void
error)
        Right [NodeOperation]
patches -> ByteString -> IO ()
forall text.
(?connection::Connection, WebSocketsData text) =>
text -> IO ()
sendTextData ([NodeOperation] -> ByteString
forall a. ToJSON a => a -> ByteString
Aeson.encode [NodeOperation]
patches)


getState :: (?instanceRef :: IORef (ComponentInstance state)) => IO state
getState :: forall state.
(?instanceRef::IORef (ComponentInstance state)) =>
IO state
getState = (.state) (ComponentInstance state -> state)
-> IO (ComponentInstance state) -> IO state
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IORef (ComponentInstance state) -> IO (ComponentInstance state)
forall a. IORef a -> IO a
readIORef ?instanceRef::IORef (ComponentInstance state)
IORef (ComponentInstance state)
?instanceRef

deriveSSC :: Name -> Q [Dec]
deriveSSC = Options -> Name -> Q [Dec]
Aeson.deriveJSON Options
Aeson.defaultOptions { allNullaryToStringTag :: Bool
allNullaryToStringTag = Bool
False, sumEncoding :: SumEncoding
sumEncoding = SumEncoding
defaultTaggedObject { tagFieldName :: String
tagFieldName = String
"action", contentsFieldName :: String
contentsFieldName = String
"payload" }}