Skip to content

Instantly share code, notes, and snippets.

@lwhorton
Created May 10, 2018
Embed
What would you like to do?
Ignore events that I don't want to see
(defn prevent-events
"Take heed, young traveler, for here be dragons.
Occasionally we have a need on a global level to prevent events from
triggering handlers through the system. For example, when a user is filling
out a multi-page form, we want to prevent them from accidentally or
intentionally clicking on a top-level navigation button, which would cause
them to lose all their progress on said form. Actually, we don't *really*
wan't to *prevent* events, but we do want some mechanism for intercepting them
and then decide what to do from there. In the case of the form example, we
probably want to spit out a modal that warns the user their progress is about
to be lost.
Such a system should not be a giant global handler where we have to maintain a
big list of events from disparate features. We should be able to determine,
on a per-feature level, which events we allow. All events not specifically
allowed to pass through this feature will trigger another event where we will
determine what to do at the feature level.
In short, when a feature requiring prevent-events is initialized, it must
register with 1) a unique key, 2) a set of allowed events 3) an event to
dispatch when we detect an event *not* belong to the set of allowed events.
When a feature is done preventing events, it must 1) unregister with the same
unique key 2) decide whether or not to allow the most recent event which
triggered the :on-any-other-event to pass or to be gobbbled up after
unregistering.
{:register :unique-id
:allow-these-events #{:a :b ...}
:on-any-other-event [:handle-interruption-event]}
{:unregister :unique-id
:allow-last-event false}
"
[]
#_(atom {:register :unique-id
:allow-these-events #{:event-a :event-b}
:on-any-other-event [:single-event]
:intercepted (or :single-event [:array :of :events])})
(let [registered (atom nil)
allowed-event? (fn [[evt & rst]] (contains? (:allow-these-events @registered) evt))]
(->interceptor
:id :prevent-events
:before (fn [ctx]
;; only prevent events if we have some preventer registered
(if-not @registered
ctx
(let [evt (get-in ctx [:coeffects :event])]
(if (allowed-event? evt)
ctx
;; when an event is not allowed, store it as the most
;; recently intercepted event, but also strip the
;; processing queue to prevent that event handler from
;; being invoked entirely ... and instead queue up the
;; on-any-other-event invocation for the next tick
(do
(swap! registered intercept evt)
(-> ctx
(update :queue (fn [queue]
(into #queue []
(remove
(fn [intc] (contains? #{:fx-handler :db-handler} (:id intc)))
queue))))
(update :effects assoc :dispatch (:on-any-other-event @registered))))))))
:after (fn [ctx]
(let [{fx :effects} ctx
dsl (:prevent-events fx)
reg (:register dsl)
unreg (:unregister dsl)]
;; for all events, check to see if we're trying to register or
;; unregister a prevent-events
(cond
;; registering is easy - just store the fx
(some? reg) (do (swap! registered register dsl) ctx)
;; when unregistering, assuming that :allow-last-event is
;; true, dispatch the last intercepted event on the next
;; tick
(some? unreg) (let [ctx (if (:allow-last-event dsl true)
(assoc-in ctx [:effects :dispatch] (:intercepted @registered))
ctx)]
(reset! registered nil)
ctx)
;; if we're not trying to reg/unreg, we have nothing to do
:else ctx))))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment