Add back group chats messages

Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
This commit is contained in:
Andrea Maria Piana 2018-07-19 17:51:06 +02:00
parent 2346c0ded7
commit 0c48d09c71
No known key found for this signature in database
GPG Key ID: AA6CCA6DE0E06424
39 changed files with 415 additions and 262 deletions

1
.env
View File

@ -17,3 +17,4 @@ CACHED_WEBVIEWS_ENABLED=1
EXTENSIONS=1
HARDWALLET_ENABLED=0
PFS_ENCRYPTION_ENABLED=0
GROUP_CHATS_ENABLED=0

View File

@ -9,6 +9,6 @@ POW_TIME=1
DEFAULT_NETWORK=testnet_rpc
INSTABUG_TOKEN=758630ed52864cbad9c5eeeac596c60c
DEBUG_WEBVIEW=1
GROUP_CHATS_ENABLED=1
GROUP_CHATS_ENABLED=0
EXTENSIONS=0
PFS_ENCRYPTION_ENABLED=0

View File

@ -13091,7 +13091,7 @@
}
},
"web3": {
"version": "github:status-im/web3.js#ff09ce57e9574a1c42138832a7d6bf6c2e699048",
"version": "github:status-im/web3.js#b1c2e2b75f6a190b320dda4be7931d3680ecb727",
"requires": {
"bignumber.js": "github:status-im/bignumber.js#cc066a0a3d6bfe0c436c9957f4ea8344bf963c89",
"crypto-js": "3.1.8",

View File

@ -84,7 +84,7 @@
"string_decoder": "0.10.31",
"text-encoding": "^0.6.4",
"url": "0.10.3",
"web3": "github:status-im/web3.js#feature/shhext"
"web3": "git+https://github.com/status-im/web3.js.git#feature/chat-api"
},
"devDependencies": {
"patch-package": "^5.1.1"

View File

@ -8795,7 +8795,7 @@
}
},
"web3": {
"version": "git+https://github.com/status-im/web3.js.git#ff09ce57e9574a1c42138832a7d6bf6c2e699048",
"version": "git+https://github.com/status-im/web3.js.git#b1c2e2b75f6a190b320dda4be7931d3680ecb727",
"requires": {
"bignumber.js": "github:status-im/bignumber.js#cc066a0a3d6bfe0c436c9957f4ea8344bf963c89",
"crypto-js": "3.1.8",

View File

@ -67,6 +67,6 @@
"string_decoder": "0.10.31",
"text-encoding": "^0.6.4",
"url": "0.10.3",
"web3": "https://github.com/status-im/web3.js.git#feature/shhext"
"web3": "git+https://github.com/status-im/web3.js.git#feature/chat-api"
}
}

View File

