From 936c443a6392685e94ba77f52bc818b314ea5134 Mon Sep 17 00:00:00 2001 From: michaelr Date: Tue, 29 Mar 2016 23:45:31 +0300 Subject: [PATCH] chats --- .../main/java/com/syngim/MainActivity.java | 2 +- syng-im/images/chat.png | Bin 0 -> 916 bytes syng-im/src/syng_im/android/core.cljs | 15 ++-- syng-im/src/syng_im/components/chat.cljs | 2 +- .../components/chats/chat_list_item.cljs | 26 +++++++ .../syng_im/components/chats/chats_list.cljs | 43 ++++++++++++ syng-im/src/syng_im/db.cljs | 14 ++-- syng-im/src/syng_im/handlers.cljs | 31 +++++---- syng-im/src/syng_im/models/chat.cljs | 6 +- syng-im/src/syng_im/models/chats.cljs | 65 ++++++++++++++++++ syng-im/src/syng_im/navigation.cljs | 12 ++-- syng-im/src/syng_im/persistence/realm.cljs | 29 ++++++-- syng-im/src/syng_im/resources.cljs | 1 + syng-im/src/syng_im/subs.cljs | 43 +++++++----- 14 files changed, 230 insertions(+), 59 deletions(-) create mode 100644 syng-im/images/chat.png create mode 100644 syng-im/src/syng_im/components/chats/chat_list_item.cljs create mode 100644 syng-im/src/syng_im/components/chats/chats_list.cljs create mode 100644 syng-im/src/syng_im/models/chats.cljs diff --git a/syng-im/android/app/src/main/java/com/syngim/MainActivity.java b/syng-im/android/app/src/main/java/com/syngim/MainActivity.java index e195a37b75..45d4f60e4a 100644 --- a/syng-im/android/app/src/main/java/com/syngim/MainActivity.java +++ b/syng-im/android/app/src/main/java/com/syngim/MainActivity.java @@ -40,7 +40,7 @@ public class MainActivity extends ReactActivity { // Launch! new Thread(new Runnable() { public void run() { - Geth.run("--bootnodes enode://e2f28126720452aa82f7d3083e49e6b3945502cb94d9750a15e27ee310eed6991618199f878e5fbc7dfa0e20f0af9554b41f491dc8f1dbae8f0f2d37a3a613aa@139.162.13.89:30303 --shh --ipcdisable --nodiscover --rpc --rpcapi db,eth,net,web3,shh --fast --datadir=" + dataFolder); + Geth.run("--bootnodes enode://e2f28126720452aa82f7d3083e49e6b3945502cb94d9750a15e27ee310eed6991618199f878e5fbc7dfa0e20f0af9554b41f491dc8f1dbae8f0f2d37a3a613aa@139.162.13.89:30303 --shh --ipcdisable --nodiscover --rpc --rpcapi db,eth,net,web3,shh,admin --fast --datadir=" + dataFolder); } }).start(); } diff --git a/syng-im/images/chat.png b/syng-im/images/chat.png new file mode 100644 index 0000000000000000000000000000000000000000..582ab023a0170d7e99229f0273fc724e876cad84 GIT binary patch literal 916 zcmV;F18e+=P)uCB+s>BlY7&gd!FK zt;vf|twvS~@OscAOZOy%i0gbff04D{UmfHiZ z?Z5tcSt)X}GO+WlSE|1|y6sGHkr7@Fd`6Z~?$-BPtKtKxHl9U1D}Qy(Ymq`DlnXcj zrKu zxM+{RjMvweprUvXI`?xz;Ecc-z!nUSQXnu6%l6!xZHfEcePcJ$8X%eiNU1PsJ5Y)+ z;1=TKPupSgBp8%$cAKKZsHuE_aL}E1{xm+(cc`(W){)AK^{0MMSu(N601<%*5Wl(hZFn+2B2ntG(=P6j0fN5g|c9sjJ|wj9@+{N#hO4>KmU*bvj#Z{Gd+mOb@H=3YoKp6Y7(sxHyf z`6CheHv&iiAOHyE2TIO<@#dwHs9Eq%?e)!RD=?P>kR;{JwlI+*#~MvOy8cbWP^{yO zKX|cHmix!YjU5LL?k}%TcfaH&RMK&NyLd7lqGgBx%RKz1ZED?#_1+Nii`Q zYy19m?}KZ{fF%~d5*ka1fpq5?D?mzm@y^TVVx3pNTrS*CX@|OC?S^;Bop|=}*15yV qbCW9t&nPn-*&Zjs {:view-id :sign-up}) + [navigator {:initial-route (clj->js {:view-id :chat-list}) :render-scene (fn [route nav] (log/debug "route" route) - (when true ;; nav/*nav-render* + (when true ;; nav/*nav-render* (let [{:keys [view-id]} (js->clj route :keywordize-keys true) view-id (keyword view-id)] (init-back-button-handler! nav) (case view-id - :contact-list (r/as-element [contact-list - {:navigator nav}]) + :chat-list (r/as-element [chats-list {:navigator nav}]) + :contact-list (r/as-element [contact-list {:navigator nav}]) :chat (r/as-element [chat {:navigator nav}]) - :sign-up (r/as-element [sign-up-view - {:navigator nav}]) - :sign-up-confirm (r/as-element [sign-up-confirm-view - {:navigator nav}])))))}]) + :sign-up (r/as-element [sign-up-view {:navigator nav}]) + :sign-up-confirm (r/as-element [sign-up-confirm-view {:navigator nav}])))))}]) (defn init [] (dispatch-sync [:initialize-db]) diff --git a/syng-im/src/syng_im/components/chat.cljs b/syng-im/src/syng_im/components/chat.cljs index d0b57cfb8e..9008ef1e1e 100644 --- a/syng-im/src/syng_im/components/chat.cljs +++ b/syng-im/src/syng_im/components/chat.cljs @@ -44,5 +44,5 @@ (invertible-scroll-view nil)) :renderRow (fn [row section-id row-id] (r/as-element [chat-message (js->clj row :keywordize-keys true)])) - :style {:backgroundColor "black"}}] + :style {:backgroundColor "white"}}] [chat-message-new]])))) diff --git a/syng-im/src/syng_im/components/chats/chat_list_item.cljs b/syng-im/src/syng_im/components/chats/chat_list_item.cljs new file mode 100644 index 0000000000..0a7c3e3902 --- /dev/null +++ b/syng-im/src/syng_im/components/chats/chat_list_item.cljs @@ -0,0 +1,26 @@ +(ns syng-im.components.chats.chat-list-item + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [syng-im.components.react :refer [view + text + image + touchable-highlight]] + [syng-im.utils.logging :as log] + [syng-im.resources :as res])) + +(defn chat-list-item [chat-obj navigator] + [touchable-highlight {:on-press (fn [] + (dispatch [:show-chat (aget chat-obj "chat-id") navigator]))} + [view {:style {:flexDirection "row" + :width 260 + :marginVertical 5}} + [image {:source res/chat-icon + :style {:borderWidth 2 + :borderColor "#FFFFFF" + :width 32 + :height 30 + :marginRight 5 + :marginLeft 5}}] + [text {:style {:fontSize 14 + :fontFamily "Avenir-Roman" + :color "#4A5258"}} + (subs (aget chat-obj "name") 0 30)]]]) \ No newline at end of file diff --git a/syng-im/src/syng_im/components/chats/chats_list.cljs b/syng-im/src/syng_im/components/chats/chats_list.cljs new file mode 100644 index 0000000000..cdb6325989 --- /dev/null +++ b/syng-im/src/syng_im/components/chats/chats_list.cljs @@ -0,0 +1,43 @@ +(ns syng-im.components.chats.chats-list + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [syng-im.components.react :refer [android? + view + text + image + touchable-highlight + navigator + toolbar-android]] + [syng-im.components.realm :refer [list-view]] + [syng-im.utils.logging :as log] + [syng-im.navigation :refer [nav-pop]] + [syng-im.resources :as res] + [syng-im.utils.listview :refer [to-realm-datasource]] + [reagent.core :as r] + [syng-im.components.chats.chat-list-item :refer [chat-list-item]])) + + +(defn chats-list [{:keys [navigator]}] + (let [chats (subscribe [:get-chats])] + (fn [] + (let [chats @chats + _ (log/debug "chats=" chats) + datasource (to-realm-datasource chats)] + [view {:style {:flex 1 + :backgroundColor "white"}} + (when android? + ;; TODO add IOS version + [toolbar-android {:logo res/logo-icon + :title "Your Chats" + :titleColor "#4A5258" + :subtitle "List of your recent chats" + :subtitleColor "#AAB2B2" + :navIcon res/nav-back-icon + :style {:backgroundColor "white" + :height 56 + :elevation 2} + :onIconClicked (fn [] + (nav-pop navigator))}]) + [list-view {:dataSource datasource + :renderRow (fn [row section-id row-id] + (r/as-element [chat-list-item row navigator])) + :style {:backgroundColor "white"}}]])))) diff --git a/syng-im/src/syng_im/db.cljs b/syng-im/src/syng_im/db.cljs index 2514a81f3b..ee9113836d 100644 --- a/syng-im/src/syng_im/db.cljs +++ b/syng-im/src/syng_im/db.cljs @@ -5,15 +5,17 @@ (def schema {:greeting s/Str}) ;; initial state of app-db -(def app-db {:greeting "Hello Clojure in iOS and Android!" - :identity-password "replace-me-with-user-entered-password" +(def app-db {:greeting "Hello Clojure in iOS and Android!" + :identity-password "replace-me-with-user-entered-password" :contacts [] - :chat {:current-chat-id "0x0479a5ed1f38cadfad1db6cd56c4b659b0ebe052bbe9efa950f6660058519fa4ca6be2dda66afa80de96ab00eb97a2605d5267a1e8f4c2a166ab551f6826608cdd"} - :chats {}}) + :chat {:current-chat-id "0x0479a5ed1f38cadfad1db6cd56c4b659b0ebe052bbe9efa950f6660058519fa4ca6be2dda66afa80de96ab00eb97a2605d5267a1e8f4c2a166ab551f6826608cdd"} + :chats {} + :chats-updated-signal 0}) (def protocol-initialized-path [:protocol-initialized]) (def identity-password-path [:identity-password]) (def current-chat-id-path [:chat :current-chat-id]) -(defn latest-msg-id-path [chat-id] - [:chats chat-id :arrived-message-id]) +(def updated-chats-signal-path [:chats-updated-signal]) +(defn updated-chat-signal-path [chat-id] + [:chats chat-id :chat-updated-signal]) diff --git a/syng-im/src/syng_im/handlers.cljs b/syng-im/src/syng_im/handlers.cljs index 2d088a88c6..225f4c0549 100644 --- a/syng-im/src/syng_im/handlers.cljs +++ b/syng-im/src/syng_im/handlers.cljs @@ -12,12 +12,16 @@ [syng-im.models.messages :refer [save-message update-message! message-by-id]] - [syng-im.models.chat :refer [signal-chat-updated]] [syng-im.handlers.server :as server] [syng-im.handlers.contacts :as contacts-service] + + [syng-im.models.chats :refer [create-chat]] + [syng-im.models.chat :refer [signal-chat-updated + set-current-chat-id]] [syng-im.utils.logging :as log] [syng-im.protocol.api :as api] - [syng-im.constants :refer [text-content-type]])) + [syng-im.constants :refer [text-content-type]] + [syng-im.navigation :refer [nav-push]])) ;; -- Middleware ------------------------------------------------------------ ;; @@ -32,14 +36,13 @@ (def validate-schema-mw (after (partial check-and-throw schema))) -;; -- Handlers -------------------------------------------------------------- + +;; -- Common -------------------------------------------------------------- (register-handler :initialize-db (fn [_ _] app-db)) -;; -- Common -------------------------------------------------------------- - (register-handler :set-loading (fn [db [_ value]] (assoc db :loading value))) @@ -61,7 +64,9 @@ (fn [db [_ {chat-id :from msg-id :msg-id :as msg}]] (save-message chat-id msg) - (signal-chat-updated db chat-id))) + (-> db + (create-chat chat-id [chat-id]) + (signal-chat-updated chat-id)))) (register-handler :acked-msg (fn [db [_ from msg-id]] @@ -77,8 +82,8 @@ (signal-chat-updated db chat-id)))) (register-handler :send-chat-msg - (fn [db [_ chat-id text]] - (log/debug "chat-id" chat-id "text" text) + (fn [db [action chat-id text]] + (log/debug action "chat-id" chat-id "text" text) (let [{msg-id :msg-id {from :from to :to} :msg} (api/send-user-msg {:to chat-id @@ -130,8 +135,10 @@ (fn [db [_ value]] (contacts/load-syng-contacts db))) -;; -- Something -------------------------------------------------------------- +;; -- Chats -------------------------------------------------------------- -(register-handler :set-greeting - (fn [db [_ value]] - (assoc db :greeting value))) +(register-handler :show-chat + (fn [db [action chat-id navigator]] + (log/debug action "chat-id" chat-id) + (nav-push navigator {:view-id :chat}) + (set-current-chat-id db chat-id))) diff --git a/syng-im/src/syng_im/models/chat.cljs b/syng-im/src/syng_im/models/chat.cljs index 28a598714b..1b773d21c6 100644 --- a/syng-im/src/syng_im/models/chat.cljs +++ b/syng-im/src/syng_im/models/chat.cljs @@ -8,13 +8,13 @@ (get-in db db/current-chat-id-path)) (defn signal-chat-updated [db chat-id] - (update-in db (db/latest-msg-id-path chat-id) (fn [current] + (update-in db (db/updated-chat-signal-path chat-id) (fn [current] (if current (inc current) 0)))) -(defn latest-msg-id [db chat-id] - (->> (db/latest-msg-id-path chat-id) +(defn chat-updated? [db chat-id] + (->> (db/updated-chat-signal-path chat-id) (get-in db))) (comment diff --git a/syng-im/src/syng_im/models/chats.cljs b/syng-im/src/syng_im/models/chats.cljs new file mode 100644 index 0000000000..35bcfa9551 --- /dev/null +++ b/syng-im/src/syng_im/models/chats.cljs @@ -0,0 +1,65 @@ +(ns syng-im.models.chats + (:require [syng-im.persistence.realm :as r] + [syng-im.utils.random :refer [timestamp]] + [clojure.string :refer [join blank?]] + [syng-im.db :as db])) + +(defn signal-chats-updated [db] + (update-in db db/updated-chats-signal-path (fn [current] + (if current + (inc current) + 0)))) + +(defn chats-updated? [db] + (get-in db db/updated-chats-signal-path)) + +(defn chat-name-from-contacts [identities] + (let [chat-name (->> identities + (map (fn [identity] + (-> (r/get-by-field :contacts :whisper-identity identity) + (r/single-cljs) + :name))) + (filter identity) + (join ","))] + (when-not (blank? chat-name) + chat-name))) + +(defn get-chat-name [chat-id identities] + (or (chat-name-from-contacts identities) + chat-id)) + +(defn create-chat + ([db chat-id identities] + (create-chat db chat-id identities nil)) + ([db chat-id identities chat-name] + (when-not (r/exists? :chats :chat-id chat-id) + (let [chat-name (or chat-name + (get-chat-name chat-id identities))] + (r/write + (fn [] + (let [group-chat? (> (count identities) 1) + contacts (mapv (fn [ident] + {:identity ident}) identities)] + (r/create :chats {:chat-id chat-id + :name chat-name + :group-chat group-chat? + :timestamp (timestamp) + :contacts contacts})))) + (signal-chats-updated db))))) + +(defn chats-list [] + (-> (r/get-all :chats) + (r/sorted :timestamp :desc))) + + +(comment + + (chats-list) + (r/delete (chats-list)) + + (swap! re-frame.db/app-db signal-chats-updated) + + (create-chat "0x0479a5ed1f38cadfad1db6cd56c4b659b0ebe052bbe9efa950f6660058519fa4ca6be2dda66afa80de96ab00eb97a2605d5267a1e8f4c2a166ab551f6826608cdd" + ["0x0479a5ed1f38cadfad1db6cd56c4b659b0ebe052bbe9efa950f6660058519fa4ca6be2dda66afa80de96ab00eb97a2605d5267a1e8f4c2a166ab551f6826608cdd"]) + + ) \ No newline at end of file diff --git a/syng-im/src/syng_im/navigation.cljs b/syng-im/src/syng_im/navigation.cljs index 1842b87ca6..9c95aa81b2 100644 --- a/syng-im/src/syng_im/navigation.cljs +++ b/syng-im/src/syng_im/navigation.cljs @@ -4,14 +4,14 @@ "Flag to suppress navigator re-renders from outside om when pushing/popping." true) +(defn nav-pop [nav] + (binding [*nav-render* true] + (.pop nav))) + (defn nav-push [nav route] - (binding [*nav-render* false] + (binding [*nav-render* true] (.push nav (clj->js route)))) (defn nav-replace [nav route] - (binding [*nav-render* false] + (binding [*nav-render* true] (.replace nav (clj->js route)))) - -(defn nav-pop [nav] - (binding [*nav-render* false] - (.pop nav))) diff --git a/syng-im/src/syng_im/persistence/realm.cljs b/syng-im/src/syng_im/persistence/realm.cljs index 6e4c38c7f8..5716d46e34 100644 --- a/syng-im/src/syng_im/persistence/realm.cljs +++ b/syng-im/src/syng_im/persistence/realm.cljs @@ -6,11 +6,15 @@ (set! js/window.Realm (js/require "realm")) -(def opts {:schema [{:name "Contact" - :properties {:phone-number "string" +(def opts {:schema [{:name :contacts + :primaryKey :whisper-identity + :properties {:phone-number {:type "string" + :optional true} :whisper-identity "string" - :name "string" - :photo-path "string"}} + :name {:type "string" + :optional true} + :photo-path {:type "string" + :optinal true}}} {:name :kv-store :primaryKey :key :properties {:key "string" @@ -27,7 +31,18 @@ :indexed true} :outgoing "bool" :delivery-status {:type "string" - :optional true}}}]}) + :optional true}}} + {:name :chat-contact + :properties {:identity "string"}} + {:name :chats + :primaryKey :chat-id + :properties {:chat-id "string" + :name "string" + :group-chat "bool" + :timestamp "int" + :contacts {:type "list" + :objectType "chat-contact"}}}]}) + (def realm (js/Realm. (clj->js opts))) @@ -66,6 +81,9 @@ (-> (.objects realm (name schema-name)) (.filtered q)))) +(defn get-all [schema-name] + (.objects realm (to-string schema-name))) + (defn sorted [results field-name order] (.sorted results (to-string field-name) (if (= order :asc) false @@ -105,5 +123,4 @@ :content "sdfd" :delivery-status "seen"}) true)) - ) \ No newline at end of file diff --git a/syng-im/src/syng_im/resources.cljs b/syng-im/src/syng_im/resources.cljs index 19a0c2ecb7..8b9925fa75 100644 --- a/syng-im/src/syng_im/resources.cljs +++ b/syng-im/src/syng_im/resources.cljs @@ -7,6 +7,7 @@ (def seen-icon (js/require "./images/seen.png")) (def delivered-icon (js/require "./images/delivered.png")) (def delivery-failed-icon (js/require "./images/deliveryfailed.png")) +(def chat-icon (js/require "./images/chat.png")) (def play (js/require "./images/play.png")) (def mic (js/require "./images/mic.png")) (def smile (js/require "./images/smile.png")) diff --git a/syng-im/src/syng_im/subs.cljs b/syng-im/src/syng_im/subs.cljs index 95b53877ae..d3a9b30b5e 100644 --- a/syng-im/src/syng_im/subs.cljs +++ b/syng-im/src/syng_im/subs.cljs @@ -2,29 +2,40 @@ (:require-macros [reagent.ratom :refer [reaction]]) (:require [re-frame.core :refer [register-sub]] [syng-im.models.chat :refer [current-chat-id - latest-msg-id]] + chat-updated?]] + [syng-im.models.chats :refer [chats-list + chats-updated?]] [syng-im.models.messages :refer [get-messages]])) -(register-sub :get-greeting (fn [db _] - (reaction - (get @db :greeting)))) +;; -- Chat -------------------------------------------------------------- (register-sub :get-chat-messages - (fn [db _] - (let [chat-id (-> (current-chat-id @db) - (reaction)) - latest-msg (-> (latest-msg-id @db @chat-id) - (reaction))] - ;; latest-msg signals us that a new message has been added - (reaction - (let [_ @latest-msg] - (get-messages @chat-id)))))) + (fn [db _] + (let [chat-id (-> (current-chat-id @db) + (reaction)) + chat-updated (-> (chat-updated? @db @chat-id) + (reaction))] + (reaction + (let [_ @chat-updated] + (get-messages @chat-id)))))) -(register-sub :get-current-chat-id (fn [db _] - (-> (current-chat-id @db) - (reaction)))) +(register-sub :get-current-chat-id + (fn [db _] + (-> (current-chat-id @db) + (reaction)))) + +;; -- Chats list -------------------------------------------------------------- + +(register-sub :get-chats + (fn [db _] + (let [chats-updated (-> (chats-updated? @db) + (reaction))] + (reaction + (let [_ @chats-updated] + (chats-list)))))) ;; -- User data -------------------------------------------------------------- + (register-sub :get-user-phone-number (fn [db _]