Merge pull request #16 from syng-im/group-chat-2-rebased

group chat actions and events implementation
This commit is contained in:
Jarrad 2016-04-19 16:24:59 +07:00
commit 891ac48d09
22 changed files with 507 additions and 109 deletions

BIN
images/add.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
images/leave.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
images/trash.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -13,6 +13,8 @@
[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.components.chat.remove-participants :refer [remove-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 +44,8 @@
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}])
:remove-participants (r/as-element [remove-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

@ -25,9 +25,12 @@
(into {}))) (into {})))
(defn add-msg-color [{:keys [from] :as msg} contact-by-identity] (defn add-msg-color [{:keys [from] :as msg} contact-by-identity]
(let [{:keys [text-color background-color]} (get contact-by-identity from)] (if (= "system" from)
(assoc msg :text-color text-color (assoc msg :text-color "#4A5258"
:background-color background-color))) :background-color "#D3EEEF")
(let [{:keys [text-color background-color]} (get contact-by-identity from)]
(assoc msg :text-color text-color
:background-color background-color))))
(defn chat [{:keys [navigator]}] (defn chat [{:keys [navigator]}]
(let [messages (subscribe [:get-chat-messages]) (let [messages (subscribe [:get-chat-messages])
@ -42,18 +45,34 @@
: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 (when (and (:group-chat @chat)
(nav-pop navigator))}]) (:is-active @chat))
[{:title "Add Contact to chat"
:icon res/add-icon
:showWithText true}
{:title "Remove Contact from chat"
:icon res/trash-icon
:showWithText true}
{:title "Leave Chat"
:icon res/leave-icon
:showWithText true}])
:onActionSelected (fn [position]
(case position
0 (dispatch [:show-add-participants navigator])
1 (dispatch [:show-remove-participants navigator])
2 (dispatch [:leave-group-chat 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))
@ -62,4 +81,5 @@
(add-msg-color contact-by-identity))] (add-msg-color contact-by-identity))]
(r/as-element [chat-message msg]))) (r/as-element [chat-message msg])))
:style {:backgroundColor "white"}}] :style {:backgroundColor "white"}}]
[chat-message-new]])))) (when (:is-active @chat)
[chat-message-new])]))))

View File

