bring messages to chat view

This commit is contained in:
michaelr 2016-03-22 00:43:03 +02:00
parent f32547aaf8
commit ef26278908
18 changed files with 296 additions and 158 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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"]

View File

@ -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)))

View File

@ -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}]))

View File

@ -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))

View File

@ -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"}}

View File

@ -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}))

View File

@ -0,0 +1,4 @@
(ns messenger.components.iname)
(defprotocol IName
(get-name [this]))

View File

@ -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)))

View File

@ -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))

View File

@ -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)))))
(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")
)

29
src/messenger/omnext.cljs Normal file
View File

@ -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}))

View File

@ -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)))

View File

@ -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))

View File

@ -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
)

View File

@ -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))

View File

@ -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
)