From 0b6cb783369779b3ed26c59e40993a896d8e99e9 Mon Sep 17 00:00:00 2001 From: michaelr Date: Tue, 22 Mar 2016 00:43:03 +0200 Subject: [PATCH] bring messages to chat view Former-commit-id: ef26278908734ada4b30511c88755d53f71fc4b2 --- env/dev/env/android/main.cljs | 4 +- env/dev/env/ios/main.cljs | 4 +- project.clj | 2 +- src/messenger/android/core.cljs | 20 +- src/messenger/comm/intercom.cljs | 7 + src/messenger/comm/services.cljs | 7 +- src/messenger/components/chat/chat.cljs | 12 +- .../components/contact_list/contact.cljs | 206 +++++++++--------- src/messenger/components/iname.cljs | 4 + src/messenger/ios/core.cljs | 4 +- src/messenger/models/chat.cljs | 8 + src/messenger/models/messages.cljs | 24 +- src/messenger/omnext.cljs | 29 +++ src/messenger/persistence/realm.cljs | 14 +- src/messenger/services/contacts.cljs | 2 +- src/messenger/services/navigate_to.cljs | 35 +++ src/messenger/services/server.cljs | 2 +- src/messenger/state.cljs | 70 +++--- 18 files changed, 296 insertions(+), 158 deletions(-) create mode 100644 src/messenger/components/iname.cljs create mode 100644 src/messenger/models/chat.cljs create mode 100644 src/messenger/omnext.cljs create mode 100644 src/messenger/services/navigate_to.cljs diff --git a/env/dev/env/android/main.cljs b/env/dev/env/android/main.cljs index 3bf98813b0..b4a8bf350b 100644 --- a/env/dev/env/android/main.cljs +++ b/env/dev/env/android/main.cljs @@ -1,7 +1,7 @@ (ns ^:figwheel-no-load env.android.main (:require [om.next :as om] [messenger.android.core :as core] - [messenger.state :as state] + [messenger.omnext :as omnext] [figwheel.client :as figwheel :include-macros true])) (enable-console-print!) @@ -9,7 +9,7 @@ (figwheel/watch-and-reload :websocket-url "ws://localhost:3449/figwheel-ws" :heads-up-display true - :jsload-callback #(om/add-root! state/reconciler core/AppRoot 1)) + :jsload-callback #(om/add-root! omnext/reconciler core/AppRoot 1)) (core/init) diff --git a/env/dev/env/ios/main.cljs b/env/dev/env/ios/main.cljs index 10199b7c20..dda2c4aeae 100644 --- a/env/dev/env/ios/main.cljs +++ b/env/dev/env/ios/main.cljs @@ -1,7 +1,7 @@ (ns ^:figwheel-no-load env.ios.main (:require [om.next :as om] [messenger.ios.core :as core] - [messenger.state :as state] + [messenger.omnext :as omnext] [figwheel.client :as figwheel :include-macros true])) (enable-console-print!) @@ -9,7 +9,7 @@ (figwheel/watch-and-reload :websocket-url "ws://localhost:3449/figwheel-ws" :heads-up-display true - :jsload-callback #(om/add-root! state/reconciler core/AppRoot 1)) + :jsload-callback #(om/add-root! omnext/reconciler core/AppRoot 1)) (core/init) diff --git a/project.clj b/project.clj index 63025bbf95..e03925f130 100644 --- a/project.clj +++ b/project.clj @@ -5,7 +5,7 @@ :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[org.clojure/clojure "1.7.0"] [org.clojure/clojurescript "1.7.170"] - [org.omcljs/om "1.0.0-alpha28" :exclusions [cljsjs/react cljsjs/react-dom]] + [org.omcljs/om "1.0.0-alpha30" :exclusions [cljsjs/react cljsjs/react-dom]] [natal-shell "0.1.6"] [syng-im/protocol "0.1.1"]] :plugins [[lein-cljsbuild "1.1.1"] diff --git a/src/messenger/android/core.cljs b/src/messenger/android/core.cljs index 662c4bb571..8c35c5ea0f 100644 --- a/src/messenger/android/core.cljs +++ b/src/messenger/android/core.cljs @@ -8,6 +8,7 @@ [natal-shell.alert :refer [alert]]) (:require [om.next :as om :refer-macros [defui]] [re-natal.support :as sup] + [messenger.omnext :as omnext] [messenger.state :as state] [messenger.utils.utils :refer [log toast]] [messenger.android.login :refer [login]] @@ -15,8 +16,11 @@ [messenger.comm.pubsub :as pubsub] [messenger.comm.intercom :as intercom :refer [load-user-phone-number]] [messenger.protocol.protocol-handler :refer [make-handler]] + [messenger.init :refer [init-simple-store]] + [messenger.components.chat.chat :as chat] [syng-im.protocol.api :refer [init-protocol]] - [messenger.init :refer [init-simple-store]])) + [syng-im.utils.logging :as log] + [messenger.components.iname :as in])) (def app-registry (.-AppRegistry js/React)) @@ -40,18 +44,20 @@ (defui AppRoot static om/IQuery (query [this] - '[:loading :contacts-ds :user-phone-number :user-identity :confirmation-code]) + [:loading :contacts-ds :user-phone-number :user-identity :confirmation-code]) Object (render [this] + (log/debug "APPROOT Rendering") (navigator {:initialRoute {:component login} :renderScene (fn [route nav] + (log/debug "RENDER SCENE") (when state/*nav-render* (init-back-button-handler! nav) - (let [{:keys [component]} - (js->clj route :keywordize-keys true)] - (component (om/computed (om/props this) - {:nav nav})))))}))) + (let [{:keys [component]} (js->clj route :keywordize-keys true) + props (om/props this) + props (om/computed props {:nav nav})] + (component props))))}))) ;; TODO to service? (swap! state/app-state assoc :contacts-ds @@ -66,5 +72,5 @@ (pubsub/setup-pub-sub) (init-protocol (make-handler)) (load-user-phone-number) - (om/add-root! state/reconciler AppRoot 1) + (om/add-root! omnext/reconciler AppRoot 1) (.registerComponent app-registry "Messenger" (fn [] app-root))) diff --git a/src/messenger/comm/intercom.cljs b/src/messenger/comm/intercom.cljs index 2997e24f78..e055a109d8 100644 --- a/src/messenger/comm/intercom.cljs +++ b/src/messenger/comm/intercom.cljs @@ -57,3 +57,10 @@ (defn save-new-msg [from payload] (publish! :service [:protocol :protocol/save-new-msg {:from from :payload payload}])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; navigate-to + +(defn show-chat [navigator chat-id] + (publish! :service [:navigate-to :scene/chat {:chat-id chat-id + :navigator navigator}])) diff --git a/src/messenger/comm/services.cljs b/src/messenger/comm/services.cljs index f3155d6112..aeb5f2d5e9 100644 --- a/src/messenger/comm/services.cljs +++ b/src/messenger/comm/services.cljs @@ -4,7 +4,8 @@ [messenger.services.user-data :refer [user-data-handler]] [messenger.services.protocol :refer [protocol-handler]] [messenger.services.server :refer [server-handler]] - [messenger.services.contacts :refer [contacts-handler]])) + [messenger.services.contacts :refer [contacts-handler]] + [messenger.services.navigate-to :refer [navigate-to-handler]])) (defmulti service (fn [state service-id args] service-id)) @@ -25,6 +26,10 @@ [state service-id args] (protocol-handler state args)) +(defmethod service :navigate-to + [state service-id args] + (navigate-to-handler state args)) + (defn services-handler [state service-id args] (log/info "handling " service-id " args = " args) (service state service-id args)) diff --git a/src/messenger/components/chat/chat.cljs b/src/messenger/components/chat/chat.cljs index 9529b6007e..e2371d06bc 100644 --- a/src/messenger/components/chat/chat.cljs +++ b/src/messenger/components/chat/chat.cljs @@ -7,7 +7,9 @@ [messenger.components.invertible-scroll-view :refer [invertible-scroll-view]] [messenger.components.chat.message :refer [message]] [messenger.components.chat.new-message :refer [new-message]] - [messenger.state :as state])) + [messenger.state :as state] + [syng-im.utils.logging :as log] + [messenger.components.iname :as in])) (defn generate-message [n] {:id n @@ -38,10 +40,18 @@ (message (js->clj row :keywordize-keys true))) (defui Chat + static in/IName + (get-name [this] + :chat) + static om/IQuery + (query [this] + '[:chat/messages]) Object (render [this] (let [{:keys [nav]} (om/get-computed this) + {{messages :chat/messages} :chat} (om/props this) + _ (log/debug "messages=" messages) messages-ds (load-messages)] (view {:style {:flex 1 :backgroundColor "white"}} diff --git a/src/messenger/components/contact_list/contact.cljs b/src/messenger/components/contact_list/contact.cljs index 74388a4320..1e832e6b07 100644 --- a/src/messenger/components/contact_list/contact.cljs +++ b/src/messenger/components/contact_list/contact.cljs @@ -5,120 +5,116 @@ [messenger.state :as state] [messenger.utils.utils :refer [log toast http-post]] [messenger.utils.resources :as res] + [messenger.comm.intercom :as intercom :refer [show-chat]] [messenger.components.chat.chat :refer [chat]])) -(defn nav-push [nav route] - (binding [state/*nav-render* false] - (.push nav (clj->js route)))) - -(defn show-chat [nav] - (nav-push nav {:component chat - :name "chat"})) +(def react-native-contacts (js/require "react-native-contacts")) (defui Contact static om/Ident (ident [this {:keys [name]}] - [:contact/by-name name]) + [:contact/by-name name]) static om/IQuery (query [this] - '[:name :photo-path :delivery-status :datetime :new-messages-count :online]) + '[:name :photo-path :delivery-status :datetime :new-messages-count :online :whisper-identity]) Object (render [this] - (let [{:keys [name photo-path delivery-status datetime new-messages-count - online]} - (dissoc (om/props this) :om.next/computed) - {:keys [nav]} (om/get-computed this)] - (touchable-highlight - {:onPress (fn [] (show-chat nav))} - (view {:style {:flexDirection "row" - :marginTop 5 - :marginBottom 5 - :paddingLeft 15 - :paddingRight 15 - :height 75}} - (view {:width 54 - :height 54} - ;;; photo - (view {:width 54 - :height 54 - :borderRadius 50 - :backgroundColor "#FFFFFF" - :elevation 6} - (image {:source (if (< 0 (count photo-path)) - {:uri photo-path} - res/user-no-photo) - :style {:borderWidth 2 - :borderColor "#FFFFFF" - :borderRadius 50 - :width 54 - :height 54 - :position "absolute"}})) - ;;; online - (when online - (view {:position "absolute" - :top 41 - :left 36 - :width 12 - :height 12 - :borderRadius 50 - :backgroundColor "#FFFFFF" - :elevation 6} - (image {:source res/online-icon - :style {:width 12 - :height 12}})))) - (view {:style {:flexDirection "column" - :marginLeft 7 - :marginRight 10 - :flex 1 - :position "relative"}} - ;;; name - (text {:style {:fontSize 15 - :fontFamily "Avenir-Roman"}} name) - ;;; last message - (text {:style {:color "#AAB2B2" - :fontFamily "Avenir-Roman" - :fontSize 14 - :marginTop 2 - :paddingRight 10}} - (str "Hi, I'm " name))) - (view {:style {:flexDirection "column"}} - ;;; delivery status - (view {:style {:flexDirection "row" - :position "absolute" - :top 0 - :right 0}} - (when delivery-status - (image {:source (if (= (keyword delivery-status) :seen) - res/seen-icon - res/delivered-icon) - :style {:marginTop 5}})) - ;;; datetime - (text {:style {:fontFamily "Avenir-Roman" - :fontSize 11 - :color "#AAB2B2" - :letterSpacing 1 - :lineHeight 15 - :marginLeft 5}} - datetime)) - ;;; new messages count - (when (< 0 new-messages-count) - (view {:style {:position "absolute" - :right 0 - :bottom 24 - :width 18 - :height 18 - :backgroundColor "#6BC6C8" - :borderColor "#FFFFFF" - :borderRadius 50 - :alignSelf "flex-end"}} - (text {:style {:width 18 - :height 17 - :fontFamily "Avenir-Roman" - :fontSize 10 - :color "#FFFFFF" - :lineHeight 19 - :textAlign "center" - :top 1}} - new-messages-count))))))))) + (let [{:keys [name photo-path delivery-status datetime new-messages-count online whisper-identity]} + (dissoc (om/props this) :om.next/computed) + {:keys [nav]} (om/get-computed this)] + (touchable-highlight + {:onPress (fn [] + (show-chat nav whisper-identity))} + (view {:style {:flexDirection "row" + :marginTop 5 + :marginBottom 5 + :paddingLeft 15 + :paddingRight 15 + :height 75}} + (view {:width 54 + :height 54} + ;;; photo + (view {:width 54 + :height 54 + :borderRadius 50 + :backgroundColor "#FFFFFF" + :elevation 6} + (image {:source (if (< 0 (count photo-path)) + {:uri photo-path} + res/user-no-photo) + :style {:borderWidth 2 + :borderColor "#FFFFFF" + :borderRadius 50 + :width 54 + :height 54 + :position "absolute"}})) + ;;; online + (when online + (view {:position "absolute" + :top 41 + :left 36 + :width 12 + :height 12 + :borderRadius 50 + :backgroundColor "#FFFFFF" + :elevation 6} + (image {:source res/online-icon + :style {:width 12 + :height 12}})))) + (view {:style {:flexDirection "column" + :marginLeft 7 + :marginRight 10 + :flex 1 + :position "relative"}} + ;;; name + (text {:style {:fontSize 15 + :fontFamily "Avenir-Roman"}} name) + ;;; last message + (text {:style {:color "#AAB2B2" + :fontFamily "Avenir-Roman" + :fontSize 14 + :marginTop 2 + :paddingRight 10}} + (str "Hi, I'm " name))) + (view {:style {:flexDirection "column"}} + ;;; delivery status + (view {:style {:flexDirection "row" + :position "absolute" + :top 0 + :right 0}} + (when delivery-status + (image {:source (if (= (keyword delivery-status) :seen) + res/seen-icon + res/delivered-icon) + :style {:marginTop 5}})) + ;;; datetime + (text {:style {:fontFamily "Avenir-Roman" + :fontSize 11 + :color "#AAB2B2" + :letterSpacing 1 + :lineHeight 15 + :marginLeft 5}} + datetime)) + ;;; new messages count + (when (< 0 new-messages-count) + (view {:style {:position "absolute" + :right 0 + :bottom 24 + :width 18 + :height 18 + :backgroundColor "#6BC6C8" + :borderColor "#FFFFFF" + :borderRadius 50 + :alignSelf "flex-end"}} + (text {:style {:width 18 + :height 17 + :fontFamily "Avenir-Roman" + :fontSize 10 + :color "#FFFFFF" + :lineHeight 19 + :textAlign "center" + :top 1}} + new-messages-count))))))))) (def contact (om/factory Contact {:keyfn :name})) + diff --git a/src/messenger/components/iname.cljs b/src/messenger/components/iname.cljs new file mode 100644 index 0000000000..dfea6e7013 --- /dev/null +++ b/src/messenger/components/iname.cljs @@ -0,0 +1,4 @@ +(ns messenger.components.iname) + +(defprotocol IName + (get-name [this])) diff --git a/src/messenger/ios/core.cljs b/src/messenger/ios/core.cljs index 3b18b50bef..db80e27637 100644 --- a/src/messenger/ios/core.cljs +++ b/src/messenger/ios/core.cljs @@ -3,7 +3,7 @@ [natal-shell.alert :refer [alert]]) (:require [om.next :as om :refer-macros [defui]] [re-natal.support :as sup] - [messenger.state :as state])) + [messenger.omnext :as omnext])) (set! js/React (js/require "react-native")) @@ -29,5 +29,5 @@ (defonce app-root (om/factory RootNode)) (defn init [] - (om/add-root! state/reconciler AppRoot 1) + (om/add-root! omnext/reconciler AppRoot 1) (.registerComponent app-registry "Messenger" (fn [] app-root))) \ No newline at end of file diff --git a/src/messenger/models/chat.cljs b/src/messenger/models/chat.cljs new file mode 100644 index 0000000000..bb7eddd8b0 --- /dev/null +++ b/src/messenger/models/chat.cljs @@ -0,0 +1,8 @@ +(ns messenger.models.chat + (:require [messenger.state :as state])) + +(defn set-current-chat-id [chat-id] + (swap! state/app-state assoc-in state/current-chat-id-path chat-id)) + +(defn current-chat-id [] + (get-in @state/app-state state/current-chat-id-path)) diff --git a/src/messenger/models/messages.cljs b/src/messenger/models/messages.cljs index 913893ea79..fbe946cdc3 100644 --- a/src/messenger/models/messages.cljs +++ b/src/messenger/models/messages.cljs @@ -1,10 +1,26 @@ (ns messenger.models.messages - (:require [messenger.persistence.realm :as r])) + (:require [messenger.persistence.realm :as r] + [syng-im.utils.random :refer [timestamp]])) (defn save-message [from {:keys [msg-id] :as msg}] (when-not (r/exists? :msgs :msg-id msg-id) (r/write (fn [] - (r/create :msgs {:msg-id msg-id - :chat-id from - :msg (with-out-str (pr msg))} true))))) \ No newline at end of file + (r/create :msgs {:msg-id msg-id + :chat-id from + :timestamp (timestamp) + :msg (with-out-str (pr msg))} true))))) + +(defn get-messages [chat-id] + (-> (r/get-by-field :msgs :chat-id chat-id) + (r/sorted :timestamp) + (r/page 0 10))) + +(comment + + (save-message "0x040028c500ff086ecf1cfbb3c1a7240179cde5b86f9802e6799b9bbe9cdd7ad1b05ae8807fa1f9ed19cc8ce930fc2e878738c59f030a6a2f94b3522dc1378ff154" + {:msg-id "23456" + :content "hi!"}) + (get-messages "0x040028c500ff086ecf1cfbb3c1a7240179cde5b86f9802e6799b9bbe9cdd7ad1b05ae8807fa1f9ed19cc8ce930fc2e878738c59f030a6a2f94b3522dc1378ff154") + + ) \ No newline at end of file diff --git a/src/messenger/omnext.cljs b/src/messenger/omnext.cljs new file mode 100644 index 0000000000..be1b1e4683 --- /dev/null +++ b/src/messenger/omnext.cljs @@ -0,0 +1,29 @@ +(ns messenger.omnext + (:require [om.next :as om] + [syng-im.utils.logging :as log] + [re-natal.support :as sup] + [messenger.models.messages :as msgs] + [messenger.models.chat :as chat] + [messenger.state :as state])) + +(defmulti read om/dispatch) + +(defmethod read :default [{:keys [state] :as env} key param] + (log/debug "reading" "key=" key "param=" param) + (let [st @state] + (if-let [[_ v] (find st key)] + {:value v} + {:value :not-found}))) + +(defmethod read :chat [env key param] + (log/debug "reading" "key=" key "param=" param) + (let [chat-id (chat/current-chat-id) + val {:value {:chat/messages (msgs/get-messages chat-id)}}] + val)) + +(defonce reconciler + (om/reconciler + {:state state/app-state + :parser (om/parser {:read read}) + :root-render sup/root-render + :root-unmount sup/root-unmount})) diff --git a/src/messenger/persistence/realm.cljs b/src/messenger/persistence/realm.cljs index c795b1f664..3904fbfc01 100644 --- a/src/messenger/persistence/realm.cljs +++ b/src/messenger/persistence/realm.cljs @@ -17,9 +17,10 @@ :value "string"}} {:name :msgs :primaryKey :msg-id - :properties {:msg-id "string" - :chat-id "string" - :msg "string"}}]}) + :properties {:msg-id "string" + :timestamp "int" + :chat-id "string" + :msg "string"}}]}) (def realm (js/Realm. (clj->js opts))) @@ -28,7 +29,6 @@ [name schema])) (into {}))) - (defn field-type [schema-name field] (get-in schema-by-name [schema-name :properties field])) @@ -57,6 +57,12 @@ (-> (.objects realm (name schema-name)) (.filtered (to-query schema-name :eq field value)))) +(defn sorted [results field-name] + (.sorted results (to-string field-name))) + +(defn page [results from to] + (js/Array.prototype.slice.call results from to)) + (defn single [result] (-> (aget result 0))) diff --git a/src/messenger/services/contacts.cljs b/src/messenger/services/contacts.cljs index 715991d965..74417b59e0 100644 --- a/src/messenger/services/contacts.cljs +++ b/src/messenger/services/contacts.cljs @@ -80,5 +80,5 @@ (sync-contacts args)) (defn contacts-handler [state [id args]] - (log/info "user notification: " args) + (log/info "contacts-handler: " args) (contacts state id args)) diff --git a/src/messenger/services/navigate_to.cljs b/src/messenger/services/navigate_to.cljs new file mode 100644 index 0000000000..e51ca0877d --- /dev/null +++ b/src/messenger/services/navigate_to.cljs @@ -0,0 +1,35 @@ +(ns messenger.services.navigate-to + (:require [syng-im.utils.logging :as log] + [messenger.state :as state] + [messenger.components.chat.chat :as chat] + [messenger.models.chat :refer [set-current-chat-id]] + [om.next :as om] + [messenger.omnext :as omnext] + [messenger.components.iname :as in])) + +(defn nav-push [nav route] + (binding [state/*nav-render* false] + (.push nav (clj->js route)))) + +(defn set-root-query [component] + (let [app-root (om/class->any omnext/reconciler (om/app-root omnext/reconciler))] + (om/set-query! app-root {:query [{(in/get-name component) (om/get-query component)}]}))) + +(defmulti navigate-to (fn [state id args] + id)) + +(defmethod navigate-to :scene/chat + [state id {:keys [navigator chat-id] :as args}] + (log/debug "handling " id "args = " (dissoc args :navigator)) + (set-current-chat-id chat-id) + (set-root-query chat/Chat) + (nav-push navigator {:component chat/chat})) + +(defn navigate-to-handler [state [id args]] + (log/debug "navigate-to-handler: " (dissoc args :navigator)) + (navigate-to state id args)) + + +(comment + + ) \ No newline at end of file diff --git a/src/messenger/services/server.cljs b/src/messenger/services/server.cljs index d1a798a15f..41bb77e748 100644 --- a/src/messenger/services/server.cljs +++ b/src/messenger/services/server.cljs @@ -27,5 +27,5 @@ handler)) (defn server-handler [state [id args]] - (log/info "user notification: " args) + (log/info "contacts handler: " args) (server state id args)) diff --git a/src/messenger/state.cljs b/src/messenger/state.cljs index 6d412b4293..d89641209e 100644 --- a/src/messenger/state.cljs +++ b/src/messenger/state.cljs @@ -1,7 +1,7 @@ (ns messenger.state (:require [cljs.core.async :as async :refer [chan pub sub]] - [om.next :as om] - [re-natal.support :as sup])) + [re-natal.support :as sup] + [syng-im.utils.logging :as log])) (def ^{:dynamic true :private true} *nav-render* "Flag to suppress navigator re-renders from outside om when pushing/popping." @@ -9,31 +9,15 @@ (set! js/React (js/require "react-native")) -(defonce app-state (atom {:component nil - :loading false - :user-phone-number nil - :user-identity nil - :confirmation-code nil - :identity-password "replace-me-with-user-entered-password" - :channels {:pub-sub-publisher (chan) - :pub-sub-publication nil}})) - -(defmulti read om/dispatch) -(defmethod read :default - [{:keys [state]} k _] - (let [st @state] - (if-let [[_ v] (find st k)] - {:value v} - {:value :not-found}))) - -(defonce reconciler - (om/reconciler - {:state app-state - :parser (om/parser {:read read}) - :root-render sup/root-render - :root-unmount sup/root-unmount})) - - +(defonce app-state (atom {:component nil + :loading false + :user-phone-number nil + :user-identity nil + :confirmation-code nil + :chat {:chat-id nil} + :identity-password "replace-me-with-user-entered-password" + :channels {:pub-sub-publisher (chan) + :pub-sub-publication nil}})) (defn state [] @app-state) (def pub-sub-bus-path [:channels :pub-sub-publisher]) @@ -42,7 +26,39 @@ (def protocol-initialized-path [:protocol-initialized]) (def simple-store-path [:simple-store]) (def identity-password-path [:identity-password]) +(def current-chat-id-path [:chat :current-chat-id]) (defn pub-sub-publisher [app] (get-in app pub-sub-bus-path)) (defn kv-store [] (get-in @app-state simple-store-path)) + + +(comment + + (use 'figwheel-sidecar.repl-api) + (cljs-repl) + + (defn read + [{:keys [state] :as env} key params] + (let [st @state] + (if-let [[_ v] (find st key)] + {:value v} + {:value :not-found}))) + + (def my-parser (om/parser {:read read})) + + (def my-state (atom {:count 0 :title "what"})) + (my-parser {:state my-state} [:count :title]) + + (defn mutate + [{:keys [state] :as env} key params] + (if (= 'increment key) + {:value {:keys [:count]} + :action #(swap! state update-in [:count] inc)} + {:value :not-found})) + + (def my-parser (om/parser {:read read :mutate mutate})) + (my-parser {:state my-state} '[(increment)]) + @my-state + + ) \ No newline at end of file