module Reflex.Auth.Class(
HasAuth(..)
, AuthedEnv
, getAuthInfoMay
, getLogged
, signout
, signin
) where
import Control.Monad.Fix
import Control.Monad.IO.Class
import Control.Monad.Reader
import Data.Maybe
import Reflex
import Reflex.ExternalRef
-- | Environment that holds changing authorization information
type AuthedEnv t m = Dynamic t (AuthInfo t m)
-- | Monad for actions that can be authorizaed or not authorized.
class (Eq (AuthInfo t m), Reflex t) => HasAuth t m | m -> t where
-- | Monad specific information about authorization
type AuthInfo t m :: *
-- | Get reference from context that tracks current state of authorization
getAuthInfoRef :: m (ExternalRef t (Maybe (AuthInfo t m)))
-- | A typical way to perform one action when user logged and perform another
-- FRP network part when the user is authorized.
liftAuth :: m a -- ^ Which widget to render when not authorized
-> ReaderT (AuthedEnv t m) m a -- ^ Which widget to render when authorized
-> m (Dynamic t a)
-- | Get dynamic that is 'Nothing' when is not authorized and 'Just' when we have authorization.
getAuthInfoMay :: (HasAuth t m, MonadHold t m, MonadFix m, MonadIO m) => m (Dynamic t (Maybe (AuthInfo t m)))
getAuthInfoMay = holdUniqDyn =<< externalRefDynamic =<< getAuthInfoRef
-- | Return dynamic that indicates whether we in authorized or in authorized state.
getLogged :: (HasAuth t m, MonadHold t m, MonadFix m, MonadIO m) => m (Dynamic t Bool)
getLogged = fmap isJust <$> getAuthInfoMay
-- | When input event is fired, the auth info inside is nullified and all `liftAuth` widgets
-- are switched back to not authorized states.
signout :: (HasAuth t m, PerformEvent t m, MonadIO m, MonadIO (Performable m)) => Event t () -> m (Event t ())
signout e = do
ref <- getAuthInfoRef
performEvent $ ffor e $ const $ writeExternalRef ref Nothing
-- | When input event is fired all `liftAuth` widgets switches to authorized states.
signin :: (HasAuth t m, PerformEvent t m, MonadIO m, MonadIO (Performable m)) => Event t (AuthInfo t m) -> m (Event t (AuthInfo t m))
signin e = do
ref <- getAuthInfoRef
performEvent $ ffor e $ \ai -> writeExternalRef ref (Just ai) >> pure ai
instance {-# OVERLAPPABLE #-} (HasAuth t m, Monad m) => HasAuth t (ReaderT e m) where
type AuthInfo t (ReaderT e m) = AuthInfo t m
getAuthInfoRef = lift getAuthInfoRef
{-# INLINE getAuthInfoRef #-}
liftAuth unauthed authed = do
e <- ask
let authed' = do
ae <- ask
lift $ runReaderT (runReaderT authed ae) e
lift $ liftAuth (runReaderT unauthed e) authed'
{-# INLINE liftAuth #-}