add participant to group chat

Former-commit-id: 19547048c3
This commit is contained in:
michaelr 2016-04-07 14:21:02 +03:00
parent c1144e1d68
commit b2d5321104
15 changed files with 210 additions and 31 deletions

BIN
images/add.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -13,6 +13,7 @@
[syng-im.components.sign-up-confirm :refer [sign-up-confirm-view]] [syng-im.components.sign-up-confirm :refer [sign-up-confirm-view]]
[syng-im.components.chats.chats-list :refer [chats-list]] [syng-im.components.chats.chats-list :refer [chats-list]]
[syng-im.components.chats.new-group :refer [new-group]] [syng-im.components.chats.new-group :refer [new-group]]
[syng-im.components.chat.new-participants :refer [new-participants]]
[syng-im.utils.logging :as log] [syng-im.utils.logging :as log]
[syng-im.navigation :as nav] [syng-im.navigation :as nav]
[syng-im.utils.encryption])) [syng-im.utils.encryption]))
@ -42,6 +43,7 @@
view-id (keyword view-id)] view-id (keyword view-id)]
(init-back-button-handler! nav) (init-back-button-handler! nav)
(case view-id (case view-id
:add-participants (r/as-element [new-participants {:navigator nav}])
:chat-list (r/as-element [chats-list {:navigator nav}]) :chat-list (r/as-element [chats-list {:navigator nav}])
:new-group (r/as-element [new-group {:navigator nav}]) :new-group (r/as-element [new-group {:navigator nav}])
:contact-list (r/as-element [contact-list {:navigator nav}]) :contact-list (r/as-element [contact-list {:navigator nav}])

View File

@ -45,18 +45,24 @@
:backgroundColor "#eef2f5"}} :backgroundColor "#eef2f5"}}
(when android? (when android?
;; TODO add IOS version ;; TODO add IOS version
[toolbar-android {:logo res/logo-icon [toolbar-android {:logo res/logo-icon
:title (or (@chat :name) :title (or (@chat :name)
"Chat name") "Chat name")
:titleColor "#4A5258" :titleColor "#4A5258"
:subtitle "Last seen just now" :subtitle "Last seen just now"
:subtitleColor "#AAB2B2" :subtitleColor "#AAB2B2"
:navIcon res/nav-back-icon :navIcon res/nav-back-icon
:style {:backgroundColor "white" :style {:backgroundColor "white"
:height 56 :height 56
:elevation 2} :elevation 2}
:onIconClicked (fn [] :actions [{:title "Add Contact to chat"
(nav-pop navigator))}]) :icon res/add-icon
:showWithText true}]
:onActionSelected (fn [position]
(case position
0 (dispatch [:show-add-participants navigator])))
:onIconClicked (fn []
(nav-pop navigator))}])
[list-view {:dataSource datasource [list-view {:dataSource datasource
:renderScrollComponent (fn [props] :renderScrollComponent (fn [props]
(invertible-scroll-view nil)) (invertible-scroll-view nil))

View File

@ -0,0 +1,36 @@
(ns syng-im.components.chat.new-participants
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
[syng-im.resources :as res]
[syng-im.components.react :refer [view toolbar-android android? text-input]]
[syng-im.components.realm :refer [list-view]]
[syng-im.utils.listview :refer [to-realm-datasource]]
[syng-im.components.chats.new-participant-contact :refer [new-participant-contact]]
[reagent.core :as r]
[syng-im.navigation :refer [nav-pop]]))
(defn new-participants [{:keys [navigator]}]
(let [contacts (subscribe [:all-new-contacts])]
(fn []
(let [contacts-ds (to-realm-datasource @contacts)]
[view {:style {:flex 1
:backgroundColor "white"}}
(when android?
;; TODO add IOS version
[toolbar-android {:logo res/logo-icon
:title "Add Participants"
:titleColor "#4A5258"
:style {:backgroundColor "white"
:height 56
:elevation 2}
:actions [{:title "Add"
:icon res/v
:show "always"}]
:onActionSelected (fn [position]
(dispatch [:add-new-participants navigator]))
:navIcon res/nav-back-icon
:onIconClicked (fn []
(nav-pop navigator))}])
[list-view {:dataSource contacts-ds
:renderRow (fn [row section-id row-id]
(r/as-element [new-participant-contact (js->clj row :keywordize-keys true) navigator]))
:style {:backgroundColor "white"}}]]))))

View File

@ -0,0 +1,24 @@
(ns syng-im.components.chats.new-participant-contact
(:require [syng-im.resources :as res]
[re-frame.core :refer [subscribe dispatch dispatch-sync]]
[syng-im.components.react :refer [view]]
[syng-im.components.contact-list.contact-inner :refer [contact-inner-view]]
[syng-im.components.item-checkbox :refer [item-checkbox]]
[syng-im.utils.logging :as log]
[reagent.core :as r]))
(defn new-participant-contact [{:keys [whisper-identity] :as contact} navigator]
(let [checked (r/atom false)]
(fn []
[view {:style {:flexDirection "row"
:marginTop 5
:marginBottom 5
:paddingLeft 15
:paddingRight 15
:height 75}}
[item-checkbox {:onToggle (fn [checked?]
(reset! checked checked?)
(dispatch [:select-new-participant whisper-identity checked?]))
:checked @checked
:size 30}]
[contact-inner-view contact]])))

View File

@ -9,10 +9,11 @@
:identity-password "replace-me-with-user-entered-password" :identity-password "replace-me-with-user-entered-password"
:contacts [] :contacts []
:chat {:current-chat-id "0x0479a5ed1f38cadfad1db6cd56c4b659b0ebe052bbe9efa950f6660058519fa4ca6be2dda66afa80de96ab00eb97a2605d5267a1e8f4c2a166ab551f6826608cdd" :chat {:current-chat-id "0x0479a5ed1f38cadfad1db6cd56c4b659b0ebe052bbe9efa950f6660058519fa4ca6be2dda66afa80de96ab00eb97a2605d5267a1e8f4c2a166ab551f6826608cdd"
:command nil} :command nil}
:chats {} :chats {}
:chats-updated-signal 0 :chats-updated-signal 0
:new-group #{}}) :new-group #{}
:new-participants #{}})
(def protocol-initialized-path [:protocol-initialized]) (def protocol-initialized-path [:protocol-initialized])
@ -28,3 +29,4 @@
(defn chat-command-content-path [chat-id] (defn chat-command-content-path [chat-id]
[:chats chat-id :command-input :content]) [:chats chat-id :command-input :content])
(def new-group-path [:new-group]) (def new-group-path [:new-group])
(def new-participants-path [:new-participants])

View File

@ -18,13 +18,18 @@
set-chat-command-content]] set-chat-command-content]]
[syng-im.handlers.sign-up :as sign-up-service] [syng-im.handlers.sign-up :as sign-up-service]
[syng-im.models.chats :refer [create-chat]] [syng-im.models.chats :refer [create-chat
chat-add-participants]]
[syng-im.models.chat :refer [signal-chat-updated [syng-im.models.chat :refer [signal-chat-updated
set-current-chat-id set-current-chat-id
current-chat-id
update-new-group-selection update-new-group-selection
update-new-participants-selection
clear-new-group clear-new-group
clear-new-participants
new-group-selection new-group-selection
set-chat-input-text]] set-chat-input-text
new-participants-selection]]
[syng-im.utils.logging :as log] [syng-im.utils.logging :as log]
[syng-im.protocol.api :as api] [syng-im.protocol.api :as api]
[syng-im.constants :refer [text-content-type]] [syng-im.constants :refer [text-content-type]]
@ -124,13 +129,15 @@
(signal-chat-updated db group-id))) (signal-chat-updated db group-id)))
(register-handler :acked-msg (register-handler :acked-msg
(fn [db [_ from msg-id]] (fn [db [action from msg-id]]
(log/debug action from msg-id)
(update-message! {:msg-id msg-id (update-message! {:msg-id msg-id
:delivery-status :delivered}) :delivery-status :delivered})
(signal-chat-updated db from))) (signal-chat-updated db from)))
(register-handler :msg-delivery-failed (register-handler :msg-delivery-failed
(fn [db [_ msg-id]] (fn [db [action msg-id]]
(log/debug action from msg-id)
(update-message! {:msg-id msg-id (update-message! {:msg-id msg-id
:delivery-status :failed}) :delivery-status :failed})
(let [{:keys [chat-id]} (message-by-id msg-id)] (let [{:keys [chat-id]} (message-by-id msg-id)]
@ -261,6 +268,29 @@
(nav-push navigator {:view-id :contact-list}) (nav-push navigator {:view-id :contact-list})
db)) db))
(register-handler :select-new-participant
(fn [db [action identity add?]]
(log/debug action identity add?)
(update-new-participants-selection db identity add?)))
(register-handler :show-add-participants
(fn [db [action navigator]]
(log/debug action)
(nav-push navigator {:view-id :add-participants})
(clear-new-participants db)))
(register-handler :add-new-participants
(fn [db [action navigator]]
(log/debug action)
(let [identities (-> (new-participants-selection db)
(vec))
chat-id (current-chat-id db)]
(chat-add-participants chat-id identities)
(dispatch [:show-chat chat-id navigator])
(doseq [ident identities]
(api/group-add-participant chat-id ident))
db)))
(register-handler :show-group-new (register-handler :show-group-new
(fn [db [action navigator]] (fn [db [action navigator]]
(log/debug action) (log/debug action)

View File

@ -19,9 +19,15 @@
(defn update-new-group-selection [db identity add?] (defn update-new-group-selection [db identity add?]
(update-in db db/new-group-path (fn [new-group] (update-in db db/new-group-path (fn [new-group]
(if add? (if add?
(conj new-group identity) (conj new-group identity)
(disj new-group identity))))) (disj new-group identity)))))
(defn update-new-participants-selection [db identity add?]
(update-in db db/new-participants-path (fn [new-participants]
(if add?
(conj new-participants identity)
(disj new-participants identity)))))
(defn new-group-selection [db] (defn new-group-selection [db]
(get-in db db/new-group-path)) (get-in db db/new-group-path))
@ -29,6 +35,12 @@
(defn clear-new-group [db] (defn clear-new-group [db]
(assoc-in db db/new-group-path #{})) (assoc-in db db/new-group-path #{}))
(defn new-participants-selection [db]
(get-in db db/new-participants-path))
(defn clear-new-participants [db]
(assoc-in db db/new-participants-path #{}))
(defn set-chat-input-text [db text] (defn set-chat-input-text [db text]
(assoc-in db (db/chat-input-text-path (current-chat-id db)) text)) (assoc-in db (db/chat-input-text-path (current-chat-id db)) text))
@ -37,5 +49,5 @@
(swap! re-frame.db/app-db (fn [db] (swap! re-frame.db/app-db (fn [db]
(signal-chat-updated db "0x0479a5ed1f38cadfad1db6cd56c4b659b0ebe052bbe9efa950f6660058519fa4ca6be2dda66afa80de96ab00eb97a2605d5267a1e8f4c2a166ab551f6826608cdd"))) (signal-chat-updated db "0x0479a5ed1f38cadfad1db6cd56c4b659b0ebe052bbe9efa950f6660058519fa4ca6be2dda66afa80de96ab00eb97a2605d5267a1e8f4c2a166ab551f6826608cdd")))
(current-chat-id @re-frame.db/app-db) (current-chat-id @re-frame.db/app-db)
) )

View File

@ -61,8 +61,30 @@
(r/single-cljs) (r/single-cljs)
(r/list-to-array :contacts))) (r/list-to-array :contacts)))
(comment (defn chat-add-participants [chat-id identities]
(r/write
(fn []
(let [chat (-> (r/get-by-field :chats :chat-id chat-id)
(r/single))
contacts (aget chat "contacts")
contacts-count (aget contacts "length")
colors (drop contacts-count group-chat-colors)
new-contacts (mapv (fn [ident {:keys [background text]}]
{:identity ident
:background-color background
:text-color text}) identities colors)]
(doseq [contact new-contacts]
(.push contacts (clj->js contact)))))))
(defn active-group-chats []
(let [results (-> (r/get-all :chats)
(r/filtered "group-chat = true"))]
(->> (.map results (fn [object index collection]
(aget object "chat-id")))
(js->clj))))
(comment
(active-group-chats)
(-> (r/get-by-field :chats :chat-id "0x04ed4c3797026cddeb7d64a54ca58142e57ea03cda21072358d67455b506db90c56d95033e3d221992f70d01922c3d90bf0697c49e4be118443d03ae4a1cd3c15c") (-> (r/get-by-field :chats :chat-id "0x04ed4c3797026cddeb7d64a54ca58142e57ea03cda21072358d67455b506db90c56d95033e3d221992f70d01922c3d90bf0697c49e4be118443d03ae4a1cd3c15c")

View File

@ -3,7 +3,8 @@
[re-frame.core :refer [subscribe dispatch dispatch-sync]] [re-frame.core :refer [subscribe dispatch dispatch-sync]]
[syng-im.utils.utils :refer [log toast]] [syng-im.utils.utils :refer [log toast]]
[syng-im.persistence.realm :as realm] [syng-im.persistence.realm :as realm]
[syng-im.persistence.realm :as r])) [syng-im.persistence.realm :as r]
[clojure.string :as s]))
;; TODO see https://github.com/rt2zz/react-native-contacts/issues/45 ;; TODO see https://github.com/rt2zz/react-native-contacts/issues/45
(def fake-phone-contacts? true) (def fake-phone-contacts? true)
@ -91,6 +92,15 @@
(-> (r/get-all :contacts) (-> (r/get-all :contacts)
(r/sorted :name :asc))) (r/sorted :name :asc)))
(defn contacts-list-exclude [exclude-idents]
(let [exclude-query (->> exclude-idents
(map (fn [ident]
(str "whisper-identity != '" ident "'")))
(s/join " && "))]
(-> (r/get-all :contacts)
(r/filtered exclude-query)
(r/sorted :name :asc))))
(defn contatct-by-identity [identity] (defn contatct-by-identity [identity]
(-> (r/get-by-field :contacts :whisper-identity identity) (-> (r/get-by-field :contacts :whisper-identity identity)
(r/single-cljs))) (r/single-cljs)))
@ -98,15 +108,25 @@
(comment (comment
(r/write #(create-contact {:phone-number "0543072333" (r/write #(create-contact {:phone-number "0543072333"
:whisper-identity "0x04ed4c3797026cddeb7d64a54ca58142e57ea03cda21072358d67455b506db90c56d95033e3d221992f70d01922c3d90bf0697c49e4be118443d03ae4a1cd3c15c" :whisper-identity "0x045326d94f999c3e17d11d6a09e13cf9a34b30f62687a970053887b8f8d1fefac3f185e6ba6f02f8217b6947227c7dbfa8d4ee3a4a2864b0cac9968c819ba9c17c"
:name "Mr. Bean" :name "Mr. Bean"
:photo-path ""})) :photo-path ""}))
(r/write #(create-contact {:phone-number "0544828649" (r/write #(create-contact {:phone-number "0544828649"
:whisper-identity "0x0498bcce41dbe05c6d4776ef50d12c2ef1a00d9d7f7144d174ece3dce85ca3428bf0900352abcccdc463bd2cfa4ec319cda46c2079152c4cb14d1cad9a00dd7571" :whisper-identity "0x0496152b9bb6640fddb3a156747baa961792762264ab459c8d7c9ac179ddceb2abb701357a9b6691ef858ef8ce2eb306754825b8267add96e7a01d854a576b9fda"
:name "Mr. Batman" :name "Mr. Batman"
:photo-path ""})) :photo-path ""}))
(r/write #(create-contact {:phone-number "0522222222"
:whisper-identity "0x04ef1f45303a14cb30af0458fabc2c7b1fd1bc9bb69fae5bb4ec63ae5b52cd53c2a194398b6674cc335899bbf6a24bfe48813a134ce9b8b85877690c1a99f0b19f"
:name "Mr. Eagle"
:photo-path ""}))
(r/write #(create-contact {:phone-number "0533333333"
:whisper-identity "0x04e9e31a92ab16dbac9cdce47f59545ac646175087f4eeb6e4442e36d37b09e20dcb6239ac743ddd3f1fa17377c1789dc60a1ebb8774d247f3df69ebd0082d46fe"
:name "Mr. PiggyBear"
:photo-path ""}))
(contacts-list) (contacts-list)
(:new-group @re-frame.db/app-db) (:new-group @re-frame.db/app-db)

