[fixes #4745] Don't query mailserver on restored account

When creating a new account / recovery we don't poll the mailserver anymore for historic messages, which solves the immediate issue of fetching only received messages

Handle messages sent from a different device in public chat / restore history. The message will be added, shown correctly as sent by the user, and the status will be set as sent ( need to check for seen race condition, as messages will now be added twice). This means that multidevice should now work for public chats.

Move contact updates to discovery topic. This is necessary as there is a pre-existing bug whereby contact updates would not work anymore after wallet recovery, as the code relies on the initial contact request being stored on the mailserver, which we cannot guarantee (we only pull 7 days of data). Not pulling history anymore exacerbate the problems but does not introduce it.
To make sure that contact updates will work after wallet recovery, we also need to consider a ContactUpdate in the same way we consider a ContactRequest (the other peer has no idea that the user has recovered the wallet). This does not change any behaviour in terms of obscurity/security as ContactRequest are automatically processed (in both case the contact will be set as pending?, not as accepted)
At this stage ContactRequest, ContactRequestConfirmed, ContactUpdate have all the same logic, i.e. update the contact information, leave the pending flag alone.

Only 1 day of history is fetched for newly joined chats, if catching up 7 days is the cap as before.

Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
This commit is contained in:
Andrea Maria Piana 2018-06-19 13:43:47 +02:00
parent 13c8cbf253
commit b34744133b
No known key found for this signature in database
GPG Key ID: AA6CCA6DE0E06424
20 changed files with 428 additions and 258 deletions

View File

@ -6,7 +6,6 @@
(defn console-message [{:keys [timestamp message-id content content-type]}]
{:message-id message-id
:outgoing false
:chat-id constants/console-chat-id
:from constants/console-chat-id
:to "me"

View File

@ -101,7 +101,6 @@
:timestamp now
:content (str content)
:content-type constants/text-content-type
:outgoing false
:clock-value (utils.clocks/send 0)
:chat-id chat-id
:from chat-id

View File

@ -101,9 +101,14 @@
status)
:data-store/tx [(user-statuses-store/save-status-tx status)]}))
(defn add-outgoing-status [{:keys [from] :as message} {:keys [db]}]
(assoc message :outgoing (= from (:current-public-key db))))
(defn- add-message
[batch? {:keys [chat-id message-id clock-value content] :as message} current-chat? {:keys [db] :as cofx}]
(let [prepared-message (prepare-message message chat-id current-chat?)]
(let [prepared-message (-> message
(prepare-message chat-id current-chat?)
(add-outgoing-status cofx))]
(let [fx {:db (cond->
(-> db
(update-in [:chats chat-id :messages] assoc message-id prepared-message)
@ -126,37 +131,60 @@
(when send-seen?
(transport/send (protocol/map->MessagesSeen {:message-ids #{message-id}}) chat-id cofx)))
(defn ensure-clock-value [{:keys [clock-value] :as message} {:keys [last-clock-value]}]
(if clock-value
message
(assoc message :clock-value (utils.clocks/send last-clock-value))))
(defn add-command-request
[{:keys [content-type content] :as message} chat {:keys [db]}]
(let [request-command (:request-command content)
current-account (:account/account db)
command-request? (and (= content-type
constants/content-type-command-request)
request-command)
{:keys [access-scope->commands-responses]
:contacts/keys [contacts]} db]
(if command-request?
(assoc-in
message
[:content :request-command-ref]
(lookup-response-ref access-scope->commands-responses
current-account
chat
contacts
request-command))
message)))
(defn- add-received-message
[batch?
{:keys [from message-id chat-id content content-type clock-value to-clock-value js-obj] :as message}
{:keys [from message-id chat-id content content-type clock-value js-obj] :as raw-message}
{:keys [db now] :as cofx}]
(let [{:keys [web3 current-chat-id view-id access-scope->commands-responses]
:contacts/keys [contacts]} db
current-account (:account/account db)
(let [{:keys [web3 current-chat-id view-id]} db
current-chat? (and (= :chat view-id) (= current-chat-id chat-id))
{:keys [last-clock-value
public?] :as chat} (get-in db [:chats chat-id])
request-command (:request-command content)
command-request? (and (= content-type constants/content-type-command-request)
request-command)
add-message-fn (if batch? add-batch-message add-single-message)]
{:keys [public?] :as chat} (get-in db [:chats chat-id])
add-message-fn (if batch? add-batch-message add-single-message)
message (-> raw-message
(ensure-clock-value chat)
;; TODO (cammellos): Refactor so it's not computed twice
(add-outgoing-status cofx)
(add-command-request chat cofx))]
(handlers-macro/merge-fx cofx
{:confirm-messages-processed [{:web3 web3
:js-obj js-obj}]}
(add-message-fn (cond-> message
(not clock-value)
(assoc :clock-value (utils.clocks/send last-clock-value)) ; TODO (cammeelos): for backward compatibility, we use received time to be removed when not an issue anymore
command-request?
(assoc-in [:content :request-command-ref]
(lookup-response-ref access-scope->commands-responses
current-account chat contacts request-command)))
current-chat?)
(add-own-status chat-id message-id (if current-chat? :seen :received))
(add-message-fn message current-chat?)
;; 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
;; for 1-to-1 care needs to be taken not to override the :seen status
(add-own-status chat-id message-id (cond (:outgoing message) :sent
current-chat? :seen
:else :received))
(requests-events/add-request chat-id message-id)
(send-message-seen chat-id message-id (and (not public?)
current-chat?
(not (chat-model/bot-only-chat? db chat-id))
(not (= constants/system from)))))))
(not (= constants/system from))
(not (:outgoing message)))))))
(def ^:private add-single-received-message (partial add-received-message false))
(def ^:private add-batch-received-message (partial add-received-message true))
@ -195,7 +223,6 @@
(defn system-message [chat-id message-id timestamp content]
{:message-id message-id
:outgoing false
:chat-id chat-id
:from constants/system
:username constants/system
@ -208,18 +235,12 @@
(#{:group-user-message :public-group-user-message} message-type))
(defn add-to-chat?
[{:keys [db]} {:keys [chat-id
clock-value
from
message-id] :as message}]
(let [{:keys [chats current-public-key]} db
{:keys [deleted-at-clock-value
messages
not-loaded-message-ids]} (get chats chat-id)]
(when (not= from current-public-key)
(not (or (get messages message-id)
(get not-loaded-message-ids message-id)
(>= deleted-at-clock-value clock-value))))))
[{:keys [db]} {:keys [chat-id clock-value message-id] :as message}]
(let [{:keys [deleted-at-clock-value messages not-loaded-message-ids]}
(get-in db [:chats chat-id])]
(not (or (get messages message-id)
(get not-loaded-message-ids message-id)
(>= deleted-at-clock-value clock-value)))))
;;;; Send message
@ -234,7 +255,6 @@
{:message-id random-id
:content (str message)
:content-type constants/text-content-type
:outgoing false
:chat-id chat-id
:from chat-id
:to "me"}
@ -243,7 +263,6 @@
{:message-id random-id
:content (assoc (:content message) :bot chat-id)
:content-type constants/content-type-command-request
:outgoing false
:chat-id chat-id
:from chat-id
:to "me"})]
@ -287,7 +306,6 @@
:content message-text
:from identity
:content-type constants/text-content-type
:outgoing true
:timestamp now
:clock-value (utils.clocks/send last-clock-value)
:show? true}
@ -398,7 +416,6 @@
(if request
constants/content-type-command-request
constants/content-type-command))
:outgoing true
:clock-value (utils.clocks/send last-clock-value)
:show? true}
chat)))

View File

@ -34,8 +34,7 @@
(map #(select-keys % [:content :timestamp]))))
(def default-values
{:outgoing false
:to nil})
{:to nil})
(re-frame/reg-cofx
:data-store/get-messages

View File

@ -1,7 +1,94 @@
(ns status-im.models.contact)
(ns status-im.models.contact
(:require
[status-im.utils.contacts :as utils.contacts]
[status-im.data-store.contacts :as contacts-store]
[status-im.transport.message.core :as transport]
[status-im.transport.message.v1.contact :as message.v1.contact]
[status-im.utils.handlers-macro :as handlers-macro]))
(defn can-add-to-contacts? [{:keys [pending? dapp?]}]
(and (not dapp?)
(or pending?
;; it's not in the contact list at all
(nil? pending?))))
(defn- build-contact [whisper-id {{:keys [chats] :contacts/keys [contacts]} :db}]
(assoc (or (get contacts whisper-id)
(utils.contacts/whisper-id->new-contact whisper-id))
:address (utils.contacts/public-key->address whisper-id)))
(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}))
(defn- add-new-contact [{:keys [whisper-identity] :as contact} {:keys [db]}]
(let [new-contact (assoc contact
:pending? false
:public-key whisper-identity)]
{:db (-> db
(update-in [:contacts/contacts whisper-identity]
merge new-contact)
(assoc-in [:contacts/new-identity] ""))
:data-store/tx [(contacts-store/save-contact-tx new-contact)]}))
(defn send-contact-request [{:keys [whisper-identity pending? dapp?] :as contact} {:keys [db] :as cofx}]
(when-not dapp?
(if pending?
(transport/send (message.v1.contact/map->ContactRequestConfirmed (own-info db)) whisper-identity cofx)
(transport/send (message.v1.contact/map->ContactRequest (own-info db)) whisper-identity cofx))))
(defn add-contact [whisper-id {:keys [db] :as cofx}]
(let [contact (build-contact whisper-id cofx)]
(handlers-macro/merge-fx cofx
(add-new-contact contact)
(send-contact-request contact))))
(defn handle-contact-update
[public-key
timestamp
{:keys [name profile-image address fcm-token] :as m}
{{:contacts/keys [contacts] :keys [current-public-key] :as db} :db :as cofx}]
;; We need to convert to timestamp ms as before we were using now in ms to
;; set last updated
;; Using whisper timestamp mostly works but breaks in a few scenarios:
;; 2 updates sent in the same second
;; when using multi-device & clocks are out of sync
;; Using logical clocks is probably the correct way to handle it, but an overkill
;; for now
(let [timestamp-ms (* timestamp 1000)
prev-last-updated (get-in db [:contacts/contacts public-key :last-updated])]
(when (and (not= current-public-key public-key)
(< prev-last-updated timestamp-ms))
(let [contact (get contacts public-key)
;; Backward compatibility with <= 0.9.21, as they don't send
;; fcm-token & address in contact updates
contact-props (cond->
{:whisper-identity public-key
:public-key public-key
:photo-path profile-image
:name name
:address (or address
(:address contact)
(utils.contacts/public-key->address public-key))
:last-updated timestamp-ms
;;NOTE (yenda) in case of concurrent contact request
:pending? (get contact :pending? true)}
fcm-token (assoc :fcm-token fcm-token))]
;;NOTE (yenda) only update if there is changes to the contact
(when-not (= contact-props
(select-keys contact [:whisper-identity :public-key :address
:photo-path :name :fcm-token :pending?]))
(handlers-macro/merge-fx cofx
{:db (update-in db [:contacts/contacts public-key]
merge contact-props)
:data-store/tx [(contacts-store/save-contact-tx
contact-props)]}))))))
(def receive-contact-request handle-contact-update)
(def receive-contact-request-confirmation handle-contact-update)
(def receive-contact-update handle-contact-update)

View File

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

View File

@ -30,7 +30,7 @@
(try
(handlers-macro/merge-fx
(assoc cofx :js-obj js-message)
(message/receive status-message (or chat-id sig) sig)
(message/receive status-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
@ -89,7 +89,7 @@
(handlers/register-handler-fx
:contact/add-new-sym-key
(fn [{:keys [db] :as cofx} [_ {:keys [sym-key-id sym-key chat-id topic message]}]]
(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
@ -106,7 +106,7 @@
: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)))))
(message/receive message chat-id chat-id timestamp)))))
#_(handlers/register-handler-fx
:send-test-message
@ -155,7 +155,7 @@
(handlers/register-handler-fx
:group/add-new-sym-key
[re-frame/trim-v (re-frame/inject-cofx :random-id)]
(fn [{:keys [db] :as cofx} [{:keys [sym-key-id sym-key chat-id signature message]}]]
(fn [{:keys [db] :as cofx} [{:keys [sym-key-id sym-key chat-id signature timestamp message]}]]
(let [{:keys [web3 current-public-key]} db
topic (transport.utils/get-topic chat-id)
fx {:db (assoc-in db
@ -174,7 +174,7 @@
(assoc :sym-key sym-key))})]}]
;; if new sym-key is wrapping some message, call receive on it as well, if not just update the transport layer
(if message
(handlers-macro/merge-fx cofx fx (message/receive message chat-id signature))
(handlers-macro/merge-fx cofx fx (message/receive message chat-id signature timestamp))
fx))))
(re-frame/reg-fx

View File

@ -1,34 +1,32 @@
(ns status-im.transport.impl.receive
(:require
[status-im.chat.models.group-chat :as models.group-chat]
[status-im.ui.screens.contacts.core :as contacts]
[status-im.models.contact :as models.contact]
[status-im.transport.message.core :as message]
[status-im.transport.message.v1.contact :as transport.contact]
[status-im.transport.message.v1.group-chat :as transport.group-chat]))
(extend-type transport.group-chat/GroupAdminUpdate
message/StatusMessage
(receive [this chat-id signature cofx]
(receive [this chat-id signature _ cofx]
(models.group-chat/handle-group-admin-update this chat-id signature cofx)))
(extend-type transport.group-chat/GroupLeave
message/StatusMessage
(receive [this chat-id signature cofx]
(receive [this chat-id signature _ cofx]
(models.group-chat/handle-group-leave chat-id signature cofx)))
(extend-type transport.contact/ContactRequest
message/StatusMessage
(receive [this chat-id signature cofx]
(contacts/receive-contact-request signature this cofx)))
(receive [this _ signature timestamp cofx]
(models.contact/receive-contact-request signature timestamp this cofx)))
(extend-type transport.contact/ContactRequestConfirmed
message/StatusMessage
(receive [this chat-id signature cofx]
(contacts/receive-contact-request-confirmation signature this cofx)))
(receive [this _ signature timestamp cofx]
(models.contact/receive-contact-request-confirmation signature timestamp this cofx)))
(extend-type transport.contact/ContactUpdate
message/StatusMessage
(receive [this chat-id signature cofx]
(contacts/receive-contact-update chat-id
signature
this cofx)))
(receive [this _ signature timestamp cofx]
(models.contact/receive-contact-update signature timestamp this cofx)))

