-----------------------------------------------------------------------------
{-# LANGUAGE OverloadedStrings #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  Miso.Subscription.Cookie
-- Copyright   :  (C) 2016-2026 David M. Johnson
-- License     :  BSD3-style (see the file LICENSE)
-- Maintainer  :  David M. Johnson <code@dmj.io>
-- Stability   :  experimental
-- Portability :  non-portable
--
-- = Overview
--
-- "Miso.Subscription.Cookie" provides 'cookieChangeSub', an optional
-- subscription that delivers
-- <https://developer.mozilla.org/en-US/docs/Web/API/CookieStore/change_event cookieStore change>
-- events into the update loop. Each event carries the list of cookies that
-- were added\/updated and the list that were deleted since the last event.
--
-- The subscription is optional — applications that only need one-shot reads
-- and writes can use 'Miso.Cookie.cookieGet', 'Miso.Cookie.cookieSet', and
-- 'Miso.Cookie.cookieDelete' directly without registering a subscription.
--
-- = Quick start
--
-- @
-- import "Miso"
-- import "Miso.Subscription.Cookie"
-- import "Miso.Cookie" ('CookieChangeEvent' (..))
--
-- data Action = CookiesChanged 'CookieChangeEvent'
--
-- subs :: ['Miso.Effect.Sub' Action]
-- subs = [ 'cookieChangeSub' CookiesChanged ]
--
-- update :: Action -> 'Miso.Effect.Effect' p props Model Action
-- update (CookiesChanged ev) =
--   'Miso.Effect.io_' (consoleLog (ms (show ('cookiesChanged' ev))))
-- @
--
-- = Availability
--
-- The CookieStore API requires a
-- <https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts secure context>
-- (HTTPS or @localhost@) and is not yet supported in all browsers. The
-- subscription silently does nothing when @cookieStore@ is unavailable.
--
-- = See also
--
-- * "Miso.Cookie" — 'Miso.Cookie.cookieGet', 'Miso.Cookie.cookieSet',
--   'Miso.Cookie.cookieDelete', 'Miso.Cookie.cookieGetAll'
-- * "Miso.Subscription.Util" — 'Miso.Subscription.Util.createSub' used internally
-- * "Miso.Subscription" — re-export hub
-----------------------------------------------------------------------------
module Miso.Subscription.Cookie
  ( -- ** Subscriptions
    cookieChangeSub
  ) where
-----------------------------------------------------------------------------
import           Miso.Cookie (CookieChangeEvent)
import           Miso.DSL (fromJSVal)
import           Miso.Effect (Sub)
import           Miso.Subscription.Util (createSub)
import qualified Miso.FFI.Internal as FFI
-----------------------------------------------------------------------------
-- | Returns a 'Sub' that fires an action on every
-- <https://developer.mozilla.org/en-US/docs/Web/API/CookieStore/change_event cookieStore change>
-- event, carrying the decoded 'CookieChangeEvent' (changed and deleted
-- cookie lists).
--
-- The subscription is self-contained: it registers the listener on mount
-- and removes it on unmount via 'Miso.Subscription.Util.createSub'.
--
-- <https://developer.mozilla.org/en-US/docs/Web/API/CookieStore/change_event>
--
cookieChangeSub
  :: (CookieChangeEvent -> action)
  -- ^ Callback: receives the change event on every cookie modification
  -> Sub action
cookieChangeSub :: forall action. (CookieChangeEvent -> action) -> Sub action
cookieChangeSub CookieChangeEvent -> action
f Sink action
sink = IO Function -> (Function -> IO ()) -> Sub action
forall a b action. IO a -> (a -> IO b) -> Sub action
createSub IO Function
acquire Function -> IO ()
release Sink action
sink
  where
    acquire :: IO Function
acquire = (JSVal -> IO ()) -> IO Function
FFI.cookieStoreAddEventListener ((JSVal -> IO ()) -> IO Function)
-> (JSVal -> IO ()) -> IO Function
forall a b. (a -> b) -> a -> b
$ \JSVal
ev ->
      JSVal -> IO (Maybe CookieChangeEvent)
forall a. FromJSVal a => JSVal -> IO (Maybe a)
fromJSVal JSVal
ev IO (Maybe CookieChangeEvent)
-> (Maybe CookieChangeEvent -> IO ()) -> IO ()
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (CookieChangeEvent -> IO ()) -> Maybe CookieChangeEvent -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (Sink action
sink Sink action
-> (CookieChangeEvent -> action) -> CookieChangeEvent -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CookieChangeEvent -> action
f)
    release :: Function -> IO ()
release = Function -> IO ()
FFI.cookieStoreRemoveEventListener
-----------------------------------------------------------------------------