NativeMessage.purs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. module ExampleWebExt.NativeMessage where
  2. import Prelude
  3. import Browser.WebExt.Listener (mkListener)
  4. import Browser.WebExt.Message (Message, mkMessage, unwrapMessage)
  5. import Browser.WebExt.Port (Port, onDisconnectAddListener, onMessageAddListener)
  6. import Browser.WebExt.Port as Port
  7. import Browser.WebExt.Runtime (Application, connectNative)
  8. import Data.Argonaut.Core (Json)
  9. import Data.Argonaut.Decode (class DecodeJson, JsonDecodeError(..), decodeJson, printJsonDecodeError)
  10. import Data.Argonaut.Decode.Generic (genericDecodeJson)
  11. import Data.Argonaut.Encode (class EncodeJson, encodeJson)
  12. import Data.Argonaut.Encode.Generic (genericEncodeJson)
  13. import Data.Either (Either(..))
  14. import Data.Generic.Rep (class Generic)
  15. import Data.Show.Generic (genericShow)
  16. import Effect (Effect)
  17. import Effect.Class.Console (log)
  18. import Record (union)
  19. data NativeMessage =
  20. NativeMessageBackground String
  21. | NativeMessageLog {level :: String, content :: String}
  22. | NativeMessageInitialConfiguration {jobsPath :: String}
  23. | NativeMessageVisitedJobPage {
  24. url :: String,
  25. jobTitle :: String,
  26. pageTitle :: String,
  27. company :: String,
  28. companyDomain :: String,
  29. companyUrl :: String,
  30. location :: String,
  31. hasSimplifiedProcess :: Boolean,
  32. flexibility :: String
  33. }
  34. | NativeMessageJobAlreadyExists {job_id :: String}
  35. | NativeMessageJobAdded {job :: NativePythonJobOffer}
  36. | NativeMessageJobOfferList {job_offers :: Array NativePythonJobOffer}
  37. type NativePythonJobOffer = {id :: String, title :: String, url :: String}
  38. type NativePythonMessage m = {tag :: String | m}
  39. type NativePythonMessageLog = NativePythonMessage (level :: String, content :: String)
  40. type NativePythonMessageInitialConfiguration = NativePythonMessage (jobsPath :: String)
  41. type NativePythonMessageJobAlreadyExists = NativePythonMessage (job_id :: String)
  42. type NativePythonMessageJobOfferList = NativePythonMessage (job_offers :: Array NativePythonJobOffer)
  43. type NativePythonMessageJobAdded = NativePythonMessage (job :: NativePythonJobOffer)
  44. derive instance Generic NativeMessage _
  45. instance Show NativeMessage where show = genericShow
  46. instance EncodeJson NativeMessage where
  47. encodeJson (NativeMessageInitialConfiguration r) = encodeJson {tag: "initial_configuration", jobsPath: r.jobsPath}
  48. encodeJson (NativeMessageVisitedJobPage r) = encodeJson $ union {tag: "visited_linkedin_job_page"} r
  49. encodeJson a = genericEncodeJson a
  50. -- A function used to transform some messages sent by the native application that are in the form
  51. -- of an object with unknown keys to an array of objects. The long-term solution is probably to
  52. -- change the format of the native message, but we'll probably need this function as well when
  53. -- we read data storage in storage.local which is stored as a giant object with unknown keys.
  54. foreign import toArrayOfObjects :: String -> Json -> Json
  55. instance DecodeJson NativeMessage where
  56. decodeJson json = case decodeJson @(NativePythonMessage ()) json of
  57. Right {tag: "log_message"} ->
  58. map (\{level, content} -> NativeMessageLog {level, content}) $ decodeJson @NativePythonMessageLog json
  59. Right {tag: "job_already_exists"} ->
  60. map (\{job_id} -> NativeMessageJobAlreadyExists {job_id}) $ decodeJson @NativePythonMessageJobAlreadyExists json
  61. Right {tag: "job_offer_list"} ->
  62. map (\o -> NativeMessageJobOfferList {job_offers: o.job_offers}) $
  63. decodeJson @NativePythonMessageJobOfferList $
  64. toArrayOfObjects "job_offers" json
  65. Right {tag: "job_added"} ->
  66. map (\{job} -> NativeMessageJobAdded {job}) $ decodeJson @NativePythonMessageJobAdded json
  67. Right _r -> Left $ UnexpectedValue json
  68. Left _ -> genericDecodeJson json
  69. connectToNativeApplication ∷ Application → Effect Port
  70. connectToNativeApplication = connectNative
  71. decodeNativeMessage ∷ Message → Either String NativeMessage
  72. decodeNativeMessage m =
  73. case unwrapMessage m of
  74. Left err -> Left $ printJsonDecodeError err
  75. Right m' -> Right m'
  76. onNativeMessageAddListener ∷ Port → (NativeMessage → Effect Unit) → Effect Unit
  77. onNativeMessageAddListener port f = onMessageAddListener port $ runtimeMessageHandler
  78. where
  79. runtimeMessageHandler = mkListener \m -> do
  80. case decodeNativeMessage m of
  81. Left err -> log err
  82. Right m' -> f m'
  83. onNativeDisconnectAddListener :: Port -> (Port -> Effect Unit) -> Effect Unit
  84. onNativeDisconnectAddListener port f = onDisconnectAddListener port $ mkListener f
  85. sendMessageToNative :: Port -> NativeMessage -> Effect Unit
  86. sendMessageToNative port msg = do
  87. _ <- Port.postMessage port $ mkMessage msg
  88. pure unit