refactor inbox

Signed-off-by: yenda <eric@status.im>
This commit is contained in:
yenda 2018-10-09 10:10:47 +02:00
parent dc8976a8b2
commit efdd76b364
No known key found for this signature in database
GPG Key ID: 0095623C0069DCE6
49 changed files with 946 additions and 1051 deletions

View File

@ -26,7 +26,6 @@
(def snoopy-filter #js {}) (def snoopy-filter #js {})
(def snoopy-bars #js {}) (def snoopy-bars #js {})
(def snoopy-buffer #js {}) (def snoopy-buffer #js {})
(def background-timer #js {:setTimeout (fn [])}) (def background-timer #js {:setTimeout (fn [cb ms] (js/setTimeout cb ms))})
(def testfairy #js {}) (def testfairy #js {})
(def react-navigation #js {:NavigationActions #js {}}) (def react-navigation #js {:NavigationActions #js {}})

View File

@ -1,7 +1,7 @@
(ns status-im.accounts.update.core (ns status-im.accounts.update.core
(:require [status-im.data-store.accounts :as accounts-store] (:require [status-im.data-store.accounts :as accounts-store]
[status-im.transport.message.core :as transport] [status-im.transport.message.protocol :as protocol]
[status-im.transport.message.v1.contact :as message.contact] [status-im.transport.message.contact :as message.contact]
[status-im.utils.fx :as fx])) [status-im.utils.fx :as fx]))
(fx/defn account-update (fx/defn account-update
@ -18,7 +18,7 @@
(if (or (:name new-account-fields) (:photo-path new-account-fields)) (if (or (:name new-account-fields) (:photo-path new-account-fields))
(fx/merge cofx (fx/merge cofx
fx fx
#(transport/send (message.contact/ContactUpdate. name photo-path address fcm-token) nil %)) #(protocol/send (message.contact/ContactUpdate. name photo-path address fcm-token) nil %))
fx))) fx)))
(fx/defn clean-seed-phrase (fx/defn clean-seed-phrase

View File

@ -5,8 +5,8 @@
;; Seen messages ;; Seen messages
(fx/defn receive-seen (fx/defn receive-seen
[{:keys [db js-obj]} chat-id sender {:keys [message-ids]}] [{:keys [db js-obj]} chat-id sender {:keys [message-ids]}]
(merge {:confirm-messages-processed [{:web3 (:web3 db) (merge {:transport/confirm-messages-processed [{:web3 (:web3 db)
:js-obj js-obj}]} :js-obj js-obj}]}
(when-let [seen-messages-ids (-> (get-in db [:chats chat-id :messages]) (when-let [seen-messages-ids (-> (get-in db [:chats chat-id :messages])
(select-keys message-ids) (select-keys message-ids)
keys)] keys)]

View File

@ -1,23 +1,18 @@
(ns status-im.chat.models (ns status-im.chat.models
(:require [clojure.string :as string] (:require [re-frame.core :as re-frame]
[re-frame.core :as re-frame]
[status-im.data-store.chats :as chats-store] [status-im.data-store.chats :as chats-store]
[status-im.data-store.messages :as messages-store] [status-im.data-store.messages :as messages-store]
[status-im.data-store.user-statuses :as user-statuses-store] [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.core :as transport]
[status-im.transport.message.v1.public-chat :as public-chat]
[status-im.transport.utils :as transport.utils]
[status-im.ui.components.styles :as styles]
[status-im.ui.screens.navigation :as navigation]
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
[status-im.utils.utils :as utils] [status-im.transport.chat.core :as transport.chat]
[status-im.transport.message.protocol :as protocol]
[status-im.transport.message.public-chat :as public-chat]
[status-im.ui.components.colors :as colors]
[status-im.ui.screens.navigation :as navigation]
[status-im.utils.clocks :as utils.clocks] [status-im.utils.clocks :as utils.clocks]
[status-im.utils.datetime :as time]
[status-im.utils.gfycat.core :as gfycat]
[status-im.utils.fx :as fx] [status-im.utils.fx :as fx]
[status-im.ui.components.colors :as colors])) [status-im.utils.gfycat.core :as gfycat]
[status-im.utils.utils :as utils]))
(defn multi-user-chat? [cofx chat-id] (defn multi-user-chat? [cofx chat-id]
(get-in cofx [:db :chats chat-id :group-chat])) (get-in cofx [:db :chats chat-id :group-chat]))
@ -105,10 +100,6 @@
:data-store/tx [(chats-store/clear-history-tx chat-id last-message-clock-value) :data-store/tx [(chats-store/clear-history-tx chat-id last-message-clock-value)
(messages-store/delete-messages-tx chat-id)]})) (messages-store/delete-messages-tx chat-id)]}))
(fx/defn remove-transport
[cofx chat-id]
(transport.utils/unsubscribe-from-chat cofx chat-id))
(fx/defn deactivate-chat (fx/defn deactivate-chat
[{:keys [db now] :as cofx} chat-id] [{:keys [db now] :as cofx} chat-id]
{:db (-> db {:db (-> db
@ -127,7 +118,7 @@
[{:keys [db now] :as cofx} chat-id] [{:keys [db now] :as cofx} chat-id]
(fx/merge cofx (fx/merge cofx
#(when (public-chat? % chat-id) #(when (public-chat? % chat-id)
(remove-transport % chat-id)) (transport.chat/unsubscribe-from-chat % chat-id))
(deactivate-chat chat-id) (deactivate-chat chat-id)
(clear-history chat-id) (clear-history chat-id)
(navigation/navigate-to-cofx :home {}))) (navigation/navigate-to-cofx :home {})))
@ -135,7 +126,7 @@
(fx/defn send-messages-seen (fx/defn send-messages-seen
[{:keys [db] :as cofx} chat-id message-ids] [{:keys [db] :as cofx} chat-id message-ids]
(when (not (get-in db [:chats chat-id :group-chat])) (when (not (get-in db [:chats chat-id :group-chat]))
(transport.message/send (protocol/map->MessagesSeen {:message-ids message-ids}) chat-id cofx))) (protocol/send (protocol/map->MessagesSeen {:message-ids message-ids}) chat-id cofx)))
;; TODO (janherich) - ressurect `constants/system` messages for group chats in the future ;; TODO (janherich) - ressurect `constants/system` messages for group chats in the future
(fx/defn mark-messages-seen (fx/defn mark-messages-seen

View File

@ -1,16 +1,16 @@
(ns status-im.chat.models.input (ns status-im.chat.models.input
(:require [clojure.string :as string] (:require [clojure.string :as string]
[re-frame.core :as re-frame]
[goog.object :as object] [goog.object :as object]
[status-im.constants :as constants] [re-frame.core :as re-frame]
[status-im.chat.constants :as chat.constants]
[status-im.chat.models :as chat]
[status-im.chat.models.message :as chat.message]
[status-im.chat.commands.core :as commands] [status-im.chat.commands.core :as commands]
[status-im.chat.commands.input :as commands.input] [status-im.chat.commands.input :as commands.input]
[status-im.chat.commands.sending :as commands.sending] [status-im.chat.commands.sending :as commands.sending]
[status-im.utils.datetime :as datetime] [status-im.chat.constants :as chat.constants]
[status-im.chat.models :as chat]
[status-im.chat.models.message :as chat.message]
[status-im.constants :as constants]
[status-im.js-dependencies :as dependencies] [status-im.js-dependencies :as dependencies]
[status-im.utils.datetime :as datetime]
[status-im.utils.fx :as fx] [status-im.utils.fx :as fx]
[taoensso.timbre :as log])) [taoensso.timbre :as log]))

View File

@ -1,11 +1,11 @@
(ns status-im.chat.models.loading (ns status-im.chat.models.loading
(:require [clojure.set :as set] (:require [clojure.set :as set]
[status-im.chat.commands.core :as commands]
[status-im.chat.models :as chat-model]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.data-store.contacts :as contacts-store] [status-im.data-store.contacts :as contacts-store]
[status-im.data-store.user-statuses :as user-statuses-store] [status-im.data-store.user-statuses :as user-statuses-store]
[status-im.utils.contacts :as utils.contacts] [status-im.utils.contacts :as utils.contacts]
[status-im.chat.commands.core :as commands]
[status-im.chat.models :as chat-model]
[status-im.utils.datetime :as time] [status-im.utils.datetime :as time]
[status-im.utils.fx :as fx])) [status-im.utils.fx :as fx]))

View File

@ -17,8 +17,7 @@
[status-im.utils.types :as types] [status-im.utils.types :as types]
[status-im.notifications.core :as notifications] [status-im.notifications.core :as notifications]
[status-im.transport.utils :as transport.utils] [status-im.transport.utils :as transport.utils]
[status-im.transport.message.core :as transport] [status-im.transport.message.protocol :as protocol]
[status-im.transport.message.v1.protocol :as protocol]
[status-im.data-store.messages :as messages-store] [status-im.data-store.messages :as messages-store]
[status-im.data-store.user-statuses :as user-statuses-store] [status-im.data-store.user-statuses :as user-statuses-store]
[status-im.utils.fx :as fx] [status-im.utils.fx :as fx]
@ -94,7 +93,7 @@
(fx/defn send-message-seen (fx/defn send-message-seen
[cofx chat-id message-id send-seen?] [cofx chat-id message-id send-seen?]
(when send-seen? (when send-seen?
(transport/send (protocol/map->MessagesSeen {:message-ids #{message-id}}) chat-id cofx))) (protocol/send (protocol/map->MessagesSeen {:message-ids #{message-id}}) chat-id cofx)))
(defn ensure-clock-value [{:keys [clock-value] :as message} {:keys [last-clock-value]}] (defn ensure-clock-value [{:keys [clock-value] :as message} {:keys [last-clock-value]}]
(if clock-value (if clock-value
@ -127,8 +126,8 @@
;; TODO (cammellos): Refactor so it's not computed twice ;; TODO (cammellos): Refactor so it's not computed twice
(add-outgoing-status cofx))] (add-outgoing-status cofx))]
(fx/merge cofx (fx/merge cofx
{:confirm-messages-processed [{:web3 web3 {:transport/confirm-messages-processed [{:web3 web3
:js-obj js-obj}]} :js-obj js-obj}]}
(add-message batch? message current-chat?) (add-message batch? message current-chat?)
;; Checking :outgoing here only works for now as we don't have a :seen ;; Checking :outgoing here only works for now as we don't have a :seen
;; status for public chats, if we add processing of our own messages ;; status for public chats, if we add processing of our own messages
@ -216,7 +215,7 @@
(group-chats/wrap-group-message cofx chat-id send-record) (group-chats/wrap-group-message cofx chat-id send-record)
send-record)] send-record)]
(transport/send wrapped-record chat-id cofx)))) (protocol/send wrapped-record chat-id cofx))))
(defn add-message-type [message {:keys [chat-id group-chat public?]}] (defn add-message-type [message {:keys [chat-id group-chat public?]}]
(cond-> message (cond-> message

View File

@ -1,6 +1,7 @@
(ns status-im.data-store.realm.schemas.account.core (ns status-im.data-store.realm.schemas.account.core
(:require [status-im.data-store.realm.schemas.account.chat :as chat] (:require [status-im.data-store.realm.schemas.account.chat :as chat]
[status-im.data-store.realm.schemas.account.transport :as transport] [status-im.data-store.realm.schemas.account.transport :as transport]
[status-im.data-store.realm.schemas.account.transport-inbox-topic :as transport-inbox-topic]
[status-im.data-store.realm.schemas.account.contact :as contact] [status-im.data-store.realm.schemas.account.contact :as contact]
[status-im.data-store.realm.schemas.account.message :as message] [status-im.data-store.realm.schemas.account.message :as message]
[status-im.data-store.realm.schemas.account.user-status :as user-status] [status-im.data-store.realm.schemas.account.user-status :as user-status]
@ -143,18 +144,22 @@
browser/v8 browser/v8
dapp-permissions/v9]) dapp-permissions/v9])
(def v14 [chat/v6 (def v14 v13)
(def v15 [chat/v7
transport/v6 transport/v6
contact/v1 contact/v1
message/v7 message/v7
mailserver/v11 mailserver/v11
user-status/v1 user-status/v1
membership-update/v1
local-storage/v1 local-storage/v1
browser/v8 browser/v8
dapp-permissions/v9]) dapp-permissions/v9])
(def v15 [chat/v7 (def v16 [chat/v7
transport/v6 transport/v7
transport-inbox-topic/v1
contact/v1 contact/v1
message/v7 message/v7
mailserver/v11 mailserver/v11
@ -209,4 +214,7 @@
:migration migrations/v14} :migration migrations/v14}
{:schema v15 {:schema v15
:schemaVersion 15 :schemaVersion 15
:migration migrations/v15}]) :migration migrations/v15}
{:schema v16
:schemaVersion 16
:migration migrations/v16}])

View File

@ -97,3 +97,6 @@
(defn v15 [old-realm new-realm] (defn v15 [old-realm new-realm]
(log/debug "migrating v15 account database")) (log/debug "migrating v15 account database"))
(defn v16 [old-realm new-realm]
(log/debug "migrating v16 account database"))

View File

@ -47,3 +47,20 @@
;;TODO (yenda) remove once go implements persistence ;;TODO (yenda) remove once go implements persistence
:sym-key {:type :string :sym-key {:type :string
:optional true}}}) :optional true}}})
(def v7 {:name :transport
:primaryKey :chat-id
:properties {:chat-id :string
:ack :string
:seen :string
:pending-ack :string
:pending-send :string
:topic {:type :string
:optional true}
:resend? {:type :string
:optional true}
:sym-key-id {:type :string
:optional true}
;;TODO (yenda) remove once go implements persistence
:sym-key {:type :string
:optional true}}})

View File

@ -0,0 +1,7 @@
(ns status-im.data-store.realm.schemas.account.transport-inbox-topic)
(def v1 {:name :transport-inbox-topic
:primaryKey :topic
:properties {:topic :string
:chat-ids :string
:last-request {:type :int :default 1}}})

View File

@ -44,3 +44,39 @@
(let [transport (core/single (let [transport (core/single
(core/get-by-field realm :transport :chat-id chat-id))] (core/get-by-field realm :transport :chat-id chat-id))]
(core/delete realm transport)))) (core/delete realm transport))))
(defn deserialize-inbox-topic [serialized-inbox-topic]
(-> serialized-inbox-topic
(dissoc :topic)
(update :chat-ids edn/read-string)))
(re-frame/reg-cofx
:data-store/transport-inbox-topics
(fn [cofx _]
(assoc cofx
:data-store/transport-inbox-topics
(reduce (fn [acc {:keys [topic] :as inbox-topic}]
(assoc acc topic (deserialize-inbox-topic inbox-topic)))
{}
(-> @core/account-realm
(core/get-all :transport-inbox-topic)
(core/all-clj :transport-inbox-topic))))))
(defn save-transport-inbox-topic-tx
"Returns tx function for saving transport inbox topic"
[{:keys [topic inbox-topic]}]
(fn [realm]
(core/create realm
:transport-inbox-topic
(-> inbox-topic
(assoc :topic topic)
(update :chat-ids pr-str))
true)))
(defn delete-transport-inbox-topic-tx
"Returns tx function for deleting transport inbox-topic"
[topic]
(fn [realm]
(let [transport-inbox-topic (core/single
(core/get-by-field realm :transport-inbox-topic :topic topic))]
(core/delete realm transport-inbox-topic))))

View File

@ -9,11 +9,11 @@
[status-im.bootnodes.core :as bootnodes] [status-im.bootnodes.core :as bootnodes]
[status-im.browser.core :as browser] [status-im.browser.core :as browser]
[status-im.browser.permissions :as browser.permissions] [status-im.browser.permissions :as browser.permissions]
[status-im.chat.models :as chat]
[status-im.chat.models.message :as chat.message]
[status-im.chat.models.loading :as chat.loading]
[status-im.chat.models.input :as chat.input]
[status-im.chat.commands.input :as commands.input] [status-im.chat.commands.input :as commands.input]
[status-im.chat.models :as chat]
[status-im.chat.models.input :as chat.input]
[status-im.chat.models.loading :as chat.loading]
[status-im.chat.models.message :as chat.message]
[status-im.data-store.core :as data-store] [status-im.data-store.core :as data-store]
[status-im.fleet.core :as fleet] [status-im.fleet.core :as fleet]
[status-im.group-chats.core :as group-chats] [status-im.group-chats.core :as group-chats]
@ -28,6 +28,8 @@
[status-im.protocol.core :as protocol] [status-im.protocol.core :as protocol]
[status-im.qr-scanner.core :as qr-scanner] [status-im.qr-scanner.core :as qr-scanner]
[status-im.signals.core :as signals] [status-im.signals.core :as signals]
[status-im.transport.inbox :as inbox]
[status-im.transport.message.core :as transport.message]
[status-im.ui.screens.currency-settings.models [status-im.ui.screens.currency-settings.models
:as :as
currency-settings.models] currency-settings.models]
@ -89,6 +91,7 @@
(re-frame/inject-cofx :data-store/get-all-contacts) (re-frame/inject-cofx :data-store/get-all-contacts)
(re-frame/inject-cofx :data-store/get-all-mailservers) (re-frame/inject-cofx :data-store/get-all-mailservers)
(re-frame/inject-cofx :data-store/transport) (re-frame/inject-cofx :data-store/transport)
(re-frame/inject-cofx :data-store/transport-inbox-topics)
(re-frame/inject-cofx :data-store/all-browsers) (re-frame/inject-cofx :data-store/all-browsers)
(re-frame/inject-cofx :data-store/all-dapp-permissions)] (re-frame/inject-cofx :data-store/all-dapp-permissions)]
(fn [cofx [_ address]] (fn [cofx [_ address]]
@ -938,7 +941,65 @@
(fn [cofx [_ group-update sender-signature]] (fn [cofx [_ group-update sender-signature]]
(group-chats/handle-membership-update cofx group-update sender-signature))) (group-chats/handle-membership-update cofx group-update sender-signature)))
;; profile module
(handlers/register-handler-fx (handlers/register-handler-fx
:profile.ui/ens-names-button-pressed :profile.ui/ens-names-button-pressed
(fn [cofx] (fn [cofx]
(browser/open-url cofx "names.statusnet.eth"))) (browser/open-url cofx "names.statusnet.eth")))
;; inbox module
(handlers/register-handler-fx
:inbox.ui/reconnect-mailserver-pressed
(fn [cofx [_ args]]
(inbox/connect-to-mailserver cofx)))
(handlers/register-handler-fx
:inbox/check-connection-timeout
(fn [cofx _]
(inbox/check-connection cofx)))
(handlers/register-handler-fx
:inbox.callback/generate-mailserver-symkey-success
(fn [cofx [_ wnode sym-key-id]]
(inbox/add-mailserver-sym-key cofx wnode sym-key-id)))
(handlers/register-handler-fx
:inbox.callback/mark-trusted-peer-success
(fn [cofx _]
(inbox/add-mailserver-trusted cofx)))
(handlers/register-handler-fx
:inbox.callback/mark-trusted-peer-error
(fn [cofx [_ error]]
(log/error "Error on mark-trusted-peer: " error)
(inbox/check-connection cofx)))
(handlers/register-handler-fx
:inbox.callback/request-messages-success
(fn [cofx [_ request]]
(inbox/add-request cofx request)))
;; transport module
(handlers/register-handler-fx
:transport/messages-received
[handlers/logged-in (re-frame/inject-cofx :random-id-generator)]
(fn [cofx [_ js-error js-messages chat-id]]
(transport.message/receive-whisper-messages cofx js-error js-messages chat-id)))
(handlers/register-handler-fx
:transport/send-status-message-error
(fn [{:keys [db] :as cofx} [_ err]]
(log/error :send-status-message-error err)))
(handlers/register-handler-fx
:transport/message-sent
(fn [cofx [_ chat-id message-id message-type envelope-hash-js]]
(transport.message/set-message-envelope-hash cofx chat-id message-id message-type envelope-hash-js)))
(handlers/register-handler-fx
:transport/contact-message-sent
(fn [cofx [_ chat-id envelope-hash]]
(transport.message/set-contact-message-envelope-hash cofx chat-id envelope-hash)))

