-----------------------------------------------------------------------------
-- |
-- Module      :  Miso.Storage
-- Copyright   :  (C) 2016-2025 David M. Johnson
-- License     :  BSD3-style (see the file LICENSE)
-- Maintainer  :  David M. Johnson <code@dmj.io>
-- Stability   :  experimental
-- Portability :  non-portable
--
-- This module provides an interface to the
-- [Web Storage API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API).
----------------------------------------------------------------------------
module Miso.Storage
  ( -- ** Local
    getLocalStorage
  , setLocalStorage
  , removeLocalStorage
  , clearLocalStorage
  , localStorageLength
    -- ** Session
  , getSessionStorage
  , setSessionStorage
  , removeSessionStorage
  , clearSessionStorage
  , sessionStorageLength
  ) where
-----------------------------------------------------------------------------
import           Data.Aeson (FromJSON(..), ToJSON, fromJSON)
import qualified Data.Aeson as A
import           Language.Javascript.JSaddle hiding (obj, val)
-----------------------------------------------------------------------------
import           Miso.FFI.Internal (jsonParse, jsonStringify)
import qualified Miso.FFI.Storage as Storage
import           Miso.String (MisoString)
-----------------------------------------------------------------------------
-- | Helper for retrieving either local or session storage
getStorageCommon
  :: FromJSON b => (t -> JSM (Maybe JSVal)) -> t -> JSM (Either String b)
getStorageCommon :: forall b t.
FromJSON b =>
(t -> JSM (Maybe JSVal)) -> t -> JSM (Either String b)
getStorageCommon t -> JSM (Maybe JSVal)
f t
key = do
  result <- t -> JSM (Maybe JSVal)
f t
key
  case result of
    Maybe JSVal
Nothing -> Either String b -> JSM (Either String b)
forall a. a -> JSM a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either String b -> JSM (Either String b))
-> Either String b -> JSM (Either String b)
forall a b. (a -> b) -> a -> b
$ String -> Either String b
forall a b. a -> Either a b
Left String
"Not Found"
    Just JSVal
v -> do
      r <- JSVal -> JSM Value
forall json. FromJSON json => JSVal -> JSM json
jsonParse JSVal
v
      pure $ case fromJSON r of
        A.Success b
x -> b -> Either String b
forall a b. b -> Either a b
Right b
x
        A.Error String
y -> String -> Either String b
forall a b. a -> Either a b
Left String
y
-----------------------------------------------------------------------------
-- | Retrieve a value stored under given key in session storage
getSessionStorage :: FromJSON model => MisoString -> JSM (Either String model)
getSessionStorage :: forall model.
FromJSON model =>
MisoString -> JSM (Either String model)
getSessionStorage =
  (MisoString -> JSM (Maybe JSVal))
-> MisoString -> JSM (Either String model)
forall b t.
FromJSON b =>
(t -> JSM (Maybe JSVal)) -> t -> JSM (Either String b)
getStorageCommon ((MisoString -> JSM (Maybe JSVal))
 -> MisoString -> JSM (Either String model))
-> (MisoString -> JSM (Maybe JSVal))
-> MisoString
-> JSM (Either String model)
forall a b. (a -> b) -> a -> b
$ \MisoString
t -> do
    s <- JSM Storage
Storage.sessionStorage
    r <- Storage.getItem s t
    fromJSVal r
-----------------------------------------------------------------------------
-- | Retrieve a value stored under given key in local storage
getLocalStorage :: FromJSON model => MisoString -> JSM (Either String model)
getLocalStorage :: forall model.
FromJSON model =>
MisoString -> JSM (Either String model)
getLocalStorage = (MisoString -> JSM (Maybe JSVal))
-> MisoString -> JSM (Either String model)
forall b t.
FromJSON b =>
(t -> JSM (Maybe JSVal)) -> t -> JSM (Either String b)
getStorageCommon ((MisoString -> JSM (Maybe JSVal))
 -> MisoString -> JSM (Either String model))
-> (MisoString -> JSM (Maybe JSVal))
-> MisoString
-> JSM (Either String model)
forall a b. (a -> b) -> a -> b
$ \MisoString
t -> do
    s <- JSM Storage
Storage.localStorage
    r <- Storage.getItem s t
    fromJSVal r
-----------------------------------------------------------------------------
-- | Set the value of a key in local storage.
--
-- @setLocalStorage key value@ sets the value of @key@ to @value@.
setLocalStorage :: ToJSON model => MisoString -> model -> JSM ()
setLocalStorage :: forall model. ToJSON model => MisoString -> model -> JSM ()
setLocalStorage MisoString
key model
model = do
  s <- JSM Storage
Storage.localStorage
  Storage.setItem s key =<< jsonStringify model
-----------------------------------------------------------------------------
-- | Set the value of a key in session storage.
--
-- @setSessionStorage key value@ sets the value of @key@ to @value@.
setSessionStorage :: ToJSON model => MisoString -> model -> JSM ()
setSessionStorage :: forall model. ToJSON model => MisoString -> model -> JSM ()
setSessionStorage MisoString
key model
model = do
  s <- JSM Storage
Storage.sessionStorage
  Storage.setItem s key =<< jsonStringify model
-----------------------------------------------------------------------------
-- | Removes an item from local storage
--
-- @removeLocalStorage key@ removes the value of @key@.
removeLocalStorage :: MisoString -> JSM ()
removeLocalStorage :: MisoString -> JSM ()
removeLocalStorage MisoString
key = do
  s <- JSM Storage
Storage.localStorage
  Storage.removeItem s key
-----------------------------------------------------------------------------
-- | Removes an item from session storage.
--
-- @removeSessionStorage key@ removes the value of @key@.
removeSessionStorage :: MisoString -> JSM ()
removeSessionStorage :: MisoString -> JSM ()
removeSessionStorage MisoString
key = do
  s <- JSM Storage
Storage.sessionStorage
  Storage.removeItem s key
-----------------------------------------------------------------------------
-- | Clear local storage
--
-- @clearLocalStorage@ removes all values from local storage.
clearLocalStorage :: JSM ()
clearLocalStorage :: JSM ()
clearLocalStorage = Storage -> JSM ()
Storage.clear (Storage -> JSM ()) -> JSM Storage -> JSM ()
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< JSM Storage
Storage.localStorage
-----------------------------------------------------------------------------
-- | Clear session storage
--
-- @clearSessionStorage@ removes all values from session storage.
clearSessionStorage :: JSM ()
clearSessionStorage :: JSM ()
clearSessionStorage = Storage -> JSM ()
Storage.clear (Storage -> JSM ()) -> JSM Storage -> JSM ()
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< JSM Storage
Storage.sessionStorage
-----------------------------------------------------------------------------
-- | Local storage length
--
-- @localStorageLength@ returns the count of items in local storage
localStorageLength :: JSM Int
localStorageLength :: JSM Int
localStorageLength = Storage -> JSM Int
Storage.length (Storage -> JSM Int) -> JSM Storage -> JSM Int
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< JSM Storage
Storage.localStorage
-----------------------------------------------------------------------------
-- | Session storage length
--
-- @sessionStorageLength@ returns the count of items in session storage
sessionStorageLength :: JSM Int
sessionStorageLength :: JSM Int
sessionStorageLength = Storage -> JSM Int
Storage.length (Storage -> JSM Int) -> JSM Storage -> JSM Int
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< JSM Storage
Storage.sessionStorage
-----------------------------------------------------------------------------