bulmex-3.0.0: src/Reflex/Bulmex/Input/Debounce.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecursiveDo #-}
{-# LANGUAGE TypeFamilies #-}
-- | This makes it easy to create auto-save operations
-- not limited to saves.
module Reflex.Bulmex.Input.Debounce
( InputStates(..)
, defStateAttr
, withInputDebounceEvt
, withInput
)
where
import Control.Monad.Fix
import Control.Monad.IO.Class (MonadIO)
import qualified Data.Text as Text
import Data.Time
import Reflex
import Reflex.Bulmex.Event
-- | Allows widgets to react according with what's happening
data InputStates
= InputStarted
| InputBuffered
| InputProcessed
| InputAborted
| InputInitial
-- | do a debounced action, create a widget with the dynamic inputstates
-- that indicates the state of doing the action
-- This allows you to automatically request the server once user is
-- finished with typing for example,
-- while not flooding the server with requests trough debouncing
withInputDebounceEvt
:: ( PostBuild t m
, MonadFix m
, MonadHold t m
, TriggerEvent t m
, MonadIO (Performable m)
, PerformEvent t m
)
=> NominalDiffTime -- ^ Delay before posting the request
-> (result -> Bool) -- ^ Was the final requess successfull?
-> (Dynamic t InputStates -> m (b, Event t inputEvt)) -- ^ Widget body reacting to states
-> (b -> Event t inputEvt -> m (Event t result)) -- ^ Action function
-> m (Event t result, b)
withInputDebounceEvt debtime succF stateF =
withInput (debounce debtime) succF
$ const
$ fmap (\(one', two) -> (one', two, one'))
. stateF
-- | Maps input state to bulmex classes: InputStarted = is-warning for example
defStateAttr :: InputStates -> Text.Text
defStateAttr InputStarted = "is-warning"
defStateAttr InputBuffered = "is-warning" -- is-loading
defStateAttr InputProcessed = "is-success"
defStateAttr InputAborted = "is-danger"
defStateAttr InputInitial = ""
-- | A general debounce widget
-- This looks a lot like 'actionForm', but it's not the same because
-- form allows user code to decide what
withInput
:: (PostBuild t m, MonadFix m, MonadHold t m)
=> (Event t inputEvt -> m (Event t inputEvt)) -- ^ change input timeline, eg pure for no change
-> (result -> Bool) -- ^ Was the final requess successfull?
-> ( Event t result
-> Dynamic t InputStates
-> m (actArgs, Event t inputEvt, finalRes)
) -- ^ Widget body reacting to states
-> (actArgs -> Event t inputEvt -> m (Event t result)) -- ^ Action function
-> m (Event t result, finalRes)
withInput timeF isSuccessF createTypeEvt reqFunc = mdo
(someData, typeEvtImmediate, result) <- createTypeEvt postFinished areaState
typeEvtDeb <- timeF typeEvtImmediate
postFinished <- reqFunc someData typeEvtDeb
areaState <-
holdDyn InputInitial
$ leftmost
$ [ InputStarted <$ typeEvtImmediate
, InputBuffered <$ typeEvtDeb
, InputProcessed <$ (blockFalse $ isSuccessF <$> postFinished)
, InputAborted <$ (blockFalse $ not . isSuccessF <$> postFinished)
]
pure (postFinished, result)