----------------------------------------------------------------------------- {-# 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 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 -----------------------------------------------------------------------------