diff --git a/src/re_frame/fx.cljs b/src/re_frame/fx.cljs new file mode 100644 index 0000000..af1aa64 --- /dev/null +++ b/src/re_frame/fx.cljs @@ -0,0 +1,112 @@ +(ns re-frame.fx + (:require [re-frame.router :refer [dispatch]] + [re-frame.db :refer [app-db]] + [re-frame.loggers :refer [console]])) + + + +;; -- Registration ------------------------------------------------------------ +;; +(def ^:private id->handler-fn (atom {})) + +(defn lookup-handler + [event-id] + (get @id->handler-fn event-id)) + + +(defn clear-handlers! + "Unregister all effects handlers" + [] + (reset! id->handler-fn {})) + + +(defn register + "register a handler fn for an effect." + [event-id handler-fn] + (when (contains? @id->handler-fn event-id) + (console :warn "re-frame: overwriting an effects handler for: " event-id)) ;; allow it, but warn. + (swap! id->handler-fn assoc event-id handler-fn)) + + +;; -- Standard effets --------------------------------------------------------- + + +(defn dispatch-helper + [effect] + (cond + (list? effect) (map dispatch effect) + (vector? effect) (dispatch effect) + :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)))) + +(register + :dispatch + (fn [effect] + (dispatch-helper effect))) + + +;; +;; {:forward-events {:listen :an-id-for-this-listner +;; :events #{:event1 :event2} +;; :dispatch-to [:eid "eg. param"]} ;; the forwared event will be conj to the end of the dispatch. +;; +;; {:forward-events {:unlisten :the-listner-id-I-originally-supplied}} +;; +(register + :forward-events + (let [id->listen-fn (atom {}) + process-entry (fn [{:keys [listen events dispatch-to unlisten]}] + (if (some? unlisten) + (do + (unregister XXXX) + (swap! id->listen-fn dissoc unlisten))))] + + + (fn [val] + (cond + (map? val) (process-entry m) + (list? val) (doseq [])) + (do-dispatches (:dispatch world))))) + + +(register + :db + (fn [world] + (if-let [db (:db world)] + (reset! app-db db)))) + +;; -- 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 write a gist for :http +;; XXX think about an undo effect +;; :undo + + +(defn fx + [handler] + (fn fx-handler + [app-db event-vec] + (let [world {:db @app-db} + result (handler world event-vec) + effects (-> result (dissoc :db) keys) + handlers (map lookup-handler effects) + retult' (reduce #(%2 %1) result handlers)] + + (if-let [db (:db result)] + (reset! app-db db)))))