2016-07-11 06:52:05 +00:00
|
|
|
(ns re-frame.fx
|
2016-07-15 01:33:35 +00:00
|
|
|
(:require [reagent.ratom :refer [IReactiveAtom]]
|
|
|
|
[re-frame.router :refer [dispatch]]
|
2016-07-11 06:52:05 +00:00
|
|
|
[re-frame.db :refer [app-db]]
|
2016-07-13 14:32:36 +00:00
|
|
|
[re-frame.events]
|
2016-07-11 06:52:05 +00:00
|
|
|
[re-frame.loggers :refer [console]]))
|
|
|
|
|
|
|
|
|
|
|
|
;; -- Registration ------------------------------------------------------------
|
2016-07-13 14:32:36 +00:00
|
|
|
|
2016-07-11 06:52:05 +00:00
|
|
|
(def ^:private id->handler-fn (atom {}))
|
|
|
|
|
|
|
|
(defn lookup-handler
|
2016-07-13 14:32:36 +00:00
|
|
|
[effect-id]
|
|
|
|
(get @id->handler-fn effect-id))
|
2016-07-11 06:52:05 +00:00
|
|
|
|
|
|
|
|
2016-07-13 14:32:36 +00:00
|
|
|
(defn clear-all-handlers!
|
2016-07-11 06:52:05 +00:00
|
|
|
[]
|
|
|
|
(reset! id->handler-fn {}))
|
|
|
|
|
|
|
|
|
2016-07-13 14:32:36 +00:00
|
|
|
(defn clear-handler!
|
|
|
|
[effect-id]
|
2016-07-14 06:41:54 +00:00
|
|
|
(if (lookup-handler effect-id)
|
|
|
|
(swap! id->handler-fn dissoc effect-id)
|
|
|
|
(console :warn "re-frame: unable to clear effect handler for " effect-id ". Not defined.")))
|
2016-07-13 14:32:36 +00:00
|
|
|
|
|
|
|
|
2016-07-11 06:52:05 +00:00
|
|
|
(defn register
|
|
|
|
"register a handler fn for an effect."
|
2016-07-14 06:41:54 +00:00
|
|
|
[effect-id handler-fn]
|
|
|
|
(when (lookup-handler effect-id)
|
|
|
|
(console :warn "re-frame: overwriting an effects handler for: " effect-id)) ;; allow it, but warn.
|
|
|
|
(swap! id->handler-fn assoc effect-id handler-fn))
|
2016-07-11 06:52:05 +00:00
|
|
|
|
|
|
|
|
2016-07-14 06:41:54 +00:00
|
|
|
;; -- Standard Builtin Effects Handlers --------------------------------------
|
2016-07-11 06:52:05 +00:00
|
|
|
|
|
|
|
(defn dispatch-helper
|
2016-07-15 13:51:40 +00:00
|
|
|
"There are cases where eitherone event is to be dipatch "
|
2016-07-11 06:52:05 +00:00
|
|
|
[effect]
|
|
|
|
(cond
|
|
|
|
(vector? effect) (dispatch effect)
|
2016-07-20 07:16:01 +00:00
|
|
|
(seq? effect) (dorun (map dispatch effect))
|
2016-07-11 06:52:05 +00:00
|
|
|
:else (console :error "re-frame: expected :dispatch effect to be a list or vector, but got: " effect)))
|
|
|
|
|
|
|
|
;; Example:
|
|
|
|
;; {:dispatch-later {200 [:event-id "param"] ;; in 200ms do this: (dispatch [:event-id "param"])
|
|
|
|
;; 100 [:also :this :in :100ms]
|
|
|
|
;; 250 (list [:do ] [:all ] [:three ])}
|
|
|
|
;;
|
|
|
|
(register
|
|
|
|
:dispatch-later
|
|
|
|
(fn [effect]
|
|
|
|
(doseq [[ms events] effect]
|
|
|
|
(js/setTimeout #(dispatch-helper events) ms))))
|
|
|
|
|
2016-07-14 06:41:54 +00:00
|
|
|
|
|
|
|
;; Supply either a vector or a list of vectors. For example:
|
|
|
|
;;
|
|
|
|
;; {:dispatch [:event-id "param"] }
|
|
|
|
;;
|
|
|
|
;; {:dispatch (list [:do :all] [:three :of] [:these]) }
|
|
|
|
;;
|
2016-07-11 06:52:05 +00:00
|
|
|
(register
|
|
|
|
:dispatch
|
2016-07-14 06:41:54 +00:00
|
|
|
(fn [val]
|
|
|
|
(dispatch-helper val)))
|
2016-07-11 06:52:05 +00:00
|
|
|
|
|
|
|
|
2016-07-13 14:32:36 +00:00
|
|
|
#_(register
|
2016-07-11 06:52:05 +00:00
|
|
|
:forward-events
|
|
|
|
(let [id->listen-fn (atom {})
|
2016-07-15 04:15:30 +00:00
|
|
|
process-one-entry (fn [{:as m :keys [unlisten listen events dispatch-to]}]
|
|
|
|
(let [_ (assert (map? m) (str "re-frame: effects handler for :forward-events expected a map or a list of maps. Got: " m))
|
|
|
|
_ (assert (or (= #{:unlisten} (-> m keys set))
|
|
|
|
(= #{:listen :events :dispatch-to} (-> m keys set))) "re-frame: effects handler for :forward-events given wrong map keys")]
|
|
|
|
(if unlisten
|
2016-07-15 01:33:35 +00:00
|
|
|
(do
|
|
|
|
(re-frame.core/remove-post-event-callback (@id->listen-fn unlisten))
|
|
|
|
(swap! id->listen-fn dissoc unlisten))
|
|
|
|
(let [post-event-callback-fn (fn [event-v _]
|
|
|
|
(when (events (first event-v))
|
|
|
|
(dispatch (conj dispatch-to event-v))))]
|
|
|
|
(re-frame.core/add-post-event-callback post-event-callback-fn)
|
|
|
|
(swap! id->listen-fn assoc listen post-event-callback-fn)))))]
|
2016-07-11 06:52:05 +00:00
|
|
|
(fn [val]
|
|
|
|
(cond
|
2016-07-15 01:33:35 +00:00
|
|
|
(map? val) (process-one-entry val)
|
2016-07-20 07:16:01 +00:00
|
|
|
(seq? val) (doall (map process-one-entry val))) ;; XXX add else
|
2016-07-15 01:33:35 +00:00
|
|
|
)))
|
2016-07-11 06:52:05 +00:00
|
|
|
|
|
|
|
|
2016-07-13 14:32:36 +00:00
|
|
|
(register
|
|
|
|
:deregister-event-handler
|
|
|
|
(fn [val]
|
2016-07-20 07:16:01 +00:00
|
|
|
(if (sequential? val)
|
2016-07-13 14:32:36 +00:00
|
|
|
(doall (map re-frame.events/clear-handler! val))
|
|
|
|
(re-frame.events/clear-handler! val))))
|
|
|
|
|
|
|
|
|
2016-07-11 06:52:05 +00:00
|
|
|
(register
|
|
|
|
:db
|
2016-07-14 06:41:54 +00:00
|
|
|
(fn [val]
|
|
|
|
(reset! app-db val)))
|
2016-07-11 06:52:05 +00:00
|
|
|
|
|
|
|
;; -- Middleware --------------------------------------------------------------
|
|
|
|
|
|
|
|
;; XXX a coeffect for jsDate ?
|
|
|
|
;; XXX add metadata saying it is fx.
|
|
|
|
;; XXX add config
|
|
|
|
;; XXX world or branch ?? Return world?
|
|
|
|
;; XXX ordering
|
|
|
|
;; XXX review other standard middleware
|
|
|
|
;; XXX think about an undo effect
|
|
|
|
|
|
|
|
|
|
|
|
(defn fx
|
|
|
|
[handler]
|
|
|
|
(fn fx-handler
|
|
|
|
[app-db event-vec]
|
2016-07-15 01:33:35 +00:00
|
|
|
(if-not (satisfies? IReactiveAtom app-db)
|
|
|
|
(if (map? app-db)
|
2016-07-15 01:40:07 +00:00
|
|
|
(console :warn "re-frame: Did you use \"fx\" middleware with \"def-event\"? Use \"def-event-fx\" instead (and don't directly use \"fx\")")
|
|
|
|
(console :warn "re-frame: \"fx\" middleware not given a Ratom. Got: " app-db)))
|
2016-07-15 08:46:16 +00:00
|
|
|
(let [run-effect (fn [[key val]]
|
|
|
|
(if-let [effect-fn (lookup-handler key)]
|
|
|
|
(effect-fn val)
|
|
|
|
(console :error "re-frame: no effects handler registered for: " key ". Ignoring")))
|
|
|
|
world {:db @app-db}]
|
|
|
|
(->> (handler world event-vec) ;; is expected to return a map of effects
|
|
|
|
(mapv run-effect))))) ;; use mapv to process the returned effects (because it isn't lazy)
|