View File

@ -23,7 +23,7 @@
;;
;; - We send a request to the mailserver, we are only interested in the
;; messages since `last-request`, the time of the last successful request,
;; and the last 7 days 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
;; instead we start receiving messages in the filters for the requested
;; topics.
@ -149,20 +149,8 @@
(re-frame/reg-fx
::request-messages
(fn [{:keys [wnode topics to from web3]}]
(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))))
(re-frame/reg-fx
::request-history
(fn [{:keys [wnode topics now-in-s web3]}]
(let [from (- now-in-s seven-days)
to now-in-s]
(fn [params]
(doseq [{:keys [wnode topics to from web3]} params]
(request-inbox-messages web3
wnode
topics
@ -245,17 +233,20 @@
(defn get-request-messages-topics
"Returns topics for which full history has already been recovered"
[db]
(conj (mapv :topic
(remove :fetch-history?
(vals (:transport/chats db))))
(conj (map :topic
(remove :fetch-history?
(vals (:transport/chats db))))
(transport.utils/get-topic constants/contact-discovery)))
(defn get-request-history-topics
"Returns topics for which full history has not been recovered"
[db]
(mapv :topic
(filter :fetch-history?
(vals (:transport/chats db)))))
(map :topic
(filter :fetch-history?
(vals (:transport/chats db)))))
(defn request-history-span [now-in-s]
(- now-in-s one-day))
(defn request-messages
([{:keys [db now] :as cofx}]
@ -268,15 +259,16 @@
request-messages-topics (get-request-messages-topics db)
request-history-topics (get-request-history-topics db)]
(when (inbox-ready? wnode cofx)
{::request-messages {:wnode wnode
:topics request-messages-topics
:from last-request
:to now-in-s
:web3 web3}
::request-history {:wnode wnode
:now-in-s now-in-s
:topics request-history-topics
:web3 web3}
{::request-messages [{:wnode wnode
: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]}]})))
@ -290,10 +282,11 @@
topic (get-in db [:transport/chats chat-id :topic])
now-in-s (quot now 1000)]
(when (inbox-ready? wnode cofx)
{::request-history {:wnode wnode
:topics [topic]
:now-in-s now-in-s
:web3 web3}
{::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]}]})))
@ -364,6 +357,7 @@
(defn initialize-offline-inbox [custom-mailservers cofx]
(handlers-macro/merge-fx cofx
(models.mailserver/add-custom-mailservers custom-mailservers)
(models.mailserver/set-initial-last-request)
(models.mailserver/set-current-mailserver)))
(handlers/register-handler-fx

View File

@ -4,4 +4,4 @@
(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 cofx] "Method producing all effects necessary for receiving the message record"))
(receive [this chat-id signature timestamp cofx] "Method producing all effects necessary for receiving the message record"))