@ -10,10 +10,11 @@
[status-im.data-store.user-statuses :as user-statuses-store]
[status-im.i18n :as i18n]
[status-im.transport.message.core :as transport.message]
[status-im.transport.message.v1.group-chat :as group-chat]
[status-im.transport.message.v1.core :as protocol]
[status-im.transport.message.v1.public-chat :as public-chat]
[status-im.ui.screens.navigation :as navigation]
[status-im.utils.fx :as fx]
[status-im.group-chats.core :as group-chats]
[status-im.utils.handlers :as handlers]
[status-im.utils.utils :as utils]))
@ -128,22 +129,22 @@
(cons username)
(string/join ", ")))
(fx/defn send-group-update [cofx group-update chat-id]
(transport.message/send group-update chat-id cofx))
(handlers/register-handler-fx
:create-new-group-chat-and-open
[(re-frame/inject-cofx :random-id)]
(fn [{:keys [db random-id] :as cofx} [_ group-name]]
(let [selected-contacts (:group/selected-contacts db)
chat-name (if-not (string/blank? group-name)
group-name
(group-name-from-contacts selected-contacts
(:contacts/contacts db)
(:username db)))]
(let [my-public-key (:current-public-key db)
selected-contacts (conj (:group/selected-contacts db)
my-public-key)
group-update (protocol/GroupMembershipUpdate. random-id group-name my-public-key selected-contacts nil nil nil)]
(fx/merge cofx
{:db (assoc db :group/selected-contacts #{})}
(models/add-group-chat random-id chat-name (:current-public-key db) selected-contacts)
(navigation/navigate-to-cofx :home nil)
(models/navigate-to-chat random-id {})
#(transport.message/send (group-chat/GroupAdminUpdate. chat-name selected-contacts) random-id %)))))
(group-chats/handle-membership-update group-update my-public-key)
(send-group-update group-update random-id)))))
(fx/defn show-profile [{:keys [db]} identity]
(navigation/navigate-to-cofx {:db (assoc db :contacts/identity identity)} :profile nil))

View File

@ -4,7 +4,7 @@
[status-im.data-store.user-statuses :as user-statuses-store]
[status-im.transport.message.core :as transport.message]
[status-im.transport.message.v1.protocol :as protocol]
[status-im.transport.message.v1.group-chat :as transport.group-chat]
[status-im.transport.message.v1.core :as transport]
[status-im.transport.utils :as transport.utils]
[status-im.ui.components.styles :as styles]
[status-im.ui.screens.navigation :as navigation]
@ -106,7 +106,7 @@
[{:keys [db] :as cofx} chat-id]
;; if this is private group chat, we have to broadcast leave and unsubscribe after that
(if (group-chat? cofx chat-id)
(transport.message/send (transport.group-chat/GroupLeave.) chat-id cofx)
(transport.message/send (transport/GroupLeave.) chat-id cofx)
(transport.utils/unsubscribe-from-chat cofx chat-id)))
(fx/defn deactivate-chat
@ -133,7 +133,7 @@
(fx/defn send-messages-seen
[{:keys [db] :as cofx} chat-id message-ids]
(when (not (get-in db [:chats chat-id :public?]))
(when (not (get-in db [:chats chat-id :group-chat]))
(transport.message/send (protocol/map->MessagesSeen {:message-ids message-ids}) chat-id cofx)))
;; TODO (janherich) - ressurect `constants/system` messages for group chats in the future

View File

@ -6,7 +6,6 @@
[status-im.ui.screens.group.core :as group]
[status-im.chat.models :as models.chat]
[status-im.transport.message.core :as message]
[status-im.transport.message.v1.group-chat :as transport.group-chat]
[status-im.chat.models.message :as models.message]
[status-im.utils.fx :as fx]))
@ -30,7 +29,14 @@
(seq removed-participants)
(str admin-name " " (i18n/label :t/removed) " " (apply str (interpose ", " removed-participants-names))))))
(defn handle-group-admin-update [{:keys [chat-name participants]} chat-id signature {:keys [now db random-id] :as cofx}]
(defn handle-group-chat-create [{:keys [chat-name participants chat-id]} signature {:keys [now db random-id] :as cofx}]
(models.chat/add-group-chat chat-id
chat-name
signature
participants
cofx))
(defn handle-group-admin-update [{:keys [chat-name participants chat-id]} chat-id signature {:keys [now db random-id] :as cofx}]
(let [me (:current-public-key db)]
;; we have to check if we already have a chat, or it's a new one
(if-let [{:keys [group-admin contacts] :as chat} (get-in db [:chats chat-id])]
@ -70,9 +76,9 @@
(when (and
(not= signature me)
(get-in db [:chats chat-id])) ;; chat is present
(fx/merge cofx
(models.message/receive
(models.message/system-message chat-id random-id now
(str participant-leaving-name " " (i18n/label :t/left))))
(group/participants-removed chat-id #{signature})
(transport.group-chat/send-new-group-key nil chat-id)))))
(group/participants-removed chat-id #{signature})))))

View File

@ -6,6 +6,7 @@
[status-im.utils.config :as config]
[status-im.utils.ethereum.core :as ethereum]
[status-im.utils.datetime :as time]
[status-im.group-chats.core :as group-chats]
[status-im.chat.models :as chat-model]
[status-im.chat.models.loading :as chat-loading]
[status-im.chat.models.input :as input]
@ -124,15 +125,15 @@
batch?
{:keys [from message-id chat-id content content-type clock-value js-obj] :as raw-message}]
(let [{:keys [web3 current-chat-id view-id]} db
current-chat? (and (or (= :chat view-id) (= :chat-modal view-id)) (= current-chat-id chat-id))
{:keys [public?] :as chat} (get-in db [:chats chat-id])
message (-> raw-message
(commands-receiving/enhance-receive-parameters cofx)
(ensure-clock-value chat)
;; TODO (cammellos): Refactor so it's not computed twice
(add-outgoing-status cofx)
;; TODO (janherich): Remove after couple of releases
update-legacy-type)]
current-chat? (and (or (= :chat view-id) (= :chat-modal view-id)) (= current-chat-id chat-id))
{:keys [group-chat] :as chat} (get-in db [:chats chat-id])
message (-> raw-message
(commands-receiving/enhance-receive-parameters cofx)
(ensure-clock-value chat)
;; TODO (cammellos): Refactor so it's not computed twice
(add-outgoing-status cofx)
;; TODO (janherich): Remove after couple of releases
update-legacy-type)]
(fx/merge cofx
{:confirm-messages-processed [{:web3 web3
:js-obj js-obj}]}
@ -145,7 +146,7 @@
:else :received))
(commands-receiving/receive message)
(display-notification chat-id)
(send-message-seen chat-id message-id (and (not public?)
(send-message-seen chat-id message-id (and (not group-chat)
current-chat?
(not (= constants/system from))
(not (:outgoing message)))))))
@ -208,7 +209,11 @@
(if (= network-status :offline)
{:dispatch-later [{:ms 10000
:dispatch [:update-message-status chat-id message-id current-public-key :not-sent]}]}
(transport/send send-record chat-id cofx)))
(let [wrapped-record (if (= (:message-type send-record) :group-user-message)
(group-chats/wrap-group-message cofx chat-id send-record)
send-record)]
(transport/send wrapped-record chat-id cofx))))
(defn add-message-type [message {:keys [chat-id group-chat public?]}]
(cond-> message

View File

@ -4,6 +4,7 @@
[status-im.ui.components.react :as react]
[reagent.core :as reagent]
status-im.transport.impl.receive
status-im.transport.impl.send
[taoensso.timbre :as log]
[status-im.utils.config :as config]
[status-im.react-native.js-dependencies :as js-dependencies]

View File

@ -10,7 +10,9 @@
(core/sorted :clock-value :desc)
(core/single-clj :message)
:clock-value)]
(assoc chat :last-clock-value (or last-clock-value 0))))
(-> chat
(update :contacts #(into #{} %))
(assoc :last-clock-value (or last-clock-value 0)))))
(re-frame/reg-cofx
:data-store/all-chats

View File

@ -14,4 +14,4 @@
{:reagent-render views/main}))
(defn init []
(core/init app-root))
(core/init app-root))

View File

