From dc9a4c016e7fa6c731cfeb8f447075aae241b382 Mon Sep 17 00:00:00 2001 From: "Ricardo J. Mendez" Date: Mon, 1 Jun 2015 13:38:33 +0300 Subject: [PATCH] Better state handling, notifications Decided that instead of binding a component against an atom, I'd just send a notification on change. That way I can keep all state in one place and clear it if necessary, something that becomes ugly when the state for the textarea is kept on an atom. Mike Thompson suggested doing it on-blur instead of updating every on-change (because of https://github.com/Day8/re-frame/issues/39). While dispatching on-blur is cleaner, I can't update the textarea to change or clean it unless I use the `value` attribute... which is blank until we exit it. Looks like I have to use `on-change`, so the event needs to be fired with dispatch-sync. --- project.clj | 5 +- .../assets/bootswatch/bootswatch.min.css | 6 +- run.sh | 1 + src-cljs/memento/core.cljs | 83 +++++++++++-------- 4 files changed, 57 insertions(+), 38 deletions(-) create mode 100755 run.sh diff --git a/project.clj b/project.clj index 2dc9179..8515672 100644 --- a/project.clj +++ b/project.clj @@ -68,7 +68,10 @@ :profiles {:uberjar {:omit-source true - :env {:production true} + :env {:production true + :cluster-name "memento" + :index-name "memento" + :host-name "localhost"} :hooks [leiningen.cljsbuild] :cljsbuild {:jar true diff --git a/resources/public/assets/bootswatch/bootswatch.min.css b/resources/public/assets/bootswatch/bootswatch.min.css index 32e755c..ce25d00 100644 --- a/resources/public/assets/bootswatch/bootswatch.min.css +++ b/resources/public/assets/bootswatch/bootswatch.min.css @@ -1 +1,5 @@ -body{padding-top:50px}body>.navbar{-webkit-transition:background-color 300ms ease-in;transition:background-color 300ms ease-in}@media (min-width:768px){body>.navbar-transparent{background-color:transparent}body>.navbar-transparent .navbar-nav>.open>a{background-color:transparent!important}}#home{padding-top:0}#home .navbar-brand{padding:13.5px 15px 12.5px}#home .navbar-brand>img{display:inline;margin:0 10px;height:100%}#banner{min-height:300px;border-bottom:none}.page-header h1{font-size:4em}.bs-docs-section{margin-top:8em}.bs-component{position:relative}.bs-component .modal{position:relative;top:auto;right:auto;left:auto;bottom:auto;z-index:1;display:block}.bs-component .modal-dialog{width:90%}.bs-component .popover{position:relative;display:inline-block;width:220px;margin:20px}#source-button{position:absolute;top:0;right:0;z-index:100;font-weight:700}.nav-tabs{margin-bottom:15px}.progress{margin-bottom:10px}footer{margin:5em 0}footer li{float:left;margin-right:1.5em;margin-bottom:1.5em}footer p{clear:left;margin-bottom:0}.splash{padding:9em 0 2em;background-color:#141d27;background-image:url(../img/bg.jpg);background-size:cover;background-attachment:fixed;color:#fff;text-align:center}.splash .logo{width:160px}.splash h1{font-size:3em}.splash #social{margin:2em 0}.splash .alert{margin:2em 0}.section-tout{padding:4em 0 3em;border-bottom:1px solid rgba(0,0,0,.05);background-color:#eaf1f1}.section-tout .fa{margin-right:.5em}.section-tout p{margin-bottom:3em}.section-preview{padding:4em 0 4em}.section-preview .preview{margin-bottom:4em;background-color:#eaf1f1}.section-preview .preview .image{position:relative}.section-preview .preview .image:before{box-shadow:inset 0 0 0 1px rgba(0,0,0,.1);position:absolute;top:0;left:0;width:100%;height:100%;content:"";pointer-events:none}.section-preview .preview .options{padding:1em 2em 2em;border:1px solid rgba(0,0,0,.05);border-top:none;text-align:center}.section-preview .preview .options p{margin-bottom:2em}.section-preview .dropdown-menu{text-align:left}.section-preview .lead{margin-bottom:2em}@media (max-width:767px){.section-preview .image img{width:100%}}.sponsor{text-align:center}.sponsor a:hover{text-decoration:none}@media (max-width:767px){.splash{padding-top:4em}.splash .logo{width:100px}.splash h1{font-size:2em}#banner{margin-bottom:2em;text-align:center}} \ No newline at end of file +body{padding-top:50px}body>.navbar{-webkit-transition:background-color 300ms ease-in;transition:background-color 300ms ease-in}@media (min-width:768px){body>.navbar-transparent{background-color:transparent}body>.navbar-transparent .navbar-nav>.open>a{background-color:transparent!important}}#home{padding-top:0}#home .navbar-brand{padding:13.5px 15px 12.5px}#home .navbar-brand>img{display:inline;margin:0 10px;height:100%}#banner{min-height:300px;border-bottom:none}.page-header h1{font-size:4em}.bs-docs-section{margin-top:8em}.bs-component{position:relative}.bs-component .modal{position:relative;top:auto;right:auto;left:auto;bottom:auto;z-index:1;display:block}.bs-component .modal-dialog{width:90%}.bs-component .popover{position:relative;display:inline-block;width:220px;margin:20px}#source-button{position:absolute;top:0;right:0;z-index:100;font-weight:700}.nav-tabs{margin-bottom:15px}.progress{margin-bottom:10px}footer{margin:5em 0}footer li{float:left;margin-right:1.5em;margin-bottom:1.5em}footer p{clear:left;margin-bottom:0}.splash{padding:9em 0 2em;background-color:#141d27;background-image:url(../img/bg.jpg);background-size:cover;background-attachment:fixed;color:#fff;text-align:center}.splash .logo{width:160px}.splash h1{font-size:3em}.splash #social{margin:2em 0}.splash .alert{margin:2em 0}.section-tout{padding:4em 0 3em;border-bottom:1px solid rgba(0,0,0,.05);background-color:#eaf1f1}.section-tout .fa{margin-right:.5em}.section-tout p{margin-bottom:3em}.section-preview{padding:4em 0 4em}.section-preview .preview{margin-bottom:4em;background-color:#eaf1f1}.section-preview .preview .image{position:relative}.section-preview .preview .image:before{box-shadow:inset 0 0 0 1px rgba(0,0,0,.1);position:absolute;top:0;left:0;width:100%;height:100%;content:"";pointer-events:none}.section-preview .preview .options{padding:1em 2em 2em;border:1px solid rgba(0,0,0,.05);border-top:none;text-align:center}.section-preview .preview .options p{margin-bottom:2em}.section-preview .dropdown-menu{text-align:left}.section-preview .lead{margin-bottom:2em}@media (max-width:767px){.section-preview .image img{width:100%}}.sponsor{text-align:center}.sponsor a:hover{text-decoration:none}@media (max-width:767px){.splash{padding-top:4em}.splash .logo{width:100px}.splash h1{font-size:2em}#banner{margin-bottom:2em;text-align:center}} + +.form-group .btn { + margin-right: 5px +} \ No newline at end of file diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..50c0543 --- /dev/null +++ b/run.sh @@ -0,0 +1 @@ +java -cp target/memento.jar clojure.main -m memento.core diff --git a/src-cljs/memento/core.cljs b/src-cljs/memento/core.cljs index e34621f..ccc589e 100644 --- a/src-cljs/memento/core.cljs +++ b/src-cljs/memento/core.cljs @@ -45,27 +45,41 @@ (register-handler :set-message - (fn [app-state [_ msg]] - (.log js/console (str "Will set " msg)) - (assoc-in app-state [:ui-state :last-message] msg))) + (fn [app-state [_ msg class]] + (assoc-in app-state [:ui-state :last-message] {:text msg :class class}))) + +(register-handler + :update-note + (fn [app-state [_ note]] + (assoc-in app-state [:note :current-note] note))) (register-handler :save-note - (fn [app-state [_ msg]] - (POST "/api/memento" {:params {:text msg} - :handler #(.log js/console %) #_ #(dispatch [:save-note-done "Success"]) - :error-handler #(dispatch [:save-note-done (str "Error saving note: " %)])}) + (fn [app-state _] + (let [note (get-in app-state [:note :current-note])] + (POST "/api/memento" {:params {:text note} + :handler #(dispatch [:save-note-success note]) + :error-handler #(dispatch [:save-note-error (str "Error saving note: " %)])})) app-state )) (register-handler - :save-note-done + :save-note-success (fn [app-state [_ msg]] (.log js/console "Done") - (dispatch [:set-message msg]) - (assoc-in app-state [:ui-state :is-busy] false))) + (dispatch [:set-message (str "Saved: " msg) "alert-success"]) + (-> app-state + (assoc-in [:ui-state :is-busy] false) + (assoc-in [:note :current-note] "") + ))) +(register-handler + :save-note-error + (fn [app-state [_ msg]] + (dispatch [:set-message (str "Error saving note: " msg) "alert-danger"]) + (assoc-in app-state [:ui-state :is-busy] false) + )) @@ -77,43 +91,40 @@ -(def thought-field - [:textarea {:field :textarea - :class "form-control" - :placeholder "I was thinking..." - :rows 12 - :id :thought - :style {:font-size "18px"} - }]) - - (defn alert [] (let [msg (subscribe [:ui-state :last-message])] (fn [] - (if (not-empty @msg) - [:div {:class "alert alert-info"} + (if (not-empty (:text @msg)) + [:div {:class (str "alert " (:class @msg))} [:button {:type :button :class "close" :on-click #(dispatch [:set-message ""])} "x"] - [:strong "Heads up! "] @msg] + (:text @msg)] ) ))) + (defn write-section [] - (let [doc (atom {}) - is-busy? (subscribe [:ui-state :is-busy]) - ] + (let [note (subscribe [:note :current-note]) + is-busy? (subscribe [:ui-state :is-busy])] (fn [] [:fielset [:legend "" - [:div {:class "form-group"} - [:div {:class "col-lg-12"} - [bind-fields thought-field doc]] - ] - [:div {:class "form-group"} - [:div {:class "col-lg-12"} - [:button {:type "reset" :class "btn btn-default" :on-click #(dispatch [:set-note ""])} "Clear"] - [:button {:type "submit" :disabled @is-busy? :class "btn btn-primary" :on-click #(dispatch [:save-note (:thought @doc)])} "Submit"] - ]] - ]] + [:div {:class "form-horizontal"} + [:div {:class "form-group"} + [:div {:class "col-lg-12"} + [:textarea {:class "form-control" + :placeholder "I was thinking..." + :rows 12 + :style {:font-size "18px"} + :on-change #(dispatch-sync [:update-note (-> % .-target .-value)]) + :value @note + }] + ]] + [:div {:class "form-group"} + [:div {:class "col-lg-12"} + [:button {:type "reset" :class "btn btn-default" :on-click #(dispatch [:update-note ""])} "Clear"] + [:button {:type "submit" :disabled (or @is-busy? (empty? @note)) :class "btn btn-primary" :on-click #(dispatch [:save-note])} "Submit"] + ]] + ]]] )))