Add directions to recipes

This commit is contained in:
Nicholas Kariniemi 2014-10-26 08:57:08 +02:00
parent f8af086c19
commit 0e2f13d63d
3 changed files with 141 additions and 100 deletions

View file

@ -9,10 +9,11 @@
(:require-macros [grub.macros :refer [log logs]]
[cljs.core.async.macros :refer [go go-loop]]))
(defn new-recipe [name grubs]
(defn new-recipe [name grubs directions]
{:id (str "recipe-" (uuid/make-random))
:name name
:grubs grubs})
:grubs grubs
:directions directions})
(defn parse-grubs-from-str [grubs-str]
(->> grubs-str
@ -36,18 +37,23 @@
(condp = [current next]
[:editing :waiting] (let [recipe (om/get-props owner)
name (om/get-state owner :name)
grubs (om/get-state owner :grubs)]
(when-not (and (= name (:name @recipe))
(= grubs (:grubs @recipe)))
(om/transact! recipe
nil
#(assoc % :name name :grubs grubs)
grubs (om/get-state owner :grubs)
directions (om/get-state owner :directions)]
(when (or (not= name (:name @recipe))
(not= grubs (:grubs @recipe))
(not= directions (:directions @recipe)))
(om/transact! recipe nil #(assoc %
:name name
:grubs grubs
:directions directions)
:local)))
nil)
(when-not (= current next) (om/set-state! owner :edit-state next))))
(defn num-newlines [str]
(count (re-seq #"\n" str)))
(if (or (nil? str) (empty? str))
1
(count (re-seq #"\n" str))))
(defn view [{:keys [id] :as recipe} owner {:keys [remove-recipe-ch]}]
(reify
@ -57,6 +63,7 @@
{:edit-state :waiting
:name (:name recipe)
:grubs (:grubs recipe)
:directions (:directions recipe)
:unmounted false}))
om/IWillReceiveProps
@ -67,7 +74,7 @@
(om/set-state! owner :grubs grubs)))
om/IRenderState
(render-state [this {:keys [edit-state name grubs]}]
(render-state [this {:keys [edit-state name grubs directions]}]
(let [update (om/get-shared owner :recipe-update)]
(html
[:div.panel.panel-default.recipe-panel
@ -97,6 +104,12 @@
:rows (inc (num-newlines grubs))
:value grubs
:on-change #(om/set-state! owner :grubs (dom/event-val %))}]
[:textarea.form-control.recipe-grubs-input
{:ref :textarea
:rows (inc (num-newlines directions))
:value directions
:placeholder "Directions"
:on-change #(om/set-state! owner :directions (dom/event-val %))}]
[:button.btn.btn-danger.pull-left.recipe-remove-btn
{:type "button"
:on-click #(put! remove-recipe-ch id)}

View file

@ -2,6 +2,7 @@
(:require [grub.view.dom :as dom]
[grub.view.grub :as grub-view]
[grub.view.recipe :as recipe]
[grub.view.recipe-new :as recipe-new]
[om.core :as om :include-macros true]
[sablono.core :as html :refer-macros [html]]
[cljs.core.async :as a :refer [<! chan]]
@ -9,99 +10,18 @@
(:require-macros [grub.macros :refer [log logs]]
[cljs.core.async.macros :refer [go go-loop]]))
(defn add-recipe [owner name grubs]
(when (and (not (empty? name))
(not (empty? 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 nil #(assoc % (:id new-recipe) new-recipe) :local))))
(def transitions
{:waiting {:click :editing}
:editing {:body-mousedown :waiting
:save :waiting}})
(defn transition-state [owner event]
(let [current (om/get-state owner :edit-state)
next (or (get-in transitions [current event]) current)]
(condp = [current event next]
[: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 owner name grubs))
nil)
(om/set-state! owner :edit-state next)))
(defn new-recipe-view [_ owner]
(reify
om/IInitState
(init-state [_]
{: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]}]
(html
[:div.panel.panel-default.recipe-panel
{:on-click #(when (not (dom/click-on-elem? % (om/get-node owner :save-btn)))
(transition-state owner :click))}
[:div.panel-heading.recipe-header.new
[:input.form-control.recipe-header-input
{:id "new-recipe-name"
:type "text"
:placeholder "New recipe"
:value new-recipe-name
:on-change #(om/set-state! owner :new-recipe-name (dom/event-val %))}]]
[:div.panel-body.recipe-grubs
{:class (when (= edit-state :waiting) "hidden")}
[:textarea.form-control.recipe-grubs-input
{:id "new-recipe-grubs"
:rows (inc (recipe/num-newlines new-recipe-grubs))
:placeholder "Recipe ingredients"
:value new-recipe-grubs
:on-change #(om/set-state! owner :new-recipe-grubs (dom/event-val %))}]
[:button.btn.btn-primary.pull-right.recipe-done-btn
{:type "button"
:ref :save-btn
:on-click #(transition-state owner :save)}
[:span.glyphicon.glyphicon-ok#save-recipe-btn]]]]))
om/IWillMount
(will-mount [_]
(let [<events (om/get-shared owner :<events)
subscriber (chan)]
(a/sub <events :body-mousedown subscriber)
(om/set-state! owner :subscriber subscriber)
(go-loop [] (let [event (<! subscriber)]
(when-not (or (nil? event)
(om/get-state owner :unmounted))
(when-not (dom/click-on-self? (:event event) (om/get-node owner))
(transition-state owner :body-mousedown))
(recur))))))
om/IWillUnmount
(will-unmount [_]
(let [<events (om/get-shared owner :<events)
subscriber (om/get-state owner :subscriber)]
(om/set-state! owner :unmounted true)
(a/unsub <events :body-mousedown subscriber)
(a/close! (om/get-state owner :subscriber))))))
(defn view [recipes owner]
(reify
om/IInitState
(init-state [_]
{:remove-recipe-ch (chan)})
{:add-recipe-ch (chan)
:remove-recipe-ch (chan)})
om/IRenderState
(render-state [_ {:keys [remove-recipe-ch]}]
(render-state [_ {:keys [remove-recipe-ch add-recipe-ch]}]
(html
[:div
[:h3.recipes-title "Recipes"]
(om/build new-recipe-view recipes)
(om/build recipe-new/view {} {:opts {:add-recipe-ch add-recipe-ch}})
[:ul#recipe-list.list-group.recipe-list
(for [recipe (vals recipes)]
(om/build recipe/view
@ -109,8 +29,14 @@
{: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 nil #(dissoc % removed-id) :local))))))))
(let [add-recipe-ch (om/get-state owner :add-recipe-ch)
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 nil #(dissoc % removed-id) :local))
(recur))))
(go (loop []
(let [added (<! add-recipe-ch)]
(om/transact! recipes nil #(assoc % (:id added) added) :local)
(recur))))))))

View file

@ -0,0 +1,102 @@
(ns grub.view.recipe-new
(:require [grub.view.dom :as dom]
[grub.view.recipe :as recipe]
[om.core :as om :include-macros true]
[sablono.core :as html :refer-macros [html]]
[cljs.core.async :as a :refer [<! chan]]
[cljs-uuid.core :as uuid])
(:require-macros [grub.macros :refer [log logs]]
[cljs.core.async.macros :refer [go go-loop]]))
(defn add-recipe [owner name grubs directions]
(when (and (not (empty? name))
(not (empty? grubs)))
(let [recipes (om/get-props owner)
new-recipe (recipe/new-recipe name grubs directions)
add-recipe-ch (om/get-state owner :add-recipe-ch)]
(om/set-state! owner :new-recipe-name "")
(om/set-state! owner :new-recipe-grubs "")
(om/set-state! owner :new-recipe-directions "")
(a/put! add-recipe-ch new-recipe))))
(def transitions
{:waiting {:click :editing}
:editing {:body-mousedown :waiting
:save :waiting}})
(defn transition-state [owner event]
(let [current (om/get-state owner :edit-state)
next (or (get-in transitions [current event]) current)]
(condp = [current event next]
[: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)
directions (om/get-state owner :new-recipe-directions)]
(add-recipe owner name grubs directions))
nil)
(om/set-state! owner :edit-state next)))
(defn view [_ owner {:keys [add-recipe-ch]}]
(reify
om/IInitState
(init-state [_]
{:add-recipe-ch add-recipe-ch
:edit-state :waiting
:new-recipe-name ""
:new-recipe-grubs ""
:new-recipe-directions ""
:unmounted false})
om/IRenderState
(render-state [this {:keys [edit-state new-recipe-name new-recipe-grubs new-recipe-directions]}]
(html
[:div.panel.panel-default.recipe-panel
{:on-click #(when (not (dom/click-on-elem? % (om/get-node owner :save-btn)))
(transition-state owner :click))}
[:div.panel-heading.recipe-header.new
[:input.form-control.recipe-header-input
{:id "new-recipe-name"
:type "text"
:placeholder "New recipe"
:value new-recipe-name
:on-change #(om/set-state! owner :new-recipe-name (dom/event-val %))}]]
[:div.panel-body.recipe-grubs
{:class (when (= edit-state :waiting) "hidden")}
[:textarea.form-control.recipe-grubs-input
{:id "new-recipe-grubs"
:rows (inc (recipe/num-newlines new-recipe-grubs))
:placeholder "Ingredients"
:value new-recipe-grubs
:on-change #(om/set-state! owner :new-recipe-grubs (dom/event-val %))}]
[:textarea.form-control.recipe-grubs-input
{:ref :textarea
:rows (inc (recipe/num-newlines new-recipe-directions))
:value new-recipe-directions
:placeholder "Directions"
:on-change #(om/set-state! owner :new-recipe-directions (dom/event-val %))}]
[:button.btn.btn-primary.pull-right.recipe-done-btn
{:type "button"
:ref :save-btn
:on-click #(transition-state owner :save)}
[:span.glyphicon.glyphicon-ok#save-recipe-btn]]]]))
om/IWillMount
(will-mount [_]
(let [<events (om/get-shared owner :<events)
subscriber (chan)]
(a/sub <events :body-mousedown subscriber)
(om/set-state! owner :subscriber subscriber)
(go-loop [] (let [event (<! subscriber)]
(when-not (or (nil? event)
(om/get-state owner :unmounted))
(when-not (dom/click-on-self? (:event event) (om/get-node owner))
(transition-state owner :body-mousedown))
(recur))))))
om/IWillUnmount
(will-unmount [_]
(let [<events (om/get-shared owner :<events)
subscriber (om/get-state owner :subscriber)]
(om/set-state! owner :unmounted true)
(a/unsub <events :body-mousedown subscriber)
(a/close! (om/get-state owner :subscriber))))))