module Gargantext.Context.Progress ( AsyncProps , asyncProgress , asyncContext ) where import Data.Either (Either(..)) import Data.Maybe (Maybe(..)) import Data.Tuple.Nested ((/\)) import Effect (Effect) import Effect.Aff (Aff, launchAff_) import Effect.Class (liftEffect) import Effect.Timer (IntervalId, clearInterval, setInterval) import Gargantext.Components.App.Store as AppStore import Gargantext.Components.Forest.Tree.Node.Tools.ProgressBar (QueryProgressData, queryProgress) import Gargantext.Components.Notifications as Notifications import Gargantext.Config.Utils (handleErrorInAsyncProgress, handleRESTError) import Gargantext.Hooks.FirstEffect (useFirstEffect') import Gargantext.Prelude import Gargantext.Sessions (Session) import Gargantext.Types (AsyncProgress) import Gargantext.Types as GT import Gargantext.Utils.Reactix as R2 import Reactix as R import Record.Extra as RX import Toestand as T type AsyncProps = ( asyncTask :: GT.AsyncTaskWithType , nodeId :: GT.ID , onFinish :: Unit -> Effect Unit , session :: Session ) here :: R2.Here here = R2.here "Gargantext.Context.Progress" asyncProgress :: R2.Component AsyncProps asyncProgress = R2.component asyncProgressCpt asyncProgressCpt :: R.Component AsyncProps asyncProgressCpt = R2.hereComponent here "asyncProgress" hCpt where hCpt hp props@{ onFinish } children = do { errors, wsNotification } <- AppStore.use -- States progress /\ progressBox <- R2.useBox' 0.0 intervalIdRef <- R.useRef (Nothing :: Maybe IntervalId) -- exponential backoff is used when errors are reported interval <- T.useBox 1000 -- Methods let exec :: Maybe GT.AsyncProgress -> Effect Unit exec Nothing = launchAff_ do let rdata = (RX.pick props :: Record QueryProgressData) eAsyncProgress <- queryProgress rdata liftEffect $ here.log2 "[progress] received asyncProgress" eAsyncProgress -- exponential backoff in case of errors -- liftEffect $ do -- case eAsyncProgress of -- Left _ -> T.modify_ (_ * 2) interval -- Right _ -> T.write_ 1000 interval -- interval' <- T.read interval -- resetInterval intervalIdRef (Just interval') exec handleRESTError hp errors eAsyncProgress onProgress exec (Just jobProgress) = do launchAff_ $ onProgress jobProgress onProgress :: AsyncProgress -> Aff Unit onProgress value@(GT.AsyncProgress { status }) = liftEffect do T.write_ (min 100.0 $ GT.progressPercent value) progressBox if (status == GT.IsFinished) || (status == GT.IsKilled) || (status == GT.IsFailure) then do -- resetInterval intervalIdRef Nothing exec -- case R.readRef intervalIdRef of -- Nothing -> R.nothing -- Just iid -> clearInterval iid handleErrorInAsyncProgress errors value onFinish unit else R.nothing useFirstEffect' $ do let (GT.AsyncTaskWithType { task: GT.AsyncTask { id: taskId } }) = props.asyncTask let cb msg = do here.log2 "callback! for job update" taskId case msg of Notifications.MJobProgress jobProgress -> do -- TODO With jobProgress we could avoid polling here -- exec (Just jobProgress) exec Nothing Notifications.MEmpty -> exec Nothing -- The modal window has some problems closing when we refresh too early. This is a HACK -- void $ setTimeout 400 $ T2.reload reload let action = Notifications.InsertCallback (Notifications.UpdateJobProgress taskId) ("task-" <> show taskId) cb ws <- T.read wsNotification Notifications.performAction ws action exec Nothing -- Hooks -- useFirstEffect' do -- resetInterval intervalIdRef (Just 1000) exec -- intervalId <- setInterval interval' $ exec unit -- R.setRef intervalIdRef $ Just intervalId -- Render pure $ R.provideContext asyncContext (progress) children resetInterval :: R.Ref (Maybe IntervalId) -> Maybe Int -> (Unit -> Effect Unit) -> Effect Unit resetInterval ref mInt exec = do case R.readRef ref /\ mInt of Nothing /\ Nothing -> pure unit Nothing /\ Just interval' -> do intervalId <- setInterval interval' $ exec unit R.setRef ref $ Just intervalId Just iid /\ Nothing -> do clearInterval iid R.setRef ref Nothing Just iid /\ Just interval' -> do clearInterval iid intervalId <- setInterval interval' $ exec unit R.setRef ref $ Just intervalId asyncContext :: R.Context (Number) asyncContext = R.createContext 0.0