@ -0,0 +1,69 @@
(ns status-im.group-chats.core
(:require
[status-im.utils.config :as config]
[status-im.transport.utils :as transport.utils]
[status-im.transport.db :as transport.db]
[status-im.transport.utils :as transport.utils]
[status-im.transport.message.core :as protocol.message]
[status-im.transport.message.v1.core :as transport]
[status-im.transport.message.v1.protocol :as transport.protocol]
[status-im.utils.fx :as fx]
[status-im.chat.models :as models.chat]))
(defn wrap-group-message [cofx chat-id message]
(when-let [chat (get-in cofx [:db :chats chat-id])]
(transport/GroupMembershipUpdate.
chat-id
(:name chat)
(:group-admin chat)
(:contacts chat)
nil
nil
message)))
(defn update-membership [cofx previous-chat {:keys [chat-id chat-name participants leaves signature version]}]
(when (< (:membership-version previous-chat)
version)
(models.chat/upsert-chat cofx
{:chat-id chat-id
:membership-version version})))
(defn send-membership-update [cofx payload chat-id]
(let [{:keys [participants]} payload
{:keys [current-public-key web3]} (:db cofx)]
(fx/merge
cofx
{:shh/send-group-message {:web3 web3
:src current-public-key
:dsts (disj participants current-public-key)
:success-event [:transport/set-message-envelope-hash
chat-id
(transport.utils/message-id (:message payload))
:group-user-message]
:payload payload}})))
(defn handle-group-leave [payload chat-id cofx]
(transport.protocol/send cofx
{:chat-id chat-id
:payload payload
:success-event [:group/unsubscribe-from-chat chat-id]}))
(fx/defn handle-membership-update [cofx {:keys [chat-id chat-name participants leaves message signature version] :as membership-update} sender-signature]
(when config/group-chats-enabled?
(let [chat-fx (if-let [previous-chat (get-in cofx [:db :chats chat-id])]
(update-membership cofx previous-chat membership-update)
(models.chat/upsert-chat
cofx
{:chat-id chat-id
:name chat-name
:is-active true
:group-chat true
:group-admin signature
:contacts participants
:membership-version version}))]
(if message
(fx/merge cofx
chat-fx
#(protocol.message/receive message chat-id sender-signature nil %))
chat-fx))))

View File

@ -134,7 +134,7 @@
(fx/defn initialize
[{{:keys [status-node-started?] :as db} :db :as cofx} address]
(if (not status-node-started?)
(start address cofx)
(start cofx address)
(restart)))
(re-frame/reg-fx

View File

@ -40,7 +40,7 @@
[cofx event-str]
(let [{:keys [type event]} (types/json->clj event-str)]
(case type
"node.started" (status-node-started cofx)
"node.ready" (status-node-started cofx)
"node.stopped" (status-node-stopped cofx)
"module.initialized" (status-module-initialized cofx)
"envelope.sent" (transport.handlers/update-envelope-status cofx (:hash event) :sent)

View File

@ -68,6 +68,6 @@
account A messages without this."
[{:keys [db]}]
(let [{:transport/keys [chats discovery-filter]} db
chat-filters (mapv :filter (vals chats))
chat-filters (keep :filter (vals chats))
all-filters (conj chat-filters discovery-filter)]
{:shh/remove-filters all-filters}))

View File

@ -13,9 +13,9 @@
(spec/def ::resend? (spec/nilable #{"contact-request" "contact-request-confirmation" "contact-update"}))
;; optional
(spec/def ::sym-key-id string?)
(spec/def ::sym-key-id (spec/nilable string?))
;;TODO (yenda) remove once go implements persistence
(spec/def ::sym-key string?)
(spec/def ::sym-key (spec/nilable string?))
(spec/def ::filter any?)
(spec/def :transport/chat (allowed-keys :req-un [::ack ::seen ::pending-ack ::pending-send ::topic ::fetch-history?]

View File

@ -6,7 +6,6 @@
[status-im.transport.message.core :as message]
[status-im.transport.message.transit :as transit]
[status-im.transport.message.v1.contact :as v1.contact]
[status-im.transport.message.v1.group-chat :as v1.group-chat]
[status-im.transport.message.v1.protocol :as protocol]
[status-im.transport.shh :as shh]
[status-im.transport.utils :as transport.utils]
@ -105,55 +104,6 @@
(fn [cofx [_ chat-id]]
(transport.utils/unsubscribe-from-chat chat-id cofx)))
(handlers/register-handler-fx
:group/send-new-sym-key
;; this is the event that is called when we want to send a message that required first
;; some async operations
(fn [{:keys [db] :as cofx} [_ {:keys [chat-id message sym-key sym-key-id]}]]
(let [{:keys [web3]} db]
(fx/merge cofx
{:db (update-in db [:transport/chats chat-id]
assoc
:sym-key-id sym-key-id
:sym-key sym-key)
:shh/add-filter {:web3 web3
:sym-key-id sym-key-id
:topic (transport.utils/get-topic chat-id)
:chat-id chat-id}
:data-store/tx [(transport-store/save-transport-tx
{:chat-id chat-id
:chat (-> (get-in db [:transport/chats chat-id])
(assoc :sym-key-id sym-key-id)
;;TODO (yenda) remove once go implements persistence
(assoc :sym-key sym-key))})]}
#(message/send (v1.group-chat/NewGroupKey. chat-id sym-key message) chat-id %)))))
(handlers/register-handler-fx
:group/add-new-sym-key
[(re-frame/inject-cofx :random-id)]
(fn [{:keys [db] :as cofx} [_ {:keys [sym-key-id sym-key chat-id signature timestamp message]}]]
(let [{:keys [web3 current-public-key]} db
topic (transport.utils/get-topic chat-id)
fx {:db (assoc-in db
[:transport/chats chat-id :sym-key-id]
sym-key-id)
:dispatch [:inbox/request-chat-history chat-id]
:shh/add-filter {:web3 web3
:sym-key-id sym-key-id
:topic topic
:chat-id chat-id}
:data-store/tx [(transport-store/save-transport-tx
{:chat-id chat-id
:chat (-> (get-in db [:transport/chats chat-id])
(assoc :sym-key-id sym-key-id)
;;TODO (yenda) remove once go implements persistence
(assoc :sym-key sym-key))})]}]
;; if new sym-key is wrapping some message, call receive on it as well, if not just update the transport layer
(fx/merge cofx
fx
#(when message
(message/receive message chat-id signature timestamp %))))))
(re-frame/reg-fx
;; TODO(janherich): this should be called after `:data-store/tx` actually
:confirm-messages-processed
@ -172,11 +122,17 @@
(handlers/register-handler-fx
:transport/set-message-envelope-hash
;; message-type is used for tracking
(fn [{:keys [db]} [_ chat-id message-id message-type envelope-hash]]
{:db (assoc-in db [:transport/message-envelopes envelope-hash]
{:chat-id chat-id
:message-id message-id
:message-type message-type})}))
(fn [{:keys [db]} [_ chat-id message-id message-type envelope-hash-js]]
;; TODO (cammellos): For group messages it returns multiple hashes, for now
;; we naively assume that if one is sent the batch is ok
(let [envelope-hash (js->clj envelope-hash-js)
hash (if (vector? envelope-hash)
(last envelope-hash)
envelope-hash)]
{:db (assoc-in db [:transport/message-envelopes hash]
{:chat-id chat-id
:message-id message-id
:message-type message-type})})))
(handlers/register-handler-fx
:transport/set-contact-message-envelope-hash

View File

@ -2,16 +2,17 @@
(:require
[status-im.chat.models.group-chat :as models.group-chat]
[status-im.models.contact :as models.contact]
[status-im.group-chats.core :as group-chats]
[status-im.transport.message.core :as message]
[status-im.transport.message.v1.contact :as transport.contact]
[status-im.transport.message.v1.group-chat :as transport.group-chat]))
[status-im.transport.message.v1.core :as transport.protocol]))
(extend-type transport.group-chat/GroupAdminUpdate
(extend-type transport.protocol/GroupMembershipUpdate
message/StatusMessage
(receive [this chat-id signature _ cofx]
(models.group-chat/handle-group-admin-update this chat-id signature cofx)))
(receive [this _ signature _ cofx]
(group-chats/handle-membership-update cofx this signature)))
(extend-type transport.group-chat/GroupLeave
(extend-type transport.protocol/GroupLeave
message/StatusMessage
(receive [this chat-id signature _ cofx]
(models.group-chat/handle-group-leave chat-id signature cofx)))

View File

@ -0,0 +1,15 @@
(ns status-im.transport.impl.send
(:require
[status-im.group-chats.core :as group-chats]
[status-im.transport.message.core :as message]
[status-im.transport.message.v1.core :as transport]))
(extend-type transport/GroupMembershipUpdate
message/StatusMessage
(send [this chat-id cofx]
(group-chats/send-membership-update cofx this chat-id)))
(extend-type transport/GroupLeave
message/StatusMessage
(send [this chat-id cofx]
(group-chats/handle-group-leave this chat-id cofx)))

View File

@ -2,7 +2,7 @@
status-im.transport.message.transit
(:require [status-im.transport.message.v1.contact :as v1.contact]
[status-im.transport.message.v1.protocol :as v1.protocol]
[status-im.transport.message.v1.group-chat :as v1.group-chat]
[status-im.transport.message.v1.core :as v1]
[cognitect.transit :as transit]))
;; When adding a new reccord implenting the StatusMessage protocol it is required to implement:
@ -55,24 +55,18 @@
(rep [this {:keys [message-ids]}]
(clj->js message-ids)))
(deftype NewGroupKeyHandler []
Object
(tag [this v] "g1")
(rep [this {:keys [chat-id sym-key message]}]
#js [chat-id sym-key message]))
(deftype GroupAdminUpdateHandler []
Object
(tag [this v] "g2")
(rep [this {:keys [chat-name participants]}]
#js [chat-name participants]))
(deftype GroupLeaveHandler []
Object
(tag [this v] "g3")
(rep [this _]
(clj->js nil)))
(deftype GroupMembershipUpdateHandler []
Object
(tag [this v] "g5")
(rep [this {:keys [chat-id chat-name admin participants signature message]}]
#js [chat-id chat-name admin participants signature message]))
(def writer (transit/writer :json
{:handlers
{v1.contact/NewContactKey (NewContactKeyHandler.)
@ -81,9 +75,8 @@
v1.contact/ContactUpdate (ContactUpdateHandler.)
v1.protocol/Message (MessageHandler.)
v1.protocol/MessagesSeen (MessagesSeenHandler.)
v1.group-chat/NewGroupKey (NewGroupKeyHandler.)
v1.group-chat/GroupAdminUpdate (GroupAdminUpdateHandler.)
v1.group-chat/GroupLeave (GroupLeaveHandler.)}}))
v1/GroupLeave (GroupLeaveHandler.)
v1/GroupMembershipUpdate (GroupMembershipUpdateHandler.)}}))
;;
;; Reader handlers
@ -103,7 +96,9 @@
"c5" (fn [message-ids]
(v1.protocol/MessagesSeen. message-ids))
"c6" (fn [[name profile-image address fcm-token]]
(v1.contact/ContactUpdate. name profile-image address fcm-token))}})) ; removed group chat handlers for https://github.com/status-im/status-react/issues/4506
(v1.contact/ContactUpdate. name profile-image address fcm-token))
"g5" (fn [[chat-id chat-name admin participants signature message]]
(v1/GroupMembershipUpdate. chat-id chat-name admin participants nil signature message))}})) ; removed group chat handlers for https://github.com/status-im/status-react/issues/4506
(defn serialize
"Serializes a record implementing the StatusMessage protocol using the custom writers"

View File

@ -0,0 +1,10 @@
(ns status-im.transport.message.v1.core
(:require [status-im.transport.message.core :as message]))
(defrecord GroupMembershipUpdate
[chat-id chat-name admin participants leaves signature message]
message/StatusMessage)
(defrecord GroupLeave
[]
message/StatusMessage)

View File

@ -1,68 +0,0 @@
(ns ^{:doc "Group chat API"}
status-im.transport.message.v1.group-chat
(:require [re-frame.core :as re-frame]
[status-im.transport.message.core :as message]
[status-im.transport.message.v1.protocol :as protocol]
[status-im.transport.utils :as transport.utils]
[status-im.utils.fx :as fx]))
;; NOTE: We ignore the chat-id from the send and receive method.
;; The chat-id is usually deduced from the filter the message comes from but not in that case because it is sent
;; individually to each participant of the group.
;; In order to be able to determine which group the message belongs to the chat-id is therefore
;; passed in the message itself
(defrecord NewGroupKey [chat-id sym-key message]
message/StatusMessage
(send [this _ cofx]
(let [public-keys (get-in cofx [:db :chats chat-id :contacts])]
(protocol/multi-send-by-pubkey {:public-keys public-keys
:chat-id chat-id
:payload this}
cofx)))
(receive [this _ signature timestamp {:keys [db] :as cofx}]
(fx/merge cofx
{:shh/add-new-sym-keys
[{:web3 (:web3 db)
:sym-key sym-key
:on-success (fn [sym-key sym-key-id]
(re-frame/dispatch
[:group/add-new-sym-key
{:chat-id chat-id
:signature signature
:timestamp timestamp
:sym-key sym-key
:sym-key-id sym-key-id
:message message}]))}]}
(protocol/init-chat {:chat-id chat-id}))))
(defn- user-is-group-admin? [cofx chat-id]
(= (get-in cofx [:db :chats chat-id :group-admin])
(get-in cofx [:db :current-public-key])))
(fx/defn send-new-group-key
[message chat-id cofx]
(when (user-is-group-admin? cofx chat-id)
{:shh/get-new-sym-keys [{:web3 (get-in cofx [:db :web3])
:on-success (fn [sym-key sym-key-id]
(re-frame/dispatch
[:group/send-new-sym-key
{:chat-id chat-id
:sym-key sym-key
:sym-key-id sym-key-id
:message message}]))}]}))
(defrecord GroupAdminUpdate [chat-name participants]
message/StatusMessage
(send [this chat-id cofx]
(fx/merge cofx
#(when (nil? (get-in % [:db :transport/chats chat-id]))
(protocol/init-chat {:chat-id chat-id} %))
(send-new-group-key this chat-id))))
(defrecord GroupLeave []
message/StatusMessage
(send [this chat-id cofx]
(protocol/send cofx
{:chat-id chat-id
:payload this
:success-event [:group/unsubscribe-from-chat chat-id]})))

