module Gargantext.Components.NgramsTable.Loader where import Gargantext.Prelude import Affjax (Error(..)) import Data.Either (Either(..)) import Data.Maybe (Maybe(..), maybe, isJust) import Effect (Effect) import Effect.Aff (Aff, launchAff_, throwError) import Effect.Class (liftEffect) import Effect.Exception (error) import Gargantext.Components.LoadingSpinner (loadingSpinner) import Gargantext.Config.REST (RESTError(..), AffRESTError) import Gargantext.Core.NgramsTable.Types (Version, Versioned(..)) import Gargantext.Utils.CacheAPI as GUC import Reactix as R import Simple.JSON as JSON import Toestand as T cacheName :: String cacheName = "ngrams-cache-api-loader" clearCache :: Unit -> Aff Unit clearCache _ = GUC.delete $ GUC.CacheName cacheName type LoaderWithCacheAPIProps path res ret = ( cacheEndpoint :: path -> AffRESTError Version , errorHandler :: RESTError -> Effect Unit , handleResponse :: Versioned res -> ret , mkRequest :: path -> GUC.Request , path :: path , renderer :: ret -> R.Element , spinnerClass :: Maybe String ) useLoaderWithCacheAPI :: forall path res ret. Eq path => JSON.ReadForeign res => Eq ret => Record (LoaderWithCacheAPIProps path res ret) -> R.Hooks R.Element useLoaderWithCacheAPI { cacheEndpoint , errorHandler , handleResponse , mkRequest , path , renderer , spinnerClass } = do state <- T.useBox Nothing state' <- T.useLive T.unequal state useCachedAPILoaderEffect { cacheEndpoint , errorHandler , handleResponse , mkRequest , path , state } pure $ maybe (loadingSpinner { additionalClass: spinnerClass }) renderer state' type LoaderWithCacheAPIEffectProps path res ret = ( cacheEndpoint :: path -> AffRESTError Version , errorHandler :: RESTError -> Effect Unit , handleResponse :: Versioned res -> ret , mkRequest :: path -> GUC.Request , path :: path , state :: T.Box (Maybe ret) ) useCachedAPILoaderEffect :: forall path res ret. Eq path => JSON.ReadForeign res => Eq ret => Record (LoaderWithCacheAPIEffectProps path res ret) -> R.Hooks Unit useCachedAPILoaderEffect { cacheEndpoint , errorHandler , handleResponse , mkRequest , path , state: state } = do oPath <- R.useRef path state' <- T.useLive T.unequal state R.useEffect' $ do if (R.readRef oPath == path) && (isJust state') then pure unit else do R.setRef oPath path let req = mkRequest path -- log2 "[useCachedLoader] mState" mState launchAff_ $ do cache <- GUC.openCache $ GUC.CacheName cacheName -- TODO Parallelize? vr@(Versioned { version }) <- GUC.cachedJson cache req eCacheReal <- cacheEndpoint path case eCacheReal of Left err -> liftEffect $ errorHandler err Right cacheReal -> do val <- if version == cacheReal then pure vr else do -- liftEffect $ do -- log "[useCachedAPILoaderEffect] versions dont match" -- log2 "[useCachedAPILoaderEffect] cached version" version -- log2 "[useCachedAPILoaderEffect] real version" cacheReal _ <- GUC.deleteReq cache req vr'@(Versioned { version: version', data: _ }) <- GUC.cachedJson cache req if version' == cacheReal then pure vr' else do liftEffect $ errorHandler $ SendResponseError $ RequestContentError $ "[useCachedAPILoaderEffect] Fetched clean cache but hashes don't match: " <> show version <> " != " <> show cacheReal throwError $ error $"[useCachedAPILoaderEffect] Fetched clean cache but hashes don't match: " <> show version <> " != " <> show cacheReal liftEffect $ do T.write_ (Just $ handleResponse val) state