View File

@ -40,8 +40,8 @@
(deftype ContactUpdateHandler []
Object
(tag [this v] "c6")
(rep [this {:keys [name profile-image]}]
#js [name profile-image]))
(rep [this {:keys [name profile-image address fcm-token]}]
#js [name profile-image address fcm-token]))
(deftype MessageHandler []
Object
@ -102,8 +102,8 @@
(v1.protocol/Message. content content-type message-type clock-value timestamp))
"c5" (fn [message-ids]
(v1.protocol/MessagesSeen. message-ids))
"c6" (fn [[name profile-image]]
(v1.contact/ContactUpdate. name profile-image))}})) ; removed group chat handlers for https://github.com/status-im/status-react/issues/4506
"c6" (fn [[name profile-image address fcm-token]]
(v1.contact/ContactUpdate. name profile-image address fcm-token))}})) ; removed group chat handlers for https://github.com/status-im/status-react/issues/4506
(defn serialize
"Serializes a record implementing the StatusMessage protocol using the custom writers"

View File

@ -38,11 +38,11 @@
"contact-request-confirmation")
:data-store/tx [(transport-store/save-transport-tx {:chat-id chat-id
:chat updated-chat})]}
(protocol/send {:chat-id chat-id
:payload this
:success-event success-event})))))
(protocol/send-with-pubkey {:chat-id chat-id
:payload this
:success-event success-event})))))
(defrecord ContactUpdate [name profile-image]
(defrecord ContactUpdate [name profile-image address fcm-token]
message/StatusMessage
(send [this _ {:keys [db] :as cofx}]
(let [public-keys (reduce (fn [acc [_ {:keys [public-key pending?]}]]
@ -65,9 +65,9 @@
[:transport/chats chat-id :resend?]
"contact-update")
:data-store/tx tx}
(protocol/send {:chat-id chat-id
:payload this
:success-event success-event}))))
(protocol/send-with-pubkey {:chat-id chat-id
:payload this
:success-event success-event}))))
recipients))))
(defn remove-chat-filter
@ -90,7 +90,7 @@
:payload this
:success-event success-event}
cofx)))
(receive [this chat-id signature {:keys [db] :as cofx}]
(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:
@ -105,6 +105,7 @@
(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
@ -119,4 +120,4 @@
;; 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 cofx)))))
(message/receive message chat-id chat-id timestamp cofx)))))