View File

@ -11,9 +11,8 @@
[status-im.transport.utils :as transport.utils] [status-im.transport.utils :as transport.utils]
[status-im.transport.db :as transport.db] [status-im.transport.db :as transport.db]
[status-im.transport.utils :as transport.utils] [status-im.transport.utils :as transport.utils]
[status-im.transport.message.core :as protocol.message] [status-im.transport.message.protocol :as protocol]
[status-im.transport.message.v1.core :as transport] [status-im.transport.message.group-chat :as message.group-chat]
[status-im.transport.message.v1.protocol :as transport.protocol]
[status-im.utils.fx :as fx] [status-im.utils.fx :as fx]
[status-im.chat.models :as models.chat])) [status-im.chat.models :as models.chat]))
@ -105,7 +104,7 @@
"Wrap a group message in a membership update" "Wrap a group message in a membership update"
[cofx chat-id message] [cofx chat-id message]
(when-let [chat (get-in cofx [:db :chats chat-id])] (when-let [chat (get-in cofx [:db :chats chat-id])]
(transport/map->GroupMembershipUpdate. (message.group-chat/map->GroupMembershipUpdate.
{:chat-id chat-id {:chat-id chat-id
:membership-updates (:membership-updates chat) :membership-updates (:membership-updates chat)
:message message}))) :message message})))
@ -123,7 +122,7 @@
{:shh/send-group-message {:web3 web3 {:shh/send-group-message {:web3 web3
:src current-public-key :src current-public-key
:dsts (disj members current-public-key) :dsts (disj members current-public-key)
:success-event [:transport/set-message-envelope-hash :success-event [:transport/message-sent
chat-id chat-id
(transport.utils/message-id (:message payload)) (transport.utils/message-id (:message payload))
:group-user-message] :group-user-message]
@ -137,8 +136,8 @@
(defn chat->group-update (defn chat->group-update
"Transform a chat in a GroupMembershipUpdate" "Transform a chat in a GroupMembershipUpdate"
[chat-id {:keys [membership-updates]}] [chat-id {:keys [membership-updates]}]
(transport/map->GroupMembershipUpdate. {:chat-id chat-id (message.group-chat/map->GroupMembershipUpdate. {:chat-id chat-id
:membership-updates membership-updates})) :membership-updates membership-updates}))
(defn handle-sign-response (defn handle-sign-response
"Callback to dispatch on sign response" "Callback to dispatch on sign response"
@ -312,9 +311,9 @@
(update-membership previous-chat membership-update) (update-membership previous-chat membership-update)
#(when (and message #(when (and message
;; don't allow anything but group messages ;; don't allow anything but group messages
(instance? transport.protocol/Message message) (instance? protocol/Message message)
(= :group-user-message (:message-type message))) (= :group-user-message (:message-type message)))
(protocol.message/receive message chat-id sender-signature nil %)))))) (protocol/receive message chat-id sender-signature nil %))))))
(defn handle-sign-success (defn handle-sign-success
"Upsert chat and send signed payload to group members" "Upsert chat and send signed payload to group members"

View File

@ -110,11 +110,6 @@
{:db (assoc db :inbox/current-id {:db (assoc db :inbox/current-id
(selected-or-random-id cofx))}) (selected-or-random-id cofx))})
(fx/defn set-initial-last-request
[{:keys [db now] :as cofx}]
{:db (update-in db [:account/account :last-request]
(fnil identity (quot now 1000)))})
(fx/defn add-custom-mailservers (fx/defn add-custom-mailservers
[{:keys [db]} mailservers] [{:keys [db]} mailservers]
{:db (reduce (fn [db {:keys [id fleet] :as mailserver}] {:db (reduce (fn [db {:keys [id fleet] :as mailserver}]

View File

@ -1,7 +1,7 @@
(ns status-im.models.contact (ns status-im.models.contact
(:require [status-im.data-store.contacts :as contacts-store] (:require [status-im.data-store.contacts :as contacts-store]
[status-im.transport.message.core :as transport] [status-im.transport.message.protocol :as protocol]
[status-im.transport.message.v1.contact :as message.v1.contact] [status-im.transport.message.contact :as message.contact]
[status-im.utils.contacts :as utils.contacts] [status-im.utils.contacts :as utils.contacts]
[status-im.utils.fx :as fx])) [status-im.utils.fx :as fx]))
@ -39,8 +39,8 @@
[{:keys [db] :as cofx} {:keys [whisper-identity pending? dapp?] :as contact}] [{:keys [db] :as cofx} {:keys [whisper-identity pending? dapp?] :as contact}]
(when-not dapp? (when-not dapp?
(if pending? (if pending?
(transport/send (message.v1.contact/map->ContactRequestConfirmed (own-info db)) whisper-identity cofx) (protocol/send (message.contact/map->ContactRequestConfirmed (own-info db)) whisper-identity cofx)
(transport/send (message.v1.contact/map->ContactRequest (own-info db)) whisper-identity cofx)))) (protocol/send (message.contact/map->ContactRequest (own-info db)) whisper-identity cofx))))
(fx/defn add-contact [{:keys [db] :as cofx} whisper-id] (fx/defn add-contact [{:keys [db] :as cofx} whisper-id]
(let [contact (build-contact whisper-id cofx)] (let [contact (build-contact whisper-id cofx)]

View File

@ -157,7 +157,7 @@
[{:keys [db] :as cofx} is-connected?] [{:keys [db] :as cofx} is-connected?]
(fx/merge cofx (fx/merge cofx
{:db (assoc db :network-status (if is-connected? :online :offline))} {:db (assoc db :network-status (if is-connected? :online :offline))}
(inbox/request-messages))) (inbox/request-messages nil)))
(defn- navigate-to-network-details (defn- navigate-to-network-details
[cofx network show-warning?] [cofx network show-warning?]

View File

@ -4,9 +4,9 @@
[status-im.transport.core :as transport] [status-im.transport.core :as transport]
[status-im.transport.inbox :as transport.inbox] [status-im.transport.inbox :as transport.inbox]
[status-im.utils.ethereum.core :as ethereum] [status-im.utils.ethereum.core :as ethereum]
[status-im.utils.fx :as fx]
[status-im.utils.semaphores :as semaphores] [status-im.utils.semaphores :as semaphores]
[status-im.utils.utils :as utils] [status-im.utils.utils :as utils]))
[status-im.utils.fx :as fx]))
(fx/defn update-sync-state (fx/defn update-sync-state
[{{:keys [sync-state sync-data] :as db} :db} error sync] [{{:keys [sync-state sync-data] :as db} :db} error sync]
@ -44,13 +44,15 @@
(semaphores/lock :check-sync-state?)))) (semaphores/lock :check-sync-state?))))
(fx/defn initialize-protocol (fx/defn initialize-protocol
[{:data-store/keys [transport mailservers] :keys [db web3] :as cofx} address] [{:data-store/keys [transport transport-inbox-topics mailservers]
:keys [db web3] :as cofx} address]
(let [network (get-in db [:account/account :network]) (let [network (get-in db [:account/account :network])
network-id (str (get-in db [:account/account :networks network :config :NetworkId]))] network-id (str (get-in db [:account/account :networks network :config :NetworkId]))]
(fx/merge cofx (fx/merge cofx
{:db (assoc db {:db (assoc db
:rpc-url constants/ethereum-rpc-url :rpc-url constants/ethereum-rpc-url
:transport/chats transport) :transport/chats transport
:transport.inbox/topics transport-inbox-topics)
:protocol/assert-correct-network {:web3 web3 :protocol/assert-correct-network {:web3 web3
:network-id network-id}} :network-id network-id}}
(start-check-sync-state) (start-check-sync-state)

View File

@ -3,11 +3,11 @@
[status-im.accounts.login.core :as accounts.login] [status-im.accounts.login.core :as accounts.login]
[status-im.init.core :as init] [status-im.init.core :as init]
[status-im.node.core :as node] [status-im.node.core :as node]
[status-im.transport.handlers :as transport.handlers]
[status-im.transport.inbox :as inbox] [status-im.transport.inbox :as inbox]
[status-im.transport.message.core :as transport.message]
[status-im.utils.fx :as fx]
[status-im.utils.types :as types] [status-im.utils.types :as types]
[taoensso.timbre :as log] [taoensso.timbre :as log]))
[status-im.utils.fx :as fx]))
(fx/defn status-node-started (fx/defn status-node-started
[{db :db :as cofx}] [{db :db :as cofx}]
@ -33,7 +33,7 @@
{:db (assoc db {:db (assoc db
:peers-summary peers-summary :peers-summary peers-summary
:peers-count peers-count)} :peers-count peers-count)}
(transport.handlers/resend-contact-messages previous-summary) (transport.message/resend-contact-messages previous-summary)
(inbox/peers-summary-change previous-summary)))) (inbox/peers-summary-change previous-summary))))
(fx/defn process (fx/defn process
@ -43,7 +43,12 @@
"node.ready" (status-node-started cofx) "node.ready" (status-node-started cofx)
"node.stopped" (status-node-stopped cofx) "node.stopped" (status-node-stopped cofx)
"module.initialized" (status-module-initialized cofx) "module.initialized" (status-module-initialized cofx)
"envelope.sent" (transport.handlers/update-envelope-status cofx (:hash event) :sent) "envelope.sent" (transport.message/update-envelope-status cofx (:hash event) :sent)
"envelope.expired" (transport.handlers/update-envelope-status cofx (:hash event) :sent) "envelope.expired" (transport.message/update-envelope-status cofx (:hash event) :sent)
"mailserver.request.completed" (when (accounts.db/logged-in? cofx)
(inbox/update-inbox-topic cofx {:request-id (:requestID event)
:cursor (:cursor event)}))
"mailserver.request.expired" (when (accounts.db/logged-in? cofx)
(inbox/resend-request cofx {:request-id (:hash event)}))
"discovery.summary" (summary cofx event) "discovery.summary" (summary cofx event)
(log/debug "Event " type " not handled")))) (log/debug "Event " type " not handled"))))

View File

@ -0,0 +1,17 @@
(ns status-im.transport.chat.core
(:require [status-im.data-store.transport :as transport-store]
[status-im.transport.inbox :as inbox]
[status-im.utils.fx :as fx]))
(fx/defn remove-transport-chat
[{:keys [db]} chat-id]
{:db (update db :transport/chats dissoc chat-id)
:data-store/tx [(transport-store/delete-transport-tx chat-id)]
:shh/remove-filter (get-in db [:transport/filters chat-id])})
(fx/defn unsubscribe-from-chat
"Unsubscribe from chat on transport layer"
[cofx chat-id]
(fx/merge cofx
(inbox/remove-chat-from-inbox-topic chat-id)
(remove-transport-chat chat-id)))

View File