@ -86,28 +86,27 @@
content)]])) content)]]))
(defn message-content [{:keys [content-type content outgoing text-color background-color]}] (defn message-content [{:keys [content-type content outgoing text-color background-color]}]
(let [_ (log/debug color)] [view {:style (merge {:borderRadius 6}
[view {:style (merge {:borderRadius 6} (if (= content-type text-content-type)
(if (= content-type text-content-type) {:paddingVertical 12
{:paddingVertical 12 :paddingHorizontal 16}
:paddingHorizontal 16} {:paddingVertical 14
{:paddingVertical 14 :paddingHorizontal 10})
:paddingHorizontal 10}) (if outgoing
(if outgoing {:backgroundColor "#D3EEEF"}
{:backgroundColor "#D3EEEF"} {:backgroundColor background-color}))}
{:backgroundColor background-color}))} (cond
(cond (= content-type text-content-type)
(= content-type text-content-type) [text {:style (merge {:fontSize 14
[text {:style (merge {:fontSize 14 :fontFamily "Avenir-Roman"}
:fontFamily "Avenir-Roman"} (if outgoing
(if outgoing {:color "#4A5258"}
{:color "#4A5258"} {:color text-color}))}
{:color text-color}))} content]
content] (= content-type content-type-command)
(= content-type content-type-command) [message-content-command content]
[message-content-command content] :else [message-content-audio {:content content
:else [message-content-audio {:content content :content-type content-type}])])
:content-type content-type}])]))
(defn message-delivery-status [{:keys [delivery-status]}] (defn message-delivery-status [{:keys [delivery-status]}]
[view {:style {:flexDirection "row" [view {:style {:flexDirection "row"

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,36 @@
(ns syng-im.components.chat.remove-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 remove-participants [{:keys [navigator]}]
(let [contacts (subscribe [:current-chat-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 "Remove Participants"
:titleColor "#4A5258"
:style {:backgroundColor "white"
:height 56
:elevation 2}
:actions [{:title "Remove"
:icon res/trash-icon
:show "always"}]
:onActionSelected (fn [position]
(dispatch [:remove-selected-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

@ -9,7 +9,7 @@
(defn chat-list-item [chat-obj navigator] (defn chat-list-item [chat-obj navigator]
[touchable-highlight {:on-press (fn [] [touchable-highlight {:on-press (fn []
(dispatch [:show-chat (aget chat-obj "chat-id") navigator]))} (dispatch [:show-chat (aget chat-obj "chat-id") navigator :push]))}
[view {:style {:flexDirection "row" [view {:style {:flexDirection "row"
:width 260 :width 260
:marginVertical 5}} :marginVertical 5}}

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,18 +18,28 @@
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
chat-remove-participants
set-chat-active]]
[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]]
[syng-im.navigation :refer [nav-push]] [syng-im.navigation :refer [nav-push
[syng-im.utils.crypt :refer [gen-random-bytes]])) nav-replace
nav-pop]]
[syng-im.utils.crypt :refer [gen-random-bytes]]
[syng-im.utils.random :as random]))
;; -- Middleware ------------------------------------------------------------ ;; -- Middleware ------------------------------------------------------------
;; ;;
@ -77,9 +87,11 @@
db)) db))
(register-handler :navigate-to (register-handler :navigate-to
(fn [db [action navigator route]] (fn [db [action navigator route nav-type]]
(log/debug action route) (log/debug action route)
(nav-push navigator route) (case nav-type
:push (nav-push navigator route)
:replace (nav-replace navigator route))
db)) db))
;; -- Protocol -------------------------------------------------------------- ;; -- Protocol --------------------------------------------------------------
@ -110,14 +122,98 @@
(save-message chat-id msg) (save-message chat-id msg)
(signal-chat-updated db chat-id))) (signal-chat-updated db chat-id)))
(defn joined-chat-msg [chat-id from msg-id]
(let [contact-name (:name (contacts/contact-by-identity from))]
(save-message chat-id {:from "system"
:msg-id msg-id
:content (str (or contact-name from) " received chat invitation")
:content-type text-content-type})))
(defn participant-invited-to-group-msg [chat-id identity from msg-id]
(let [inviter-name (:name (contacts/contact-by-identity from))
invitee-name (:name (contacts/contact-by-identity identity))]
(save-message chat-id {:from "system"
:msg-id msg-id
:content (str (or inviter-name from) " invited " (or invitee-name identity))
:content-type text-content-type})))
(defn participant-removed-from-group-msg [chat-id identity from msg-id]
(let [remover-name (:name (contacts/contact-by-identity from))
removed-name (:name (contacts/contact-by-identity identity))]
(save-message chat-id {:from "system"
:msg-id msg-id
:content (str (or remover-name from) " removed " (or removed-name identity))
:content-type text-content-type})))
(defn you-removed-from-group-msg [chat-id from msg-id]
(let [remover-name (:name (contacts/contact-by-identity from))]
(save-message chat-id {:from "system"
:msg-id msg-id
:content (str (or remover-name from) " removed you from group chat")
:content-type text-content-type})))
(defn participant-left-group-msg [chat-id from msg-id]
(let [left-name (:name (contacts/contact-by-identity from))]
(save-message chat-id {:from "system"
:msg-id msg-id
:content (str (or left-name from) " left")
:content-type text-content-type})))
(defn removed-participant-msg [chat-id identity]
(let [contact-name (:name (contacts/contact-by-identity identity))]
(save-message chat-id {:from "system"
:msg-id (random/id)
:content (str "You've removed " (or contact-name identity))
:content-type text-content-type})))
(defn left-chat-msg [chat-id]
(save-message chat-id {:from "system"
:msg-id (random/id)
:content "You left this chat"
:content-type text-content-type}))
(register-handler :group-chat-invite-acked
(fn [db [action from group-id ack-msg-id]]
(log/debug action from group-id ack-msg-id)
(joined-chat-msg group-id from ack-msg-id)
(signal-chat-updated db group-id)))
(register-handler :participant-removed-from-group
(fn [db [action from group-id identity msg-id]]
(log/debug action msg-id from group-id identity)
(chat-remove-participants group-id [identity])
(participant-removed-from-group-msg group-id identity from msg-id)
(signal-chat-updated db group-id)))
(register-handler :you-removed-from-group
(fn [db [action from group-id msg-id]]
(log/debug action msg-id from group-id)
(you-removed-from-group-msg group-id from msg-id)
(set-chat-active group-id false)
(signal-chat-updated db group-id)))
(register-handler :participant-left-group
(fn [db [action from group-id msg-id]]
(log/debug action msg-id from group-id)
(participant-left-group-msg group-id from msg-id)
(signal-chat-updated db group-id)))
(register-handler :participant-invited-to-group
(fn [db [action from group-id identity msg-id]]
(log/debug action msg-id from group-id identity)
(participant-invited-to-group-msg group-id identity from msg-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 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)]
@ -128,7 +224,7 @@
(log/debug action "chat-id" chat-id "text" text) (log/debug action "chat-id" chat-id "text" text)
(let [msg (if (= chat-id "console") (let [msg (if (= chat-id "console")
(sign-up-service/send-console-msg text) (sign-up-service/send-console-msg text)
(let [{msg-id :msg-id (let [{msg-id :msg-id
{from :from {from :from
to :to} :msg} (api/send-user-msg {:to chat-id to :to} :msg} (api/send-user-msg {:to chat-id
:content text})] :content text})]
@ -141,13 +237,22 @@
(save-message chat-id msg) (save-message chat-id msg)
(signal-chat-updated db chat-id)))) (signal-chat-updated db chat-id))))
(register-handler :leave-group-chat
(fn [db [action navigator]]
(log/debug action)
(let [chat-id (current-chat-id db)]
(api/leave-group-chat chat-id)
(set-chat-active chat-id false)
(left-chat-msg chat-id)
(signal-chat-updated db chat-id))))
(register-handler :send-chat-command (register-handler :send-chat-command
(fn [db [action chat-id command content]] (fn [db [action chat-id command content]]
(log/debug action "chat-id" chat-id "command" command "content" content) (log/debug action "chat-id" chat-id "command" command "content" content)
(let [msg (if (= chat-id "console") (let [msg (if (= chat-id "console")
(sign-up-service/send-console-command command content) (sign-up-service/send-console-command command content)
;; TODO handle command, now sends as plain message ;; TODO handle command, now sends as plain message
(let [{msg-id :msg-id (let [{msg-id :msg-id
{from :from {from :from
to :to} :msg} (api/send-user-msg {:to chat-id to :to} :msg} (api/send-user-msg {:to chat-id
:content content})] :content content})]
@ -216,10 +321,10 @@
;; -- Chats -------------------------------------------------------------- ;; -- Chats --------------------------------------------------------------
(register-handler :show-chat (register-handler :show-chat
(fn [db [action chat-id navigator]] (fn [db [action chat-id navigator nav-type]]
(log/debug action "chat-id" chat-id) (log/debug action "chat-id" chat-id)
(let [db (set-current-chat-id db chat-id)] (let [db (set-current-chat-id db chat-id)]
(dispatch [:navigate-to navigator {:view-id :chat}]) (dispatch [:navigate-to navigator {:view-id :chat} nav-type])
db))) db)))
(register-handler :set-sign-up-chat (register-handler :set-sign-up-chat
@ -248,6 +353,48 @@
(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-remove-participants
(fn [db [action navigator]]
(log/debug action)
(nav-push navigator {:view-id :remove-participants})
(clear-new-participants db)))
(register-handler :remove-selected-participants
(fn [db [action navigator]]
(log/debug action)
(let [identities (-> (new-participants-selection db)
(vec))
chat-id (current-chat-id db)]
(chat-remove-participants chat-id identities)
(nav-pop navigator)
(doseq [ident identities]
(api/group-remove-participant chat-id ident)
(removed-participant-msg chat-id ident))
(signal-chat-updated db chat-id))))
(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)
(nav-pop 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)
@ -266,7 +413,7 @@
(vec)) (vec))
group-id (api/start-group-chat identities group-name) group-id (api/start-group-chat identities group-name)
db (create-chat db group-id identities true group-name)] db (create-chat db group-id identities true group-name)]
(dispatch [:show-chat group-id navigator]) (dispatch [:show-chat group-id navigator :replace])
db))) db)))
(register-handler :group-chat-invite-received (register-handler :group-chat-invite-received

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

@ -4,7 +4,8 @@
[clojure.string :refer [join blank?]] [clojure.string :refer [join blank?]]
[syng-im.db :as db] [syng-im.db :as db]
[syng-im.utils.logging :as log] [syng-im.utils.logging :as log]
[syng-im.constants :refer [group-chat-colors]])) [syng-im.constants :refer [group-chat-colors]]
[syng-im.persistence.realm-queries :refer [include-query]]))
(defn signal-chats-updated [db] (defn signal-chats-updated [db]
(update-in db db/updated-chats-signal-path (fn [current] (update-in db db/updated-chats-signal-path (fn [current]
@ -46,6 +47,7 @@
:background-color background :background-color background
:text-color text}) identities group-chat-colors)] :text-color text}) identities group-chat-colors)]
(r/create :chats {:chat-id chat-id (r/create :chats {:chat-id chat-id
:is-active true
:name chat-name :name chat-name
:group-chat group-chat? :group-chat group-chat?
:timestamp (timestamp) :timestamp (timestamp)
@ -61,8 +63,52 @@
(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 [contacts (-> (r/get-by-field :chats :chat-id chat-id)
(r/single)
(aget "contacts"))
colors-in-use (->> (.map contacts (fn [object index collection]
{:text-color (aget object "text-color")
:background-color (aget object "background-color")}))
(set))
colors (->> group-chat-colors
(filter (fn [color]
(not (contains? colors-in-use color)))))
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 chat-remove-participants [chat-id identities]
(r/write
(fn []
(let [query (include-query :identity identities)
chat (-> (r/get-by-field :chats :chat-id chat-id)
(r/single))]
(-> (aget chat "contacts")
(r/filtered query)
(r/delete))))))
(defn active-group-chats []
(let [results (-> (r/get-all :chats)
(r/filtered "group-chat = true && is-active = true"))]
(->> (.map results (fn [object index collection]
(aget object "chat-id")))
(js->clj))))
(defn set-chat-active [chat-id active?]
(r/write (fn []
(-> (r/get-by-field :chats :chat-id chat-id)
(r/single)
(aset "is-active" active?)))))
(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,10 @@
[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]
[syng-im.persistence.realm-queries :refer [include-query
exclude-query]]
[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,18 +94,44 @@
(-> (r/get-all :contacts) (-> (r/get-all :contacts)
(r/sorted :name :asc))) (r/sorted :name :asc)))
(defn contacts-list-exclude [exclude-idents]
(let [query (exclude-query :whisper-identity exclude-idents)]
(-> (r/get-all :contacts)
(r/filtered query)
(r/sorted :name :asc))))
(defn contacts-list-include [include-indents]
(let [query (include-query :whisper-identity include-indents)]
(-> (r/get-all :contacts)
(r/filtered query)
(r/sorted :name :asc))))
(defn contact-by-identity [identity]
(-> (r/get-by-field :contacts :whisper-identity identity)
(r/single-cljs)))
(comment (comment
(r/write #(create-contact {:phone-number "0543072333" (r/write #(create-contact {:phone-number "0543072333"
:whisper-identity "0x04ed4c3797026cddeb7d64a54ca58142e57ea03cda21072358d67455b506db90c56d95033e3d221992f70d01922c3d90bf0697c49e4be118443d03ae4a1cd3c15c" :whisper-identity "0x043e3a8344049fb48fef030084212a9d41577a5dea18aeb4c8f285c16f783aa84e43f84c32eb8601e22827b12d5f93f14e545f9023034a0521dc18484bbbc44704"
: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 "0x04e9b01298dd12c4d8f0393d7890302b25762966d825158d1fdffe124703c0efcd7f23a6cf71c466ca50b2af3d54264ea5f224a19ba7775779c1ddbcb237258c5c"
:name "Mr. Batman" :name "Mr. Batman"
:photo-path ""})) :photo-path ""}))
(r/write #(create-contact {:phone-number "0522222222"
:whisper-identity "0x0487954e7fa746d8cf787403c2c491aadad540b9bb1f0f7b8184792e91c33b6a394079295f5777ec6d4af9ad5ba24794b3ff1ec8be9ff6a708c85a163733192665"
:name "Mr. Eagle"
:photo-path ""}))
(r/write #(create-contact {:phone-number "0533333333"
:whisper-identity "0x04e43e861a6dd99ad9eee7bd58af89dcaa430188ebec8698de7b7bad54573324fff4ac5cb9bb277af317efd7abfc917b91bf48cc41e40bf70062fd79400016a1f9"
: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,9 @@
: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}
:is-active "bool"
:timestamp "int" :timestamp "int"
:contacts {:type "list" :contacts {:type "list"
:objectType "chat-contact"}}}]}) :objectType "chat-contact"}}}]})
@ -92,6 +94,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))
@ -111,8 +116,7 @@
(read-string value)) (read-string value))
(defn delete [obj] (defn delete [obj]
(write (fn [] (.delete realm obj))
(.delete realm obj))))
(defn exists? [schema-name field value] (defn exists? [schema-name field value]
(> (.-length (get-by-field schema-name field value)) (> (.-length (get-by-field schema-name field value))
@ -127,22 +131,4 @@
(comment (comment
(write #(.create realm "msgs" (clj->js {:msg-id "12"
:content "sdfd"
:from "sdfsd"
:chat-id "56"
:content-type "fg"
:timestamp 2
:outgoing true
:to "sfs"
:delivery-status "seen"}) true))
(.addListener realm "change" (fn [& args]
(log/debug args)))
;realm.addListener('change', () => {
; // Update UI
; ...
; });
) )

View File

@ -0,0 +1,19 @@
(ns syng-im.persistence.realm-queries
(:require [clojure.string :as s]
[syng-im.utils.types :refer [to-string]]))
(defn include-query [field-name values]
(->> values
(map (fn [val]
(str (to-string field-name) " == " (if (string? val)
(str "'" val "'")
val))))
(s/join " || ")))
(defn exclude-query [field-name values]
(->> values
(map (fn [val]
(str (to-string field-name) " != " (if (string? val)
(str "'" val "'")
val))))
(s/join " && ")))

View File

@ -17,8 +17,9 @@
(contains-key? [_ key] (contains-key? [_ key]
(r/exists? :kv-store :key key)) (r/exists? :kv-store :key key))
(delete [_ key] (delete [_ key]
(-> (r/get-by-field :kv-store :key key) (r/write (fn []
(r/single) (-> (r/get-by-field :kv-store :key key)
(r/delete)))) (r/single)
(r/delete))))))
(def kv-store (->SimpleKvStore)) (def kv-store (->SimpleKvStore))

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))
@ -28,18 +30,14 @@
payload :payload} event] payload :payload} event]
(dispatch [:group-received-msg (assoc payload :from from (dispatch [:group-received-msg (assoc payload :from from
:group-id group-id)])) :group-id group-id)]))
;:group-chat-invite-acked (let [{:keys [from group-id]} event] :group-chat-invite-acked (let [{:keys [from group-id ack-msg-id]} event]
; (add-to-chat "group-chat" ":" (str "Received ACK for group chat invitation from " from " for group-id: " group-id))) (dispatch [:group-chat-invite-acked from group-id ack-msg-id]))
;:group-new-participant (let [{:keys [group-id identity from]} event] :group-new-participant (let [{:keys [group-id identity from msg-id]} event]
; (add-to-chat "group-chat" ":" (str (shorten from) " added " (shorten identity) " to group chat")) (dispatch [:participant-invited-to-group from group-id identity msg-id]))
; (add-identity-to-group-list identity)) :group-removed-participant (let [{:keys [group-id identity from msg-id]} event]
;:group-removed-participant (let [{:keys [group-id identity from]} event] (dispatch [:participant-removed-from-group from group-id identity msg-id]))
; (add-to-chat "group-chat" ":" (str (shorten from) " removed " (shorten identity) " from group chat")) :removed-from-group (let [{:keys [group-id from msg-id]} event]
; (remove-identity-from-group-list identity)) (dispatch [:you-removed-from-group from group-id msg-id]))
;:removed-from-group (let [{:keys [group-id from]} event] :participant-left-group (let [{:keys [group-id from msg-id]} event]
; (add-to-chat "group-chat" ":" (str (shorten from) " removed you from group chat"))) (dispatch [:participant-left-group from group-id msg-id]))
;:participant-left-group (let [{:keys [group-id from]} event] (log/info "Don't know how to handle" event-type)))})
; (add-to-chat "group-chat" ":" (str (shorten from) " left group chat")))
;(add-to-chat "chat" ":" (str "Don't know how to handle " event-type))
(log/info "Don't know how to handle" event-type)
))})

View File

@ -13,3 +13,6 @@
(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"))
(def trash-icon (js/require "./images/trash.png"))
(def leave-icon (js/require "./images/leave.png"))

View File

@ -8,7 +8,9 @@
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
contacts-list-include]]
[syng-im.handlers.suggestions :refer [get-suggestions]])) [syng-im.handlers.suggestions :refer [get-suggestions]]))
;; -- Chat -------------------------------------------------------------- ;; -- Chat --------------------------------------------------------------
@ -58,10 +60,13 @@
(register-sub :get-current-chat (register-sub :get-current-chat
(fn [db _] (fn [db _]
(let [current-chat-id (-> (current-chat-id @db) (let [current-chat-id (-> (current-chat-id @db)
(reaction))
chat-updated (-> (chat-updated? @db @current-chat-id)
(reaction))] (reaction))]
(-> (when-let [chat-id @current-chat-id] (reaction
(chat-by-id chat-id)) (let [_ @chat-updated]
(reaction))))) (when-let [chat-id @current-chat-id]
(chat-by-id chat-id)))))))
;; -- User data -------------------------------------------------------------- ;; -- User data --------------------------------------------------------------
@ -99,3 +104,31 @@
(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)))))))
(register-sub :current-chat-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-include current-participants)))))))