View File

@ -19,7 +19,7 @@
:chat-id chat-id
:payload this}
cofx)))
(receive [this _ signature {:keys [db] :as cofx}]
(receive [this _ signature timestamp {:keys [db] :as cofx}]
(handlers-macro/merge-fx
cofx
{:shh/add-new-sym-keys
@ -30,6 +30,7 @@
[:group/add-new-sym-key
{:chat-id chat-id
:signature signature
:timestamp timestamp
:sym-key sym-key
:sym-key-id sym-key-id
:message message}]))}]}

View File

@ -76,12 +76,12 @@
(defrecord Ack [message-ids]
message/StatusMessage
(send [this cofx chat-id])
(receive [this db chat-id sig]))
(receive [this chat-id sig timestamp cofx]))
(defrecord Seen [message-ids]
message/StatusMessage
(send [this cofx chat-id])
(receive [this cofx chat-id sig]))
(receive [this chat-id sig timestamp cofx]))
(defrecord Message [content content-type message-type clock-value timestamp]
message/StatusMessage
@ -97,7 +97,7 @@
config/use-sym-key)
(send params cofx)
(send-with-pubkey params cofx))))
(receive [this chat-id signature cofx]
(receive [this chat-id signature _ cofx]
{:chat-received-message/add-fx
[(assoc (into {} this)
:message-id (transport.utils/message-id this)
@ -112,5 +112,5 @@
(send {:chat-id chat-id
:payload this}
cofx))
(receive [this chat-id signature cofx]
(receive [this chat-id signature _ cofx]
(chat/receive-seen chat-id signature this cofx)))

View File

@ -12,14 +12,18 @@
([new-account-fields after-update-event {:keys [db] :as cofx}]
(let [current-account (:account/account db)
new-account (merge current-account new-account-fields)
fcm-token (get-in db [:notifications :fcm-token])
fx {:db (assoc db :account/account new-account)
:data-store/base-tx [(accounts-store/save-account-tx
(assoc new-account
:after-update-event
after-update-event))]}
{:keys [name photo-path]} new-account]
{:keys [name photo-path address]} new-account]
(if (or (:name new-account-fields) (:photo-path new-account-fields))
(handlers-macro/merge-fx cofx fx (transport/send (message.contact/ContactUpdate. name photo-path) nil))
(handlers-macro/merge-fx
cofx
fx
(transport/send (message.contact/ContactUpdate. name photo-path address fcm-token) nil))
fx))))
(defn clean-seed-phrase

View File

@ -1,59 +0,0 @@
(ns status-im.ui.screens.contacts.core
(:require [status-im.utils.handlers-macro :as handlers-macro]
[status-im.chat.models :as chat.models]
[status-im.data-store.contacts :as contacts-store]))
(defn receive-contact-request
[public-key
{:keys [name profile-image address fcm-token]}
{{:contacts/keys [contacts] :as db} :db :as cofx}]
(let [contact (get contacts public-key)
contact-props {:whisper-identity public-key
:public-key public-key
:address address
:photo-path profile-image
:name name
:fcm-token fcm-token
;;NOTE (yenda) in case of concurrent contact request
:pending? (get contact :pending? true)}]
;;NOTE (yenda) only update if there is changes to the contact
(when-not (= contact-props
(select-keys contact [:whisper-identity :public-key :address
:photo-path :name :fcm-token :pending?]))
(handlers-macro/merge-fx cofx
{:db (update-in db [:contacts/contacts public-key]
merge contact-props)
:data-store/tx [(contacts-store/save-contact-tx
contact-props)]}))))
(defn receive-contact-request-confirmation
[public-key {:keys [name profile-image address fcm-token]}
{{:contacts/keys [contacts] :as db} :db :as cofx}]
(when-let [contact (get contacts public-key)]
(let [contact-props {:whisper-identity public-key
:public-key public-key
:address address
:photo-path profile-image
:name name
:fcm-token fcm-token}]
(handlers-macro/merge-fx cofx
{:db (update-in db [:contacts/contacts public-key]
merge contact-props)
:data-store/tx [(contacts-store/save-contact-tx
contact-props)]}))))
(defn- update-contact [{:keys [whisper-identity] :as contact} {:keys [db]}]
(when (get-in db [:contacts/contacts whisper-identity])
{:db (update-in db [:contacts/contacts whisper-identity] merge contact)
:data-store/tx [(contacts-store/save-contact-tx contact)]}))
(defn receive-contact-update [chat-id public-key {:keys [name profile-image]} {:keys [db now] :as cofx}]
(let [{:keys [chats current-public-key]} db]
(when (not= current-public-key public-key)
(let [prev-last-updated (get-in db [:contacts/contacts public-key :last-updated])]
(when (<= prev-last-updated now)
(let [contact {:whisper-identity public-key
:name name
:photo-path profile-image
:last-updated now}]
(update-contact contact cofx)))))))

View File

@ -1,18 +1,21 @@
(ns status-im.ui.screens.contacts.events
(:require [re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[status-im.chat.events :as chat.events]
[status-im.data-store.contacts :as contacts-store]
[status-im.transport.message.core :as transport]
[status-im.transport.message.v1.contact :as message.v1.contact]
[status-im.ui.screens.add-new.new-chat.db :as new-chat.db]
[status-im.ui.screens.contacts.default-dapps :as default-dapps]
[status-im.ui.screens.navigation :as navigation]
[status-im.utils.contacts :as utils.contacts]
[status-im.utils.handlers :as handlers]
[status-im.utils.handlers-macro :as handlers-macro]
[status-im.utils.js-resources :as js-res]
[status-im.utils.utils :as utils]))
[status-im.chat.events :as chat.events]
[status-im.ui.screens.navigation :as navigation]
[status-im.utils.utils :as utils]
[status-im.models.contact :as models.contact]))
(defn add-contact-and-open-chat [whisper-id cofx]
(handlers-macro/merge-fx cofx
(navigation/navigate-to-clean :home)
(models.contact/add-contact whisper-id)
(chat.events/start-chat whisper-id {})))
(re-frame/reg-cofx
:get-default-contacts
@ -26,11 +29,6 @@
;;;; Handlers
(defn- update-contact [{:keys [whisper-identity] :as contact} {:keys [db]}]
(when (get-in db [:contacts/contacts whisper-identity])
{:db (update-in db [:contacts/contacts whisper-identity] merge contact)
:data-store/tx [(contacts-store/save-contact-tx contact)]}))
(handlers/register-handler-fx
:load-contacts
[(re-frame/inject-cofx :data-store/get-all-contacts)]
@ -39,52 +37,11 @@
contacts (into {} contacts-list)]
{:db (update db :contacts/contacts #(merge contacts %))})))
(defn- add-new-contact [{:keys [whisper-identity] :as contact} {:keys [db]}]
(let [new-contact (assoc contact
:pending? false
:public-key whisper-identity)]
{:db (-> db
(update-in [:contacts/contacts whisper-identity]
merge new-contact)
(assoc-in [:contacts/new-identity] ""))
:data-store/tx [(contacts-store/save-contact-tx new-contact)]}))
(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}))
(defn send-contact-request [{:keys [whisper-identity pending? dapp?] :as contact} {:keys [db] :as cofx}]
(when-not dapp?
(if pending?
(transport/send (message.v1.contact/map->ContactRequestConfirmed (own-info db)) whisper-identity cofx)
(transport/send (message.v1.contact/map->ContactRequest (own-info db)) whisper-identity cofx))))
(defn- build-contact [whisper-id {{:keys [chats] :contacts/keys [contacts]} :db}]
(assoc (or (get contacts whisper-id)
(utils.contacts/whisper-id->new-contact whisper-id))
:address (utils.contacts/public-key->address whisper-id)))
(defn add-contact [whisper-id {:keys [db] :as cofx}]
(let [contact (build-contact whisper-id cofx)]
(handlers-macro/merge-fx cofx
(add-new-contact contact)
(send-contact-request contact))))
(defn add-contact-and-open-chat [whisper-id cofx]
(handlers-macro/merge-fx cofx
(navigation/navigate-to-clean :home)
(add-contact whisper-id)
(chat.events/start-chat whisper-id {})))
(handlers/register-handler-fx
:add-contact
[(re-frame/inject-cofx :random-id)]
(fn [cofx [_ whisper-id]]
(add-contact whisper-id cofx)))
(models.contact/add-contact whisper-id cofx)))
(handlers/register-handler-fx
:set-contact-identity-from-qr
@ -111,10 +68,7 @@
:open-chat-with-contact
[(re-frame/inject-cofx :random-id)]
(fn [cofx [_ {:keys [whisper-identity]}]]
(handlers-macro/merge-fx cofx
(navigation/navigate-to-clean :home)
(add-contact whisper-identity)
(chat.events/start-chat whisper-identity {}))))
(add-contact-and-open-chat whisper-identity cofx)))
(handlers/register-handler-fx
:add-contact-handler

View File

@ -11,13 +11,6 @@
:from "a"
:clock-value 1
:chat-id "a"})))
(testing "it returns false when from is the same as pk"
(is (not (message/add-to-chat? {:db {:current-public-key "pk"
:chats {"a" {}}}}
{:message-id "message-id"
:from "pk"
:clock-value 1
:chat-id "a"}))))
(testing "it returns false when it's already in the loaded message"
(is (not (message/add-to-chat? {:db {:chats {"a" {:messages {"message-id" {}}}}}}
{:message-id "message-id"
@ -49,6 +42,34 @@
:clock-value 0
:chat-id "a"})))))
(deftest add-own-received-message
(let [db {:current-public-key "me"
:view-id :chat
:current-chat-id "chat-id"
:chats {"chat-id" {:messages {}}}}]
(testing "a message coming from you!"
(let [actual (message/receive
{:from "me"
:message-id "id"
:chat-id "chat-id"
:content "b"
:clock-value 1}
{:db db})
message (get-in actual [:db :chats "chat-id" :messages "id"])
status (get-in actual [:db :chats "chat-id" :message-statuses "id" "me" :status])]
(testing "it adds the message"
(is message))
(testing "it marks the message as outgoing"
(is (= true (:outgoing message))))
(testing "it confirm the message as processed"
(is (:confirm-messages-processed actual)))
(testing "it stores the message"
(is (:data-store/tx actual)))
(testing "it does not send a seen confirmation"
(is (not (:shh/post actual))))
(testing "it marks it as sent"
(is (= :sent status)))))))
(deftest receive-send-seen
(let [db {:db {:chats {"chat-id" {}}
:account/account {:public-key "a"}

View File

@ -2,6 +2,9 @@
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.models.contact :as model]))
(def public-key "0x04fcf40c526b09ff9fb22f4a5dbd08490ef9b64af700870f8a0ba2133f4251d5607ed83cd9047b8c2796576bc83fa0de23a13a4dced07654b8ff137fe744047917")
(def address "71adb0644e2b590e37dafdfea8bd58f0c7668c7f")
(deftest can-add-to-contact-test
(testing "a user is already in contacts"
(is (not (model/can-add-to-contacts? {:pending? false}))))
@ -16,3 +19,123 @@
(is (model/can-add-to-contacts? {})))
(testing "a dapp"
(is (not (model/can-add-to-contacts? {:dapp? true}))))))
(deftest handle-contact-update-test
(testing "the contact is not in contacts"
(let [actual (model/handle-contact-update
public-key
1
{:name "name"
:profile-image "image"
:address "address"
:fcm-token "token"}
{})
contact (get-in actual [:db :contacts/contacts public-key])]
(testing "it stores the contact in the database"
(is (:data-store/tx actual)))
(testing "it adds a new contact with pending? true"
(is (= {:whisper-identity public-key
:public-key public-key
:photo-path "image"
:name "name"
:last-updated 1000
:pending? true
:fcm-token "token"
:address "address"} contact)))))
(testing "the contact is already in contacts"
(testing "timestamp is greather than last-updated"
(let [actual (model/handle-contact-update
public-key
1
{:name "new-name"
:profile-image "new-image"
:address "new-address"
:fcm-token "new-token"}
{:db {:contacts/contacts
{public-key {:whisper-identity public-key
:public-key public-key
:photo-path "old-image"
:name "old-name"
:last-updated 0
:pending? false
:fcm-token "old-token"
:address "old-address"}}}})
contact (get-in actual [:db :contacts/contacts public-key])]
(testing "it stores the contact in the database"
(is (:data-store/tx actual)))
(testing "it updates the contact leaving pending unchanged"
(is (= {:whisper-identity public-key
:public-key public-key
:photo-path "new-image"
:name "new-name"
:last-updated 1000
:pending? false
:fcm-token "new-token"
:address "new-address"} contact)))))
(testing "timestamp is equal than last-updated"
(let [actual (model/handle-contact-update
public-key
1
{:name "new-name"
:profile-image "new-image"
:address "new-address"
:fcm-token "new-token"}
{:db {:contacts/contacts
{public-key {:whisper-identity public-key
:public-key public-key
:photo-path "old-image"
:name "old-name"
:last-updated 1000
:pending? false
:fcm-token "old-token"
:address "old-address"}}}})
contact (get-in actual [:db :contacts/contacts public-key])]
(testing "it does nothing"
(is (nil? actual)))))
(testing "timestamp is less than last-updated"
(let [actual (model/handle-contact-update
public-key
0
{:name "new-name"
:profile-image "new-image"
:address "new-address"
:fcm-token "new-token"}
{:db {:contacts/contacts
{public-key {:whisper-identity public-key
:public-key public-key
:photo-path "old-image"
:name "old-name"
:last-updated 1000
:pending? false
:fcm-token "old-token"
:address "old-address"}}}})
contact (get-in actual [:db :contacts/contacts public-key])]
(testing "it does nothing"
(is (nil? actual))))))
(testing "backward compatibility"
(let [actual (model/handle-contact-update
public-key
1
{:name "new-name"
:profile-image "new-image"}
{:db {:contacts/contacts
{public-key {:whisper-identity public-key
:public-key public-key
:photo-path "old-image"
:name "old-name"
:last-updated 0
:pending? false}}}})
contact (get-in actual [:db :contacts/contacts public-key])]
(testing "it stores the contact in the database"
(is (:data-store/tx actual)))
(testing "it updates the contact leaving pending unchanged"
(is (= {:whisper-identity public-key
:public-key public-key
:photo-path "new-image"
:name "new-name"
:last-updated 1000
:pending? false
:address address} contact)))))
(testing "the message is coming from us"
(testing "it does not update contacts"
(is (nil? (model/handle-contact-update "me" 1 {} {:db {:current-public-key "me"}}))))))