@ -4,12 +4,13 @@
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.data-store.transport :as transport-store] [status-im.data-store.transport :as transport-store]
[status-im.transport.handlers :as transport.handlers]
[status-im.transport.inbox :as inbox] [status-im.transport.inbox :as inbox]
[status-im.transport.message.core :as message]
[status-im.transport.shh :as shh]
[status-im.transport.utils :as transport.utils] [status-im.transport.utils :as transport.utils]
[status-im.utils.fx :as fx]
[status-im.utils.handlers :as handlers] [status-im.utils.handlers :as handlers]
[taoensso.timbre :as log] [taoensso.timbre :as log]))
[status-im.utils.fx :as fx]))
(fx/defn init-whisper (fx/defn init-whisper
"Initialises whisper protocol by: "Initialises whisper protocol by:
@ -19,6 +20,7 @@
[{:keys [db web3] :as cofx} current-account-id] [{:keys [db web3] :as cofx} current-account-id]
(log/debug :init-whisper) (log/debug :init-whisper)
(when-let [public-key (get-in db [:account/account :public-key])] (when-let [public-key (get-in db [:account/account :public-key])]
(let [sym-key-added-callback (fn [chat-id sym-key sym-key-id] (let [sym-key-added-callback (fn [chat-id sym-key sym-key-id]
(re-frame/dispatch [::sym-key-added {:chat-id chat-id (re-frame/dispatch [::sym-key-added {:chat-id chat-id
:sym-key sym-key :sym-key sym-key
@ -32,7 +34,7 @@
:transport (:transport/chats db) :transport (:transport/chats db)
:on-success sym-key-added-callback}} :on-success sym-key-added-callback}}
(inbox/connect-to-mailserver) (inbox/connect-to-mailserver)
(transport.handlers/resend-contact-messages []))))) (message/resend-contact-messages [])))))
;;TODO (yenda) remove once go implements persistence ;;TODO (yenda) remove once go implements persistence
;;Since symkeys are not persisted, we restore them via add sym-keys, ;;Since symkeys are not persisted, we restore them via add sym-keys,
@ -67,7 +69,5 @@
to clean-up after logout. When logging out of account A and logging in account B, account B would receive to clean-up after logout. When logging out of account A and logging in account B, account B would receive
account A messages without this." account A messages without this."
[{:keys [db]}] [{:keys [db]}]
(let [{:transport/keys [chats discovery-filter]} db (let [{:transport/keys [filters]} db]
chat-filters (keep :filter (vals chats)) {:shh/remove-filters (vals filters)}))
all-filters (conj chat-filters discovery-filter)]
{:shh/remove-filters all-filters}))

View File

@ -1,41 +1,61 @@
(ns ^{:doc "DB spec and utils for the transport layer"} (ns ^{:doc "DB spec and utils for the transport layer"}
status-im.transport.db status-im.transport.db
(:require-macros [status-im.utils.db :refer [allowed-keys]])
(:require [cljs.spec.alpha :as spec] (:require [cljs.spec.alpha :as spec]
[clojure.string :as s]
status-im.ui.screens.contacts.db status-im.ui.screens.contacts.db
[status-im.utils.clocks :as utils.clocks] [status-im.utils.clocks :as utils.clocks])
[clojure.string :as s])) (:require-macros [status-im.utils.db :refer [allowed-keys]]))
;; required ;; required
(spec/def ::ack (spec/coll-of string? :kind vector?)) (spec/def ::ack (spec/coll-of string? :kind vector?))
(spec/def ::seen (spec/coll-of string? :kind vector?)) (spec/def ::seen (spec/coll-of string? :kind vector?))
(spec/def ::pending-ack (spec/coll-of string? :kind vector?)) (spec/def ::pending-ack (spec/coll-of string? :kind vector?))
(spec/def ::pending-send (spec/coll-of string? :kind vector?)) (spec/def ::pending-send (spec/coll-of string? :kind vector?))
(spec/def ::topic string?)
(spec/def ::fetch-history? boolean?)
(spec/def ::resend? (spec/nilable #{"contact-request" "contact-request-confirmation" "contact-update"})) (spec/def ::resend? (spec/nilable #{"contact-request" "contact-request-confirmation" "contact-update"}))
(spec/def ::request-from pos-int?)
;; optional ;; optional
(spec/def ::topic (spec/nilable string?))
(spec/def ::request-id (spec/nilable string?))
(spec/def ::request-to (spec/nilable pos-int?))
(spec/def ::sym-key-id (spec/nilable string?)) (spec/def ::sym-key-id (spec/nilable string?))
;;TODO (yenda) remove once go implements persistence ;;TODO (yenda) remove once go implements persistence
(spec/def ::sym-key (spec/nilable string?)) (spec/def ::sym-key (spec/nilable string?))
(spec/def ::filter any?) (spec/def :transport/filter-id (spec/or :keyword keyword?
:chat-id :global/not-empty-string))
(spec/def :transport/filter any?)
(spec/def :request/from pos-int?)
(spec/def :request/to pos-int?)
(spec/def :request/cursor :global/not-empty-string)
(spec/def :transport.inbox/request (spec/keys :req-un [:request/from :request/to ::topic]))
(spec/def ::request-from pos-int?)
(spec/def :transport.inbox.topic/last-request ::request-from)
(spec/def :transport.inbox.topic/chat-id (spec/or :keyword keyword?
:chat-id :global/not-empty-string))
(spec/def :transport.inbox.topic/chat-ids (spec/coll-of :transport.inbox.topic/chat-id
:kind set?
:min-count 1))
(spec/def :transport.inbox.topic/request-pending? boolean?)
(spec/def :transport/chat (allowed-keys :req-un [::ack ::seen ::pending-ack ::pending-send ::topic ::fetch-history?] (spec/def :transport.inbox/topic (allowed-keys :req-un [:transport.inbox.topic/last-request
:opt-un [::sym-key-id ::sym-key ::filter ::resend?])) :transport.inbox.topic/chat-ids]
:opt-un [:transport.inbox.topic/request-pending?]))
(spec/def :transport/chat (allowed-keys :req-un [::ack ::seen ::pending-ack ::pending-send ::topic]
:opt-un [::sym-key-id ::sym-key ::resend?]))
(spec/def :transport/chats (spec/map-of :global/not-empty-string :transport/chat)) (spec/def :transport/chats (spec/map-of :global/not-empty-string :transport/chat))
(spec/def :transport/discovery-filter (spec/nilable any?)) (spec/def :transport/filters (spec/map-of :transport/filter-id :transport/filter))
(spec/def :transport.inbox/topics (spec/map-of :global/not-empty-string :transport.inbox/topic))
(spec/def :transport.inbox/requests (spec/map-of :global/not-empty-string :transport.inbox/request))
(defn create-chat (defn create-chat
"Initialize datastructure for chat representation at the transport level "Initialize datastructure for chat representation at the transport level
Currently only :topic is actually used" Currently only :topic is actually used"
[{:keys [topic resend?]}] [{:keys [topic resend? now]}]
{:ack [] {:ack []
:seen [] :seen []
:pending-ack [] :pending-ack []
:pending-send [] :pending-send []
:fetch-history? true
:resend? resend? :resend? resend?
:topic topic}) :topic topic})

View File

@ -1,9 +1,10 @@
(ns ^{:doc "API for whisper filters"} (ns ^{:doc "API for whisper filters"}
status-im.transport.filters status-im.transport.filters
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[status-im.utils.handlers :as handlers] [status-im.transport.inbox :as inbox]
[status-im.transport.utils :as utils] [status-im.transport.utils :as utils]
[status-im.utils.config :as config] [status-im.utils.fx :as fx]
[status-im.utils.handlers :as handlers]
[taoensso.timbre :as log])) [taoensso.timbre :as log]))
(defn remove-filter! [filter] (defn remove-filter! [filter]
@ -14,47 +15,41 @@
(log/debug :removed-filter filter)))) (log/debug :removed-filter filter))))
(log/debug :stop-watching filter)) (log/debug :stop-watching filter))
(defn add-shh-filter!
[web3 options callback]
(.newMessageFilter (utils/shh web3) (clj->js options)
callback
#(log/warn :add-filter-error (.stringify js/JSON (clj->js options)) %)))
(defn add-filter! (defn add-filter!
[web3 {:keys [topics to] :as options} callback] [web3 {:keys [topics to] :as options} callback chat-id]
(let [options (assoc options :allowP2P true)] (let [options (assoc options :allowP2P true)]
(log/debug :add-filter options) (log/debug :add-filter options)
(add-shh-filter! web3 options callback))) (when-let [filter (.newMessageFilter (utils/shh web3)
(clj->js options)
callback
#(log/warn :add-filter-error (.stringify js/JSON (clj->js options)) %))]
(re-frame/dispatch [:shh.callback/filter-added (first topics) chat-id filter]))))
(re-frame/reg-fx (re-frame/reg-fx
:shh/add-filter :shh/add-filter
(fn [{:keys [web3 sym-key-id topic chat-id]}] (fn [{:keys [web3 sym-key-id topic chat-id]}]
(when-let [filter (add-filter! web3 (let [params {:topics [topic]
{:topics [topic] :symKeyID sym-key-id}
:symKeyID sym-key-id} callback (fn [js-error js-message]
(fn [js-error js-message] (re-frame/dispatch [:transport/messages-received js-error js-message chat-id]))]
(re-frame/dispatch [:protocol/receive-whisper-message js-error js-message chat-id])))] (add-filter! web3 params callback chat-id))))
(re-frame/dispatch [::filter-added chat-id filter]))))
(handlers/register-handler-fx
::filter-added
(fn [{:keys [db]} [_ chat-id filter]]
{:db (assoc-in db [:transport/chats chat-id :filter] filter)}))
(re-frame/reg-fx (re-frame/reg-fx
:shh/add-discovery-filter :shh/add-discovery-filter
(fn [{:keys [web3 private-key-id topic]}] (fn [{:keys [web3 private-key-id topic]}]
(when-let [filter (add-filter! web3 (let [params {:topics [topic]
{:topics [topic] :privateKeyID private-key-id}
:privateKeyID private-key-id} callback (fn [js-error js-message]
(fn [js-error js-message] (re-frame/dispatch [:transport/messages-received js-error js-message]))]
(re-frame/dispatch [:protocol/receive-whisper-message js-error js-message])))] (add-filter! web3 params callback :discovery-topic))))
(re-frame/dispatch [::discovery-filter-added filter]))))
(handlers/register-handler-fx (handlers/register-handler-fx
::discovery-filter-added :shh.callback/filter-added
(fn [{:keys [db]} [_ filter]] (fn [{:keys [db] :as cofx} [_ topic chat-id filter]]
{:db (assoc db :transport/discovery-filter filter)})) (fx/merge cofx
{:db (assoc-in db [:transport/filters chat-id] filter)}
(inbox/upsert-inbox-topic {:topic topic
:chat-id chat-id}))))
(re-frame/reg-fx (re-frame/reg-fx
:shh/remove-filter :shh/remove-filter

View File

@ -1,214 +0,0 @@
(ns ^{:doc "Events for message handling"}
status-im.transport.handlers
(:require [re-frame.core :as re-frame]
[status-im.chat.models.message :as models.message]
[status-im.data-store.transport :as transport-store]
[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.protocol :as protocol]
[status-im.transport.shh :as shh]
[status-im.transport.utils :as transport.utils]
[status-im.utils.fx :as fx]
[status-im.utils.handlers :as handlers]
[taoensso.timbre :as log]))
(fx/defn update-last-received-from-inbox
"Distinguishes messages that are expired from those that are not
Expired messages are coming from offline inboxing"
[{:keys [db now] :as cofx} now-in-s timestamp ttl]
(when (> (- now-in-s timestamp) ttl)
{:db (assoc db :inbox/last-received now)}))
(fx/defn receive-message
[cofx now-in-s chat-id js-message]
(let [{:keys [payload sig timestamp ttl]} (js->clj js-message :keywordize-keys true)
status-message (-> payload
transport.utils/to-utf8
transit/deserialize)]
(when (and sig status-message)
(try
(when-let [valid-message (message/validate status-message)]
(fx/merge (assoc cofx :js-obj js-message)
#(message/receive valid-message (or chat-id sig) sig timestamp %)
(update-last-received-from-inbox now-in-s timestamp ttl)))
(catch :default e nil))))) ; ignore unknown message types
(defn- js-array->seq [array]
(for [i (range (.-length array))]
(aget array i)))
(fx/defn receive-whisper-messages
[{:keys [now] :as cofx} js-error js-messages chat-id]
(if (and (not js-error)
js-messages)
(let [now-in-s (quot now 1000)
receive-message-fxs (map (fn [message]
(receive-message now-in-s chat-id message))
(js-array->seq js-messages))]
(apply fx/merge cofx receive-message-fxs))
(do (log/error "Something went wrong" js-error js-messages)
cofx)))
(handlers/register-handler-fx
:protocol/receive-whisper-message
[handlers/logged-in (re-frame/inject-cofx :random-id-generator)]
(fn [cofx [_ js-error js-messages chat-id]]
(receive-whisper-messages cofx js-error js-messages chat-id)))
(handlers/register-handler-fx
:protocol/send-status-message-error
(fn [{:keys [db] :as cofx} [_ err]]
(log/error :send-status-message-error err)))
(handlers/register-handler-fx
:contact/send-new-sym-key
(fn [{:keys [db] :as cofx}
[_ {:keys [chat-id topic message sym-key sym-key-id]}]]
(let [{:keys [web3 current-public-key]} db
chat-transport-info (-> (get-in db [:transport/chats chat-id])
(assoc :sym-key-id sym-key-id
:sym-key sym-key
:topic topic))]
(fx/merge cofx
{:db (assoc-in db [:transport/chats chat-id] chat-transport-info)
: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 chat-transport-info})]}
#(message/send (v1.contact/NewContactKey. sym-key topic message)
chat-id %)))))
(handlers/register-handler-fx
:contact/add-new-sym-key
(fn [{:keys [db] :as cofx} [_ {:keys [sym-key-id sym-key chat-id topic timestamp message]}]]
(let [{:keys [web3 current-public-key]} db
chat-transport-info (-> (get-in db [:transport/chats chat-id])
(assoc :sym-key-id sym-key-id
:sym-key sym-key
:topic topic))]
(fx/merge cofx
{:db (assoc-in db
[:transport/chats chat-id]
chat-transport-info)
: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 chat-transport-info})]}
#(message/receive message chat-id chat-id timestamp %)))))
(handlers/register-handler-fx
:group/unsubscribe-from-chat
(fn [cofx [_ chat-id]]
(transport.utils/unsubscribe-from-chat chat-id cofx)))
(re-frame/reg-fx
;; TODO(janherich): this should be called after `:data-store/tx` actually
:confirm-messages-processed
(fn [messages]
(let [{:keys [web3]} (first messages)
js-messages (->> messages
(keep :js-obj)
(apply array))]
(when (pos? (.-length js-messages))
(.confirmMessagesProcessed (transport.utils/shh web3)
js-messages
(fn [err resp]
(when err
(log/info "Confirming messages processed failed"))))))))
(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-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
(fn [{:keys [db]} [_ chat-id envelope-hash]]
{:db (assoc-in db [:transport/message-envelopes envelope-hash]
{:chat-id chat-id
:message-type :contact-message})}))
(fx/defn remove-hash [{:keys [db] :as cofx} envelope-hash]
{:db (update db :transport/message-envelopes dissoc envelope-hash)})
(fx/defn update-resend-contact-message [{:keys [db] :as cofx} chat-id]
(let [chat (get-in db [:transport/chats chat-id])
updated-chat (assoc chat :resend? nil)]
{:db (assoc-in db [:transport/chats chat-id :resend?] nil)
:data-store/tx [(transport-store/save-transport-tx {:chat-id chat-id
:chat updated-chat})]}))
(fx/defn update-envelope-status
[{:keys [db] :as cofx} envelope-hash status]
(let [{:keys [chat-id message-type message-id]}
(get-in db [:transport/message-envelopes envelope-hash])]
(case message-type
:contact-message
(when (= :sent status)
(fx/merge cofx
(remove-hash envelope-hash)
(update-resend-contact-message chat-id)))
(when-let [{:keys [from]} (get-in db [:chats chat-id :messages message-id])]
(let [{:keys [fcm-token]} (get-in db [:contacts/contacts chat-id])]
(fx/merge cofx
(remove-hash envelope-hash)
(models.message/update-message-status chat-id message-id status)
(models.message/send-push-notification fcm-token status)))))))
(defn- own-info [db]
(let [{:keys [name photo-path address]} (:account/account db)
fcm-token (get-in db [:notifications :fcm-token])]
{:name name
:profile-image photo-path
:address address
:fcm-token fcm-token}))
(fx/defn resend-contact-request [cofx own-info chat-id {:keys [sym-key topic]}]
(message/send (v1.contact/NewContactKey. sym-key
topic
(v1.contact/map->ContactRequest own-info))
chat-id cofx))
(fx/defn resend-contact-message
[cofx own-info chat-id]
(let [{:keys [resend?] :as chat} (get-in cofx [:db :transport/chats chat-id])]
(case resend?
"contact-request"
(resend-contact-request cofx own-info chat-id chat)
"contact-request-confirmation"
(message/send (v1.contact/map->ContactRequestConfirmed own-info)
chat-id
cofx)
"contact-update"
(protocol/send cofx
{:chat-id chat-id
:payload (v1.contact/map->ContactUpdate own-info)})
nil)))
(fx/defn resend-contact-messages
[{:keys [db] :as cofx} previous-summary]
(when (and (zero? (count previous-summary))
(= :online (:network-status db))
(pos? (count (:peers-summary db))))
(let [own-info (own-info db)
resend-contact-message-fxs (map (fn [chat-id]
(resend-contact-message own-info chat-id))
(keys (:transport/chats db)))]
(apply fx/merge cofx resend-contact-message-fxs))))

View File

@ -1,27 +1,26 @@
(ns status-im.transport.impl.receive (ns status-im.transport.impl.receive
(:require (:require [status-im.group-chats.core :as group-chats]
[status-im.models.contact :as models.contact] [status-im.models.contact :as models.contact]
[status-im.group-chats.core :as group-chats] [status-im.transport.message.contact :as transport.contact]
[status-im.transport.message.core :as message] [status-im.transport.message.group-chat :as transport.group-chat]
[status-im.transport.message.v1.contact :as transport.contact] [status-im.transport.message.protocol :as protocol]))
[status-im.transport.message.v1.core :as transport.protocol]))
(extend-type transport.protocol/GroupMembershipUpdate (extend-type transport.group-chat/GroupMembershipUpdate
message/StatusMessage protocol/StatusMessage
(receive [this _ signature _ cofx] (receive [this _ signature _ cofx]
(group-chats/handle-membership-update-received cofx this signature))) (group-chats/handle-membership-update-received cofx this signature)))
(extend-type transport.contact/ContactRequest (extend-type transport.contact/ContactRequest
message/StatusMessage protocol/StatusMessage
(receive [this _ signature timestamp cofx] (receive [this _ signature timestamp cofx]
(models.contact/receive-contact-request signature timestamp this cofx))) (models.contact/receive-contact-request signature timestamp this cofx)))
(extend-type transport.contact/ContactRequestConfirmed (extend-type transport.contact/ContactRequestConfirmed
message/StatusMessage protocol/StatusMessage
(receive [this _ signature timestamp cofx] (receive [this _ signature timestamp cofx]
(models.contact/receive-contact-request-confirmation signature timestamp this cofx))) (models.contact/receive-contact-request-confirmation signature timestamp this cofx)))
(extend-type transport.contact/ContactUpdate (extend-type transport.contact/ContactUpdate
message/StatusMessage protocol/StatusMessage
(receive [this _ signature timestamp cofx] (receive [this _ signature timestamp cofx]
(models.contact/receive-contact-update signature timestamp this cofx))) (models.contact/receive-contact-update signature timestamp this cofx)))

View File

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

View File

@ -2,7 +2,6 @@
status-im.transport.inbox status-im.transport.inbox
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.data-store.accounts :as accounts-store]
[status-im.data-store.core :as data-store] [status-im.data-store.core :as data-store]
[status-im.data-store.transport :as transport-store] [status-im.data-store.transport :as transport-store]
[status-im.fleet.core :as fleet] [status-im.fleet.core :as fleet]
@ -10,38 +9,28 @@
[status-im.native-module.core :as status] [status-im.native-module.core :as status]
[status-im.transport.utils :as transport.utils] [status-im.transport.utils :as transport.utils]
[status-im.utils.fx :as fx] [status-im.utils.fx :as fx]
[status-im.utils.handlers :as handlers]
[status-im.utils.utils :as utils] [status-im.utils.utils :as utils]
[taoensso.timbre :as log])) [taoensso.timbre :as log]))
;; How does offline inboxing work ? ;; How does offline inboxing work ?
;; ;;
;; - We send a request to the mailserver, we are only interested in the ;; - We send a request to the mailserver, we are only interested in the
;; messages since `last-request`, the time of the last successful request, ;; messages since `last-request` up to the last seven days
;; and the last 24 hours for topics that were just joined ;; and the last 24 hours for topics that were just joined
;; - The mailserver doesn't directly respond to the request and ;; - The mailserver doesn't directly respond to the request and
;; instead we start receiving messages in the filters for the requested ;; instead we start receiving messages in the filters for the requested
;; topics. ;; topics.
;; - These messages are expired that is how we differentiate them from
;; normal whisper messages to update last-received
;; - After fetching-timeout is reached since the last mailserver message
;; was received without a connection incident, we consider the request
;; successfull and update `last-request` and `fetch-history?` fields of each
;; topic to false
;; - If the mailserver was not ready when we tried for instance to request ;; - If the mailserver was not ready when we tried for instance to request
;; the history of a topic after joining a chat, the request will be done ;; the history of a topic after joining a chat, the request will be done
;; as soon as the mailserver becomes available ;; as soon as the mailserver becomes available
(def one-day (* 24 3600))
(def seven-days (* 7 one-day))
(def connection-timeout (def connection-timeout
"Time after which mailserver connection is considered to have failed" "Time after which mailserver connection is considered to have failed"
15000) 5000)
(def fetching-timeout
"Time we should wait after last message was fetch from mailserver before we
consider it done
Needs to be at least 10 seconds because that is the time it takes for the app
to realize it was disconnected"
10000)
(defn- parse-json (defn- parse-json
;; NOTE(dmitryn) Expects JSON response like: ;; NOTE(dmitryn) Expects JSON response like:
@ -61,7 +50,7 @@
(catch :default e (catch :default e
{:error (.-message e)}))) {:error (.-message e)})))
(defn- response-handler [error-fn success-fn] (defn- response-handler [success-fn error-fn]
(fn handle-response (fn handle-response
([response] ([response]
(let [{:keys [error result]} (parse-json response)] (let [{:keys [error result]} (parse-json response)]
@ -71,126 +60,86 @@
(error-fn error) (error-fn error)
(success-fn result))))) (success-fn result)))))
(fx/defn add-sym-key-id-to-wnode (defn add-peer! [wnode]
[{:keys [db]} {:keys [id]} sym-key-id] (status/add-peer wnode
(let [current-fleet (fleet/current-fleet db)] (response-handler #(log/debug "offline inbox: add-peer success" %)
{:db (assoc-in db [:inbox/wnodes current-fleet id :sym-key-id] sym-key-id)})) #(log/error "offline inbox: add-peer error" %))))
(defn registered-peer? [peers enode] (re-frame/reg-fx
:transport.inbox/add-peer
(fn [wnode]
(add-peer! wnode)))
(defn mark-trusted-peer! [web3 enode]
(.markTrustedPeer (transport.utils/shh web3)
enode
(fn [error response]
(if error
(re-frame/dispatch [:inbox.callback/mark-trusted-peer-error error])
(re-frame/dispatch [:inbox.callback/mark-trusted-peer-success response])))))
(re-frame/reg-fx
:transport.inbox/mark-trusted-peer
(fn [{:keys [wnode web3]}]
(mark-trusted-peer! web3 wnode)))
(fx/defn generate-mailserver-symkey
[{:keys [db] :as cofx} {:keys [password id] :as wnode}]
(let [current-fleet (fleet/current-fleet db)]
{:db (assoc-in db [:inbox/wnodes current-fleet id :generating-sym-key?] true)
:shh/generate-sym-key-from-password
[{:password password
:web3 (:web3 db)
:on-success (fn [_ sym-key-id]
(re-frame/dispatch [:inbox.callback/generate-mailserver-symkey-success wnode sym-key-id]))
:on-error #(log/error "offline inbox: get-sym-key error" %)}]}))
(defn registered-peer?
"truthy if the enode is a registered peer"
[peers enode]
(let [peer-ids (into #{} (map :id) peers) (let [peer-ids (into #{} (map :id) peers)
enode-id (transport.utils/extract-enode-id enode)] enode-id (transport.utils/extract-enode-id enode)]
(contains? peer-ids enode-id))) (contains? peer-ids enode-id)))
(defn add-peer [enode success-fn error-fn] (defn update-mailserver-status [db state]
(status/add-peer enode (response-handler error-fn success-fn))) (assoc db :mailserver-status state))
(defn mark-trusted-peer [web3 enode success-fn error-fn] (fx/defn mark-trusted-peer
(.markTrustedPeer (transport.utils/shh web3) [{:keys [db] :as cofx}]
enode (let [{:keys [address sym-key-id generating-sym-key?] :as wnode} (mailserver/fetch-current cofx)]
(fn [err resp] (fx/merge cofx
(if-not err {:db (update-mailserver-status db :added)
(success-fn resp) :transport.inbox/mark-trusted-peer {:web3 (:web3 db)
(error-fn err))))) :wnode address}}
(when-not (or sym-key-id generating-sym-key?)
(generate-mailserver-symkey wnode)))))
(def one-day (* 24 3600)) (fx/defn add-peer
(def seven-days (* 7 one-day)) [{:keys [db] :as cofx}]
(let [{:keys [address sym-key-id generating-sym-key?] :as wnode} (mailserver/fetch-current cofx)]
(defn request-inbox-messages-params [mailserver from to topics] (fx/merge cofx
(let [days (conj {:db (update-mailserver-status db :connecting)
(into [] (range from to one-day)) :transport.inbox/add-peer address
to) :utils/dispatch-later [{:ms connection-timeout
day-ranges (map vector days (drop 1 days))] :dispatch [:inbox/check-connection-timeout]}]}
(for [topic topics (when-not (or sym-key-id generating-sym-key?)
[current-from current-to] day-ranges] (generate-mailserver-symkey wnode)))))
{:topic topic
:mailServerPeer (:address mailserver)
:symKeyID (:sym-key-id mailserver)
:from current-from
:to current-to})))
(defn request-inbox-messages
[web3 mailserver topics start-from end-to success-fn error-fn]
(log/info "offline inbox: request-messages request for topics " topics " from " start-from " to " end-to)
(doseq [{:keys [topic] :as params} (request-inbox-messages-params
mailserver
start-from
end-to
topics)]
(log/info "offline inbox: request-messages for: "
" topic " topic
" from " (:from params)
" to " (:to params))
(.requestMessages (transport.utils/shh web3)
(clj->js params)
(fn [err resp]
(if-not err
(success-fn resp topic)
(error-fn err topic))))))
(re-frame/reg-fx
::add-peer
(fn [{:keys [wnode]}]
(add-peer wnode
#(log/debug "offline inbox: add-peer success" %)
#(log/error "offline inbox: add-peer error" %))))
(re-frame/reg-fx
::mark-trusted-peer
(fn [{:keys [wnode web3]}]
(mark-trusted-peer web3
wnode
#(re-frame/dispatch [:inbox/mailserver-trusted %])
#(re-frame/dispatch [:inbox/check-connection]))))
(re-frame/reg-fx
::request-messages
(fn [params]
(doseq [{:keys [wnode topics to from web3]} params]
(request-inbox-messages web3
wnode
topics
from
to
#(log/info "offline inbox: request-messages response" %1 %2 from to)
#(log/error "offline inbox: request-messages error" %1 %2 from to)))))
(fx/defn update-mailserver-status [{:keys [db]} transition]
(let [state transition]
{:db (assoc db
:mailserver-status state
:inbox/fetching? false)}))
(fx/defn generate-mailserver-symkey [{:keys [db] :as cofx} wnode]
(when-not (:sym-key-id wnode)
{:shh/generate-sym-key-from-password
[{:password (:password wnode)
:web3 (:web3 db)
:on-success (fn [_ sym-key-id]
(re-frame/dispatch [:inbox/get-sym-key-success wnode sym-key-id]))
:on-error #(log/error "offline inbox: get-sym-key error" %)}]}))
(fx/defn connect-to-mailserver (fx/defn connect-to-mailserver
"Add mailserver as a peer using ::add-peer cofx and generate sym-key when "Add mailserver as a peer using `::add-peer` cofx and generate sym-key when
it doesn't exists it doesn't exists
Peer summary will change and we will receive a signal from status go when Peer summary will change and we will receive a signal from status go when
this is successful this is successful
A connection-check is made after `connection timeout` is reached and A connection-check is made after `connection timeout` is reached and
mailserver-status is changed to error if it is not connected by then" mailserver-status is changed to error if it is not connected by then"
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [web3 (:web3 db) (let [{:keys [address] :as wnode} (mailserver/fetch-current cofx)
{:keys [address] :as wnode} (mailserver/fetch-current cofx) {:keys [peers-summary]} db
peers-summary (:peers-summary db) added? (registered-peer? peers-summary
connected? (registered-peer? peers-summary address)] address)]
(if connected? (if added?
(fx/merge cofx (mark-trusted-peer cofx)
(update-mailserver-status :connected) (add-peer cofx))))
(generate-mailserver-symkey wnode))
(fx/merge cofx
{::add-peer {:wnode address}
:utils/dispatch-later [{:ms connection-timeout
:dispatch [:inbox/check-connection]}]}
(update-mailserver-status :connecting)
(generate-mailserver-symkey wnode)))))
(fx/defn peers-summary-change (fx/defn peers-summary-change
"There is only 2 summary changes that require offline inboxing action: "There is only 2 summary changes that require offline inboxing action:
@ -199,183 +148,191 @@
[{:keys [db] :as cofx} previous-summary] [{:keys [db] :as cofx} previous-summary]
(when (:account/account db) (when (:account/account db)
(let [{:keys [peers-summary peers-count]} db (let [{:keys [peers-summary peers-count]} db
wnode (:address (mailserver/fetch-current cofx)) {:keys [address sym-key-id] :as wnode} (mailserver/fetch-current cofx)
mailserver-was-registered? (registered-peer? previous-summary mailserver-was-registered? (registered-peer? previous-summary
wnode) address)
mailserver-is-registered? (registered-peer? peers-summary mailserver-is-registered? (registered-peer? peers-summary
wnode) address)
;; the mailserver just connected mailserver-added? (and mailserver-is-registered?
mailserver-connected? (and mailserver-is-registered? (not mailserver-was-registered?))
(not mailserver-was-registered?)) mailserver-removed? (and mailserver-was-registered?
;; the mailserver just disconnected (not mailserver-is-registered?))]
mailserver-disconnected? (and mailserver-was-registered?
(not mailserver-is-registered?))]
(cond (cond
mailserver-disconnected? mailserver-added?
(connect-to-mailserver cofx) (mark-trusted-peer cofx)
mailserver-removed?
(connect-to-mailserver cofx)))))
mailserver-connected? (defn request-messages! [web3 {:keys [sym-key-id address]} {:keys [topic to from]}]
{::mark-trusted-peer {:web3 (:web3 db) (log/info "offline inbox: request-messages for: "
:wnode wnode}})))) " topic " topic
" from " from
" to " to)
(.requestMessages (transport.utils/shh web3)
(clj->js {:topic topic
:mailServerPeer address
:symKeyID sym-key-id
:timeout 20
:from from
:to to})
(fn [err request-id]
(if-not err
(re-frame/dispatch [:inbox.callback/request-messages-success {:topic topic
:request-id request-id
:from from
:to to}])
(log/error "offline inbox: messages request error for topic " topic ": " err)))))
(defn inbox-ready? [{:keys [sym-key-id]} {:keys [db]}] (re-frame/reg-fx
(let [mailserver-status (:mailserver-status db)] :transport.inbox/request-messages
(and (= :connected mailserver-status) (fn [{:keys [web3 wnode requests]}]
sym-key-id))) (doseq [request requests]
(request-messages! web3 wnode request))))
(defn get-request-messages-topics (defn prepare-request [now-in-s topic {:keys [last-request request-pending?]}]
"Returns topics for which full history has already been recovered" (when-not request-pending?
[db] {:from (max last-request
(conj (map :topic (- now-in-s one-day))
(remove :fetch-history? :to now-in-s
(vals (:transport/chats db)))) :topic topic}))
(transport.utils/get-topic constants/contact-discovery)))
(defn get-request-history-topics (defn prepare-requests [now-in-s topics]
"Returns topics for which full history has not been recovered" (remove nil? (map (fn [[topic inbox-topic]]
[db] (prepare-request now-in-s topic inbox-topic))
(map :topic topics)))
(filter :fetch-history?
(vals (:transport/chats db)))))
(defn request-history-span [now-in-s] (defn get-wnode-when-ready
(- now-in-s one-day)) "return the wnode if the inbox is ready"
[{:keys [db] :as cofx}]
(let [{:keys [sym-key-id] :as wnode} (mailserver/fetch-current cofx)
mailserver-status (:mailserver-status db)]
(when (and (= :connected mailserver-status)
sym-key-id)
wnode)))
(fx/defn request-messages (fx/defn request-messages
[{:keys [db now] :as cofx}] "request messages if the inbox is ready"
(let [wnode (mailserver/fetch-current cofx) [{:keys [db now] :as cofx} topic]
web3 (:web3 db) (when-let [wnode (get-wnode-when-ready cofx)]
now-in-s (quot now 1000) (let [web3 (:web3 db)
last-request (max now-in-s (quot now 1000)
(get-in db [:account/account :last-request]) requests (if topic
(- now-in-s seven-days)) [(prepare-request now-in-s topic (get-in db [:transport.inbox/topics topic]))]
request-messages-topics (get-request-messages-topics db) (prepare-requests now-in-s (:transport.inbox/topics db)))]
request-history-topics (get-request-history-topics db)] {:transport.inbox/request-messages {:web3 web3
(when (inbox-ready? wnode cofx) :wnode wnode
{::request-messages [{:wnode wnode :requests requests}})))
:topics request-messages-topics
:from last-request
:to now-in-s
:web3 web3}
{:wnode wnode
:from (request-history-span now-in-s)
:to now-in-s
:topics request-history-topics
:web3 web3}]
:db (assoc db :inbox/fetching? true)
:dispatch-later [{:ms fetching-timeout
:dispatch [:inbox/check-fetching now-in-s]}]})))
(fx/defn request-chat-history [{:keys [db now] :as cofx} chat-id] (fx/defn add-mailserver-trusted
(let [wnode (mailserver/fetch-current cofx) "the current mailserver has been trusted
web3 (:web3 db) update mailserver status to `:connected` and request messages
topic (get-in db [:transport/chats chat-id :topic]) if wnode is ready"
now-in-s (quot now 1000)] [{:keys [db] :as cofx}]
(when (inbox-ready? wnode cofx)
{::request-messages [{:wnode wnode
:topics [topic]
:from (request-history-span now-in-s)
:to now-in-s
:web3 web3}]
:db (assoc db :inbox/fetching? true)
:dispatch-later [{:ms fetching-timeout
:dispatch [:inbox/check-fetching now-in-s chat-id]}]})))
;;;; Handlers
(handlers/register-handler-fx
:inbox/mailserver-trusted
(fn [{:keys [db] :as cofx} _]
(fx/merge cofx
(update-mailserver-status :connected)
(request-messages))))
(handlers/register-handler-fx
:inbox/get-sym-key-success
(fn [{:keys [db] :as cofx} [_ wnode sym-key-id]]
(fx/merge cofx
(add-sym-key-id-to-wnode wnode sym-key-id)
(request-messages))))
(handlers/register-handler-fx
:inbox/request-chat-history
(fn [{:keys [db] :as cofx} [_ chat-id]]
(request-chat-history cofx chat-id)))
(handlers/register-handler-fx
:inbox/check-connection
(fn [{:keys [db] :as cofx} _]
(when (= :connecting (:mailserver-status db))
(if (mailserver/preferred-mailserver-id cofx)
(update-mailserver-status cofx :error)
(fx/merge cofx
(mailserver/set-current-mailserver)
(connect-to-mailserver))))))
(fx/defn update-last-request
[{:keys [db]} last-request]
(let [chats (:transport/chats db)
transport-txs (reduce (fn [txs [chat-id chat]]
(if (:fetch-history? chat)
(conj txs
(transport-store/save-transport-tx
{:chat-id chat-id
:chat (assoc chat
:fetch-history? false)}))
txs))
[]
chats)
chats-update (reduce (fn [acc [chat-id chat]]
(if (:fetch-history? chat)
(assoc acc chat-id (assoc chat :fetch-history? false))
(assoc acc chat-id chat)))
{}
chats)]
{:db (-> db
(assoc :transport/chats chats-update)
(assoc-in [:account/account :last-request]
last-request))
:data-store/base-tx [(accounts-store/save-account-tx
(assoc (:account/account db)
:last-request last-request))]
:data-store/tx transport-txs}))
(fx/defn update-fetch-history [{:keys [db]} chat-id]
{:db (assoc-in db
[:transport/chats chat-id :fetch-history?]
false)
:data-store/tx [(transport-store/save-transport-tx
{:chat-id chat-id
:chat (assoc (get-in db [:transport/chats chat-id])
:fetch-history? false)})]})
(fx/defn initialize-offline-inbox [cofx custom-mailservers]
(fx/merge cofx (fx/merge cofx
(mailserver/add-custom-mailservers custom-mailservers) {:db (update-mailserver-status db :connected)}
(mailserver/set-initial-last-request) (request-messages nil)))
(mailserver/set-current-mailserver)))
(handlers/register-handler-fx (fx/defn add-mailserver-sym-key
:inbox/check-fetching "the current mailserver sym-key has been generated
(fn [{:keys [db now] :as cofx} [_ last-request chat-id]] add sym-key to the wnode in app-db and request messages if
(when (and (:inbox/fetching? db) wnode is ready"
;; if chat was removed before messages were fetched no need [{:keys [db] :as cofx} {:keys [id]} sym-key-id]
;; to proceed with further actions (let [current-fleet (fleet/current-fleet db)]
(or (not chat-id) (contains? (:transport/chats db) chat-id))) (fx/merge cofx
(let [time-since-last-received (- now (:inbox/last-received db))] {:db (-> db
(if (> time-since-last-received fetching-timeout) (assoc-in [:inbox/wnodes current-fleet id :sym-key-id] sym-key-id)
(if chat-id (update-in [:inbox/wnodes current-fleet id] dissoc :generating-sym-key?))}
(fx/merge cofx (request-messages nil))))
{:db (assoc db :inbox/fetching? false)}
(update-fetch-history chat-id))
(fx/merge cofx
{:db (assoc db :inbox/fetching? false)}
(update-last-request last-request)))
{:dispatch-later [{:ms (- fetching-timeout
time-since-last-received)
:dispatch [:inbox/check-fetching last-request chat-id]}]})))))
(handlers/register-handler-fx (fx/defn check-connection
:inbox/reconnect "check if mailserver is connected
(fn [cofx [_ args]] mark mailserver status as `:error` if custom mailserver is used
(connect-to-mailserver cofx))) otherwise try to reconnect to another mailserver"
[{:keys [db] :as cofx}]
(when (= :connecting (:mailserver-status db))
(if (mailserver/preferred-mailserver-id cofx)
{:db (update-mailserver-status db :error)}
(fx/merge cofx
(mailserver/set-current-mailserver)
(connect-to-mailserver)))))
(fx/defn remove-chat-from-inbox-topic
"if the chat is the only chat of the inbox topic delete the inbox topic
otherwise remove the chat-id of the chat from the inbox topic and save"
[{:keys [db now] :as cofx} chat-id]
(let [topic (get-in db [:transport/chats chat-id :topic])
{:keys [chat-ids] :as inbox-topic} (update (get-in db [:transport.inbox/topics topic])
:chat-ids
disj chat-id)]
(if (empty? chat-ids)
{:db (update db :transport.inbox/topics dissoc topic)
:data-store/tx [(transport-store/delete-transport-inbox-topic-tx topic)]}
{:db (assoc-in db [:transport.inbox/topics topic] inbox-topic)
:data-store/tx [(transport-store/save-transport-inbox-topic-tx
{:topic topic
:inbox-topic inbox-topic})]})))
(fx/defn update-inbox-topic
"TODO: add support for cursors
if there is a cursor, do not update `request-to` and `request-from`"
[{:keys [db now] :as cofx} {:keys [request-id cursor]}]
(let [{:keys [from to topic]} (get-in db [:transport.inbox/requests request-id])
inbox-topic (-> (get-in db [:transport.inbox/topics topic])
(assoc :last-request to)
(dissoc :request-pending?))]
(fx/merge cofx
{:db (-> db
(update :transport.inbox/requests dissoc request-id)
(assoc-in [:transport.inbox/topics topic] inbox-topic))
:data-store/tx [(transport-store/save-transport-inbox-topic-tx
{:topic topic
:inbox-topic inbox-topic})]})))
(fx/defn upsert-inbox-topic
"if the chat-id is already in the topic we do nothing, otherwise we update
the topic
if the topic already existed we add the chat-id andreset last-request
because there was no filter for the chat and messages were ignored
if the topic didn't exist we created"
[{:keys [db] :as cofx} {:keys [topic chat-id]}]
(let [{:keys [chat-ids last-request] :as current-inbox-topic}
(get-in db [:transport.inbox/topics topic] {:chat-ids #{}})]
(when-let [inbox-topic (when-not (chat-ids chat-id)
(-> current-inbox-topic
(assoc :last-request 1)
(update :chat-ids conj chat-id)))]
(fx/merge cofx
{:db (assoc-in db [:transport.inbox/topics topic] inbox-topic)
:data-store/tx [(transport-store/save-transport-inbox-topic-tx
{:topic topic
:inbox-topic inbox-topic})]}
(request-messages topic)))))
(fx/defn resend-request
[{:keys [db] :as cofx} {:keys [request-id]}]
(let [{:keys [from to topic]} (get-in db [:transport.inbox/requests request-id])]
(log/info "offline inbox: message request" request-id " expired for inbox topic" topic "from" from "to" to)
(fx/merge cofx
{:db (-> db
(update :transport.inbox/requests dissoc request-id)
(update-in [:transport.inbox/topics topic] dissoc :request-pending?))}
(request-messages topic))))
(fx/defn add-request
[{:keys [db] :as cofx} {:keys [topic request-id from to]}]
(log/info "offline inbox: message request " request-id "sent for inbox topic" topic "from" from "to" to)
{:db (-> db
(assoc-in [:transport.inbox/requests request-id] {:from from
:to to
:topic topic})
(assoc-in [:transport.inbox/topics topic :request-pending?] true))})
(fx/defn initialize-offline-inbox
[cofx custom-mailservers]
(let [discovery-topic (transport.utils/get-topic constants/contact-discovery)]
(fx/merge cofx
(mailserver/add-custom-mailservers custom-mailservers)
(mailserver/set-current-mailserver)
(when-not (get-in cofx [:db :transport.inbox/topics discovery-topic])
(upsert-inbox-topic {:topic discovery-topic
:chat-id :discovery-topic})))))

View File

@ -0,0 +1,96 @@
(ns ^{:doc "Contact request and update API"}
status-im.transport.message.contact
(:require [cljs.spec.alpha :as spec]
[status-im.data-store.transport :as transport-store]
[status-im.transport.db :as transport.db]
[status-im.transport.message.protocol :as protocol]
[status-im.utils.fx :as fx]))
(defrecord ContactRequest [name profile-image address fcm-token]
protocol/StatusMessage
(send [this chat-id {:keys [db random-id-generator] :as cofx}]
(fx/merge cofx
(protocol/init-chat {:chat-id chat-id
:resend? "contact-request"})
(protocol/send-with-pubkey {:chat-id chat-id
:payload this
:success-event [:transport/contact-message-sent chat-id]})))
(validate [this]
(when (spec/valid? :message/contact-request this)
this)))
(defrecord ContactRequestConfirmed [name profile-image address fcm-token]
protocol/StatusMessage
(send [this chat-id {:keys [db] :as cofx}]
(let [success-event [:transport/contact-message-sent chat-id]
chat (get-in db [:transport/chats chat-id])
updated-chat (if chat
(assoc chat :resend? "contact-request-confirmation")
(transport.db/create-chat {:resend? "contact-request-confirmation"}))]
(fx/merge cofx
{:db (assoc-in db
[:transport/chats chat-id] updated-chat)
:data-store/tx [(transport-store/save-transport-tx {:chat-id chat-id
:chat updated-chat})]}
(protocol/send-with-pubkey {:chat-id chat-id
:payload this
:success-event success-event}))))
(validate [this]
(when (spec/valid? :message/contact-request-confirmed this)
this)))
(fx/defn send-contact-update
[{:keys [db] :as cofx} chat-id payload]
(when-let [chat (get-in cofx [:db :transport/chats chat-id])]
(let [updated-chat (assoc chat :resend? "contact-update")
tx [(transport-store/save-transport-tx {:chat-id chat-id
:chat updated-chat})]
success-event [:transport/contact-message-sent chat-id]]
(fx/merge cofx
{:db (assoc-in db
[:transport/chats chat-id :resend?]
"contact-update")
:data-store/tx tx}
(protocol/send-with-pubkey {:chat-id chat-id
:payload payload
:success-event success-event})))))
(defrecord ContactUpdate [name profile-image address fcm-token]
protocol/StatusMessage
(send [this _ {:keys [db] :as cofx}]
;;TODO: here we look for contact which have a :public-key to differentiate
;;actual contacts from dapps
;;This is not an ideal solution and we should think about a more reliable way
;;to do this when we refactor app-db
(let [contact-public-keys (reduce (fn [acc [_ {:keys [public-key pending?]}]]
(if (and public-key
(not pending?))
(conj acc public-key)
acc))
#{}
(:contacts/contacts db))
;;NOTE: chats with contacts use public-key as chat-id
send-contact-update-fxs (map #(send-contact-update % this) contact-public-keys)]
(apply fx/merge cofx send-contact-update-fxs)))
(validate [this]
(when (spec/valid? :message/contact-update this)
this)))
(fx/defn remove-chat-filter
"Stops the filter for the given chat-id"
[{:keys [db]} chat-id]
(when-let [filter (get-in db [:transport/filters chat-id])]
{:shh/remove-filter filter}))
(defrecord NewContactKey [sym-key topic message]
protocol/StatusMessage
(send
;; no-op, we don't send NewContactKey anymore
[this chat-id cofx])
(receive
;;for compatibility with old clients, we only care about the message within
[this chat-id _ timestamp {:keys [db] :as cofx}]
(protocol/receive message chat-id chat-id timestamp cofx))
(validate [this]
(when (spec/valid? :message/new-contact-key this)
this)))

View File

@ -1,8 +1,143 @@
(ns ^{:doc "Definition of the StatusMessage protocol"} (ns ^{:doc "Definition of the StatusMessage protocol"}
status-im.transport.message.core) status-im.transport.message.core
(:require [re-frame.core :as re-frame]
[status-im.chat.models.message :as models.message]
[status-im.data-store.transport :as transport-store]
[status-im.transport.message.contact :as contact]
[status-im.transport.message.protocol :as protocol]
[status-im.transport.message.transit :as transit]
[status-im.transport.utils :as transport.utils]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]))
(defprotocol StatusMessage (fx/defn receive-message
"Protocol for the messages that are sent through the transport layer" [cofx now-in-s chat-id js-message]
(send [this chat-id cofx] "Method producing all effects necessary for sending the message record") (let [{:keys [payload sig timestamp ttl]} (js->clj js-message :keywordize-keys true)
(receive [this chat-id signature timestamp cofx] "Method producing all effects necessary for receiving the message record") status-message (-> payload
(validate [this] "Method returning the message if it is valid or nil if it is not")) transport.utils/to-utf8
transit/deserialize)]
(when (and sig status-message)
(try
(when-let [valid-message (protocol/validate status-message)]
(fx/merge (assoc cofx :js-obj js-message)
#(protocol/receive valid-message (or chat-id sig) sig timestamp %)))
(catch :default e nil))))) ; ignore unknown message types
(defn- js-array->seq [array]
(for [i (range (.-length array))]
(aget array i)))
(fx/defn receive-whisper-messages
[{:keys [now] :as cofx} js-error js-messages chat-id]
(if (and (not js-error)
js-messages)
(let [now-in-s (quot now 1000)
receive-message-fxs (map (fn [message]
(receive-message now-in-s chat-id message))
(js-array->seq js-messages))]
(apply fx/merge cofx receive-message-fxs))
(do (log/error "Something went wrong" js-error js-messages)
cofx)))
(fx/defn remove-hash
[{:keys [db] :as cofx} envelope-hash]
{:db (update db :transport/message-envelopes dissoc envelope-hash)})
(fx/defn update-resend-contact-message
[{:keys [db] :as cofx} chat-id]
(let [chat (get-in db [:transport/chats chat-id])
updated-chat (assoc chat :resend? nil)]
{:db (assoc-in db [:transport/chats chat-id :resend?] nil)
:data-store/tx [(transport-store/save-transport-tx {:chat-id chat-id
:chat updated-chat})]}))
(fx/defn update-envelope-status
[{:keys [db] :as cofx} envelope-hash status]
(let [{:keys [chat-id message-type message-id]}
(get-in db [:transport/message-envelopes envelope-hash])]
(case message-type
:contact-message
(when (= :sent status)
(fx/merge cofx
(remove-hash envelope-hash)
(update-resend-contact-message chat-id)))
(when-let [{:keys [from]} (get-in db [:chats chat-id :messages message-id])]
(let [{:keys [fcm-token]} (get-in db [:contacts/contacts chat-id])]
(fx/merge cofx
(remove-hash envelope-hash)
(models.message/update-message-status chat-id message-id status)
(models.message/send-push-notification fcm-token status)))))))
(fx/defn set-contact-message-envelope-hash
[{:keys [db] :as cofx} chat-id envelope-hash]
{:db (assoc-in db [:transport/message-envelopes envelope-hash]
{:chat-id chat-id
:message-type :contact-message})})
(fx/defn set-message-envelope-hash
"message-type is used for tracking
TODO (cammellos): For group messages it returns multiple hashes, for now
we naively assume that if one is sent the batch is ok"
[{:keys [db] :as cofx} chat-id message-id message-type envelope-hash-js]
(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})}))
(defn- own-info [db]
(let [{:keys [name photo-path address]} (:account/account db)
fcm-token (get-in db [:notifications :fcm-token])]
{:name name
:profile-image photo-path
:address address
:fcm-token fcm-token}))
(fx/defn resend-contact-request [cofx own-info chat-id {:keys [sym-key topic]}]
(protocol/send (contact/map->ContactRequest own-info)
chat-id cofx))
(fx/defn resend-contact-message
[cofx own-info chat-id]
(let [{:keys [resend?] :as chat} (get-in cofx [:db :transport/chats chat-id])]
(case resend?
"contact-request"
(resend-contact-request cofx own-info chat-id chat)
"contact-request-confirmation"
(protocol/send (contact/map->ContactRequestConfirmed own-info)
chat-id
cofx)
"contact-update"
(protocol/send-with-pubkey cofx
{:chat-id chat-id
:payload (contact/map->ContactUpdate own-info)})
nil)))
(fx/defn resend-contact-messages
[{:keys [db] :as cofx} previous-summary]
(when (and (zero? (count previous-summary))
(= :online (:network-status db))
(pos? (count (:peers-summary db))))
(let [own-info (own-info db)
resend-contact-message-fxs (map (fn [chat-id]
(resend-contact-message own-info chat-id))
(keys (:transport/chats db)))]
(apply fx/merge cofx resend-contact-message-fxs))))
(re-frame/reg-fx
;; TODO(janherich): this should be called after `:data-store/tx` actually
:transport/confirm-messages-processed
(fn [messages]
(let [{:keys [web3]} (first messages)
js-messages (->> messages
(keep :js-obj)
(apply array))]
(when (pos? (.-length js-messages))
(.confirmMessagesProcessed (transport.utils/shh web3)
js-messages
(fn [err resp]
(when err
(log/info "Confirming messages processed failed"))))))))

View File

@ -1,11 +1,11 @@
(ns status-im.transport.message.v1.core (ns status-im.transport.message.group-chat
(:require [status-im.transport.message.core :as message] (:require [cljs.spec.alpha :as spec]
[taoensso.timbre :as log] [status-im.transport.message.protocol :as protocol]
[cljs.spec.alpha :as spec])) [taoensso.timbre :as log]))
(defrecord GroupMembershipUpdate (defrecord GroupMembershipUpdate
[chat-id membership-updates message] [chat-id membership-updates message]
message/StatusMessage protocol/StatusMessage
(validate [this] (validate [this]
(if (spec/valid? :message/group-membership-update this) (if (spec/valid? :message/group-membership-update this)
this this

View File

@ -1,17 +1,19 @@
(ns ^{:doc "Protocol API and protocol utils"} (ns ^{:doc "Protocol API and protocol utils"}
status-im.transport.message.v1.protocol status-im.transport.message.protocol
(:require [status-im.utils.config :as config] (:require [cljs.spec.alpha :as spec]
[status-im.constants :as constants]
[taoensso.timbre :as log]
[status-im.utils.config :as config]
[status-im.chat.core :as chat] [status-im.chat.core :as chat]
[status-im.utils.clocks :as utils.clocks] [status-im.constants :as constants]
[status-im.transport.db :as transport.db] [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.transport.utils :as transport.utils]
[status-im.utils.config :as config]
[status-im.utils.fx :as fx] [status-im.utils.fx :as fx]
[cljs.spec.alpha :as spec])) [taoensso.timbre :as log]))
(defprotocol StatusMessage
"Protocol for the messages that are sent through the transport layer"
(send [this chat-id cofx] "Method producing all effects necessary for sending the message record")
(receive [this chat-id signature timestamp cofx] "Method producing all effects necessary for receiving the message record")
(validate [this] "Method returning the message if it is valid or nil if it is not"))
(def ^:private whisper-opts (def ^:private whisper-opts
{:ttl 10 ;; ttl of 10 sec {:ttl 10 ;; ttl of 10 sec
@ -21,15 +23,15 @@
(fx/defn init-chat (fx/defn init-chat
"Initialises chat on protocol layer. "Initialises chat on protocol layer.
If topic is not passed as argument it is derived from `chat-id`" If topic is not passed as argument it is derived from `chat-id`"
[{:keys [db]} [{:keys [db now]}
{:keys [chat-id topic resend?] {:keys [chat-id topic resend?]}]
:or {topic (transport.utils/get-topic chat-id)}}]
{:db (assoc-in db {:db (assoc-in db
[:transport/chats chat-id] [:transport/chats chat-id]
(transport.db/create-chat {:topic topic (transport.db/create-chat {:topic topic
:resend? resend?}))}) :resend? resend?
:now now}))})
(fx/defn send (fx/defn send-with-sym-key
"Sends the payload using symetric key and topic from db (looked up by `chat-id`)" "Sends the payload using symetric key and topic from db (looked up by `chat-id`)"
[{:keys [db] :as cofx} {:keys [payload chat-id success-event]}] [{:keys [db] :as cofx} {:keys [payload chat-id success-event]}]
;; we assume that the chat contains the contact public-key ;; we assume that the chat contains the contact public-key
@ -76,11 +78,11 @@
whisper-opts)}]})) whisper-opts)}]}))
(defrecord Message [content content-type message-type clock-value timestamp] (defrecord Message [content content-type message-type clock-value timestamp]
message/StatusMessage StatusMessage
(send [this chat-id cofx] (send [this chat-id cofx]
(let [params {:chat-id chat-id (let [params {:chat-id chat-id
:payload this :payload this
:success-event [:transport/set-message-envelope-hash :success-event [:transport/message-sent
chat-id chat-id
(transport.utils/message-id this) (transport.utils/message-id this)
message-type]}] message-type]}]
@ -92,7 +94,7 @@
chat-id chat-id
(:success-event params) (:success-event params)
this) this)
(send cofx params)) (send-with-sym-key cofx params))
:user-message :user-message
(if config/pfs-encryption-enabled? (if config/pfs-encryption-enabled?
@ -116,15 +118,15 @@
(log/warn "failed to validate Message" (spec/explain :message/message this))))) (log/warn "failed to validate Message" (spec/explain :message/message this)))))
(defrecord MessagesSeen [message-ids] (defrecord MessagesSeen [message-ids]
message/StatusMessage StatusMessage
(send [this chat-id cofx] (send [this chat-id cofx]
(if config/pfs-encryption-enabled? (if config/pfs-encryption-enabled?
(send-direct-message cofx (send-direct-message cofx
chat-id chat-id
nil nil
this) this)
(send cofx {:chat-id chat-id (send-with-sym-key cofx {:chat-id chat-id
:payload this}))) :payload this})))
(receive [this chat-id signature _ cofx] (receive [this chat-id signature _ cofx]
(chat/receive-seen cofx chat-id signature this)) (chat/receive-seen cofx chat-id signature this))
(validate [this] (validate [this]

View File

@ -1,8 +1,8 @@
(ns ^{:doc "Public chat API"} (ns ^{:doc "Public chat API"}
status-im.transport.message.v1.public-chat status-im.transport.message.public-chat
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[status-im.data-store.transport :as transport-store] [status-im.data-store.transport :as transport-store]
[status-im.transport.message.v1.protocol :as protocol] [status-im.transport.message.protocol :as protocol]
[status-im.transport.utils :as transport.utils] [status-im.transport.utils :as transport.utils]
[status-im.utils.fx :as fx] [status-im.utils.fx :as fx]
[status-im.utils.handlers :as handlers])) [status-im.utils.handlers :as handlers]))
@ -22,7 +22,8 @@
{:shh/generate-sym-key-from-password [{:web3 (:web3 db) {:shh/generate-sym-key-from-password [{:web3 (:web3 db)
:password chat-id :password chat-id
:on-success on-success}]} :on-success on-success}]}
(protocol/init-chat {:chat-id chat-id}))))) (protocol/init-chat {:chat-id chat-id
:topic (transport.utils/get-topic chat-id)})))))
(handlers/register-handler-fx (handlers/register-handler-fx
::add-new-sym-key ::add-new-sym-key
@ -39,5 +40,4 @@
:chat (-> (get-in db [:transport/chats chat-id]) :chat (-> (get-in db [:transport/chats chat-id])
(assoc :sym-key-id sym-key-id) (assoc :sym-key-id sym-key-id)
;;TODO (yenda) remove once go implements persistence ;;TODO (yenda) remove once go implements persistence
(assoc :sym-key sym-key))})] (assoc :sym-key sym-key))})]})))
:dispatch [:inbox/request-chat-history chat-id]})))

View File

@ -1,8 +1,8 @@
(ns ^{:doc "Transit custom readers and writers, required when adding a new record implementing StatusMessage protocol"} (ns ^{:doc "Transit custom readers and writers, required when adding a new record implementing StatusMessage protocol"}
status-im.transport.message.transit status-im.transport.message.transit
(:require [status-im.transport.message.v1.contact :as v1.contact] (:require [status-im.transport.message.contact :as contact]
[status-im.transport.message.v1.protocol :as v1.protocol] [status-im.transport.message.protocol :as protocol]
[status-im.transport.message.v1.core :as v1] [status-im.transport.message.group-chat :as group-chat]
[status-im.constants :as constants] [status-im.constants :as constants]
[cognitect.transit :as transit])) [cognitect.transit :as transit]))
@ -89,13 +89,13 @@
(def writer (transit/writer :json (def writer (transit/writer :json
{:handlers {:handlers
{v1.contact/NewContactKey (NewContactKeyHandler.) {contact/NewContactKey (NewContactKeyHandler.)
v1.contact/ContactRequest (ContactRequestHandler.) contact/ContactRequest (ContactRequestHandler.)
v1.contact/ContactRequestConfirmed (ContactRequestConfirmedHandler.) contact/ContactRequestConfirmed (ContactRequestConfirmedHandler.)
v1.contact/ContactUpdate (ContactUpdateHandler.) contact/ContactUpdate (ContactUpdateHandler.)
v1.protocol/Message (MessageHandler.) protocol/Message (MessageHandler.)
v1.protocol/MessagesSeen (MessagesSeenHandler.) protocol/MessagesSeen (MessagesSeenHandler.)
v1/GroupMembershipUpdate (GroupMembershipUpdateHandler.)}})) group-chat/GroupMembershipUpdate (GroupMembershipUpdateHandler.)}}))
;; ;;
;; Reader handlers ;; Reader handlers
@ -131,22 +131,22 @@
(def reader (transit/reader :json (def reader (transit/reader :json
{:handlers {:handlers
{"c1" (fn [[sym-key topic message]] {"c1" (fn [[sym-key topic message]]
(v1.contact/NewContactKey. sym-key topic message)) (contact/NewContactKey. sym-key topic message))
"c2" (fn [[name profile-image address fcm-token]] "c2" (fn [[name profile-image address fcm-token]]
(v1.contact/ContactRequest. name profile-image address fcm-token)) (contact/ContactRequest. name profile-image address fcm-token))
"c3" (fn [[name profile-image address fcm-token]] "c3" (fn [[name profile-image address fcm-token]]
(v1.contact/ContactRequestConfirmed. name profile-image address fcm-token)) (contact/ContactRequestConfirmed. name profile-image address fcm-token))
"c4" (fn [[legacy-content content-type message-type clock-value timestamp content]] "c4" (fn [[legacy-content content-type message-type clock-value timestamp content]]
(let [[new-content new-content-type] (legacy->new-message-data (or content legacy-content) content-type)] (let [[new-content new-content-type] (legacy->new-message-data (or content legacy-content) content-type)]
(v1.protocol/Message. new-content new-content-type message-type clock-value timestamp))) (protocol/Message. new-content new-content-type message-type clock-value timestamp)))
"c7" (fn [[content content-type message-type clock-value timestamp]] "c7" (fn [[content content-type message-type clock-value timestamp]]
(v1.protocol/Message. content content-type message-type clock-value timestamp)) (protocol/Message. content content-type message-type clock-value timestamp))
"c5" (fn [message-ids] "c5" (fn [message-ids]
(v1.protocol/MessagesSeen. message-ids)) (protocol/MessagesSeen. message-ids))
"c6" (fn [[name profile-image address fcm-token]] "c6" (fn [[name profile-image address fcm-token]]
(v1.contact/ContactUpdate. name profile-image address fcm-token)) (contact/ContactUpdate. name profile-image address fcm-token))
"g5" (fn [[chat-id membership-updates message]] "g5" (fn [[chat-id membership-updates message]]
(v1/GroupMembershipUpdate. chat-id membership-updates message))}})) (group-chat/GroupMembershipUpdate. chat-id membership-updates message))}}))
(defn serialize (defn serialize
"Serializes a record implementing the StatusMessage protocol using the custom writers" "Serializes a record implementing the StatusMessage protocol using the custom writers"

View File

@ -1,142 +0,0 @@
(ns ^{:doc "Contact request and update API"}
status-im.transport.message.v1.contact
(:require [re-frame.core :as re-frame]
[status-im.data-store.transport :as transport-store]
[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]
[cljs.spec.alpha :as spec]))
(defrecord ContactRequest [name profile-image address fcm-token]
message/StatusMessage
(send [this chat-id {:keys [db random-id-generator] :as cofx}]
(let [topic (transport.utils/get-topic (random-id-generator))
on-success (fn [sym-key sym-key-id]
(re-frame/dispatch [:contact/send-new-sym-key
{:sym-key-id sym-key-id
:sym-key sym-key
:chat-id chat-id
:topic topic
:message this}]))]
(fx/merge cofx
{:shh/get-new-sym-keys [{:web3 (:web3 db)
:on-success on-success}]}
(protocol/init-chat {:chat-id chat-id
:topic topic
:resend? "contact-request"}))))
(validate [this]
(when (spec/valid? :message/contact-request this)
this)))
(defrecord ContactRequestConfirmed [name profile-image address fcm-token]
message/StatusMessage
(send [this chat-id {:keys [db] :as cofx}]
(let [success-event [:transport/set-contact-message-envelope-hash chat-id]
chat (get-in db [:transport/chats chat-id])
updated-chat (assoc chat :resend? "contact-request-confirmation")]
(fx/merge cofx
{:db (assoc-in db
[:transport/chats chat-id :resend?]
"contact-request-confirmation")
:data-store/tx [(transport-store/save-transport-tx {:chat-id chat-id
:chat updated-chat})]}
(protocol/send-with-pubkey {:chat-id chat-id
:payload this
:success-event success-event}))))
(validate [this]
(when (spec/valid? :message/contact-request-confirmed this)
this)))
(fx/defn send-contact-update
[{:keys [db] :as cofx} chat-id payload]
(when-let [chat (get-in cofx [:db :transport/chats chat-id])]
(let [updated-chat (assoc chat :resend? "contact-update")
tx [(transport-store/save-transport-tx {:chat-id chat-id
:chat updated-chat})]
success-event [:transport/set-contact-message-envelope-hash chat-id]]
(fx/merge cofx
{:db (assoc-in db
[:transport/chats chat-id :resend?]
"contact-update")
:data-store/tx tx}
(protocol/send-with-pubkey {:chat-id chat-id
:payload payload
:success-event success-event})))))
(defrecord ContactUpdate [name profile-image address fcm-token]
message/StatusMessage
(send [this _ {:keys [db] :as cofx}]
;;TODO: here we look for contact which have a :public-key to differentiate
;;actual contacts from dapps
;;This is not an ideal solution and we should think about a more reliable way
;;to do this when we refactor app-db
(let [contact-public-keys (reduce (fn [acc [_ {:keys [public-key pending?]}]]
(if (and public-key
(not pending?))
(conj acc public-key)
acc))
#{}
(:contacts/contacts db))
;;NOTE: chats with contacts use public-key as chat-id
send-contact-update-fxs (map #(send-contact-update % this) contact-public-keys)]
(apply fx/merge cofx send-contact-update-fxs)))
(validate [this]
(when (spec/valid? :message/contact-update this)
this)))
(fx/defn remove-chat-filter
"Stops the filter for the given chat-id"
[{:keys [db]} chat-id]
(when-let [filter (get-in db [:transport/chats chat-id :filter])]
{:shh/remove-filter filter}))
(fx/defn init-chat
[cofx chat-id topic]
(when-not (get-in cofx [:db :transport/chats chat-id])
(protocol/init-chat cofx
{:chat-id chat-id
:topic topic})))
(defrecord NewContactKey [sym-key topic message]
message/StatusMessage
(send [this chat-id cofx]
(let [success-event [:transport/set-contact-message-envelope-hash chat-id]]
(protocol/send-with-pubkey cofx
{:chat-id chat-id
:payload this
:success-event success-event})))
(receive [this chat-id _ timestamp {:keys [db] :as cofx}]
(let [current-sym-key (get-in db [:transport/chats chat-id :sym-key])
;; NOTE(yenda) to support concurrent contact request without additional
;; interactions we don't save the new key if these conditions are met:
;; - the message is a contact request
;; - we already have a sym-key
;; - this sym-key is first in alphabetical order compared to the new one
save-key? (not (and (= ContactRequest (type message))
current-sym-key
(= current-sym-key
(first (sort [current-sym-key sym-key])))))]
(if save-key?
(let [on-success (fn [sym-key sym-key-id]
(re-frame/dispatch [:contact/add-new-sym-key
{:sym-key-id sym-key-id
:timestamp timestamp
:sym-key sym-key
:chat-id chat-id
:topic topic
:message message}]))]
(fx/merge cofx
{:shh/add-new-sym-keys [{:web3 (:web3 db)
:sym-key sym-key
:on-success on-success}]}
(init-chat chat-id topic)
;; in case of concurrent contact request we want
;; to stop the filter for the previous key before
;; dereferrencing it
(remove-chat-filter chat-id)))
;; if we don't save the key, we read the message directly
(message/receive message chat-id chat-id timestamp cofx))))
(validate [this]
(when (spec/valid? :message/new-contact-key this)
this)))

View File

@ -1,9 +1,9 @@
(ns ^{:doc "Whisper API and events for managing keys and posting messages"} (ns ^{:doc "Whisper API and events for managing keys and posting messages"}
status-im.transport.shh status-im.transport.shh
(:require [taoensso.timbre :as log] (:require [re-frame.core :as re-frame]
[re-frame.core :as re-frame] [status-im.transport.message.transit :as transit]
[status-im.transport.utils :as transport.utils] [status-im.transport.utils :as transport.utils]
[status-im.transport.message.transit :as transit])) [taoensso.timbre :as log]))
(defn get-new-key-pair [{:keys [web3 on-success on-error]}] (defn get-new-key-pair [{:keys [web3 on-success on-error]}]
(if web3 (if web3
@ -70,7 +70,7 @@
:shh/send-direct-message :shh/send-direct-message
(fn [post-calls] (fn [post-calls]
(doseq [{:keys [web3 payload src dst success-event error-event] (doseq [{:keys [web3 payload src dst success-event error-event]
:or {error-event :protocol/send-status-message-error}} post-calls] :or {error-event :transport/send-status-message-error}} post-calls]
(let [direct-message (clj->js {:pubKey dst (let [direct-message (clj->js {:pubKey dst
:sig src :sig src
:payload (-> payload :payload (-> payload
@ -86,7 +86,7 @@
:shh/send-group-message :shh/send-group-message
(fn [params] (fn [params]
(let [{:keys [web3 payload src dsts success-event error-event] (let [{:keys [web3 payload src dsts success-event error-event]
:or {error-event :protocol/send-status-message-error}} params :or {error-event :transport/send-status-message-error}} params
message (clj->js {:pubKeys dsts message (clj->js {:pubKeys dsts
:sig src :sig src
:payload (-> payload :payload (-> payload
@ -102,7 +102,7 @@
:shh/send-public-message :shh/send-public-message
(fn [post-calls] (fn [post-calls]
(doseq [{:keys [web3 payload src chat success-event error-event] (doseq [{:keys [web3 payload src chat success-event error-event]
:or {error-event :protocol/send-status-message-error}} post-calls] :or {error-event :transport/send-status-message-error}} post-calls]
(let [message (clj->js {:chat chat (let [message (clj->js {:chat chat
:sig src :sig src
:payload (-> payload :payload (-> payload
@ -118,7 +118,7 @@
:shh/post :shh/post
(fn [post-calls] (fn [post-calls]
(doseq [{:keys [web3 message success-event error-event] (doseq [{:keys [web3 message success-event error-event]
:or {error-event :protocol/send-status-message-error}} post-calls] :or {error-event :transport/send-status-message-error}} post-calls]
(post-message {:web3 web3 (post-message {:web3 web3
:whisper-message (update message :payload (comp transport.utils/from-utf8 :whisper-message (update message :payload (comp transport.utils/from-utf8
transit/serialize)) transit/serialize))
@ -127,28 +127,6 @@
#(log/debug :shh/post-success)) #(log/debug :shh/post-success))
:on-error #(re-frame/dispatch [error-event %])})))) :on-error #(re-frame/dispatch [error-event %])}))))
;; This event params contain a recipients key because it's a vector of map with public-key and topic keys.
;; the :shh/post event has public-key and topic keys at the top level of the args map.
;; This event is used to send messages to multiple recipients when you can't send it on a topic.
;; It is used for renewing keys in a private group chat, because if someone leaves/join.
;; We want to change the symmetric key but we can't do that in the group topic with the old key
;; otherwise leavers can still eavesdrop / joiners can read past history."
(re-frame/reg-fx
:shh/multi-post
(fn [{:keys [web3 message recipients success-event error-event]
:or {error-event :protocol/send-status-message-error}}]
(let [whisper-message (update message :payload (comp transport.utils/from-utf8
transit/serialize))]
(doseq [{:keys [sym-key-id topic]} recipients]
(post-message {:web3 web3
:whisper-message (assoc whisper-message
:topic topic
:symKeyID sym-key-id)
:on-success (if success-event
#(re-frame/dispatch success-event)
#(log/debug :shh/post-success))
:on-error #(re-frame/dispatch [error-event %])})))))
(defn add-sym-key (defn add-sym-key
[{:keys [web3 sym-key on-success on-error]}] [{:keys [web3 sym-key on-success on-error]}]
(.. web3 (.. web3

View File

@ -1,19 +1,7 @@
(ns ^{:doc "Utils for transport layer"} (ns ^{:doc "Utils for transport layer"}
status-im.transport.utils status-im.transport.utils
(:require [cljs-time.coerce :refer [to-long]] (:require [clojure.string :as string]
[cljs-time.core :refer [now]] [status-im.js-dependencies :as dependencies]))
[clojure.string :as string]
[status-im.js-dependencies :as dependencies]
[status-im.data-store.transport :as transport-store]
[status-im.utils.fx :as fx]))
(fx/defn unsubscribe-from-chat
"Unsubscribe from chat on transport layer"
[{:keys [db]} chat-id]
(let [filter (get-in db [:transport/chats chat-id :filter])]
{:db (update db :transport/chats dissoc chat-id)
:data-store/tx [(transport-store/delete-transport-tx chat-id)]
:shh/remove-filter filter}))
(defn from-utf8 [s] (defn from-utf8 [s]
(.fromUtf8 dependencies/Web3.prototype s)) (.fromUtf8 dependencies/Web3.prototype s))

View File

@ -19,7 +19,7 @@
:accessibility-label :connection-status-text} :accessibility-label :connection-status-text}
[react/text {:style styles/text [react/text {:style styles/text
:on-press (when mailserver-error? :on-press (when mailserver-error?
#(re-frame/dispatch [:inbox/reconnect]))} #(re-frame/dispatch [:inbox.ui/reconnect-mailserver-pressed]))}
(i18n/label label)]])) (i18n/label label)]]))
(defview error-view [{:keys [top]}] (defview error-view [{:keys [top]}]

View File

@ -49,7 +49,10 @@
:inbox/wnodes fleet/default-wnodes :inbox/wnodes fleet/default-wnodes
:my-profile/editing? false :my-profile/editing? false
:transport/chats {} :transport/chats {}
:transport/filters {}
:transport/message-envelopes {} :transport/message-envelopes {}
:transport.inbox/topics {}
:transport.inbox/requests {}
:chat/cooldowns 0 :chat/cooldowns 0
:chat/cooldown-enabled? false :chat/cooldown-enabled? false
:chat/last-outgoing-message-sent-at 0 :chat/last-outgoing-message-sent-at 0
@ -86,7 +89,7 @@
;;:online - presence of internet connection in the phone ;;:online - presence of internet connection in the phone
(spec/def ::network-status (spec/nilable keyword?)) (spec/def ::network-status (spec/nilable keyword?))
(spec/def ::mailserver-status (spec/nilable keyword?)) (spec/def ::mailserver-status (spec/nilable #{:disconnected :connecting :added :connected :error}))
(spec/def ::app-state string?) (spec/def ::app-state string?)
@ -212,7 +215,6 @@
:mailservers/manage :mailservers/manage
:bootnodes/manage :bootnodes/manage
:inbox/wnodes :inbox/wnodes
:inbox/last-received
:inbox/current-id :inbox/current-id
:inbox/fetching? :inbox/fetching?
:universal-links/url :universal-links/url
@ -227,7 +229,9 @@
:chat/spam-messages-frequency :chat/spam-messages-frequency
:transport/message-envelopes :transport/message-envelopes
:transport/chats :transport/chats
:transport/discovery-filter :transport/filters
:transport.inbox/topics
:transport.inbox/requests
:desktop/desktop :desktop/desktop
:dimensions/window :dimensions/window
:dapps/permissions] :dapps/permissions]

View File

@ -104,7 +104,7 @@
(fx/defn on-return-from-background [cofx] (fx/defn on-return-from-background [cofx]
(fx/merge cofx (fx/merge cofx
(inbox/request-messages) (inbox/request-messages nil)
(hardwallet/return-back-from-nfc-settings))) (hardwallet/return-back-from-nfc-settings)))
(defn app-state-change [state {:keys [db] :as cofx}] (defn app-state-change [state {:keys [db] :as cofx}]

View File

@ -3,8 +3,6 @@
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
[status-im.chat.models.message :as models.message] [status-im.chat.models.message :as models.message]
[status-im.ui.screens.navigation :as navigation] [status-im.ui.screens.navigation :as navigation]
[status-im.transport.message.v1.core :as protocol]
[status-im.transport.message.core :as transport]
[status-im.utils.handlers :as handlers] [status-im.utils.handlers :as handlers]
[status-im.data-store.chats :as chats-store] [status-im.data-store.chats :as chats-store]
[status-im.utils.fx :as fx])) [status-im.utils.fx :as fx]))

View File

@ -12,10 +12,11 @@
(spec/def :wnode/user-defined boolean?) (spec/def :wnode/user-defined boolean?)
(spec/def :wnode/password ::not-blank-string) (spec/def :wnode/password ::not-blank-string)
(spec/def :wnode/sym-key-id string?) (spec/def :wnode/sym-key-id string?)
(spec/def :wnode/generating-sym-key? boolean?)
(spec/def :wnode/wnode (allowed-keys :req-un [:wnode/address :wnode/name :wnode/id] (spec/def :wnode/wnode (allowed-keys :req-un [:wnode/address :wnode/name :wnode/id]
:opt-un [:wnode/sym-key-id :opt-un [:wnode/sym-key-id
:wnode/generating-sym-key?
:wnode/user-defined :wnode/user-defined
:wnode/password])) :wnode/password]))
(spec/def :inbox/wnodes (spec/nilable (spec/map-of keyword? (spec/map-of :wnode/id :wnode/wnode)))) (spec/def :inbox/wnodes (spec/nilable (spec/map-of keyword? (spec/map-of :wnode/id :wnode/wnode))))
(spec/def :inbox/last-received integer?)

View File

@ -48,7 +48,7 @@
(reg-sub :fetching? (reg-sub :fetching?
(fn [db] (fn [db]
(get db :inbox/fetching?))) (pos-int? (count (get db :transport.inbox/requests)))))
(reg-sub :offline? (reg-sub :offline?
:<- [:network-status] :<- [:network-status]

View File

@ -12,7 +12,7 @@
(def ^:private mergable-keys (def ^:private mergable-keys
#{:data-store/tx :data-store/base-tx :chat-received-message/add-fx #{:data-store/tx :data-store/base-tx :chat-received-message/add-fx
:shh/add-new-sym-keys :shh/get-new-sym-keys :shh/post :shh/add-new-sym-keys :shh/get-new-sym-keys :shh/post
:shh/generate-sym-key-from-password :confirm-messages-processed :shh/generate-sym-key-from-password :transport/confirm-messages-processed
:group-chats/extract-membership-signature :utils/dispatch-later}) :group-chats/extract-membership-signature :utils/dispatch-later})
(defn- safe-merge [fx new-fx] (defn- safe-merge [fx new-fx]

View File

@ -1,6 +1,6 @@
(ns status-im.test.chat.models.message (ns status-im.test.chat.models.message
(:require [cljs.test :refer-macros [deftest is testing]] (:require [cljs.test :refer-macros [deftest is testing]]
[status-im.transport.message.v1.protocol :as protocol] [status-im.transport.message.protocol :as protocol]
[status-im.chat.models.message :as message] [status-im.chat.models.message :as message]
[status-im.utils.datetime :as time])) [status-im.utils.datetime :as time]))
@ -62,7 +62,7 @@
(testing "it marks the message as outgoing" (testing "it marks the message as outgoing"
(is (= true (:outgoing message)))) (is (= true (:outgoing message))))
(testing "it confirm the message as processed" (testing "it confirm the message as processed"
(is (:confirm-messages-processed actual))) (is (:transport/confirm-messages-processed actual)))
(testing "it stores the message" (testing "it stores the message"
(is (:data-store/tx actual))) (is (:data-store/tx actual)))
(testing "it does not send a seen confirmation" (testing "it does not send a seen confirmation"

View File

@ -8,7 +8,7 @@
[status-im.transport.utils :as transport.utils] [status-im.transport.utils :as transport.utils]
[day8.re-frame.test :refer-macros [run-test-async run-test-sync] :as rf-test] [day8.re-frame.test :refer-macros [run-test-async run-test-sync] :as rf-test]
[status-im.test.protocol.node :as node] [status-im.test.protocol.node :as node]
[status-im.transport.message.v1.contact :as transport.contact] [status-im.transport.message.contact :as transport.contact]
[status-im.test.protocol.utils :as utils])) [status-im.test.protocol.utils :as utils]))
;; NOTE(oskarth): All these tests are evaluated in NodeJS ;; NOTE(oskarth): All these tests are evaluated in NodeJS
@ -45,7 +45,7 @@
(rf-test/wait-for [::transport.contact/send-new-sym-key] (rf-test/wait-for [::transport.contact/send-new-sym-key]
(rf/dispatch [:set-chat-input-text "test message"]) (rf/dispatch [:set-chat-input-text "test message"])
(rf/dispatch [:send-current-message]) (rf/dispatch [:send-current-message])
(rf-test/wait-for [:update-message-status :protocol/send-status-message-error] (rf-test/wait-for [:update-message-status :transport/send-status-message-error]
(is true))))))) (is true)))))))
(deftest test-whisper-version! (deftest test-whisper-version!

View File

@ -19,7 +19,6 @@
[status-im.test.models.wallet] [status-im.test.models.wallet]
[status-im.test.transport.core] [status-im.test.transport.core]
[status-im.test.transport.inbox] [status-im.test.transport.inbox]
[status-im.test.transport.handlers]
[status-im.test.chat.models] [status-im.test.chat.models]
[status-im.test.chat.models.input] [status-im.test.chat.models.input]
[status-im.test.chat.models.loading] [status-im.test.chat.models.loading]
@ -97,7 +96,6 @@
'status-im.test.i18n 'status-im.test.i18n
'status-im.test.transport.core 'status-im.test.transport.core
'status-im.test.transport.inbox 'status-im.test.transport.inbox
'status-im.test.transport.handlers
'status-im.test.protocol.web3.inbox 'status-im.test.protocol.web3.inbox
'status-im.test.utils.utils 'status-im.test.utils.utils
'status-im.test.utils.money 'status-im.test.utils.money

View File

@ -1,7 +1,7 @@
(ns status-im.test.transport.core (ns status-im.test.transport.core
(:require [cljs.test :refer-macros [deftest is testing]] (:require [cljs.test :refer-macros [deftest is testing]]
[status-im.protocol.core :as protocol] [status-im.protocol.core :as protocol]
[status-im.transport.core :as transport])) [status-im.transport.message.core :as message]))
(deftest init-whisper (deftest init-whisper
(let [cofx {:db {:account/account {:public-key "1"} (let [cofx {:db {:account/account {:public-key "1"}
@ -41,6 +41,30 @@
ms-2 ms-2
ms-3])] ms-3])]
(is (= expected-wnodes (is (= expected-wnodes
(get-in (-> (get-in
(protocol/initialize-protocol cofx-with-ms "user-address") (protocol/initialize-protocol cofx-with-ms "user-address")
[:db :inbox/wnodes]))))))) [:db :inbox/wnodes])
(update-in [:eth.beta "1"] dissoc :generating-sym-key?)
(update-in [:eth.beta "2"] dissoc :generating-sym-key?)
(update-in [:eth.test "3"] dissoc :generating-sym-key?))))))))
(def sig "0x04325367620ae20dd878dbb39f69f02c567d789dd21af8a88623dc5b529827c2812571c380a2cd8236a2851b8843d6486481166c39debf60a5d30b9099c66213e4")
(def messages #js [{:sig sig
:ttl 10
:timestamp 1527692015
:topic "0x9c22ff5f"
:payload "0x5b227e236334222c5b2246222c22746578742f706c61696e222c227e3a7075626c69632d67726f75702d757365722d6d657373616765222c3135323736393230313433383130312c313532373639323031343337375d5d"
:padding "0xbf06347cc7f9aa18b4a846032264a88f559d9b14079975d14b10648847c0543a77a80624e101c082d19b502ae3b4f97958d18abf59eb0a82afc1301aa22470495fac739a30c2f563599fa8d8e09363a43d39311596b7f119dee7b046989c08224f1ef5cdc385"
:pow 0.002631578947368421
:hash "0x220ef9994a4fae64c112b27ed07ef910918159cbe6fcf8ac515ee2bf9a6711a0"}])
(deftest receive-whisper-messages-test
(testing "an error is reported"
(is (nil? (:chat-received-message/add-fx (message/receive-whisper-messages {:db {}} "error" #js [] nil)))))
(testing "messages is undefined"
(is (nil? (:chat-received-message/add-fx (message/receive-whisper-messages {:db {}} nil js/undefined nil)))))
(testing "happy path"
(let [actual (message/receive-whisper-messages {:db {}} nil messages sig)]
(testing "it add an fx for the message"
(is (:chat-received-message/add-fx actual))))))

View File

@ -1,24 +0,0 @@
(ns status-im.test.transport.handlers
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.transport.handlers :as handlers]))
(def sig "0x04325367620ae20dd878dbb39f69f02c567d789dd21af8a88623dc5b529827c2812571c380a2cd8236a2851b8843d6486481166c39debf60a5d30b9099c66213e4")
(def messages #js [{:sig sig
:ttl 10
:timestamp 1527692015
:topic "0x9c22ff5f"
:payload "0x5b227e236334222c5b2246222c22746578742f706c61696e222c227e3a7075626c69632d67726f75702d757365722d6d657373616765222c3135323736393230313433383130312c313532373639323031343337375d5d"
:padding "0xbf06347cc7f9aa18b4a846032264a88f559d9b14079975d14b10648847c0543a77a80624e101c082d19b502ae3b4f97958d18abf59eb0a82afc1301aa22470495fac739a30c2f563599fa8d8e09363a43d39311596b7f119dee7b046989c08224f1ef5cdc385"
:pow 0.002631578947368421
:hash "0x220ef9994a4fae64c112b27ed07ef910918159cbe6fcf8ac515ee2bf9a6711a0"}])
(deftest receive-whisper-messages-test
(testing "an error is reported"
(is (nil? (:chat-received-message/add-fx (handlers/receive-whisper-messages {:db {}} "error" #js [] nil)))))
(testing "messages is undefined"
(is (nil? (:chat-received-message/add-fx (handlers/receive-whisper-messages {:db {}} nil js/undefined nil)))))
(testing "happy path"
(let [actual (handlers/receive-whisper-messages {:db {}} nil messages sig)]
(testing "it add an fx for the message"
(is (:chat-received-message/add-fx actual))))))

View File

@ -20,20 +20,20 @@
[]))) [])))
(deftest peers-summary-change (deftest peers-summary-change
(testing "Mailserver connected" (testing "Mailserver added, sym-key doesn't exist"
(let [result (peers-summary-change-result false true false)] (let [result (peers-summary-change-result false true false)]
(is (= (into #{} (keys result)) (is (= (into #{} (keys result))
#{:status-im.transport.inbox/mark-trusted-peer})))) #{:transport.inbox/mark-trusted-peer :shh/generate-sym-key-from-password :db}))))
(testing "Mailserver disconnected, sym-key exists" (testing "Mailserver disconnected, sym-key exists"
(let [result (peers-summary-change-result true false true)] (let [result (peers-summary-change-result true false true)]
(is (= (into #{} (keys result)) (is (= (into #{} (keys result))
#{:db :status-im.transport.inbox/add-peer :utils/dispatch-later})) #{:db :transport.inbox/add-peer :utils/dispatch-later}))
(is (= (get-in result [:db :mailserver-status]) (is (= (get-in result [:db :mailserver-status])
:connecting)))) :connecting))))
(testing "Mailserver disconnected, sym-key doesn't exists (unlikely situation in practice)" (testing "Mailserver disconnected, sym-key doesn't exists (unlikely situation in practice)"
(let [result (peers-summary-change-result false false true)] (let [result (peers-summary-change-result false false true)]
(is (= (into #{} (keys result)) (is (= (into #{} (keys result))
#{:db :status-im.transport.inbox/add-peer :utils/dispatch-later :shh/generate-sym-key-from-password})) #{:db :transport.inbox/add-peer :utils/dispatch-later :shh/generate-sym-key-from-password}))
(is (= (get-in result [:db :mailserver-status]) (is (= (get-in result [:db :mailserver-status])
:connecting)))) :connecting))))
(testing "Mailserver isn't concerned by peer summary changes" (testing "Mailserver isn't concerned by peer summary changes"
@ -51,8 +51,8 @@
{:settings {:fleet :eth.beta {:settings {:fleet :eth.beta
:wnode {:eth.beta "wnodeid"}}}}] :wnode {:eth.beta "wnodeid"}}}}]
(testing "it adds the peer" (testing "it adds the peer"
(is (= {:wnode "wnode-address"} (is (= "wnode-address"
(::inbox/add-peer (inbox/connect-to-mailserver {:db db}))))) (:transport.inbox/add-peer (inbox/connect-to-mailserver {:db db})))))
(testing "it generates a sym key if hasn't been generated before" (testing "it generates a sym key if hasn't been generated before"
(is (= "wnode-password" (is (= "wnode-password"
(-> (inbox/connect-to-mailserver {:db db}) (-> (inbox/connect-to-mailserver {:db db})
@ -67,118 +67,60 @@
:shh/generate-sym-key-from-password :shh/generate-sym-key-from-password
first))))))) first)))))))
(deftest request-messages #_(deftest request-messages
(let [db {:mailserver-status :connected (let [db {:mailserver-status :connected
:inbox/current-id "wnodeid" :inbox/current-id "wnodeid"
:inbox/wnodes {:eth.beta {"wnodeid" {:address "wnode-address" :inbox/wnodes {:eth.beta {"wnodeid" {:address "wnode-address"
:sym-key-id "something" :sym-key-id "something"
:password "wnode-password"}}} :password "wnode-password"}}}
:account/account {:settings {:fleet :eth.beta}} :account/account {:settings {:fleet :eth.beta}}
:transport/chats :transport/chats
{:dont-fetch-history {:topic "dont-fetch-history"} {:dont-fetch-history {:topic "dont-fetch-history"}
:fetch-history {:topic "fetch-history" :fetch-history {:topic "fetch-history"}}}
:fetch-history? true}}} cofx {:db db :now 1000000000}]
cofx {:db db :now 1000000000}] (testing "inbox is ready"
(testing "inbox is ready" (testing "last request is > the 7 days ago"
(testing "last request is > the 7 days ago" (let [cofx-with-last-request (assoc-in cofx [:db :account/account :last-request] 400000)
(let [cofx-with-last-request (assoc-in cofx [:db :account/account :last-request] 400000) actual (inbox/request-messages cofx-with-last-request nil)]
actual (inbox/request-messages cofx-with-last-request)] (testing "it uses last request"
(testing "it uses last request" (is (= 400000 (get-in actual [:transport.inbox/request-messages :requests]))))))
(is (= 400000 (get-in actual [::inbox/request-messages 0 :from])))))) (testing "last request is < the 7 days ago"
(testing "last request is < the 7 days ago" (let [cofx-with-last-request (assoc-in cofx [:db :account/account :last-request] 2)
(let [cofx-with-last-request (assoc-in cofx [:db :account/account :last-request] 2) actual (inbox/request-messages cofx-with-last-request nil)]
actual (inbox/request-messages cofx-with-last-request)] (testing "it uses last 7 days for catching up"
(testing "it uses last 7 days for catching up" (is (= 395200 (get-in actual [:transport.inbox/request-messages :requests]))))
(is (= 395200 (get-in actual [::inbox/request-messages 0 :from])))) (testing "it only uses topics that dont have fetch history set"
(testing "it only uses topics that dont have fetch history set" (is (= ["0xf8946aac" "dont-fetch-history"]
(is (= ["0xf8946aac" "dont-fetch-history"] (get-in actual [:transport.inbox/request-messages :requests]))))
(get-in actual [::inbox/request-messages 0 :topics])))) (testing "it uses the last 24 hours to request history"
(testing "it uses the last 24 hours to request history" (is (= 913600
(is (= 913600 (get-in actual [:transport.inbox/request-messages :requests]))))
(get-in actual [::inbox/request-messages 1 :from])))) (testing "it fetches the right topic for history"
(testing "it fetches the right topic for history" (is (= ["fetch-history"]
(is (= ["fetch-history"] (get-in actual [:transport.inbox/request-messages :requests])))))))
(get-in actual [::inbox/request-messages 1 :topics]))))))) (testing "inbox is not ready"
(testing "inbox is not ready" (testing "it does not do anything"
(testing "it does not do anything" (is (nil? (inbox/request-messages {:db {}} nil)))))))
(is (nil? (inbox/request-messages {:db {}})))))))
(deftest request-messages-params #_(deftest initialize-offline-inbox
(let [mailserver {:address "peer" (let [db {:mailserver-status :connected
:sym-key-id "id"}] :account/account {:settings {:fleet :eth.beta}}
(testing "from is greater that to" :inbox/current-id "wnodeid"
(testing "it returns an empty sequence" :inbox/wnodes {:eth.beta {"wnodeid" {:address "wnode-address"
(is (empty? (inbox/request-inbox-messages-params mailserver 2 0 ["a" "b" "c"]))))) :sym-key-id "something"
(testing "from is equal to to" :password "wnode-password"}}}}]
(testing "it returns an empty sequence" (testing "last-request is not set"
(is (empty? (inbox/request-inbox-messages-params mailserver 2 2 ["a" "b" "c"]))))) (testing "it sets it to now in seconds"
(testing "to is less than the step" (is (= 10
(is (= #{{:topic "a" (get-in
:mailServerPeer "peer" (inbox/initialize-offline-inbox {:now 10000 :db db} [])
:symKeyID "id" [:db :account/account :last-request])))))
:from 0 (testing "last-request is set"
:to 3} (testing "leaves it unchanged"
{:topic "b" (is (= "sometimeago"
:mailServerPeer "peer" (get-in
:symKeyID "id" (inbox/initialize-offline-inbox
:from 0 {:now "now"
:to 3}} :db (assoc-in db [:account/account :last-request] "sometimeago")}
(into #{} (inbox/request-inbox-messages-params mailserver 0 3 ["a" "b"]))))) [])
(testing "to is equal the step" [:db :account/account :last-request])))))))
(is (= #{{:topic "a"
:mailServerPeer "peer"
:symKeyID "id"
:from 0
:to 86400}
{:topic "b"
:mailServerPeer "peer"
:symKeyID "id"
:from 0
:to 86400}}
(into #{} (inbox/request-inbox-messages-params mailserver 0 86400 ["a" "b"])))))
(testing "to is greather than the step"
(is (= #{{:topic "a"
:mailServerPeer "peer"
:symKeyID "id"
:from 0
:to 86400}
{:topic "b"
:mailServerPeer "peer"
:symKeyID "id"
:from 0
:to 86400}
{:topic "a"
:mailServerPeer "peer"
:symKeyID "id"
:from 86400
:to 90000}
{:topic "b"
:mailServerPeer "peer"
:symKeyID "id"
:from 86400
:to 90000}}
(into #{} (inbox/request-inbox-messages-params mailserver 0 90000 ["a" "b"])))))))
(deftest initialize-offline-inbox
(let [db {:mailserver-status :connected
:account/account {:settings {:fleet :eth.beta}}
:inbox/current-id "wnodeid"
:inbox/wnodes
{:eth.beta {"wnodeid" {:address "wnode-address"
:sym-key-id "something"
:password "wnode-password"}}}}]
(testing "last-request is not set"
(testing "it sets it to now in seconds"
(is (= 10
(get-in
(inbox/initialize-offline-inbox {:now 10000 :db db} [])
[:db :account/account :last-request])))))
(testing "last-request is set"
(testing "leaves it unchanged"
(is (= "sometimeago"
(get-in
(inbox/initialize-offline-inbox
{:now "now"
:db (assoc-in db [:account/account :last-request] "sometimeago")}
[])
[:db :account/account :last-request])))))))