Grubs can be edited by long-pressing
This commit is contained in:
parent
f8abd291ef
commit
f4eaf7cef9
11 changed files with 372 additions and 195 deletions
|
@ -1,3 +1,11 @@
|
|||
html, body {
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
@ -27,16 +35,47 @@ tr:hover .grub-close {
|
|||
user-select: none;
|
||||
}
|
||||
|
||||
.grub-item:active {
|
||||
.grub-item.grub-active {
|
||||
color: #ffffff;
|
||||
background-color: rgb(71, 73, 73);
|
||||
}
|
||||
|
||||
.grub-item .input-span {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.grub-item .grub-static {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.grub-item .grub-input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.grub-item.edit .grub-static {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.grub-item.edit .grub-input {
|
||||
display: inline;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.grub-item .glyphicon {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.completed {
|
||||
text-decoration: line-through;
|
||||
color: #a9a9a9;
|
||||
}
|
||||
|
||||
.completed.edit {
|
||||
text-decoration: none;
|
||||
color: default;
|
||||
}
|
||||
|
||||
#clear-all-btn {
|
||||
float: right;
|
||||
}
|
||||
|
|
|
@ -51,6 +51,18 @@
|
|||
{:_id (:_id test-grub) :completed false}
|
||||
(mc/find-one-as-map db/grub-collection {:_id (:_id test-grub)})))))
|
||||
|
||||
(describe "Update"
|
||||
(it "should update grub info when an update event comes"
|
||||
(let [test-grub {:_id 123456 :grub "original"}]
|
||||
(mc/insert db/grub-collection test-grub)
|
||||
(>!! @db/incoming-events {:event :update
|
||||
:_id (:_id test-grub)
|
||||
:grub "updated"})
|
||||
(short-delay)
|
||||
(should=
|
||||
{:_id (:_id test-grub) :grub "updated"}
|
||||
(mc/find-one-as-map db/grub-collection {:_id (:_id test-grub)})))))
|
||||
|
||||
(describe "Delete"
|
||||
(it "should delete a grub when a delete event comes"
|
||||
(let [test-grub {:_id 123456 :completed true}]
|
||||
|
|
|
@ -56,22 +56,4 @@
|
|||
clear-all-event {:event :clear-all}]
|
||||
(reset! state/grubs [test-grub])
|
||||
(state/handle-event clear-all-event)
|
||||
(should= [] @state/grubs)))))
|
||||
|
||||
(describe
|
||||
"view event handling"
|
||||
(describe "Create"
|
||||
(it "should add a new grub to the state when a create event comes"
|
||||
(let [test-grub {:grub "testgrub"}
|
||||
create-event (assoc test-grub :event :create)]
|
||||
(state/handle-view-event create-event)
|
||||
(js/setTimeout (fn [] (let [created-grub (first @state/grubs)]
|
||||
(should= (:grub test-grub) (:grub created-grub)))))))
|
||||
(it "should generate an _id for the new grub"
|
||||
(let [test-grub {:grub "testgrub"}
|
||||
create-event (assoc test-grub :event :create)]
|
||||
(state/handle-view-event create-event)
|
||||
(js/setTimeout (fn []
|
||||
(let [added-grub (first (filter #(= (:grub %) (:grub test-grub))
|
||||
@state/grubs))]
|
||||
(should-not-be-nil (:_id added-grub))))))))))
|
||||
(should= [] @state/grubs))))))
|
||||
|
|
|
@ -29,6 +29,11 @@
|
|||
{:_id (:_id event)}
|
||||
{mo/$set {:completed false}}))
|
||||
|
||||
(defmethod handle-event :update [event]
|
||||
(mc/update grub-collection
|
||||
{:_id (:_id event)}
|
||||
{mo/$set {:grub (:grub event)}}))
|
||||
|
||||
(defmethod handle-event :delete [event]
|
||||
(mc/remove grub-collection {:_id (:_id event)}))
|
||||
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
(ns grub.async-utils
|
||||
(:require [cljs.core.async :as async :refer [<! >! chan put! alts!]])
|
||||
(:require-macros [cljs.core.async.macros :as m :refer [go]]
|
||||
[grub.macros :refer [go-loop]]))
|
||||
(:refer-clojure :exclude [map filter])
|
||||
(:require [cljs.core.async :as async :refer [<! >! chan put! alts! close!]])
|
||||
(:require-macros [cljs.core.async.macros :refer [go]]
|
||||
[grub.macros :refer [do-chan]]))
|
||||
|
||||
(defn log [in]
|
||||
(let [out (chan)]
|
||||
(do-chan [e in]
|
||||
(.log js/console e)
|
||||
(>! out e))
|
||||
out))
|
||||
|
||||
(defn put-all! [cs x]
|
||||
(doseq [c cs]
|
||||
|
@ -22,53 +30,57 @@
|
|||
|
||||
(defn fan-in
|
||||
([ins] (fan-in (chan) ins))
|
||||
([c ins]
|
||||
(go-loop
|
||||
(let [[x] (alts! ins)]
|
||||
(>! c x)))
|
||||
c))
|
||||
([out ins]
|
||||
(go (loop [ins (vec ins)]
|
||||
(when (> (count ins) 0)
|
||||
(let [[x in] (alts! ins)]
|
||||
(when x
|
||||
(>! out x)
|
||||
(recur ins))
|
||||
(recur (vec (disj (set ins) in))))))
|
||||
(close! out))
|
||||
out))
|
||||
|
||||
(defn copy-chan
|
||||
(defn copy
|
||||
([c]
|
||||
(first (fan-out c 1)))
|
||||
([out c]
|
||||
(first (fan-out c [out]))))
|
||||
|
||||
(defn event-chan
|
||||
([type] (event-chan js/window type))
|
||||
([el type] (event-chan (chan) el type))
|
||||
([c el type]
|
||||
(let [writer #(put! c %)]
|
||||
(.addEventListener el type writer)
|
||||
{:chan c
|
||||
:unsubscribe #(.removeEventListener el type writer)})))
|
||||
|
||||
(defn map-chan
|
||||
([f source] (map-chan (chan) f source))
|
||||
([c f source]
|
||||
(go-loop
|
||||
(>! c (f (<! source))))
|
||||
c))
|
||||
|
||||
(defn filter-chan
|
||||
([f source] (filter-chan (chan) f source))
|
||||
([c f source]
|
||||
(go-loop
|
||||
(let [v (<! source)]
|
||||
(when (f v)
|
||||
(>! c v))))
|
||||
c))
|
||||
|
||||
(defn do-chan! [f source]
|
||||
(go-loop
|
||||
(let [v (<! source)]
|
||||
(f v))))
|
||||
|
||||
(defn do-chan [f source]
|
||||
(defn map [f in]
|
||||
(let [out (chan)]
|
||||
(go-loop
|
||||
(let [v (<! source)]
|
||||
(f v)
|
||||
(>! out v)))
|
||||
(go (loop []
|
||||
(if-let [x (<! in)]
|
||||
(do (>! out (f x))
|
||||
(recur))
|
||||
(close! out))))
|
||||
out))
|
||||
|
||||
(defn map-filter [f in]
|
||||
(let [out (chan)]
|
||||
(go (loop []
|
||||
(if-let [x (<! in)]
|
||||
(do
|
||||
(when-let [val (f x)]
|
||||
(>! out val))
|
||||
(recur))
|
||||
(close! out))))
|
||||
out))
|
||||
|
||||
(defn filter [pred in]
|
||||
(let [out (chan)]
|
||||
(go (loop []
|
||||
(if-let [x (<! in)]
|
||||
(do (when (pred x) (>! out x))
|
||||
(recur))
|
||||
(close! out))))
|
||||
out))
|
||||
|
||||
(defn siphon
|
||||
([in] (siphon in []))
|
||||
([in coll]
|
||||
(go (loop [coll coll]
|
||||
(if-let [v (<! in)]
|
||||
(recur (conj coll v))
|
||||
coll)))))
|
||||
|
||||
|
|
|
@ -9,13 +9,14 @@
|
|||
[cljs.core.async.macros :refer [go]]))
|
||||
|
||||
(defn handle-grub-events []
|
||||
(a/copy-chan state/incoming-view-events view/outgoing-events)
|
||||
(a/copy-chan state/incoming-events ws/outgoing-events)
|
||||
(a/copy-chan ws/incoming-events state/outgoing-events))
|
||||
(a/fan-out view/outgoing-events [state/incoming-events ws/incoming-events])
|
||||
(a/copy state/incoming-events ws/outgoing-events)
|
||||
(a/copy ws/incoming-events state/outgoing-events))
|
||||
|
||||
(defn init []
|
||||
(view/init)
|
||||
(ws/connect-to-server)
|
||||
(state/init)
|
||||
(view/init)
|
||||
(handle-grub-events))
|
||||
|
||||
(init)
|
||||
|
|
120
src/cljs/grub/dom.cljs
Normal file
120
src/cljs/grub/dom.cljs
Normal file
|
@ -0,0 +1,120 @@
|
|||
(ns grub.dom
|
||||
(:require [grub.async-utils :as a]
|
||||
[dommy.core :as dommy]
|
||||
[cljs.core.async :refer [<! >! chan timeout close!]])
|
||||
(:require-macros [grub.macros :refer [log logs go-loop]]
|
||||
[dommy.macros :refer [deftemplate sel1 node]]
|
||||
[cljs.core.async.macros :refer [go]]))
|
||||
|
||||
(defn listen
|
||||
([el type] (listen el type nil))
|
||||
([el type f] (listen el type f (chan)))
|
||||
([el type f out]
|
||||
(let [push-fn (fn [e] (when f (f e)) (go (>! out e)))
|
||||
unlisten #(do (dommy/unlisten! el type push-fn)
|
||||
(close! out))]
|
||||
(dommy/listen! el type push-fn)
|
||||
{:chan out :unlisten unlisten})))
|
||||
|
||||
(defn listen-once
|
||||
([el type] (listen el type nil))
|
||||
([el type f] (listen el type f (chan)))
|
||||
([el type f out]
|
||||
(let [push-fn (fn [e] (when f (f e)) (go (>! out e)))
|
||||
unlisten #(do (dommy/unlisten! el type push-fn)
|
||||
(close! out))]
|
||||
(dommy/listen-once! el type push-fn)
|
||||
{:chan out :unlisten unlisten})))
|
||||
|
||||
(def add-grub-text
|
||||
(node [:input.form-control {:id "add-grub-input" :type "text" :placeholder "2 grubs"}]))
|
||||
|
||||
(def add-grub-btn
|
||||
(node [:button.btn.btn-primary {:id "add-grub-btn" :type "button"} "Add"]))
|
||||
|
||||
(def clear-all-btn
|
||||
(node [:button.btn.hidden {:id "clear-all-btn" :type "button"} "Clear all"]))
|
||||
|
||||
(defn make-grub-node [grub]
|
||||
(node [:li.list-group-item.grub-item
|
||||
{:id (:_id grub)
|
||||
:class (when (:completed grub) "completed")}
|
||||
[:span.grub-static
|
||||
(if (:completed grub)
|
||||
[:span.glyphicon.glyphicon-check]
|
||||
[:span.glyphicon.glyphicon-unchecked])
|
||||
[:span.grub-text (:grub grub)]]
|
||||
[:input.grub-input {:type "text" :value (:grub grub)}]]))
|
||||
|
||||
(defn grubs-selector []
|
||||
[(sel1 :#grub-list) :.grub-item])
|
||||
|
||||
(deftemplate main-template []
|
||||
[:div.container
|
||||
[:div.row.show-grid
|
||||
[:div.col-lg-4]
|
||||
[:div.col-lg-4
|
||||
[:h3 "Grub List"]
|
||||
[:div.input-group
|
||||
add-grub-text
|
||||
[:span.input-group-btn
|
||||
add-grub-btn]]
|
||||
[:ul#grub-list.list-group]
|
||||
clear-all-btn]
|
||||
[:div.col-lg-4]
|
||||
[:div.col-lg-2]]])
|
||||
|
||||
(defn render-body []
|
||||
(dommy/prepend! (sel1 :body) (main-template)))
|
||||
|
||||
(defn render-grub-list [grubs]
|
||||
(let [grub-list (sel1 :#grub-list)
|
||||
sorted-grubs (sort-by (juxt :completed :_id) grubs)]
|
||||
(aset grub-list "innerHTML" "")
|
||||
(doseq [grub sorted-grubs]
|
||||
(let [node (make-grub-node grub)]
|
||||
(dommy/append! grub-list node)))))
|
||||
|
||||
(defn get-add-grub-text []
|
||||
(dommy/value add-grub-text))
|
||||
|
||||
(defn clear-add-grub-text []
|
||||
(dommy/set-value! add-grub-text ""))
|
||||
|
||||
|
||||
(defprotocol IActivatable
|
||||
(-activate! [view])
|
||||
(-deactivate! [view]))
|
||||
|
||||
(defprotocol IHideable
|
||||
(-hide! [view])
|
||||
(-show! [view]))
|
||||
|
||||
(defprotocol IEditable
|
||||
(-set-editing! [view])
|
||||
(-unset-editing! [view]))
|
||||
|
||||
(extend-type js/HTMLElement
|
||||
IActivatable
|
||||
(-activate! [view]
|
||||
(dommy/add-class! view :grub-active))
|
||||
(-deactivate! [view]
|
||||
(dommy/remove-class! view :grub-active)))
|
||||
|
||||
(extend-type js/HTMLElement
|
||||
IHideable
|
||||
(-hide! [view]
|
||||
(dommy/add-class! view :hidden))
|
||||
(-show! [view]
|
||||
(dommy/remove-class! view :hidden)))
|
||||
|
||||
(extend-type js/HTMLLIElement
|
||||
IEditable
|
||||
(-set-editing! [view]
|
||||
(-deactivate! view)
|
||||
(dommy/add-class! view :edit)
|
||||
(.focus (sel1 view :input)))
|
||||
(-unset-editing! [view]
|
||||
(dommy/remove-class! view :edit)))
|
||||
|
||||
|
|
@ -11,3 +11,13 @@
|
|||
`(cljs.core.async.macros/go
|
||||
(while true
|
||||
~@body)))
|
||||
|
||||
(defmacro do-chan [[binding chan] & body]
|
||||
`(let [chan# ~chan]
|
||||
(cljs.core.async.macros/go
|
||||
(loop []
|
||||
(if-let [~binding (cljs.core.async/<! chan#)]
|
||||
(do
|
||||
~@body
|
||||
(recur))
|
||||
:done)))))
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
(ns grub.state
|
||||
(:require [cljs.core.async :refer [chan <!]])
|
||||
(:require [grub.async-utils :as a]
|
||||
[cljs.core.async :refer [chan <!]])
|
||||
(:require-macros [grub.macros :refer [log logs go-loop]]
|
||||
[cljs.core.async.macros :refer [go]]))
|
||||
|
||||
(def incoming-events (chan))
|
||||
(def incoming-view-events (chan))
|
||||
(def outgoing-events (chan))
|
||||
|
||||
(def grubs (atom []))
|
||||
|
@ -38,6 +38,13 @@
|
|||
incomplete-grub (assoc grub :completed false)]
|
||||
(assoc current grub-index incomplete-grub)))))
|
||||
|
||||
(defmethod handle-event :update [event]
|
||||
(swap! grubs
|
||||
(fn [current]
|
||||
(let [[grub-index grub] (get-grub-with-index current (:_id event))
|
||||
updated-grub (assoc grub :grub (:grub event))]
|
||||
(assoc current grub-index updated-grub)))))
|
||||
|
||||
(defmethod handle-event :delete [event]
|
||||
(swap! grubs
|
||||
(fn [current]
|
||||
|
@ -49,33 +56,9 @@
|
|||
(defmethod handle-event :unknown-event [event]
|
||||
(logs "Cannot handle unknown event:" event))
|
||||
|
||||
|
||||
|
||||
(defn pass-on-view-event [event]
|
||||
(go (>! incoming-events event))
|
||||
(go (>! outgoing-events event)))
|
||||
|
||||
(defmulti handle-view-event :event :default :unknown-event)
|
||||
|
||||
(defmethod handle-view-event :create [event]
|
||||
(let [create-event (-> event
|
||||
(assoc :event :add)
|
||||
(assoc :_id (str "grub-" (.now js/Date)))
|
||||
(assoc :completed false))]
|
||||
(pass-on-view-event create-event)))
|
||||
|
||||
(defmethod handle-view-event :unknown-event [event]
|
||||
(pass-on-view-event event))
|
||||
|
||||
|
||||
(defn handle-incoming-events []
|
||||
(go-loop (handle-event (<! incoming-events))))
|
||||
(go-loop (let [event (<! incoming-events)]
|
||||
(handle-event event))))
|
||||
|
||||
(defn handle-incoming-view-events []
|
||||
(go-loop (handle-view-event (<! incoming-view-events))))
|
||||
|
||||
(defn init []
|
||||
(handle-incoming-events)
|
||||
(handle-incoming-view-events))
|
||||
|
||||
(init)
|
||||
(handle-incoming-events))
|
||||
|
|
|
@ -1,122 +1,135 @@
|
|||
(ns grub.view
|
||||
(:require [grub.async-utils
|
||||
:refer [do-chan! do-chan event-chan map-chan fan-in filter-chan]]
|
||||
(:require [grub.async-utils :as a]
|
||||
[grub.state :as state]
|
||||
[grub.dom :as dom]
|
||||
[dommy.core :as dommy]
|
||||
[cljs.core.async :refer [<! >! chan]])
|
||||
(:require-macros [grub.macros :refer [log logs go-loop]]
|
||||
[cljs.core.async :refer [<! >! chan timeout close!]])
|
||||
(:require-macros [grub.macros :refer [log logs go-loop do-chan]]
|
||||
[dommy.macros :refer [deftemplate sel1 node]]
|
||||
[cljs.core.async.macros :refer [go]]))
|
||||
|
||||
(def outgoing-events (chan))
|
||||
|
||||
(def add-grub-text
|
||||
(node [:input.form-control {:id "add-grub-input" :type "text" :placeholder "2 grubs"}]))
|
||||
|
||||
(def add-grub-btn
|
||||
(node [:button.btn.btn-primary {:id "add-grub-btn" :type "button"} "Add"]))
|
||||
|
||||
(deftemplate main-template []
|
||||
[:div.container
|
||||
[:div.row.show-grid
|
||||
[:div.col-lg-4]
|
||||
[:div.col-lg-4
|
||||
[:h3 "Grub List"]
|
||||
[:div.input-group
|
||||
add-grub-text
|
||||
[:span.input-group-btn
|
||||
add-grub-btn]]
|
||||
[:ul#grub-list.list-group]
|
||||
[:button.btn.hidden {:id "clear-all-btn" :type "button"} "Clear all"]]
|
||||
[:div.col-lg-4]
|
||||
[:div.col-lg-2]]])
|
||||
|
||||
(defn make-grub-node [grub]
|
||||
(if (:completed grub)
|
||||
(node [:li.list-group-item.completed.grub-item {:id (:_id grub)}
|
||||
[:span.glyphicon.glyphicon-check]
|
||||
(str " " (:grub grub))])
|
||||
(node [:li.list-group-item.grub-item {:id (:_id grub)}
|
||||
[:span.glyphicon.glyphicon-unchecked]
|
||||
(str " " (:grub grub))])))
|
||||
|
||||
(defn render-body []
|
||||
(dommy/prepend! (sel1 :body) (main-template)))
|
||||
|
||||
(defprotocol IHideable
|
||||
(-hide! [view])
|
||||
(-show! [view]))
|
||||
|
||||
(extend-type js/HTMLElement
|
||||
IHideable
|
||||
(-hide! [view]
|
||||
(dommy/add-class! view :hidden))
|
||||
(-show! [view]
|
||||
(dommy/remove-class! view :hidden)))
|
||||
|
||||
(defn get-add-grub-text []
|
||||
(let [text (dommy/value add-grub-text)]
|
||||
(dommy/set-value! add-grub-text "")
|
||||
text))
|
||||
|
||||
(defn get-grubs-from-clicks []
|
||||
(let [out (chan)]
|
||||
(dommy/listen! add-grub-btn :click #(go (>! out (get-add-grub-text))))
|
||||
out))
|
||||
|
||||
(defn put-grubs-if-enter-pressed [out event]
|
||||
(when (= (.-keyIdentifier event) "Enter")
|
||||
(go (>! out (get-add-grub-text)))))
|
||||
(->> (:chan (dom/listen dom/add-grub-btn :click))
|
||||
(a/map #(dom/get-add-grub-text))))
|
||||
|
||||
(defn get-grubs-from-enter []
|
||||
(let [out (chan)]
|
||||
(dommy/listen! add-grub-text
|
||||
:keyup
|
||||
(partial put-grubs-if-enter-pressed out))
|
||||
out))
|
||||
(->> (:chan (dom/listen dom/add-grub-text :keyup))
|
||||
(a/filter #(= (.-keyIdentifier %) "Enter"))
|
||||
(a/map #(dom/get-add-grub-text))))
|
||||
|
||||
(defn get-added-events []
|
||||
(let [grubs (fan-in [(get-grubs-from-clicks)
|
||||
(get-grubs-from-enter)])]
|
||||
(defn get-created-events []
|
||||
(let [grubs (a/fan-in [(get-grubs-from-clicks)
|
||||
(get-grubs-from-enter)])]
|
||||
(->> grubs
|
||||
(filter-chan #(not (empty? %)))
|
||||
(map-chan (fn [g] {:event :create :grub g})))))
|
||||
(a/filter #(not (empty? %)))
|
||||
(a/map (fn [g] {:grub g})))))
|
||||
|
||||
(defn get-completed-event [event]
|
||||
(defn get-clear-all-events []
|
||||
(:chan (dom/listen dom/clear-all-btn :click)))
|
||||
|
||||
(defn get-grub-mousedown-events []
|
||||
(:chan (dom/listen (dom/grubs-selector) :mousedown)))
|
||||
|
||||
(defn get-grub-mouseup-events []
|
||||
(:chan (dom/listen (dom/grubs-selector) :mouseup)))
|
||||
|
||||
(defn get-grub-mouseleave-events []
|
||||
(:chan (dom/listen (dom/grubs-selector) :mouseleave)))
|
||||
|
||||
(defn get-body-clicks []
|
||||
(:chan (dom/listen (sel1 :body) :click)))
|
||||
|
||||
(defn get-enters []
|
||||
(->> (:chan (dom/listen (sel1 :body) :keyup))
|
||||
(a/filter #(= (.-keyIdentifier %) "Enter"))))
|
||||
|
||||
(defn parse-completed-event [event]
|
||||
(let [target (.-target event)
|
||||
id (.-id target)
|
||||
completed (dommy/has-class? target "completed")
|
||||
event-type (if completed :uncomplete :complete)]
|
||||
{:_id id :event event-type}))
|
||||
|
||||
(defn get-clear-all-events []
|
||||
(let [events (chan)]
|
||||
(dommy/listen! (sel1 :#clear-all-btn) :click #(go (>! events {:event :clear-all})))
|
||||
events))
|
||||
|
||||
(defn render-grub-list [grubs]
|
||||
(let [grub-list (sel1 :#grub-list)
|
||||
sorted-grubs (sort-by (juxt :completed :_id) grubs)]
|
||||
(aset grub-list "innerHTML" "")
|
||||
(doseq [grub sorted-grubs]
|
||||
(let [node (make-grub-node grub)]
|
||||
(dommy/listen! node :click #(go (>! outgoing-events (get-completed-event %))))
|
||||
(dommy/append! grub-list node)))))
|
||||
|
||||
(defn push-outgoing-events []
|
||||
(fan-in outgoing-events [(get-added-events)
|
||||
(get-clear-all-events)]))
|
||||
|
||||
(defn watch-for-state-changes []
|
||||
(defn re-render-when-state-changes []
|
||||
(add-watch state/grubs
|
||||
:grub-add-watch
|
||||
(fn [key ref old new]
|
||||
(if (empty? new)
|
||||
(-hide! (sel1 :#clear-all-btn))
|
||||
(-show! (sel1 :#clear-all-btn)))
|
||||
(render-grub-list new))))
|
||||
(dom/-hide! dom/clear-all-btn)
|
||||
(dom/-show! dom/clear-all-btn))
|
||||
(dom/render-grub-list new))))
|
||||
|
||||
(defn event-loop []
|
||||
(let [out (chan)
|
||||
created (get-created-events)
|
||||
clear-all (get-clear-all-events)
|
||||
mousedown (get-grub-mousedown-events)
|
||||
mouseup (get-grub-mouseup-events)
|
||||
mouseleave (get-grub-mouseleave-events)
|
||||
body-click (get-body-clicks)
|
||||
edit (chan)
|
||||
enter (get-enters)]
|
||||
(go (loop [mousedown-time nil
|
||||
edit-elem nil]
|
||||
(let [[event c] (alts! [created clear-all mousedown
|
||||
mouseup mouseleave edit body-click
|
||||
enter])]
|
||||
(if edit-elem
|
||||
(cond
|
||||
(or (and (= c body-click)
|
||||
(not (dommy/descendant? (.-target event) edit-elem)))
|
||||
(= c enter))
|
||||
(do (dom/-unset-editing! edit-elem)
|
||||
(let [grub-text (.-value (sel1 edit-elem :.grub-input))
|
||||
id (.-id edit-elem)]
|
||||
(>! out {:event :update
|
||||
:grub grub-text
|
||||
:_id id}))
|
||||
(recur nil nil))
|
||||
|
||||
:else (recur nil edit-elem))
|
||||
(cond
|
||||
(= c created)
|
||||
(let [add-event (-> event
|
||||
(assoc :event :add)
|
||||
(assoc :_id (str "grub-" (.now js/Date)))
|
||||
(assoc :completed false))]
|
||||
(>! out add-event)
|
||||
(dom/clear-add-grub-text)
|
||||
(recur mousedown-time edit-elem))
|
||||
|
||||
(= c clear-all)
|
||||
(do (>! out {:event :clear-all})
|
||||
(recur mousedown-time edit-elem))
|
||||
|
||||
(= c mousedown)
|
||||
(do (dom/-activate! (.-selectedTarget event))
|
||||
(let [now (.now js/Date)]
|
||||
(go (<! (timeout 500))
|
||||
(>! edit {:mousedown-time now :elem (.-selectedTarget event)}))
|
||||
(recur now edit-elem)))
|
||||
|
||||
(= c mouseup)
|
||||
(do (dom/-deactivate! (.-selectedTarget event))
|
||||
(>! out (parse-completed-event event))
|
||||
(recur nil edit-elem))
|
||||
|
||||
(= c mouseleave)
|
||||
(do (dom/-deactivate! (.-selectedTarget event))
|
||||
(recur nil edit-elem))
|
||||
|
||||
(= c edit)
|
||||
(if (and mousedown-time (= (:mousedown-time event) mousedown-time))
|
||||
(do (dom/-set-editing! (:elem event))
|
||||
(recur nil (:elem event)))
|
||||
(recur nil edit-elem))
|
||||
|
||||
:else (recur mousedown-time edit-elem))))))
|
||||
out))
|
||||
|
||||
(defn init []
|
||||
(render-body)
|
||||
(watch-for-state-changes)
|
||||
(push-outgoing-events))
|
||||
(dom/render-body)
|
||||
(re-render-when-state-changes)
|
||||
(a/copy outgoing-events (event-loop)))
|
||||
|
|
|
@ -22,8 +22,8 @@
|
|||
(go (>! outgoing-events grub-event))))))
|
||||
|
||||
(defn connect-to-server []
|
||||
(let [full-url (str "ws://" (.-host (.-location js/document)) "/ws")]
|
||||
(reset! websocket* (js/WebSocket. full-url))
|
||||
(let [server-url (str "ws://" (.-host (.-location js/document)) "/ws")]
|
||||
(reset! websocket* (js/WebSocket. server-url))
|
||||
(aset @websocket* "onopen" (fn [event] (log "Connected:" event)))
|
||||
(aset @websocket* "onclose" (fn [event] (log "Connection closed:" event)))
|
||||
(aset @websocket* "onerror" (fn [event] (log "Connection error:" event)))
|
||||
|
|
Loading…
Reference in a new issue