View File

@ -77,29 +77,32 @@
:sym-key-id "something"
:password "wnode-password"}}}
:account/account
{:networks {"mainnet" {:config {:NetworkId 1}}}}}
{:networks {"mainnet" {:config {:NetworkId 1}}}}
:transport/chats
{:dont-fetch-history {:topic "dont-fetch-history"}
:fetch-history {:topic "fetch-history"
:fetch-history? true}}}
cofx {:db db :now 1000000000}]
(testing "inbox is ready"
(testing "last-request is set"
(testing "last request is > the 7 days ago"
(let [cofx-with-last-request (assoc-in cofx [:db :account/account :last-request] 400000)
actual (inbox/request-messages cofx-with-last-request)]
(testing "it uses last request"
(is (= 400000 (get-in actual [::inbox/request-messages :from]))))))
(testing "last request is < the 7 days ago"
(let [cofx-with-last-request (assoc-in cofx [:db :account/account :last-request] 2)
actual (inbox/request-messages cofx-with-last-request)]
(testing "it uses last 7 days"
(is (= 395200 (get-in actual [::inbox/request-messages :from])))))))
(testing "last-request is not set"
(let [actual (inbox/request-messages cofx)]
(testing "it defaults to the last 7 days"
(is (= 395200 (get-in actual [::inbox/request-messages :from]))))))
(testing "last-request is nil"
(let [cofx-with-last-request (assoc-in cofx [:db :account/account :last-request] nil)
(testing "last request is > the 7 days ago"
(let [cofx-with-last-request (assoc-in cofx [:db :account/account :last-request] 400000)
actual (inbox/request-messages cofx-with-last-request)]
(testing "it defaults to the last 7 days"
(is (= 395200 (get-in actual [::inbox/request-messages :from])))))))
(testing "it uses last request"
(is (= 400000 (get-in actual [::inbox/request-messages 0 :from]))))))
(testing "last request is < the 7 days ago"
(let [cofx-with-last-request (assoc-in cofx [:db :account/account :last-request] 2)
actual (inbox/request-messages cofx-with-last-request)]
(testing "it uses last 7 days for catching up"
(is (= 395200 (get-in actual [::inbox/request-messages 0 :from]))))
(testing "it only uses topics that dont have fetch history set"
(is (= ["0xf8946aac" "dont-fetch-history"]
(get-in actual [::inbox/request-messages 0 :topics]))))
(testing "it uses the last 24 hours to request history"
(is (= 913600
(get-in actual [::inbox/request-messages 1 :from]))))
(testing "it fetches the right topic for history"
(is (= ["fetch-history"]
(get-in actual [::inbox/request-messages 1 :topics])))))))
(testing "inbox is not ready"
(testing "it does not do anything"
(is (nil? (inbox/request-messages {})))))))
@ -159,3 +162,29 @@
:from 86400
:to 90000}}
(into #{} (inbox/request-inbox-messages-params mailserver 0 90000 ["a" "b"])))))))
(deftest initialize-offline-inbox
(let [db {:network "mainnet"
:mailserver-status :connected
:inbox/current-id "wnodeid"
:inbox/wnodes
{:mainnet {"wnodeid" {:address "wnode-address"
:sym-key-id "something"
:password "wnode-password"}}}
:account/account
{:networks {"mainnet" {:config {:NetworkId 1}}}}}]
(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])))))))