changed navigation method, send new message

Former-commit-id: 77cae2cc42
This commit is contained in:
michaelr 2016-03-23 12:46:41 +02:00
parent 5df3bf5c73
commit 95817924d6
19 changed files with 307 additions and 120 deletions

View File

@ -1,7 +1,7 @@
{
"name": "Messenger",
"interface": "om-next",
"androidHost": "localhost",
"androidHost": "10.0.3.2",
"modules": [
"react-native-contacts",
"react-native-invertible-scroll-view",

View File

@ -7,7 +7,7 @@
},
"dependencies": {
"awesome-phonenumber": "^1.0.12",
"react-native": "^0.21.0",
"react-native": "^0.22.0",
"react-native-contacts": "^0.2.1",
"react-native-i18n": "0.0.8",
"react-native-invertible-scroll-view": "^0.2.0",

View File

@ -11,8 +11,7 @@
[messenger.omnext :as omnext]
[messenger.state :as state]
[messenger.utils.utils :refer [log toast]]
[messenger.android.login :refer [login]]
[messenger.components.contact-list.contact-list :refer [contact-list]]
[messenger.android.login :refer [login Login]]
[messenger.comm.pubsub :as pubsub]
[messenger.comm.intercom :as intercom :refer [load-user-phone-number]]
[messenger.protocol.protocol-handler :refer [make-handler]]
@ -20,7 +19,9 @@
[messenger.components.chat.chat :as chat]
[syng-im.protocol.api :refer [init-protocol]]
[syng-im.utils.logging :as log]
[messenger.components.iname :as in]))
[messenger.components.iname :as in]
[messenger.models.navigation :as n]
[messenger.components.contact-list.contact-list :refer [ContactList contact-list]]))
(def app-registry (.-AppRegistry js/React))
@ -44,12 +45,13 @@
(defui AppRoot
static om/IQuery
(query [this]
[:loading :contacts-ds :user-phone-number :user-identity :confirmation-code])
(let [component (n/current-screen-class)]
[{(in/get-name component) (om/get-query component)}]))
Object
(render [this]
(log/debug "APPROOT Rendering")
(navigator
{:initialRoute {:component login}
{:initialRoute {:component contact-list}
:renderScene (fn [route nav]
(log/debug "RENDER SCENE")
(when state/*nav-render*
@ -68,6 +70,7 @@
(defonce app-root (om/factory RootNode))
(defn init []
(n/set-current-screen-class ContactList)
(init-simple-store)
(pubsub/setup-pub-sub)
(init-protocol (make-handler))

View File

@ -12,15 +12,14 @@
[messenger.utils.resources :as res]
[messenger.components.spinner :refer [spinner]]
[messenger.android.sign-up-confirm :refer [sign-up-confirm]]
[messenger.constants :refer [ethereum-rpc-url]]))
[messenger.constants :refer [ethereum-rpc-url]]
[messenger.components.iname :as in]))
(def nav-atom (atom nil))
(defn show-confirm-view []
(swap! state/app-state assoc :loading false)
(binding [state/*nav-render* false]
(.replace @nav-atom (clj->js {:component sign-up-confirm
:name "sign-up-confirm"}))))
(intercom/show-signup-confirm @nav-atom))
(defn sign-up []
(swap! state/app-state assoc :loading true)
@ -34,12 +33,15 @@
(set-user-phone-number formatted)))
(defui Login
static in/IName
(get-name [this]
:login/login)
static om/IQuery
(query [this]
'[:user-phone-number :user-identity :loading])
Object
(render [this]
(let [{:keys [user-phone-number user-identity loading]} (om/props this)
(let [{{:keys [user-phone-number user-identity loading]} :login/login} (om/props this)
{:keys [nav]} (om/get-computed this)]
(reset! nav-atom nav)
(view

View File

@ -12,15 +12,14 @@
[messenger.utils.resources :as res]
[messenger.components.spinner :refer [spinner]]
[messenger.components.contact-list.contact-list :refer [contact-list]]
[messenger.comm.intercom :as intercom :refer [set-confirmation-code]]))
[messenger.comm.intercom :as intercom :refer [set-confirmation-code]]
[messenger.components.iname :as in]))
(def nav-atom (atom nil))
(defn show-home-view []
(swap! state/app-state assoc :loading false)
(binding [state/*nav-render* false]
(.replace @nav-atom (clj->js {:component contact-list
:name "contact-list"}))))
(intercom/show-contacts @nav-atom))
(defn sync-contacts []
(intercom/sync-contacts show-home-view))
@ -47,13 +46,16 @@
(set-confirmation-code formatted)))
(defui SignUpConfirm
static in/IName
(get-name [this]
:signup/confirm)
static om/IQuery
(query [this]
'[:confirmation-code :loading])
Object
(render
[this]
(let [{:keys [confirmation-code loading]} (om/props this)
(let [{{:keys [confirmation-code loading]} :signup/confirm} (om/props this)
{:keys [nav]} (om/get-computed this)]
(reset! nav-atom nav)
(view

View File

@ -54,9 +54,12 @@
(defn protocol-initialized [identity]
(publish! :service [:protocol :protocol/initialized {:identity identity}]))
(defn save-new-msg [from payload]
(publish! :service [:protocol :protocol/save-new-msg {:from from
:payload payload}]))
(defn save-new-msg [msg]
(publish! :service [:protocol :protocol/save-new-msg {:msg msg}]))
(defn send-msg [chat-id text]
(publish! :service [:protocol :protocol/send-msg {:chat-id chat-id
:text text}]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; navigate-to
@ -64,3 +67,9 @@
(defn show-chat [navigator chat-id]
(publish! :service [:navigate-to :scene/chat {:chat-id chat-id
:navigator navigator}]))
(defn show-signup-confirm [navigator]
(publish! :service [:navigate-to :scene/signup-confirm {:navigator navigator}]))
(defn show-contacts [navigator]
(publish! :service [:navigate-to :scene/contacts {:navigator navigator}]))

View File

@ -6,31 +6,38 @@
[messenger.utils.resources :as res]
[messenger.components.invertible-scroll-view :refer [invertible-scroll-view]]
[messenger.components.chat.message :refer [message]]
[messenger.components.chat.new-message :refer [new-message]]
[messenger.components.chat.new-message :refer [new-message NewMessage]]
[messenger.state :as state]
[syng-im.utils.logging :as log]
[messenger.components.iname :as in]))
[messenger.components.iname :as in]
[messenger.omnext :as omnext]))
(defn generate-message [n]
{:id n
:type (if (= (rem n 4) 3)
:audio
:text)
:body (if (= (rem n 3) 0)
(apply str n "." (repeat 5 " This is a text."))
(str n ". This is a text."))
:outgoing (< (rand) 0.5)
:delivery-status (if (< (rand) 0.5) :delivered :seen)
:date "TODAY"
:new-day (= (rem n 3) 0)})
;(defn generate-message [n]
; {:id n
; :type (if (= (rem n 4) 3)
; :audio
; :text)
; :body (if (= (rem n 3) 0)
; (apply str n "." (repeat 5 " This is a text."))
; (str n ". This is a text."))
; :outgoing (< (rand) 0.5)
; :delivery-status (if (< (rand) 0.5) :delivered :seen)
; :date "TODAY"
; :new-day (= (rem n 3) 0)})
;
;(defn generate-messages [n]
; (map generate-message (range 1 (inc n))))
(defn generate-messages [n]
(map generate-message (range 1 (inc n))))
;(defn load-messages []
; (clone-with-rows (data-source {:rowHasChanged (fn [row1 row2]
; (not= row1 row2))})
; (vec (generate-messages 100))))
;
(defn load-messages []
(clone-with-rows (data-source {:rowHasChanged (fn [row1 row2]
(defn to-datasource [msgs]
(-> (data-source {:rowHasChanged (fn [row1 row2]
(not= row1 row2))})
(vec (generate-messages 100))))
(clone-with-rows msgs)))
(defn nav-pop [nav]
(binding [state/*nav-render* false]
@ -42,17 +49,21 @@
(defui Chat
static in/IName
(get-name [this]
:chat)
:chat/chat)
static om/IQuery
(query [this]
'[:chat/messages])
`[:chat/messages ~@(om/get-query NewMessage)])
Object
(componentDidMount [this]
(om.next.protocols/reindex! omnext/reconciler))
(render
[this]
(let [{:keys [nav]} (om/get-computed this)
{{messages :chat/messages} :chat} (om/props this)
{{messages :chat/messages
chat-id :chat/chat-id} :chat/chat} (om/props this)
_ (log/debug "messages=" messages)
messages-ds (load-messages)]
messages-ds (when messages
(to-datasource messages))]
(view {:style {:flex 1
:backgroundColor "white"}}
(toolbar-android {:logo res/logo-icon
@ -66,13 +77,15 @@
:elevation 2}
:onIconClicked (fn []
(nav-pop nav))})
(when messages-ds
(list-view {:dataSource messages-ds
:renderScrollComponent
(fn [props]
(invertible-scroll-view nil))
:renderRow render-row
:style {:backgroundColor "white"}})
(new-message)))))
:style {:backgroundColor "white"}}))
(new-message {:chat-id chat-id})))))
(def chat (om/factory Chat))

View File

@ -2,21 +2,20 @@
(:require-macros
[natal-shell.components :refer [view text image]])
(:require [om.next :as om :refer-macros [defui]]
[messenger.utils.resources :as res]))
[messenger.utils.resources :as res]
[messenger.constants :refer [text-content-type]]))
(defui Message
static om/Ident
(ident [this {:keys [id]}]
[:message/by-id id])
(ident [this {:keys [msg-id]}]
[:message/by-id msg-id])
static om/IQuery
(query [this]
'[:id :type :body :outgoing :delivery-status :date :new-day])
'[:msg-id :content :content-type :outgoing :delivery-status :date :new-day])
Object
(render
[this]
(let [{:keys [id body outgoing delivery-status date new-day] :as props}
(om/props this)
type (keyword (:type props))]
(let [{:keys [msg-id content content-type outgoing delivery-status date new-day] :as props} (om/props this)]
(view {:paddingHorizontal 15}
;;; date
(when new-day
@ -39,7 +38,7 @@
{:alignSelf "flex-start"
:alignItems "flex-start"}))}
(view {:style (merge {:borderRadius 6}
(if (= type :text)
(if (= content-type text-content-type)
{:paddingVertical 12
:paddingHorizontal 16}
{:paddingVertical 14
@ -47,11 +46,11 @@
(if outgoing
{:backgroundColor "#D3EEEF"}
{:backgroundColor "#FBF6E3"}))}
(if (= type :text)
(if (= content-type text-content-type)
(text {:style {:fontSize 14
:fontFamily "Avenir-Roman"
:color "#4A5258"}}
body)
content)
;;; audio
(view {:style {:flexDirection "row"
:alignItems "center"}}
@ -105,4 +104,4 @@
"Seen"
"Delivered")))))))))
(def message (om/factory Message {:keyfn :id}))
(def message (om/factory Message {:keyfn :msg-id}))

View File

@ -2,12 +2,19 @@
(:require-macros
[natal-shell.components :refer [view image text-input]])
(:require [om.next :as om :refer-macros [defui]]
[messenger.utils.resources :as res]))
[messenger.utils.resources :as res]
[syng-im.utils.logging :as log]
[messenger.utils.state :refer [from-state]]
[messenger.comm.intercom :refer [send-msg]]))
(def local-state (atom {}))
(defui NewMessage
static om/IQuery
(query [this]
'[:chat/chat-id])
Object
(render
[this]
(render [this]
(view {:style {:flexDirection "row"
:margin 10
:height 40
@ -24,8 +31,21 @@
:lineHeight 42
:fontSize 14
:fontFamily "Avenir-Roman"
:color "#9CBFC0"}}
"Your message")
:color "#9CBFC0"}
:autoFocus true
:placeholder "Enter your message here"
:value (from-state this :text)
:onChangeText (fn [text]
;(log/debug (with-out-str (pr (js->clj (om/props this)))) (-> (om/props this) :chat-id))
;(om/set-state! this (clj->js {:text text}))
(swap! local-state assoc :text text)
)
:onSubmitEditing (fn [e]
(let [chat-id (-> (om/props this) :chat-id)
;text (from-state this :text)
text (get @local-state :text)]
;(om/set-state! this (clj->js {:text nil}))
(send-msg chat-id text)))})
(image {:source res/smile
:style {:marginTop 11
:marginRight 12

View File

@ -4,11 +4,12 @@
toolbar-android]]
[natal-shell.core :refer [with-error-view]])
(:require [om.next :as om :refer-macros [defui]]
[messenger.state :as state]
[messenger.utils.utils :refer [log toast http-post]]
[messenger.utils.resources :as res]
[messenger.comm.intercom :as intercom]
[messenger.components.contact-list.contact :refer [contact]]))
[messenger.components.contact-list.contact :refer [contact]]
[messenger.components.iname :as in]
[syng-im.utils.logging :as log]))
(defn render-row [nav row section-id row-id]
(contact (om/computed (js->clj row :keywordize-keys true)
@ -18,6 +19,9 @@
(intercom/load-syng-contacts))
(defui ContactList
static in/IName
(get-name [this]
:contacts/contacts)
static om/IQuery
(query [this]
'[:contacts-ds])
@ -25,7 +29,7 @@
(componentDidMount [this]
(load-contacts))
(render [this]
(let [{:keys [contacts-ds]} (om/props this)
(let [{{contacts-ds :contacts-ds} :contacts/contacts} (om/props this)
{:keys [nav]} (om/get-computed this)]
(view {:style {:flex 1
:backgroundColor "white"}}
@ -35,8 +39,9 @@
:style {:backgroundColor "white"
:height 56
:elevation 2}})
(when contacts-ds
(list-view {:dataSource contacts-ds
:renderRow (partial render-row nav)
:style {:backgroundColor "white"}})))))
:style {:backgroundColor "white"}}))))))
(def contact-list (om/factory ContactList))

View File

@ -1,3 +1,5 @@
(ns messenger.constants)
(def ethereum-rpc-url "http://localhost:8545")
(def text-content-type "text/plain")

View File

@ -1,26 +1,42 @@
(ns messenger.models.messages
(:require [messenger.persistence.realm :as r]
[cljs.reader :refer [read-string]]
[syng-im.utils.random :refer [timestamp]]))
(defn save-message [from {:keys [msg-id] :as msg}]
(defn save-message [chat-id {:keys [from to msg-id content content-type outgoing] :or {outgoing false} :as msg}]
(when-not (r/exists? :msgs :msg-id msg-id)
(r/write
(fn []
(r/create :msgs {:msg-id msg-id
:chat-id from
:timestamp (timestamp)
:msg (with-out-str (pr msg))} true)))))
(r/create :msgs {:chat-id chat-id
:msg-id msg-id
:from from
:to to
:content content
:content-type content-type
:outgoing outgoing
:timestamp (timestamp)} true)))))
(defn get-messages* [chat-id]
(-> (r/get-by-field :msgs :chat-id chat-id)
(r/sorted :timestamp :desc)
(r/page 0 10)))
(defn get-messages [chat-id]
(-> (r/get-by-field :msgs :chat-id chat-id)
(r/sorted :timestamp)
(r/page 0 10)))
(-> (get-messages* chat-id)
(js->clj :keywordize-keys true)))
(comment
(save-message "0x040028c500ff086ecf1cfbb3c1a7240179cde5b86f9802e6799b9bbe9cdd7ad1b05ae8807fa1f9ed19cc8ce930fc2e878738c59f030a6a2f94b3522dc1378ff154"
{:msg-id "23456"
:content "hi!"})
(get-messages "0x040028c500ff086ecf1cfbb3c1a7240179cde5b86f9802e6799b9bbe9cdd7ad1b05ae8807fa1f9ed19cc8ce930fc2e878738c59f030a6a2f94b3522dc1378ff154")
{:msg-id "153"
:content "hello!"
:content-type "text/plain"})
(get-messages* "0x040028c500ff086ecf1cfbb3c1a7240179cde5b86f9802e6799b9bbe9cdd7ad1b05ae8807fa1f9ed19cc8ce930fc2e878738c59f030a6a2f94b3522dc1378ff154")
(get-messages "0x043df89d36f6e3d8ade18e55ac3e2e39406ebde152f76f2f82d674681d59319ffd9880eebfb4f5f8d5c222ec485b44d6e30ba3a03c96b1c946144fdeba1caccd43")
(doseq [msg (get-messages* "0x043df89d36f6e3d8ade18e55ac3e2e39406ebde152f76f2f82d674681d59319ffd9880eebfb4f5f8d5c222ec485b44d6e30ba3a03c96b1c946144fdeba1caccd43")]
(r/delete msg))
)

View File

@ -0,0 +1,8 @@
(ns messenger.models.navigation
(:require [messenger.state :as state]))
(defn set-current-screen-class [class]
(swap! state/app-state assoc-in [:current-screen-class] class))
(defn current-screen-class []
(get-in @state/app-state [:current-screen-class]))

View File

@ -4,7 +4,8 @@
[re-natal.support :as sup]
[messenger.models.messages :as msgs]
[messenger.models.chat :as chat]
[messenger.state :as state]))
[messenger.state :as state]
[messenger.components.iname :as in]))
(defmulti read om/dispatch)
@ -15,15 +16,59 @@
{:value v}
{:value :not-found})))
(defmethod read :chat [env key param]
(defmethod read :chat/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 {:value {:chat/messages (msgs/get-messages chat-id)
:chat/chat-id chat-id}}
_ (log/debug "returning" val)]
val))
(defmethod read :chat/messages [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)
:chat/chat-id chat-id}}
_ (log/debug "returning" val)]
val))
(defmethod read :contacts/contacts [env key param]
(log/debug "reading" "key=" key "param=" param)
(let [val {:value {:contacts-ds (get-in @state/app-state [:contacts-ds])}}
_ (log/debug "returning" val)]
val))
(defmethod read :login/login [env key param]
(log/debug "reading" "key=" key "param=" param)
(let [val {:value (select-keys @state/app-state [:user-phone-number :user-identity :loading])}
_ (log/debug "returning" val)]
val))
(defmethod read :signup/confirm [env key param]
(log/debug "reading" "key=" key "param=" param)
(let [val {:value (select-keys @state/app-state [:confirmation-code :loading])}
_ (log/debug "returning" val)]
val))
(defmulti mutate om/dispatch)
(defmethod mutate 'chat/add-msg-to-chat [{:keys [state] :as env} key {:keys [chat-id msg] :as param}]
(log/debug "writing" "key=" key "param=" param)
{:action #(do
(log/debug "Writing msg to db")
(msgs/save-message chat-id msg)
(swap! state/app-state assoc-in [:chat :messages] msg))})
(defonce reconciler
(om/reconciler
{:state state/app-state
:parser (om/parser {:read read})
:parser (om/parser {:read read
:mutate mutate})
:root-render sup/root-render
:root-unmount sup/root-unmount}))
(defn set-root-query [component]
(let [app-root (om/class->any reconciler (om/app-root reconciler))]
(om/set-query! app-root {:query [{(in/get-name component) (om/get-query component)}]})
(om.next.protocols/reindex! reconciler)))

View File

@ -18,9 +18,13 @@
{:name :msgs
:primaryKey :msg-id
:properties {:msg-id "string"
:from "string"
:to "string"
:content "string" ;; TODO make it ArrayBuffer
:content-type "string"
:timestamp "int"
:chat-id "string"
:msg "string"}}]})
:outgoing "bool"}}]})
(def realm (js/Realm. (clj->js opts)))
@ -57,8 +61,10 @@
(-> (.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 sorted [results field-name order]
(.sorted results (to-string field-name) (if (= order :asc)
false
true)))
(defn page [results from to]
(js/Array.prototype.slice.call results from to))

View File

@ -16,8 +16,8 @@
(case event-type
:initialized (let [{:keys [identity]} event]
(protocol-initialized identity))
:new-msg (let [{:keys [from payload]} event]
(save-new-msg from payload))
:new-msg (let [{:keys [from to payload]} event]
(save-new-msg (assoc payload :from from :to to)))
;:msg-acked (let [{:keys [msg-id]} event]
; (add-to-chat "chat" ":" (str "Message " msg-id " was acked")))
;:delivery-failed (let [{:keys [msg-id]} event]

View File

@ -2,18 +2,21 @@
(:require [syng-im.utils.logging :as log]
[messenger.state :as state]
[messenger.components.chat.chat :as chat]
[messenger.android.sign-up-confirm :as sc]
[messenger.components.contact-list.contact-list :as cl]
[messenger.models.chat :refer [set-current-chat-id]]
[om.next :as om]
[messenger.omnext :as omnext]
[messenger.components.iname :as in]))
[messenger.components.iname :as in]
[messenger.models.navigation :as n]))
(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)}]})))
(defn nav-replace [nav route]
(binding [state/*nav-render* false]
(.replace nav (clj->js route))))
(defmulti navigate-to (fn [state id args]
id))
@ -21,10 +24,24 @@
(defmethod navigate-to :scene/chat
[state id {:keys [navigator chat-id] :as args}]
(log/debug "handling " id "args = " (dissoc args :navigator))
(n/set-current-screen-class chat/Chat)
(set-current-chat-id chat-id)
(set-root-query chat/Chat)
(nav-push navigator {:component chat/chat}))
(defmethod navigate-to :scene/signup-confirm
[state id {:keys [navigator] :as args}]
(log/debug "handling " id "args = " (dissoc args :navigator))
(n/set-current-screen-class sc/SignUpConfirm)
(nav-replace navigator {:component sc/sign-up-confirm
:name "sign-up-confirm"}))
(defmethod navigate-to :scene/contacts
[state id {:keys [navigator] :as args}]
(log/debug "handling " id "args = " (dissoc args :navigator))
(n/set-current-screen-class cl/ContactList)
(nav-replace navigator {:component cl/contact-list
:name "contact-list"}))
(defn navigate-to-handler [state [id args]]
(log/debug "navigate-to-handler: " (dissoc args :navigator))
(navigate-to state id args))

View File

@ -3,7 +3,11 @@
update-identity]]
[messenger.models.messages :refer [save-message]]
[messenger.models.user-data :refer [set-identity]]
[syng-im.utils.logging :as log]))
[syng-im.utils.logging :as log]
[syng-im.protocol.api :as api]
[messenger.omnext :as omnext]
[om.next :as om]
[messenger.constants :refer [text-content-type]]))
(defmulti protocol (fn [state id args]
id))
@ -16,10 +20,39 @@
(set-initialized true))
(defmethod protocol :protocol/save-new-msg
[state id {:keys [from payload] :as args}]
[state id {{from :from :as msg} :msg :as args}]
(log/debug "handling " id "args = " args)
(save-message from payload))
(let [chat-id from]
(om/transact! omnext/reconciler `[(chat/add-msg-to-chat {:msg ~msg
:chat-id ~chat-id}) [:chat/messages :chat/chat-id :chat/chat]])))
(defmethod protocol :protocol/send-msg
[state id {:keys [chat-id text] :as args}]
(log/debug "handling " id "args = " args)
(let [{msg-id :msg-id
{from :from
to :to} :msg} (api/send-user-msg {:to chat-id
:content text})
msg {:msg-id msg-id
:from from
:to to
:content text
:content-type text-content-type
:outgoing true}]
(om/transact! omnext/reconciler ;;(om/class->any omnext/reconciler messenger.components.chat.chat/Chat)
`[(chat/add-msg-to-chat {:msg ~msg
:chat-id ~chat-id}) [:chat/messages :chat/chat-id :chat/chat]])))
(defn protocol-handler [state [id args]]
(log/debug "protocol-handler: " args)
(protocol state id args))
(comment
(om/transact! omnext/reconciler `[(chat/add-msg-to-chat {:msg {:msg-id "1458670960090-ed5f995a-b686-5cbe-bf96-8a60ada8f6c3"}
:chat-id "1"}) [:chat/chat]])
(om/get-indexer omnext/reconciler)
(om.next.protocols/reindex! omnext/reconciler)
)

View File

@ -0,0 +1,7 @@
(ns messenger.utils.state
(:require [om.next :as om]))
(defn from-state [component key]
(-> (om/get-state component)
(js->clj :keywordize-keys true)
key))