View File

@ -2,9 +2,12 @@
status-im.transport.message.v1.protocol
(:require [status-im.utils.config :as config]
[status-im.constants :as constants]
[taoensso.timbre :as log]
[status-im.utils.config :as config]
[status-im.chat.core :as chat]
[status-im.transport.db :as transport.db]
[status-im.transport.message.core :as message]
[status-im.transport.message.v1.core :as transport]
[status-im.transport.utils :as transport.utils]
[status-im.utils.fx :as fx]))
@ -38,6 +41,26 @@
:topic topic}
whisper-opts)}]}))
(fx/defn send-direct-message
"Sends the payload using to dst"
[{:keys [db] :as cofx} dst success-event payload]
(let [{:keys [current-public-key web3]} db]
{:shh/send-direct-message [{:web3 web3
:success-event success-event
:src current-public-key
:dst dst
:payload payload}]}))
(defn send-public-message
"Sends the payload to topic"
[{:keys [db] :as cofx} chat-id success-event payload]
(let [{:keys [current-public-key web3]} db]
{:shh/send-public-message [{:web3 web3
:success-event success-event
:src current-public-key
:chat chat-id
:payload payload}]}))
(fx/defn send-with-pubkey
"Sends the payload using asymetric key (`:current-public-key` in db) and fixed discovery topic"
[{:keys [db] :as cofx} {:keys [payload chat-id success-event]}]
@ -50,34 +73,6 @@
:topic (transport.utils/get-topic constants/contact-discovery)}
whisper-opts)}]}))
(defn- prepare-recipients [public-keys db]
(map (fn [public-key]
(select-keys (get-in db [:transport/chats public-key]) [:topic :sym-key-id]))
public-keys))
(fx/defn multi-send-by-pubkey
"Sends payload to multiple participants selected by `:public-keys` key. "
[{:keys [db] :as cofx} {:keys [payload public-keys success-event]}]
(let [{:keys [current-public-key web3]} db
recipients (prepare-recipients public-keys db)]
{:shh/multi-post {:web3 web3
:success-event success-event
:recipients recipients
:message (merge {:sig current-public-key
:payload payload}
whisper-opts)}}))
;; TODO currently not used
(defrecord Ack [message-ids]
message/StatusMessage
(send [this cofx chat-id])
(receive [this chat-id sig timestamp cofx]))
(defrecord Seen [message-ids]
message/StatusMessage
(send [this cofx chat-id])
(receive [this chat-id sig timestamp cofx]))
(defrecord Message [content content-type message-type clock-value timestamp]
message/StatusMessage
(send [this chat-id cofx]
@ -86,12 +81,25 @@
:success-event [:transport/set-message-envelope-hash
chat-id
(transport.utils/message-id this)
message-type]}
group-chat (get-in cofx [:db :chats chat-id :group-chat])]
(if (or group-chat
config/use-sym-key)
(send cofx params)
(send-with-pubkey cofx params))))
message-type]}]
(case message-type
:public-group-user-message
(if config/pfs-encryption-enabled?
(send-public-message
cofx
chat-id
(:success-event params)
this)
(send cofx params))
:user-message
(if config/pfs-encryption-enabled?
(send-direct-message
cofx
chat-id
(:success-event params)
this)
(send-with-pubkey cofx params)))))
(receive [this chat-id signature _ cofx]
{:chat-received-message/add-fx
[(assoc (into {} this)
@ -104,8 +112,12 @@
(defrecord MessagesSeen [message-ids]
message/StatusMessage
(send [this chat-id cofx]
(send cofx
{:chat-id chat-id
:payload this}))
(if config/pfs-encryption-enabled?
(send-direct-message cofx
chat-id
nil
this)
(send cofx {:chat-id chat-id
:payload this})))
(receive [this chat-id signature _ cofx]
(chat/receive-seen cofx chat-id signature this)))

View File

@ -58,6 +58,62 @@
(on-success resp)
(on-error err))))))
(defn handle-response [success-event error-event]
(fn [err resp]
(if-not err
(if success-event
(re-frame/dispatch (conj success-event resp))
(log/debug :shh/post-success))
(re-frame/dispatch [error-event err resp]))))
(re-frame/reg-fx
:shh/send-direct-message
(fn [post-calls]
(doseq [{:keys [web3 payload src dst success-event error-event]
:or {error-event :protocol/send-status-message-error}} post-calls]
(let [direct-message (clj->js {:pubKey dst
:sig src
:payload (-> payload
transit/serialize
transport.utils/from-utf8)})]
(.. web3
-shh
(sendDirectMessage
direct-message
(handle-response success-event error-event)))))))
(re-frame/reg-fx
:shh/send-group-message
(fn [params]
(let [{:keys [web3 payload src dsts success-event error-event]
:or {error-event :protocol/send-status-message-error}} params
message (clj->js {:pubKeys dsts
:sig src
:payload (-> payload
transit/serialize
transport.utils/from-utf8)})]
(.. web3
-shh
(sendGroupMessage
message
(handle-response success-event error-event))))))
(re-frame/reg-fx
:shh/send-public-message
(fn [post-calls]
(doseq [{:keys [web3 payload src chat success-event error-event]
:or {error-event :protocol/send-status-message-error}} post-calls]
(let [message (clj->js {:chat chat
:sig src
:payload (-> payload
transit/serialize
transport.utils/from-utf8)})]
(.. web3
-shh
(sendPublicMessage
message
(handle-response success-event error-event)))))))
(re-frame/reg-fx
:shh/post
(fn [post-calls]
@ -128,11 +184,12 @@
:shh/restore-sym-keys
(fn [{:keys [web3 transport on-success]}]
(doseq [[chat-id {:keys [sym-key]}] transport]
(add-sym-key {:web3 web3
:sym-key sym-key
:on-success (fn [sym-key-id]
(on-success chat-id sym-key sym-key-id))
:on-error log-error}))))
(when sym-key
(add-sym-key {:web3 web3
:sym-key sym-key
:on-success (fn [sym-key-id]
(on-success chat-id sym-key sym-key-id))
:on-error log-error})))))
(defn add-new-sym-key [{:keys [web3 sym-key on-success]}]
(add-sym-key {:web3 web3

View File

@ -38,4 +38,6 @@
:font-size 15
:letter-spacing -0.2
:padding-horizontal 14
:desktop {:height 30
:width "100%"}
:android {:padding 0}})

View File

@ -56,7 +56,7 @@
:font :toolbar-subtitle}
(if public?
(i18n/label :t/public-group-status)
(let [cnt (inc (count contacts))]
(let [cnt (count contacts)]
(if (zero? cnt)
(i18n/label :members-active-none)
(i18n/label-pluralize cnt :t/members-active))))]])))

