Update local state directly with om cursors

This commit is contained in:
Nicholas Kariniemi 2014-08-09 18:18:27 +03:00
parent 5230dc9601
commit 11597a9b63
6 changed files with 106 additions and 131 deletions

View file

@ -66,13 +66,11 @@
(assoc state :recipes (dissoc (:recipes state) (:id event))))
(defn update-state-and-render [remote]
(let [out (chan)
view-events (view/render-app app-state)]
(view/render-app app-state)
(let [out (chan)]
(go-loop []
(let [[event ch] (alts! [remote view-events])
(let [event (<! remote)
new-state (handle-event event @app-state)]
(reset! app-state new-state)
(when (= ch view-events)
(>! out event))
(recur)))
out))

View file

@ -26,29 +26,12 @@
(dom/on-window-scroll #(put! >events {:type :body-scroll :event %}))))))
(defn render-app [state]
(let [grub-add (chan)
grub-update (chan)
grub-clear-all (chan)
grub-remove (chan)
recipe-add (chan)
recipe-add-grubs (chan)
recipe-update (chan)
recipe-remove (chan)
out (a/merge [grub-add grub-update grub-clear-all grub-remove
recipe-add recipe-add-grubs recipe-update recipe-remove])
>events (chan)
<events (a/pub >events :type)]
(let [>events (chan)
<events (a/pub >events :type)
add-grubs-ch (chan)]
(om/root app-view
state
{:target (.getElementById js/document "container")
:shared {:grub-add grub-add
:grub-update grub-update
:grub-clear-all grub-clear-all
:grub-remove grub-remove
:recipe-add recipe-add
:recipe-add-grubs recipe-add-grubs
:recipe-update recipe-update
:recipe-remove recipe-remove
:>events >events
:<events <events}})
out))
:shared {:>events >events
:<events <events
:add-grubs-ch add-grubs-ch}})))

View file

@ -7,32 +7,11 @@
(:require-macros [grub.macros :refer [log logs]]
[cljs.core.async.macros :refer [go go-loop]]))
(defn new-grub [grub]
(defn new-grub [text]
{:id (str "grub-" (uuid/make-random))
:grub grub
:text text
:completed false})
(defn add-event [grub]
(assoc (new-grub grub) :event :add-grub))
(defn add-list-event [grubs]
{:event :add-grub-list
:grubs grubs})
(defn edit-event [id grub]
{:event :update-grub
:id id
:grub grub})
(defn complete-event [{:keys [id completed]}]
{:event :update-grub
:id id
:completed (not completed)})
(defn remove-event [id]
{:event :remove-grub
:id id})
(def transitions
{:waiting {:mouse-down :pressed
:touch-start :pressed}
@ -54,21 +33,18 @@
timeout-id (js/setTimeout timeout-fn 500)]
(om/set-state! owner :timeout-id timeout-id))
[:pressed :waiting] (js/clearTimeout (om/get-state owner :timeout-id))
[:editing :waiting] (let [update-ch (om/get-shared owner :grub-update)
id (:id @(om/get-props owner))
edit-event (edit-event id (om/get-state owner :grub))]
(put! update-ch edit-event))
[:editing :waiting] (let [grub (om/get-props owner)]
(om/transact! grub #(assoc % :text (om/get-state owner :grub-text))))
nil)
(om/set-state! owner :edit-state next)))
(defn view [{:keys [id grub completed] :as props} owner]
(defn view [{:keys [id text completed] :as grub} owner {:keys [remove-ch]}]
(reify
om/IInitState
(init-state [_]
(let [publisher (chan)]
{:edit-state :waiting
:grub grub
:unmounted false}))
{:edit-state :waiting
:grub-text text
:unmounted false})
om/IRenderState
(render-state [_ {:keys [edit-state] :as state}]
@ -77,9 +53,9 @@
{:class [(when completed "completed")
(when (= edit-state :pressed) "grub-active")
(when (= edit-state :editing) "edit")]
:on-click #(when (#{:waiting :pressed} edit-state)
(put! (om/get-shared owner :grub-update) (complete-event @props))
(.blur (om/get-node owner :grub-input)))
:on-click (fn [e] (when (#{:waiting :pressed} edit-state)
(om/transact! grub #(assoc % :completed (not completed)))
(.blur (om/get-node owner :grub-input))))
:on-mouse-down #(transition-state owner :mouse-down)
:on-mouse-up #(transition-state owner :mouse-up)
:on-mouse-leave #(transition-state owner :mouse-leave)
@ -90,12 +66,12 @@
{:type "text"
:readOnly (if (= edit-state :editing) "" "readonly")
:ref :grub-input
:value (:grub state)
:on-change #(om/set-state! owner :grub (.. % -target -value))
:value (:grub-text state)
:on-change #(om/set-state! owner :grub-text (.. % -target -value))
:on-key-up #(when (dom/enter-pressed? %) (transition-state owner :enter))}]
(when (= edit-state :editing)
[:span.glyphicon.glyphicon-remove.pull-right
{:on-click #(put! (om/get-shared owner :grub-remove) (remove-event id))}])]))
{:on-click #(put! remove-ch id)}])]))
om/IDidMount
(did-mount [_]

View file

@ -1,10 +1,9 @@
(ns grub.view.grub-list
(:require [om.core :as om :include-macros true]
[sablono.core :as html :refer-macros [html]]
[cljs.core.async :as a :refer [<! put! chan]]
[cljs.core.async :as a :refer [<! chan]]
[grub.view.dom :as dom]
[grub.view.grub :as grub-view]
[cljs-uuid.core :as uuid])
[grub.view.grub :as grub-view])
(:require-macros [grub.macros :refer [log logs]]
[cljs.core.async.macros :refer [go go-loop]]))
@ -17,18 +16,20 @@
(defn sort-grubs [grubs]
(sort-by (juxt :completed get-grub-ingredient :grub) (vals grubs)))
(defn add-grub [add new-grub owner]
(when (not (empty? new-grub))
(om/set-state! owner :new-grub "")
(put! add (grub-view/add-event new-grub))))
(defn add-grub [owner grubs new-grub-text]
(when (not (empty? new-grub-text))
(let [new-grub (grub-view/new-grub new-grub-text)]
(om/set-state! owner :new-grub-text "")
(om/transact! grubs #(assoc % (:id new-grub) new-grub)))))
(defn view [props owner]
(defn view [grubs owner]
(reify
om/IInitState
(init-state [_]
{:new-grub ""})
{:new-grub-text ""
:remove-grub-ch (chan)})
om/IRenderState
(render-state [this {:keys [new-grub] :as state}]
(render-state [this {:keys [new-grub-text remove-grub-ch] :as state}]
(let [add (om/get-shared owner :grub-add)]
(html
[:div
@ -38,22 +39,35 @@
[:input.form-control#add-grub-input
{:type "text"
:placeholder "What do you need?"
:value new-grub
:value new-grub-text
:on-key-up #(when (dom/enter-pressed? %)
(add-grub add new-grub owner))
:on-change #(om/set-state! owner :new-grub (dom/event-val %))}]]
(add-grub owner grubs new-grub-text))
:on-change #(om/set-state! owner :new-grub-text (dom/event-val %))}]]
[:button.btn.btn-primary
{:id "add-grub-btn"
:type "button"
:on-click #(add-grub add new-grub owner)}
:on-click #(add-grub owner grubs new-grub-text)}
[:span.glyphicon.glyphicon-plus]]]
[:ul#grub-list.list-group
(for [grub (sort-grubs props)]
(om/build grub-view/view grub {:key :id}))]
(for [grub (sort-grubs grubs)]
(om/build grub-view/view grub {:key :id :opts {:remove-ch remove-grub-ch}}))]
[:button.btn.pull-right
{:id "clear-all-btn"
:class (when (empty? props) "hidden")
:class (when (empty? grubs) "hidden")
:type "button"
:on-click #(put! (om/get-shared owner :grub-clear-all)
{:event :clear-all-grubs})}
"Clear all"]])))))
:on-click #(om/update! grubs {})}
"Clear all"]])))
om/IWillMount
(will-mount [_]
(let [add-grubs-ch (om/get-shared owner :add-grubs-ch)
remove-grub-ch (om/get-state owner :remove-grub-ch)]
(go-loop []
(let [grubs-map (<! add-grubs-ch)]
(when-not (nil? grubs-map)
(om/transact! grubs #(merge % grubs-map))
(recur))))
(go-loop []
(let [id (<! remove-grub-ch)]
(when-not (nil? id)
(om/transact! grubs #(dissoc % id))
(recur))))))))

View file

@ -8,32 +8,26 @@
(:require-macros [grub.macros :refer [log logs]]
[cljs.core.async.macros :refer [go go-loop]]))
(defn add-event [name grubs]
{:event :add-recipe
:id (str "recipe-" (uuid/make-random))
(defn new-recipe [name grubs]
{:id (str "recipe-" (uuid/make-random))
:name name
:grubs grubs})
(defn update-event [id name grubs]
{:event :update-recipe
:id id
:name name
:grubs grubs})
(defn remove-event [id]
{:event :remove-recipe
:id id})
(defn parse-grubs-from-str [grubs-str]
(->> grubs-str
(clojure.string/split-lines)
(map grub-view/new-grub)
(into [])))
(defn map-by-key [key coll]
(->> coll
(map (fn [a] [(get a key) a]))
(into {})))
(defn add-grubs [add-grubs-ch grubs-str]
(let [grubs (parse-grubs-from-str grubs-str)
event (grub-view/add-list-event grubs)]
(put! add-grubs-ch event)))
grubs-map (map-by-key :id grubs)]
(put! add-grubs-ch grubs-map)))
(def transitions
{:waiting {:click :editing}
@ -44,38 +38,35 @@
(let [current (om/get-state owner :edit-state)
next (or (get-in transitions [current event]) current)]
(condp = [current next]
[:editing :waiting] (let [update-ch (om/get-shared owner :recipe-update)
id (:id @(om/get-props owner))
[:editing :waiting] (let [recipe (om/get-props owner)
name (om/get-state owner :name)
grubs (om/get-state owner :grubs)
event (update-event id name grubs)]
(put! update-ch event))
grubs (om/get-state owner :grubs)]
(om/transact! recipe #(assoc % :name name :grubs grubs)))
nil)
(om/set-state! owner :edit-state next)))
(defn num-newlines [str]
(count (re-seq #"\n" str)))
(defn view [{:keys [id] :as props} owner]
(defn view [{:keys [id] :as recipe} owner {:keys [remove-recipe-ch]}]
(reify
om/IInitState
(init-state [_]
(let [publisher (chan)]
{:edit-state :waiting
:name (:name props)
:grubs (:grubs props)
:name (:name recipe)
:grubs (:grubs recipe)
:unmounted false}))
om/IWillReceiveProps
(will-receive-props [this next-props]
(om/set-state! owner :name (:name next-props))
(om/set-state! owner :grubs (:grubs next-props)))
(will-receive-props [this next-recipe]
(om/set-state! owner :name (:name next-recipe))
(om/set-state! owner :grubs (:grubs next-recipe)))
om/IRenderState
(render-state [this {:keys [edit-state name grubs]}]
(let [update (om/get-shared owner :recipe-update)
add-grubs-ch (om/get-shared owner :recipe-add-grubs)]
(let [update (om/get-shared owner :recipe-update)]
(html
[:div.panel.panel-default.recipe-panel
{:on-click
@ -92,7 +83,7 @@
{:type "button"
:class (when (= edit-state :editing) "hidden")
:ref :add-grubs-btn
:on-click #(add-grubs add-grubs-ch grubs)}
:on-click #(add-grubs (om/get-shared owner :add-grubs-ch) grubs)}
[:span.glyphicon.glyphicon-plus]
" Grubs"]]
[:div.panel-body.recipe-grubs
@ -105,7 +96,7 @@
:on-change #(om/set-state! owner :grubs (dom/event-val %))}]
[:button.btn.btn-danger.pull-left.recipe-remove-btn
{:type "button"
:on-click #(put! (om/get-shared owner :recipe-remove) (remove-event id))}
:on-click #(put! remove-recipe-ch id)}
[:span.glyphicon.glyphicon-trash]]
[:button.btn.btn-primary.pull-right.recipe-done-btn
{:type "button"

View file

@ -1,7 +1,7 @@
(ns grub.view.recipe-list
(:require [om.core :as om :include-macros true]
[sablono.core :as html :refer-macros [html]]
[cljs.core.async :as a :refer [<! put! chan]]
[cljs.core.async :as a :refer [<! chan]]
[cljs-uuid.core :as uuid]
[grub.view.dom :as dom]
[grub.view.grub :as grub-view]
@ -9,12 +9,14 @@
(:require-macros [grub.macros :refer [log logs]]
[cljs.core.async.macros :refer [go go-loop]]))
(defn add-recipe [ch name grubs owner]
(defn add-recipe [owner name grubs]
(when (and (not (empty? name))
(not (empty? grubs)))
(om/set-state! owner :new-recipe-name "")
(om/set-state! owner :new-recipe-grubs "")
(put! ch (recipe/add-event name grubs))))
(let [recipes (om/get-props owner)
new-recipe (recipe/new-recipe name grubs)]
(om/set-state! owner :new-recipe-name "")
(om/set-state! owner :new-recipe-grubs "")
(om/transact! recipes #(assoc % (:id new-recipe) new-recipe)))))
(def transitions
{:waiting {:click :editing}
@ -28,7 +30,7 @@
[:editing :save :waiting] (let [add-ch (om/get-shared owner :recipe-add)
name (om/get-state owner :new-recipe-name)
grubs (om/get-state owner :new-recipe-grubs)]
(add-recipe add-ch name grubs owner))
(add-recipe owner name grubs))
nil)
(om/set-state! owner :edit-state next)))
@ -36,11 +38,10 @@
(reify
om/IInitState
(init-state [_]
(let [publisher (chan)]
{:edit-state :waiting
:new-recipe-name ""
:new-recipe-grubs ""
:unmounted false}))
{:edit-state :waiting
:new-recipe-name ""
:new-recipe-grubs ""
:unmounted false})
om/IRenderState
(render-state [this {:keys [edit-state new-recipe-name new-recipe-grubs]}]
@ -67,7 +68,7 @@
{:type "button"
:ref :save-btn
:on-click #(transition-state owner :save)}
"Save"]]]))
[:span.glyphicon.glyphicon-ok]]]]))
om/IWillMount
(will-mount [_]
@ -92,12 +93,24 @@
(defn view [recipes owner]
(reify
om/IRender
(render [this]
om/IInitState
(init-state [_]
{:remove-recipe-ch (chan)})
om/IRenderState
(render-state [_ {:keys [remove-recipe-ch]}]
(html
[:div
[:h3.recipes-title "Recipes"]
(om/build new-recipe-view recipes)
[:ul#recipe-list.list-group.recipe-list
(for [recipe (vals recipes)]
(om/build recipe/view recipe {:key :id}))]]))))
(om/build recipe/view
recipe
{:key :id :opts {:remove-recipe-ch remove-recipe-ch}}))]]))
om/IWillMount
(will-mount [_]
(let [remove-recipe-ch (om/get-state owner :remove-recipe-ch)]
(go-loop []
(let [removed-id (<! remove-recipe-ch)]
(when-not (nil? removed-id)
(om/transact! recipes #(dissoc % removed-id)))))))))