Bläddra i källkod

Add loopUntilElementAppears async function to API

jherve 1 år sedan
förälder
incheckning
c7b65479c8
4 ändrade filer med 54 tillägg och 1 borttagningar
  1. 2 0
      spago.dhall
  2. 6 1
      src/LinkedIn.purs
  3. 3 0
      src/LinkedIn/Loadable.js
  4. 43 0
      src/LinkedIn/Loadable.purs

+ 2 - 0
spago.dhall

@@ -19,6 +19,8 @@ You can edit this file as you like.
   , "free"
   , "int64"
   , "integers"
+  , "js-promise"
+  , "js-promise-aff"
   , "lists"
   , "maybe"
   , "node-buffer"

+ 6 - 1
src/LinkedIn.purs

@@ -1,4 +1,4 @@
-module LinkedIn (APIError(..), encodeToJson, getContext, getContextJson, extractFromDocument, extractFromDocumentJson, forceExtract) where
+module LinkedIn (APIError(..), encodeToJson, getContext, getContextJson, extractFromDocument, extractFromDocumentJson, forceExtract, loopUntilElementAppears) where
 
 import Prelude
 
@@ -14,10 +14,12 @@ import Data.Traversable (class Traversable)
 import Effect (Effect)
 import LinkedIn.CanBeQueried (class CanBeQueried)
 import LinkedIn.Extractible (class Extractible)
+import LinkedIn.Loadable (waitFor)
 import LinkedIn.Output (OutputError, run, toOutput)
 import LinkedIn.Output.Types (Output)
 import LinkedIn.PageUrl (PageUrl, pageUrlP)
 import Parsing (runParser)
+import Promise.Aff (Promise, fromAff)
 import Type.Proxy (Proxy)
 import Web.DOM (Document)
 import Web.DOM.Document (url)
@@ -66,6 +68,9 @@ toOutput' ctx dom = withExceptT (\err -> ErrorExtraction err) $ toOutput ctx dom
 encodeToJson :: Either String Output -> Json
 encodeToJson = encodeJson
 
+loopUntilElementAppears ∷ String → Document → Effect (Promise Boolean)
+loopUntilElementAppears selector q = fromAff $ waitFor 200 50 selector q
+
 -- | Force extraction of data from a page, when the context given by the URL is imprecise
 -- | or plain wrong (e.g. for local files).
 -- | Can be call e.g. `forceExtract (Proxy :: Proxy JobOfferPage) dom`

+ 3 - 0
src/LinkedIn/Loadable.js

@@ -0,0 +1,3 @@
+const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
+
+export const sleepImpl = ms => () => wait(ms);

+ 43 - 0
src/LinkedIn/Loadable.purs

@@ -0,0 +1,43 @@
+module LinkedIn.Loadable where
+
+import Prelude
+
+import Control.Monad.Except (runExceptT)
+import Data.Either (Either(..), isRight)
+import Effect (Effect)
+import Effect.Aff (Aff, launchAff_)
+import Effect.Class (liftEffect)
+import Effect.Class.Console (log)
+import LinkedIn.QueryRunner (queryOne)
+import LinkedIn.Queryable (class Queryable)
+import Promise (Promise)
+import Promise.Aff (toAffE)
+
+foreign import sleepImpl :: Int -> Effect (Promise Unit)
+
+sleep :: Int -> Aff Unit
+sleep = sleepImpl >>> toAffE
+
+waitForElement' ∷ ∀ q. Queryable q ⇒ Int → Int → String → q → Effect Unit
+waitForElement' loopDelay maxLoops selector q = launchAff_ do
+  res <- waitForElement loopDelay maxLoops selector q
+  case res of
+    Left l -> log $ "failed after " <> show l
+    Right r -> log $ "succeeded after " <> show r
+
+waitForElement ∷ ∀ q. Queryable q ⇒ Int → Int → String → q → Aff (Either Int Int)
+waitForElement loopDelay maxLoops selector q = waitingLoop 0
+  where
+    waitingLoop :: Int -> Aff (Either Int Int)
+    waitingLoop iter | iter >= maxLoops = pure $ Left iter
+    waitingLoop iter = do
+      n <- liftEffect $ runExceptT $ queryOne selector q
+      case n of
+        Right _ -> pure $ Right iter
+        Left _ -> do
+          sleep loopDelay
+          waitingLoop (iter + 1)
+
+-- Returns true if the element was found after waiting for maxLoops
+waitFor ∷ ∀ q. Queryable q ⇒ Int → Int → String → q → Aff Boolean
+waitFor loopDelay maxLoops selector q = pure <<< isRight =<< waitForElement loopDelay maxLoops selector q