View File

@ -1,6 +1,7 @@
(ns status-im.ui.screens.desktop.main.add-new.views
(:require-macros [status-im.utils.views :as views])
(:require [status-im.ui.components.icons.vector-icons :as icons]
[status-im.utils.config :as config]
[status-im.ui.screens.add-new.new-public-chat.view :as public-chat]
[status-im.ui.components.list.views :as list]
[clojure.string :as string]
@ -13,6 +14,19 @@
[status-im.ui.components.react :as react]
[status-im.ui.components.colors :as colors]))
(def group-chat-section
^{:key "groupchat"}
[react/view {:style styles/new-contact-title}
[react/text {:style styles/new-contact-title-text
:font :medium}
(i18n/label :new-group-chat)]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:open-contact-toggle-list])}
[react/view
{:style (styles/add-contact-button nil)}
[react/text
{:style (styles/add-contact-button-text nil)}
(i18n/label :start-chat)]]]])
(views/defview new-contact []
(views/letsubs [new-contact-identity [:get :contacts/new-identity]
contacts [:all-added-people-contacts]
@ -60,6 +74,7 @@
[react/image {:style styles/suggested-contact-image
:source {:uri (:photo-path c)}}]
[react/text {:style styles/new-contact-subtitle} (:name c)]]]))]]
(when config/group-chats-enabled? group-chat-section)
^{:key "publicchat"}
[react/view {:style styles/new-contact-title}
[react/text {:style styles/new-contact-title-text

View File

@ -3,6 +3,9 @@
(:require [status-im.ui.screens.desktop.main.views :as main.views]
[status-im.ui.components.react :as react]
[status-im.ui.screens.intro.views :as intro.views]
[status-im.ui.screens.group.add-contacts.views :refer [contact-toggle-list]]
[status-im.ui.screens.group.views :refer [new-group]]
[status-im.ui.screens.accounts.create.views :as create.views]
[status-im.ui.screens.accounts.login.views :as login.views]
[status-im.ui.screens.accounts.recover.views :as recover.views]
@ -17,6 +20,8 @@
:accounts accounts.views/accounts
:recover recover.views/recover
:create-account create.views/create-account
:new-group new-group
:contact-toggle-list contact-toggle-list
(:new-contact
:advanced-settings
:chat :home

View File

@ -3,7 +3,7 @@
[status-im.i18n :as i18n]
[status-im.chat.models.message :as models.message]
[status-im.ui.screens.navigation :as navigation]
[status-im.transport.message.v1.group-chat :as group-chat]
[status-im.transport.message.v1.core :as protocol]
[status-im.transport.message.core :as transport]
[status-im.utils.handlers :as handlers]
[status-im.data-store.chats :as chats-store]
@ -33,7 +33,7 @@
(models.message/receive
(models.message/system-message current-chat-id message-id now
(str "You've added " (apply str (interpose ", " added-participants-names)))))
#(transport/send (group-chat/GroupAdminUpdate. nil participants) current-chat-id %)))))
#_(transport/send (protocol/GroupAdminUpdate. nil participants current-chat-id) current-chat-id)))))
(handlers/register-handler-fx
:remove-group-chat-participants
@ -48,7 +48,7 @@
(models.message/receive
(models.message/system-message current-chat-id message-id now
(str "You've removed " (apply str (interpose ", " removed-participants-names)))))
#(transport/send (group-chat/GroupAdminUpdate. nil participants) current-chat-id %)))))
#_(transport/send (protocol/GroupAdminUpdate. nil participants current-chat-id) current-chat-id)))))
(handlers/register-handler-fx
:set-group-chat-name

View File

@ -37,10 +37,10 @@
(defn actions [admin? chat-id]
(concat
(when admin?
[{:label (i18n/label :add-members)
:icon :icons/add
:action #(re-frame/dispatch [:navigate-to :add-participants-toggle-list])}])
#_(when admin?
[{:label (i18n/label :add-members)
:icon :icons/add
:action #(re-frame/dispatch [:navigate-to :add-participants-toggle-list])}])
[{:label (i18n/label :t/clear-history)
:icon :icons/close
:action #(re-frame/dispatch [:chat.ui/clear-history-pressed])
@ -53,8 +53,8 @@
(defn contact-actions [contact]
[{:action #(re-frame/dispatch [:show-profile (:whisper-identity contact)])
:label (i18n/label :t/view-profile)}
{:action #(re-frame/dispatch [:remove-group-chat-participants #{(:whisper-identity contact)}])
:label (i18n/label :t/remove-from-chat)}])
#_{:action #(re-frame/dispatch [:remove-group-chat-participants #{(:whisper-identity contact)}])
:label (i18n/label :t/remove-from-chat)}])
(defn render-contact [{:keys [name whisper-identity] :as contact} admin? group-admin-identity current-user-identity]
[react/view

View File

@ -153,16 +153,20 @@
(stack-screens
{:main-stack
{:screens
{:home (main-tabs/get-main-tab :home)
:chat chat
:profile profile.contact/profile
:new add-new
:new-chat new-chat
:qr-scanner qr-scanner
:new-public-chat new-public-chat
:open-dapp open-dapp
:dapp-description dapp-description
:browser browser}
{:home (main-tabs/get-main-tab :home)
:chat chat
:profile profile.contact/profile
:new add-new
:new-chat new-chat
:qr-scanner qr-scanner
:new-group new-group
:add-participants-toggle-list add-participants-toggle-list
:contact-toggle-list contact-toggle-list
:group-chat-profile profile.group-chat/group-chat-profile
:new-public-chat new-public-chat
:open-dapp open-dapp
:dapp-description dapp-description
:browser browser}
:config
{:headerMode "none"
:initialRouteName "home"}}

View File

@ -22,7 +22,7 @@
(def bootnodes-settings-enabled? (enabled? (get-config :BOOTNODES_SETTINGS_ENABLED "1")))
(def rpc-networks-only? (enabled? (get-config :RPC_NETWORKS_ONLY "1")))
(def group-chats-enabled? (enabled? (get-config :GROUP_CHATS_ENABLED)))
(def group-chats-enabled? (enabled? (get-config :GROUP_CHATS_ENABLED "0")))
(def mainnet-warning-enabled? (enabled? (get-config :MAINNET_WARNING_ENABLED 0)))
(def pfs-encryption-enabled? (enabled? (get-config :PFS_ENCRYPTION_ENABLED "0")))
(def in-app-notifications-enabled? (enabled? (get-config :IN_APP_NOTIFICATIONS_ENABLED 0)))

View File

@ -170,15 +170,15 @@
:public? true)
chat-id)]
(is (not (get-in actual [:db :transport/chats chat-id])))))
(testing "it sends a leave group request if it's a group-chat"
(let [actual (chat/remove-chat (assoc-in
cofx
[:db :chats chat-id :group-chat]
true)
chat-id)]
(is (:shh/post actual))
(testing "it does not remove transport, only after send is successful"
(is (get-in actual [:db :transport/chats chat-id])))))
#_(testing "it sends a leave group request if it's a group-chat"
(let [actual (chat/remove-chat (assoc-in
cofx
[:db :chats chat-id :group-chat]
true)
chat-id)]
(is (:shh/post actual))
(testing "it does not remove transport, only after send is successful"
(is (get-in actual [:db :transport/chats chat-id])))))
(testing "it does not remove it from transport if it's a one-to-one"
(let [actual (chat/remove-chat cofx chat-id)]
(is (get-in actual [:db :transport/chats chat-id]))))
@ -214,6 +214,7 @@
(def test-db
{:current-public-key "me"
:chats {"status" {:public? true
:group-chat true
:unviewed-messages #{"6" "5" "4" "3" "2" "1"}
:message-statuses {"6" {"me" {:message-id "6"
:chat-id "status"

View File

@ -84,10 +84,11 @@
(is (instance? protocol/MessagesSeen
(extract-seen (message/receive cofx message))))
(is (= #{"1"} (:message-ids (extract-seen (message/receive cofx message))))))
(testing "it does not send any when the chat is public"
(testing "it does not send any when the chat is a group-chat"
(is (nil? (extract-seen
(message/receive (assoc-in cofx [:db :chats "chat-id" :public?] true)
message)))))
(message/receive
(assoc-in cofx [:db :chats "chat-id" :group-chat] true)
message)))))
(testing "it does not send any when we are in a different chat"
(is (nil? (extract-seen
(message/receive (assoc-in cofx [:db :current-chat-id] :different)

View File

@ -0,0 +1,58 @@
(ns status-im.test.group-chats.core
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.utils.config :as config]
[status-im.group-chats.core :as group-chats]))
(def chat-id "0xba")
(def chat-name "chat-name")
(def member-1 "member-1")
(def member-2 "member-2")
(def admin member-1)
(def invitation-m1 {:id "m-1"
:user member-1})
(def invitation-m2 {:id "m-2"
:user member-2})
(def initial-message {:chat-id chat-id
:chat-name chat-name
:admin admin
:participants [invitation-m1
invitation-m2]
:leaves []
:signature "some"
:version 1})
(deftest handle-group-membership-update
(with-redefs [config/group-chats-enabled? true]
(testing "a brand new chat"
(let [actual (->
(group-chats/handle-membership-update {:db {}} initial-message admin)
:db
:chats
(get chat-id))]
(testing "it creates a new chat"
(is actual))
(testing "it sets the right participants"
(is (= [invitation-m1
invitation-m2]
(:contacts actual))))
(testing "it sets the right version"
(is (= 1
(:membership-version actual))))))
(testing "an already existing chat"
(let [cofx {:db {:chats {chat-id {:contacts [invitation-m1
invitation-m2]
:group-admin admin
:membership-version 2}}}}]
(testing "an update from the admin is received"
(testing "the message is an older version"
(let [actual (group-chats/handle-membership-update cofx initial-message admin)]
(testing "it noops"
(is (not actual)))))
(testing "the message is a more recent version"
(testing "it sets the right participants")))
(testing "a leave from a member is received"
(testing "the user is removed"))))))

View File

@ -10,6 +10,7 @@
[status-im.test.wallet.transactions.subs]
[status-im.test.wallet.transactions.views]
[status-im.test.mailserver.core]
[status-im.test.group-chats.core]
[status-im.test.node.core]
[status-im.test.models.bootnode]
[status-im.test.models.account]
@ -74,6 +75,7 @@
'status-im.test.init.core
'status-im.test.data-store.realm.core
'status-im.test.mailserver.core
'status-im.test.group-chats.core
'status-im.test.node.core
'status-im.test.models.bootnode
'status-im.test.models.account

View File

@ -2,7 +2,9 @@
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.transport.handlers :as handlers]))
(def messages #js [{:sig "0x04325367620ae20dd878dbb39f69f02c567d789dd21af8a88623dc5b529827c2812571c380a2cd8236a2851b8843d6486481166c39debf60a5d30b9099c66213e4"
(def sig "0x04325367620ae20dd878dbb39f69f02c567d789dd21af8a88623dc5b529827c2812571c380a2cd8236a2851b8843d6486481166c39debf60a5d30b9099c66213e4")
(def messages #js [{:sig sig
:ttl 10
:timestamp 1527692015
:topic "0x9c22ff5f"
@ -17,6 +19,6 @@
(testing "messages is undefined"
(is (nil? (handlers/receive-whisper-messages {:db {}} [nil nil js/undefined nil]))))
(testing "happy path"
(let [actual (handlers/receive-whisper-messages {:db {}} [nil nil messages "1"])]
(let [actual (handlers/receive-whisper-messages {:db {}} [nil nil messages sig])]
(testing "it add an fx for the message"
(is (:chat-received-message/add-fx actual))))))