{- |
Module      : Hypermedia.Datastar.PatchSignals
Description : Update the browser's reactive signal store

Signals are Datastar's reactive state — key-value pairs that live in the
browser and drive the UI. The server can update signals at any time by
sending a @datastar-patch-signals@ event.

Signal patching uses JSON Merge Patch semantics: set a key to update it,
set a key to @null@ to remove it, and omit a key to leave it unchanged.

@
sendPatchSignals gen (patchSignals \"{\\\"count\\\": 42}\")
@

To set initial state without overwriting values the user may have already
changed (e.g. form inputs), use 'psOnlyIfMissing':

@
sendPatchSignals gen
  (patchSignals \"{\\\"name\\\": \\\"default\\\"}\")
    { psOnlyIfMissing = True }
@
-}
module Hypermedia.Datastar.PatchSignals where

import Data.Text (Text)
import Data.Text qualified as T
import Hypermedia.Datastar.Types

{- | Configuration for a @datastar-patch-signals@ SSE event.

Construct values with 'patchSignals', then customise with record updates.
-}
data PatchSignals = PatchSignals
  { PatchSignals -> Text
psSignals :: Text
  {- ^ JSON object containing the signal values to patch. Uses JSON Merge
  Patch semantics: set a key to update it, set to @null@ to remove it.
  -}
  , PatchSignals -> Bool
psOnlyIfMissing :: Bool
  {- ^ When 'True', signal values are only set if the key doesn't already
  exist in the browser's store. Useful for setting initial state that
  shouldn't overwrite user changes (e.g. form input defaults).
  Default: 'False'.
  -}
  , PatchSignals -> Maybe Text
psEventId :: Maybe Text
  -- ^ Optional SSE event ID for reconnection. See 'Hypermedia.Datastar.PatchElements.PatchElements' for details.
  , PatchSignals -> Int
psRetryDuration :: Int
  -- ^ SSE retry interval in milliseconds. Default: @1000@.
  }
  deriving (PatchSignals -> PatchSignals -> Bool
(PatchSignals -> PatchSignals -> Bool)
-> (PatchSignals -> PatchSignals -> Bool) -> Eq PatchSignals
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: PatchSignals -> PatchSignals -> Bool
== :: PatchSignals -> PatchSignals -> Bool
$c/= :: PatchSignals -> PatchSignals -> Bool
/= :: PatchSignals -> PatchSignals -> Bool
Eq, Int -> PatchSignals -> ShowS
[PatchSignals] -> ShowS
PatchSignals -> String
(Int -> PatchSignals -> ShowS)
-> (PatchSignals -> String)
-> ([PatchSignals] -> ShowS)
-> Show PatchSignals
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> PatchSignals -> ShowS
showsPrec :: Int -> PatchSignals -> ShowS
$cshow :: PatchSignals -> String
show :: PatchSignals -> String
$cshowList :: [PatchSignals] -> ShowS
showList :: [PatchSignals] -> ShowS
Show)

{- | Build a 'PatchSignals' event with sensible defaults.

The argument is a JSON object (as 'Text') describing the signals to update.

@
patchSignals \"{\\\"count\\\": 42, \\\"label\\\": \\\"hello\\\"}\"
@
-}
patchSignals :: Text -> PatchSignals
patchSignals :: Text -> PatchSignals
patchSignals Text
sigs =
  PatchSignals
    { psSignals :: Text
psSignals = Text
sigs
    , psOnlyIfMissing :: Bool
psOnlyIfMissing = Bool
defaultOnlyIfMissing
    , psEventId :: Maybe Text
psEventId = Maybe Text
forall a. Maybe a
Nothing
    , psRetryDuration :: Int
psRetryDuration = Int
defaultRetryDuration
    }

toDatastarEvent :: PatchSignals -> DatastarEvent
toDatastarEvent :: PatchSignals -> DatastarEvent
toDatastarEvent PatchSignals
ps =
  DatastarEvent
    { eventType :: EventType
eventType = EventType
EventPatchSignals
    , eventId :: Maybe Text
eventId = PatchSignals -> Maybe Text
psEventId PatchSignals
ps
    , retry :: Int
retry = PatchSignals -> Int
psRetryDuration PatchSignals
ps
    , dataLines :: [Text]
dataLines = [Text]
ifMissingLine [Text] -> [Text] -> [Text]
forall a. [a] -> [a] -> [a]
++ [Text]
signalLines
    }
 where
  ifMissingLine :: [Text]
ifMissingLine = [Text
"onlyIfMissing true" | PatchSignals -> Bool
psOnlyIfMissing PatchSignals
ps]
  signalLines :: [Text]
signalLines = (Text -> Text) -> [Text] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map (Text
"signals " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>) ([Text] -> [Text]) -> [Text] -> [Text]
forall a b. (a -> b) -> a -> b
$ Text -> [Text]
T.lines (Text -> [Text]) -> Text -> [Text]
forall a b. (a -> b) -> a -> b
$ PatchSignals -> Text
psSignals PatchSignals
ps