Fork me on GitHub
#missionary
<
2023-01-11
>
denik20:01:46

new problem: I have a discrete flow (DOM "copy" event listener). On every event I have to use the async Clipboard API to retrieve the contents of the clipboard. the goal is to have a p/def that represents the current clipboard contents at all time

leonoel20:01:20

(m/reductions {} ""
  (m/ap
    (m/?> (m/relieve {} (m/observe copy-events)))
    (let [text (m/dfv)]
      (-> (.-clipboard js/Navigator)
        (.readText)
        (.then text))
      (m/? text))))

leonoel20:01:53

(untested)

denik20:01:28

great, both of these work, the latter being a version of yours:

(p/def clipboard2
  #?(:cljs
     (->> (m/observe
            (fn [!]
              (let [handler (fn [e] (sdom/read-clipboard !))]
                (.addEventListener js/document "copy" handler)
                #(.removeEventListener js/document "copy" handler))))
          (m/reductions {} nil))))

(p/def clipboard3
  #?(:cljs
     (->>
       (m/ap
         (m/?> (m/relieve {} (m/observe
                               (fn [!]
                                 (.addEventListener js/document "copy" !)
                                 #(.removeEventListener js/document "copy" !)))))
         (m/? (doto (m/dfv) sdom/read-clipboard)))
       (m/reductions {} ""))))

denik21:01:27

can two flows be merged into one? for example, the app should read the clipboard a) when the user copies something b) when the user focuses the window

(p/def clipboard+focus
  #?(:cljs
     (->>
       (m/ap
         (m/?> (m/relieve {}
                            (m/observe
                              (fn [!]
                                (.addEventListener js/document "copy" !)
                                #(.removeEventListener js/document "copy" !)))))
         (m/?> (m/relieve {}
                          (m/observe
                            (fn [!]
                              (let [handler (fn [e]
                                              (println :running (j/call js/document :hasFocus))
                                              (when (j/call js/document :hasFocus)
                                                (! e)))]
                                (.addEventListener js/window "focus" handler)
                                #(.removeEventListener js/window "focus" handler))))))
         (m/? (doto (m/dfv) sdom/read-clipboard)))
       (m/reductions {} ""))))

denik23:01:29

that works!

(p/def clipboard+focus2
  #?(:cljs
     (->>
       (m/ap
         (m/?> #_(m/relieve {})
               (m/amb=
                 (m/observe
                   (fn [!]
                     (.addEventListener js/document "copy" !)
                     #(.removeEventListener js/document "copy" !)))
                 (m/observe
                   (fn [!]
                     (let [handler (fn [e]
                                     (println :running (j/call js/document :hasFocus))
                                     (when (j/call js/document :hasFocus)
                                       (! e)))]
                       (.addEventListener js/window "focus" handler)
                       #(.removeEventListener js/window "focus" handler))))))
         (m/? (doto (m/dfv) sdom/read-clipboard)))
       (m/reductions {} "")
       new)))
it also appears that m/relieve is not needed in this case?

leonoel07:01:13

relieve covers the case of two consecutive events where the former is still being processed when the latter occurs. relieve {} explicitly says we want to keep the latest, without it observe would complain about backpressure in the form of errors in the console.

denik17:01:22

I see. so in that case it seems like there should be a relieve expr around each observe rather than one wrapping the amb=, correct?