View File

@ -7,6 +7,7 @@
(defn save-message [chat-id {:keys [from to msg-id content content-type outgoing] :or {outgoing false (defn save-message [chat-id {:keys [from to msg-id content content-type outgoing] :or {outgoing false
to nil} :as msg}] to nil} :as msg}]
(log/debug "save-message" chat-id msg)
(when-not (r/exists? :msgs :msg-id msg-id) (when-not (r/exists? :msgs :msg-id msg-id)
(r/write (r/write
(fn [] (fn []
@ -28,10 +29,12 @@
(-> (r/get-by-field :msgs :msg-id msg-id) (-> (r/get-by-field :msgs :msg-id msg-id)
(r/single-cljs))) (r/single-cljs)))
(defn update-message! [msg] (defn update-message! [{:keys [msg-id] :as msg}]
(log/debug "update-message!" msg)
(r/write (r/write
(fn [] (fn []
(r/create :msgs msg true)))) (when (r/exists? :msgs :msg-id msg-id)
(r/create :msgs msg true)))))
(comment (comment

View File

@ -41,7 +41,8 @@
:primaryKey :chat-id :primaryKey :chat-id
:properties {:chat-id "string" :properties {:chat-id "string"
:name "string" :name "string"
:group-chat "bool" :group-chat {:type "bool"
:indexed true}
:timestamp "int" :timestamp "int"
:contacts {:type "list" :contacts {:type "list"
:objectType "chat-contact"}}}]}) :objectType "chat-contact"}}}]})
@ -92,6 +93,9 @@
false false
true))) true)))
(defn filtered [results filter-query]
(.filtered results filter-query))
(defn page [results from to] (defn page [results from to]
(js/Array.prototype.slice.call results from to)) (js/Array.prototype.slice.call results from to))

