JobsUnifiedTopCard.purs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. module LinkedIn.UI.Components.JobsUnifiedTopCard where
  2. import Prelude
  3. import Control.Alt ((<|>))
  4. import Control.Monad.Error.Class (throwError)
  5. import Data.Foldable (class Foldable, foldMap, foldlDefault, foldrDefault)
  6. import Data.Generic.Rep (class Generic)
  7. import Data.Lens (Lens', Prism', Traversal', lens', prism', traversed, view)
  8. import Data.Lens.Record (prop)
  9. import Data.List.Types (List, NonEmptyList)
  10. import Data.Maybe (Maybe(..))
  11. import Data.Show.Generic (genericShow)
  12. import Data.Traversable (class Traversable, sequence, traverseDefault)
  13. import Data.Tuple (Tuple(..))
  14. import LinkedIn.CanBeQueried (class CanBeQueried, subQueryNEL, subQueryList, subQueryOne)
  15. import LinkedIn.QueryRunner (QueryError(..), queryList, queryOne, querySelf, queryText)
  16. import LinkedIn.Queryable (class Queryable, toNode)
  17. import Type.Proxy (Proxy(..))
  18. import Web.DOM.Node as N
  19. data JobsUnifiedTopCardElement a = JobsUnifiedTopCardElement {
  20. header :: a,
  21. primaryDescription :: TopCardPrimaryDescription a,
  22. insights :: List (TopCardInsight a),
  23. actions :: List (TopCardAction a)
  24. }
  25. data TopCardPrimaryDescription a = TopCardPrimaryDescription {
  26. link :: a,
  27. text :: a,
  28. tvmText :: List a
  29. }
  30. data TopCardInsight a = TopCardInsight {
  31. icon :: a,
  32. content :: TopCardInsightContent a
  33. }
  34. data TopCardInsightContent a =
  35. TopCardInsightContentSingle a
  36. | TopCardInsightContentSecondary {primary :: a, secondary :: NonEmptyList (TopCardSecondaryInsight a)}
  37. | TopCardInsightContentButton a
  38. data TopCardSecondaryInsight a =
  39. TopCardSecondaryInsightNested a
  40. | TopCardSecondaryInsightPlain a
  41. -- External application : <button id="ember74" class="jobs-apply-button artdeco-button artdeco-button--3 artdeco-button--primary ember-view artdeco-button--icon-right" role="link">
  42. -- LinkedIn Applcation : <button id="ember115" class="jobs-apply-button artdeco-button artdeco-button--3 artdeco-button--primary ember-view" data-job-id="3786945580">
  43. data TopCardAction a = TopCardActionButton a
  44. derive instance Generic (JobsUnifiedTopCardElement a) _
  45. derive instance Eq a => Eq (JobsUnifiedTopCardElement a)
  46. instance Show a => Show (JobsUnifiedTopCardElement a) where
  47. show = genericShow
  48. derive instance Functor JobsUnifiedTopCardElement
  49. instance Foldable JobsUnifiedTopCardElement where
  50. foldMap f (JobsUnifiedTopCardElement {header, primaryDescription, insights, actions}) =
  51. f header
  52. <> foldMap f primaryDescription
  53. <> foldMap (foldMap f) insights
  54. <> foldMap (foldMap f) actions
  55. foldl = \x -> foldlDefault x
  56. foldr = \x -> foldrDefault x
  57. instance Traversable JobsUnifiedTopCardElement where
  58. sequence (JobsUnifiedTopCardElement {header, primaryDescription, insights, actions}) = ado
  59. h <- header
  60. pd <- sequence primaryDescription
  61. i <- sequence (map sequence insights)
  62. a <- sequence (map sequence actions)
  63. in JobsUnifiedTopCardElement {header: h, primaryDescription: pd, insights: i, actions: a}
  64. traverse = \x -> traverseDefault x
  65. instance Queryable q => CanBeQueried q JobsUnifiedTopCardElement where
  66. query n = do
  67. header <- queryOne "h1.job-details-jobs-unified-top-card__job-title" n
  68. primaryDescription <- subQueryOne "div.job-details-jobs-unified-top-card__primary-description-container > div" n
  69. insights <- subQueryList "li.job-details-jobs-unified-top-card__job-insight" n
  70. actions <- subQueryList ".mt5 button" n
  71. pure $ JobsUnifiedTopCardElement {
  72. header,
  73. primaryDescription,
  74. insights,
  75. actions
  76. }
  77. derive instance Generic (TopCardPrimaryDescription a) _
  78. derive instance Eq a => Eq (TopCardPrimaryDescription a)
  79. instance Show a => Show (TopCardPrimaryDescription a) where
  80. show = genericShow
  81. derive instance Functor TopCardPrimaryDescription
  82. instance Foldable TopCardPrimaryDescription where
  83. foldMap f (TopCardPrimaryDescription {link, text, tvmText}) = f link <> f text <> (foldMap f) tvmText
  84. foldl = \x -> foldlDefault x
  85. foldr = \x -> foldrDefault x
  86. instance Traversable TopCardPrimaryDescription where
  87. sequence (TopCardPrimaryDescription {link, text, tvmText}) = ado
  88. l <- link
  89. t <- text
  90. tvm <- sequence tvmText
  91. in TopCardPrimaryDescription {link: l, text: t, tvmText: tvm}
  92. traverse = \x -> traverseDefault x
  93. instance Queryable q => CanBeQueried q TopCardPrimaryDescription where
  94. query n = do
  95. link <- queryOne ":scope > a" n
  96. text <- queryText 1 n
  97. tvmText <- queryList "span.tvm__text" n
  98. pure $ TopCardPrimaryDescription {link, text, tvmText: tvmText}
  99. derive instance Generic (TopCardInsight a) _
  100. derive instance Eq a => Eq (TopCardInsight a)
  101. instance Show a => Show (TopCardInsight a) where
  102. show = genericShow
  103. derive instance Functor TopCardInsight
  104. instance Foldable TopCardInsight where
  105. foldMap f (TopCardInsight {icon, content}) = f icon <> foldMap f content
  106. foldl = \x -> foldlDefault x
  107. foldr = \x -> foldrDefault x
  108. instance Traversable TopCardInsight where
  109. sequence (TopCardInsight {icon, content}) = ado
  110. i <- icon
  111. c <- sequence content
  112. in TopCardInsight {icon: i, content: c}
  113. traverse = \x -> traverseDefault x
  114. instance Queryable q => CanBeQueried q TopCardInsight where
  115. query n = do
  116. icon <- queryOne ":scope li-icon" n <|> queryOne ":scope svg" n
  117. content <- subQueryOne ":scope > span" n <|> subQueryOne ":scope > button" n
  118. pure $ TopCardInsight {icon, content}
  119. derive instance Generic (TopCardInsightContent a) _
  120. derive instance Eq a => Eq (TopCardInsightContent a)
  121. instance Show a => Show (TopCardInsightContent a) where
  122. show = genericShow
  123. derive instance Functor TopCardInsightContent
  124. instance Foldable TopCardInsightContent where
  125. foldMap f (TopCardInsightContentSingle a) = f a
  126. foldMap f (TopCardInsightContentButton a) = f a
  127. foldMap f (TopCardInsightContentSecondary {primary, secondary}) = f primary <> foldMap (foldMap f) secondary
  128. foldl = \x -> foldlDefault x
  129. foldr = \x -> foldrDefault x
  130. instance Traversable TopCardInsightContent where
  131. sequence (TopCardInsightContentSingle ins) = TopCardInsightContentSingle <$> ins
  132. sequence (TopCardInsightContentButton ins) = TopCardInsightContentButton <$> ins
  133. sequence (TopCardInsightContentSecondary {primary, secondary}) = ado
  134. p <- primary
  135. s <- sequence (map sequence secondary)
  136. in TopCardInsightContentSecondary {primary: p, secondary: s}
  137. traverse = \x -> traverseDefault x
  138. instance Queryable q => CanBeQueried q TopCardInsightContent where
  139. query n =
  140. queryTopCardInsightContentSecondary n
  141. <|> queryTopCardInsightContentButton n
  142. <|> queryTopCardInsightContentSingle n
  143. where
  144. queryTopCardInsightContentSingle n = do
  145. n' <- querySelf n
  146. pure $ TopCardInsightContentSingle n'
  147. queryTopCardInsightContentButton n =
  148. if type_ == "BUTTON"
  149. then do
  150. n' <- querySelf n
  151. pure $ TopCardInsightContentButton n'
  152. else throwError (QNodeUnexpectedType "BUTTON" type_)
  153. where type_ = N.nodeName $ toNode n
  154. queryTopCardInsightContentSecondary n = do
  155. primary <- queryOne ":scope > span:first-child span[aria-hidden=true]" n
  156. secondary <- subQueryNEL ":scope > span.job-details-jobs-unified-top-card__job-insight-view-model-secondary" n
  157. pure $ TopCardInsightContentSecondary {primary, secondary}
  158. derive instance Generic (TopCardSecondaryInsight a) _
  159. derive instance Eq a => Eq (TopCardSecondaryInsight a)
  160. instance Show a => Show (TopCardSecondaryInsight a) where
  161. show = genericShow
  162. derive instance Functor TopCardSecondaryInsight
  163. instance Foldable TopCardSecondaryInsight where
  164. foldMap f (TopCardSecondaryInsightNested a) = f a
  165. foldMap f (TopCardSecondaryInsightPlain a) = f a
  166. foldl = \x -> foldlDefault x
  167. foldr = \x -> foldrDefault x
  168. instance Traversable TopCardSecondaryInsight where
  169. sequence (TopCardSecondaryInsightNested ins) = TopCardSecondaryInsightNested <$> ins
  170. sequence (TopCardSecondaryInsightPlain ins) = TopCardSecondaryInsightPlain <$> ins
  171. traverse = \x -> traverseDefault x
  172. instance Queryable q => CanBeQueried q TopCardSecondaryInsight where
  173. query n = queryTopCardSecondaryInsightNested n <|> queryTopCardSecondaryInsightPlain n
  174. where
  175. queryTopCardSecondaryInsightNested n = do
  176. nested <- queryOne ":scope span[aria-hidden=true]" n
  177. pure $ TopCardSecondaryInsightNested nested
  178. queryTopCardSecondaryInsightPlain n = do
  179. n' <- querySelf n
  180. pure $ TopCardSecondaryInsightPlain n'
  181. derive instance Generic (TopCardAction a) _
  182. derive instance Eq a => Eq (TopCardAction a)
  183. instance Show a => Show (TopCardAction a) where
  184. show = genericShow
  185. derive instance Functor TopCardAction
  186. instance Foldable TopCardAction where
  187. foldMap f (TopCardActionButton a) = f a
  188. foldl = \x -> foldlDefault x
  189. foldr = \x -> foldrDefault x
  190. instance Traversable TopCardAction where
  191. sequence (TopCardActionButton app) = ado
  192. a <- app
  193. in TopCardActionButton a
  194. traverse = \x -> traverseDefault x
  195. instance Queryable q => CanBeQueried q TopCardAction where
  196. query n = do
  197. n' <- querySelf n
  198. pure $ TopCardActionButton n'
  199. toHeader ∷ forall a. JobsUnifiedTopCardElement a → a
  200. toHeader = view $ _top_card <<< prop (Proxy :: Proxy "header")
  201. toPrimaryDescriptionLink ∷ forall a. JobsUnifiedTopCardElement a → a
  202. toPrimaryDescriptionLink = view $ _top_card
  203. <<< prop (Proxy :: Proxy "primaryDescription")
  204. <<< _primary_description
  205. <<< prop (Proxy :: Proxy "link")
  206. toPrimaryDescriptionText ∷ forall a. JobsUnifiedTopCardElement a → a
  207. toPrimaryDescriptionText = view $ _top_card
  208. <<< prop (Proxy :: Proxy "primaryDescription")
  209. <<< _primary_description
  210. <<< prop (Proxy :: Proxy "text")
  211. _top_to_insights ∷ ∀ a. Traversal' (JobsUnifiedTopCardElement a) (TopCardInsight a)
  212. _top_to_insights = _top_card
  213. <<< prop (Proxy :: Proxy "insights")
  214. <<< traversed
  215. _insight_to_content = prop (Proxy :: Proxy "content")
  216. <<< traversed
  217. _top_to_action_buttons ∷ ∀ a. Traversal' (JobsUnifiedTopCardElement a) a
  218. _top_to_action_buttons = _top_card
  219. <<< prop (Proxy :: Proxy "actions")
  220. <<< traversed
  221. <<< _action_button
  222. _top_card ∷ forall a. Lens' (JobsUnifiedTopCardElement a) { actions ∷ List (TopCardAction a) , header ∷ a , insights ∷ List (TopCardInsight a) , primaryDescription ∷ TopCardPrimaryDescription a }
  223. _top_card = lens' \(JobsUnifiedTopCardElement c) -> Tuple c \c' -> JobsUnifiedTopCardElement c'
  224. _insight ∷ forall a. Lens' (TopCardInsight a) { content ∷ TopCardInsightContent a , icon ∷ a }
  225. _insight = lens' \(TopCardInsight i) -> Tuple i \i' -> TopCardInsight i'
  226. _action_button ∷ forall a. Lens' (TopCardAction a) a
  227. _action_button = lens' \(TopCardActionButton i) -> Tuple i \i' -> TopCardActionButton i'
  228. _primary_description ∷ ∀ a. Lens' (TopCardPrimaryDescription a) { link ∷ a , text ∷ a , tvmText ∷ List a }
  229. _primary_description = lens' \(TopCardPrimaryDescription i) -> Tuple i \i' -> TopCardPrimaryDescription i'
  230. _insight_content_single ∷ forall a. Prism' (TopCardInsightContent a) a
  231. _insight_content_single = prism' TopCardInsightContentSingle case _ of
  232. TopCardInsightContentSingle i -> Just i
  233. _ -> Nothing
  234. _insight_content_button ∷ forall a. Prism' (TopCardInsightContent a) a
  235. _insight_content_button = prism' TopCardInsightContentButton case _ of
  236. TopCardInsightContentButton i -> Just i
  237. _ -> Nothing
  238. _insight_content_secondary ∷ forall a. Prism' (TopCardInsightContent a) { primary ∷ a , secondary ∷ NonEmptyList (TopCardSecondaryInsight a) }
  239. _insight_content_secondary = prism' TopCardInsightContentSecondary case _ of
  240. TopCardInsightContentSecondary i -> Just i
  241. _ -> Nothing
  242. _insight_content_secondary_nested ∷ forall a. Prism' (TopCardSecondaryInsight a) a
  243. _insight_content_secondary_nested = prism' TopCardSecondaryInsightNested case _ of
  244. TopCardSecondaryInsightNested i -> Just i
  245. _ -> Nothing
  246. _insight_content_secondary_plain ∷ forall a. Prism' (TopCardSecondaryInsight a) a
  247. _insight_content_secondary_plain = prism' TopCardSecondaryInsightPlain case _ of
  248. TopCardSecondaryInsightPlain i -> Just i
  249. _ -> Nothing