2014-12-15 11:56:32 +00:00
|
|
|
(ns re-frame.handlers
|
2015-02-20 05:46:40 +00:00
|
|
|
(:refer-clojure :exclude [flush])
|
|
|
|
(:require-macros [cljs.core.async.macros :refer [go-loop go]])
|
|
|
|
(:require [reagent.core :refer [flush]]
|
|
|
|
[re-frame.db :refer [app-db]]
|
2014-12-20 02:12:40 +00:00
|
|
|
[re-frame.utils :refer [first-in-vector]]
|
2015-02-18 08:43:50 +00:00
|
|
|
[cljs.core.async :refer [chan put! <! timeout]]))
|
2014-12-08 03:48:59 +00:00
|
|
|
|
|
|
|
|
2015-02-20 05:46:40 +00:00
|
|
|
;; -- special handlers ----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
;; if true then pause the dispatch loop, generally to give the GUI a chance to sync
|
|
|
|
(def wait-one-annimation-frame (atom false))
|
|
|
|
|
|
|
|
(defn flush-reagent-handler
|
|
|
|
"Will force reagent to render any pending changes.
|
|
|
|
Useful when a handler is about to hog the CPU for a while and
|
|
|
|
there are pending GUI changes telling the user about progress.
|
|
|
|
Flushes any GUI changes through into the DOM."
|
|
|
|
[_ _]
|
|
|
|
(flush)
|
|
|
|
(reset! wait-one-annimation-frame true))
|
|
|
|
|
|
|
|
(def special-handlers {
|
|
|
|
:flush-reagent flush-reagent-handler})
|
|
|
|
|
|
|
|
|
2014-12-20 02:12:40 +00:00
|
|
|
;; -- register of handlers ------------------------------------------------------------------------
|
|
|
|
|
2015-02-20 05:46:40 +00:00
|
|
|
|
|
|
|
(def ^:private id->fn (atom special-handlers))
|
2014-12-15 11:56:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
(defn register
|
|
|
|
"register a handler for an event"
|
|
|
|
[event-id handler-fn]
|
|
|
|
(if (contains? @id->fn event-id)
|
|
|
|
(println "Warning: overwritting an event-handler" event-id)) ;; TODO: more generic logging
|
|
|
|
(swap! id->fn
|
|
|
|
assoc event-id handler-fn))
|
|
|
|
|
|
|
|
|
2014-12-20 02:12:40 +00:00
|
|
|
;; -- dispatching and routing ---------------------------------------------------------------------
|
|
|
|
|
|
|
|
(def ^:private dispatch-chan (chan))
|
|
|
|
|
2014-12-11 08:24:19 +00:00
|
|
|
(defn dispatch
|
2015-02-18 08:43:50 +00:00
|
|
|
"components send events by calling this function.
|
2014-12-15 11:56:32 +00:00
|
|
|
Usage example:
|
|
|
|
(dispatch [:delete-item 42])"
|
|
|
|
[event-v]
|
2014-12-20 02:12:40 +00:00
|
|
|
(assert (some? event-v)) ;; nil would close the channel
|
|
|
|
(put! dispatch-chan event-v))
|
|
|
|
|
2015-01-05 21:49:20 +00:00
|
|
|
(defn dispatch-sync
|
|
|
|
"sync version of above that actually does the dispatch"
|
|
|
|
[event-v]
|
|
|
|
(let [event-id (first-in-vector event-v)
|
|
|
|
handler-fn (get @id->fn event-id)]
|
2015-02-18 08:43:50 +00:00
|
|
|
(assert (some? handler-fn) (str "No event handler registered for event: " event-id ))
|
2015-01-05 21:49:20 +00:00
|
|
|
(handler-fn app-db event-v)))
|
|
|
|
|
2014-12-20 02:12:40 +00:00
|
|
|
|
|
|
|
(defn- router
|
|
|
|
"route an event, arriving on the dispatch channel, to the right handler"
|
|
|
|
[]
|
|
|
|
(go-loop []
|
2015-02-20 05:46:40 +00:00
|
|
|
(let [ ;; if a small pause is required (dispatch [:flush-reagent])
|
|
|
|
_ (if @wait-one-annimation-frame
|
|
|
|
(do
|
|
|
|
(reset! wait-one-annimation-frame false)
|
|
|
|
(<! (timeout 16))) ;; Wait one annimation frame, which will make sure any pending GUI work is done.
|
|
|
|
(<! (timeout 0))) ;; just in case we are handling one dispatch after an other, give the GUI a chance to do its stuff.
|
|
|
|
event-v (<! dispatch-chan)]
|
2015-01-05 21:49:20 +00:00
|
|
|
(dispatch-sync event-v)
|
2014-12-20 02:12:40 +00:00
|
|
|
(recur))))
|
|
|
|
|
2015-02-20 05:46:40 +00:00
|
|
|
;; run the router loop - sending event after event to the handlers
|
|
|
|
(router)
|
2014-12-20 02:12:40 +00:00
|
|
|
|
2014-12-08 03:48:59 +00:00
|
|
|
|
2014-12-20 02:12:40 +00:00
|
|
|
;; -- helper --------------------------------------------------------------------------------------
|
2014-12-08 03:48:59 +00:00
|
|
|
|
2014-12-20 02:12:40 +00:00
|
|
|
;; TODO: this has to go.
|
2014-12-15 11:56:32 +00:00
|
|
|
(defn transaction!
|
|
|
|
"A helper fucntion to be used when writting event handlers.
|
|
|
|
Allows a handler to perform an atomic modification of the atom.
|
|
|
|
Modification consist of one or more mutations, wrapped by a function,
|
|
|
|
followed by a call to a validation fucntion which may also annotate the
|
|
|
|
data structures with further information.
|
|
|
|
|
|
|
|
XXX This feels a bit too nested."
|
|
|
|
|
|
|
|
([db description mutation-fn]
|
|
|
|
(transaction! db description mutation-fn identity))
|
|
|
|
|
|
|
|
([db description mutation-fn validation-fn]
|
|
|
|
(reset! db
|
|
|
|
(-> @db
|
|
|
|
(assoc :undo-description description)
|
|
|
|
mutation-fn
|
|
|
|
validation-fn))))
|
2014-12-08 03:48:59 +00:00
|
|
|
|