View File

@ -3,12 +3,14 @@
[syng-im.constants :refer [ethereum-rpc-url]] [syng-im.constants :refer [ethereum-rpc-url]]
[re-frame.core :refer [dispatch]] [re-frame.core :refer [dispatch]]
[syng-im.models.protocol :refer [stored-identity]] [syng-im.models.protocol :refer [stored-identity]]
[syng-im.persistence.simple-kv-store :as kv])) [syng-im.persistence.simple-kv-store :as kv]
[syng-im.models.chats :refer [active-group-chats]]))
(defn make-handler [db] (defn make-handler [db]
{:ethereum-rpc-url ethereum-rpc-url {:ethereum-rpc-url ethereum-rpc-url
:identity (stored-identity db) :identity (stored-identity db)
:active-group-ids (active-group-chats)
:storage kv/kv-store :storage kv/kv-store
:handler (fn [{:keys [event-type] :as event}] :handler (fn [{:keys [event-type] :as event}]
(log/info "Event:" (clj->js event)) (log/info "Event:" (clj->js event))

View File

@ -13,3 +13,4 @@
(def smile (js/require "./images/smile.png")) (def smile (js/require "./images/smile.png"))
(def att (js/require "./images/att.png")) (def att (js/require "./images/att.png"))
(def v (js/require "./images/v.png")) (def v (js/require "./images/v.png"))
(def add-icon (js/require "./images/add.png"))

View File

@ -8,7 +8,8 @@
chats-updated? chats-updated?
chat-by-id]] chat-by-id]]
[syng-im.models.messages :refer [get-messages]] [syng-im.models.messages :refer [get-messages]]
[syng-im.models.contacts :refer [contacts-list]] [syng-im.models.contacts :refer [contacts-list
contacts-list-exclude]]
[syng-im.handlers.suggestions :refer [get-suggestions]])) [syng-im.handlers.suggestions :refer [get-suggestions]]))
;; -- Chat -------------------------------------------------------------- ;; -- Chat --------------------------------------------------------------
@ -99,3 +100,17 @@
(fn [db _] (fn [db _]
(reaction (reaction
(contacts-list)))) (contacts-list))))
(register-sub :all-new-contacts
(fn [db _]
(let [current-chat-id (-> (current-chat-id @db)
(reaction))
chat (-> (when-let [chat-id @current-chat-id]
(chat-by-id chat-id))
(reaction))]
(reaction
(when @chat
(let [current-participants (->> @chat
:contacts
(map :identity))]
(contacts-list-exclude current-participants)))))))