[fix] support offline contact request, confirmation and update

- fix a bug in profile where app would was returning directly to profile
screen after photo capture, showing updating to user but not actually saving
and broadcasting it
- adds support for offline contact request, confirmation and updates by
leveraging the :sent and :not-sent signal from status-go. Each chat
in the protocol layer uses a `resend?` field to keep track of the current
state of the contact, between `nil`, `contact-request`,
`contact-request-confirmation` and `contact-update`. There is no case
were it can be more than one case because if `contact-request` or
`contact-request-confirmation` was not sent yet `contact-update` is not
necessary as the latest contact infos will be sent with the request/confirmation

Signed-off-by: Eric Dvorsak <eric@status.im>
This commit is contained in:
Eric Dvorsak 2018-05-30 03:15:17 +02:00 committed by Eric Dvorsak
parent 8fd6d55a37
commit 68eb36f7b1
No known key found for this signature in database
GPG Key ID: 32D08503358DB921
14 changed files with 238 additions and 92 deletions

View File

@ -13,7 +13,6 @@
[status-im.utils.handlers :as handlers]
[status-im.utils.handlers-macro :as handlers-macro]
[status-im.utils.contacts :as utils.contacts]
[status-im.transport.core :as transport]
[status-im.transport.message.core :as transport.message]
[status-im.transport.message.v1.protocol :as protocol]
[status-im.transport.message.v1.public-chat :as public-chat]
@ -99,25 +98,6 @@
(-> (get-in new-db msg-path)
(select-keys [:message-id :user-statuses])))]})))
(handlers/register-handler-fx
:transport/set-message-envelope-hash
[re-frame/trim-v]
;; message-type is used for tracking
(fn [{:keys [db]} [chat-id message-id message-type envelope-hash]]
{:db (assoc-in db [:transport/message-envelopes envelope-hash] {:chat-id chat-id
:message-id message-id})}))
(handlers/register-handler-fx
:signals/envelope-status
[re-frame/trim-v]
(fn [{:keys [db] :as cofx} [envelope-hash status]]
(let [{:keys [chat-id message-id]} (get-in db [:transport/message-envelopes envelope-hash])
message (get-in db [:chats chat-id :messages message-id])
{:keys [fcm-token]} (get-in db [:contacts/contacts chat-id])]
(handlers-macro/merge-fx cofx
(models.message/update-message-status message status)
(models.message/send-push-notification fcm-token status)))))
;; Change status of messages which are still in "sending" status to "not-sent"
;; (If signal from status-go has not been received)
(handlers/register-handler-fx

View File

@ -4,7 +4,8 @@
[status-im.data-store.realm.schemas.account.v2.core :as v2]
[status-im.data-store.realm.schemas.account.v3.core :as v3]
[status-im.data-store.realm.schemas.account.v4.core :as v4]
[status-im.data-store.realm.schemas.account.v5.core :as v5]))
[status-im.data-store.realm.schemas.account.v5.core :as v5]
[status-im.data-store.realm.schemas.account.v6.core :as v6]))
;; TODO(oskarth): Add failing test if directory vXX exists but isn't in schemas.
@ -23,4 +24,7 @@
:migration v4/migration}
{:schema v5/schema
:schemaVersion 5
:migration v5/migration}])
:migration v5/migration}
{:schema v6/schema
:schemaVersion 6
:migration v6/migration}])

View File

@ -0,0 +1,24 @@
(ns status-im.data-store.realm.schemas.account.v6.core
(:require [status-im.data-store.realm.schemas.account.v5.chat :as chat]
[status-im.data-store.realm.schemas.account.v6.transport :as transport]
[status-im.data-store.realm.schemas.account.v1.contact :as contact]
[status-im.data-store.realm.schemas.account.v1.message :as message]
[status-im.data-store.realm.schemas.account.v1.request :as request]
[status-im.data-store.realm.schemas.account.v1.user-status :as user-status]
[status-im.data-store.realm.schemas.account.v1.local-storage :as local-storage]
[status-im.data-store.realm.schemas.account.v2.mailserver :as mailserver]
[status-im.data-store.realm.schemas.account.v1.browser :as browser]
[taoensso.timbre :as log]))
(def schema [chat/schema
transport/schema
contact/schema
message/schema
request/schema
mailserver/schema
user-status/schema
local-storage/schema
browser/schema])
(defn migration [old-realm new-realm]
(log/debug "migrating v6 account database: " old-realm new-realm))

View File

@ -0,0 +1,19 @@
(ns status-im.data-store.realm.schemas.account.v6.transport)
(def schema {:name :transport
:primaryKey :chat-id
:properties {:chat-id :string
:ack :string
:seen :string
:pending-ack :string
:pending-send :string
:topic :string
:fetch-history? {:type :bool
:default false}
: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

@ -1,18 +1,15 @@
(ns ^{:doc "API to init and stop whisper messaging"}
status-im.transport.core
(:require [cljs.spec.alpha :as spec]
[re-frame.core :as re-frame]
(:require [re-frame.core :as re-frame]
[status-im.constants :as constants]
[status-im.transport.message.core :as message]
[status-im.transport.filters :as filters]
[status-im.transport.utils :as transport.utils]
[taoensso.timbre :as log]
[status-im.data-store.transport :as transport-store]
[status-im.transport.handlers :as transport.handlers]
[status-im.transport.inbox :as inbox]
status-im.transport.filters
[status-im.transport.utils :as transport.utils]
[status-im.utils.handlers :as handlers]
[status-im.utils.handlers-macro :as handlers-macro]
[status-im.transport.db :as transport.db]
[status-im.transport.utils :as transport.utils]
[status-im.data-store.transport :as transport-store]))
[taoensso.timbre :as log]))
(defn init-whisper
"Initialises whisper protocol by:
@ -34,7 +31,8 @@
:shh/restore-sym-keys {:web3 web3
:transport (:transport/chats db)
:on-success sym-key-added-callback}}
(inbox/connect-to-mailserver)))))
(inbox/connect-to-mailserver)
(transport.handlers/resend-contact-messages)))))
;;TODO (yenda) remove once go implements persistence
;;Since symkeys are not persisted, we restore them via add sym-keys,

View File

@ -10,6 +10,7 @@
(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"}))
;; optional
(spec/def ::sym-key-id string?)
@ -18,7 +19,7 @@
(spec/def ::filter any?)
(spec/def :transport/chat (allowed-keys :req-un [::ack ::seen ::pending-ack ::pending-send ::topic ::fetch-history?]
:opt-un [::sym-key-id ::sym-key ::filter]))
:opt-un [::sym-key-id ::sym-key ::filter ::resend?]))
(spec/def :transport/chats (spec/map-of :global/not-empty-string :transport/chat))
(spec/def :transport/discovery-filter (spec/nilable any?))
@ -26,10 +27,11 @@
(defn create-chat
"Initialize datastructure for chat representation at the transport level
Currently only :topic is actually used"
[topic]
{:ack []
:seen []
:pending-ack []
:pending-send []
:fetch-history? true
:topic topic})
[{:keys [topic resend?]}]
{:ack []
:seen []
:pending-ack []
:pending-send []
:fetch-history? true
:resend? resend?
:topic topic})

View File

@ -1,22 +1,18 @@
(ns ^{:doc "Events for message handling"}
status-im.transport.handlers
(:require [re-frame.core :as re-frame]
[status-im.utils.handlers :as handlers]
[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.core :as transport]
[status-im.chat.models :as models.chat]
[status-im.utils.datetime :as datetime]
[taoensso.timbre :as log]
[status-im.transport.utils :as transport.utils]
[cljs.reader :as reader]
[status-im.transport.message.transit :as transit]
[status-im.transport.shh :as shh]
[status-im.transport.filters :as filters]
[status-im.transport.message.core :as message]
[status-im.utils.handlers-macro :as handlers-macro]
[status-im.transport.message.v1.contact :as v1.contact]
[status-im.transport.message.v1.group-chat :as v1.group-chat]
[status-im.data-store.transport :as transport-store]))
[status-im.transport.shh :as shh]
[status-im.transport.utils :as transport.utils]
[status-im.utils.handlers :as handlers]
[status-im.utils.handlers-macro :as handlers-macro]
[taoensso.timbre :as log]
[status-im.transport.message.v1.protocol :as protocol]))
(defn update-last-received-from-inbox
"Distinguishes messages that are expired from those that are not
@ -70,11 +66,10 @@
(fn [{:keys [db random-id] :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)
;;TODO (yenda) remove once go implements persistence
(assoc :sym-key sym-key))]
(assoc :sym-key-id sym-key-id
:sym-key sym-key))]
(handlers-macro/merge-fx cofx
{:db (assoc-in db [:transport/chats chat-id :sym-key-id] sym-key-id)
{:db (assoc-in db [:transport/chats chat-id] chat-transport-info)
:shh/add-filter {:web3 web3
:sym-key-id sym-key-id
:topic topic
@ -89,13 +84,12 @@
(fn [{:keys [db] :as cofx} [_ {:keys [sym-key-id sym-key chat-id topic 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)
;;TODO (yenda) remove once go implements persistence
(assoc :sym-key sym-key))]
(assoc :sym-key-id sym-key-id
:sym-key sym-key))]
(handlers-macro/merge-fx cofx
{:db (assoc-in db
[:transport/chats chat-id :sym-key-id]
sym-key-id)
[: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
@ -133,7 +127,10 @@
(fn [{:keys [db] :as cofx} [{:keys [chat-id message sym-key sym-key-id]}]]
(let [{:keys [web3]} db]
(handlers-macro/merge-fx cofx
{:db (assoc-in db [:transport/chats chat-id :sym-key-id] sym-key-id)
{:db (update-in db [:transport/chats chat-id]
assoc
:sym-key-id sym-key-id
:sym-key sym-key)
:shh/add-filter {:web3 web3
:sym-key-id sym-key-id
:topic (transport.utils/get-topic chat-id)
@ -185,3 +182,89 @@
(fn [err resp]
(when err
(log/info "Confirming messages processed failed"))))))))
(handlers/register-handler-fx
:transport/set-message-envelope-hash
[re-frame/trim-v]
;; message-type is used for tracking
(fn [{:keys [db]} [chat-id message-id message-type envelope-hash]]
{:db (assoc-in db [:transport/message-envelopes envelope-hash]
{:chat-id chat-id
:message-id message-id
:message-type message-type})}))
(handlers/register-handler-fx
:transport/set-contact-message-envelope-hash
[re-frame/trim-v]
(fn [{:keys [db]} [chat-id envelope-hash]]
{:db (assoc-in db [:transport/message-envelopes envelope-hash]
{:chat-id chat-id
:message-type :contact-message})}))
(defn remove-hash [envelope-hash {:keys [db] :as cofx}]
{:db (update db :transport/message-envelopes dissoc envelope-hash)})
(defn update-resend-contact-message [chat-id {:keys [db] :as cofx}]
(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})]}))
(handlers/register-handler-fx
:signals/envelope-status
[re-frame/trim-v]
(fn [{: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)
(handlers-macro/merge-fx cofx
(remove-hash envelope-hash)
(update-resend-contact-message chat-id)))
(let [message (get-in db [:chats chat-id :messages message-id])
{:keys [fcm-token]} (get-in db [:contacts/contacts chat-id])]
(handlers-macro/merge-fx cofx
(remove-hash envelope-hash)
(models.message/update-message-status message 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}))
(defn resend-contact-request [own-info chat-id {:keys [sym-key topic]} cofx]
(message/send (v1.contact/NewContactKey. sym-key
topic
(v1.contact/map->ContactRequest own-info))
chat-id cofx))
(defn resend-contact-messages
([cofx]
(resend-contact-messages [] cofx))
([previous-summary {:keys [db] :as cofx}]
(when (and (zero? (count previous-summary))
(= :online (:network-status db))
(pos? (count (:peers-summary db))))
(let [own-info (own-info db)]
(handlers-macro/merge-effects
cofx
(fn [[chat-id {:keys [resend?] :as chat}] temp-cofx]
(case resend?
"contact-request"
(resend-contact-request own-info chat-id chat temp-cofx)
"contact-request-confirmation"
(message/send (v1.contact/map->ContactRequestConfirmed own-info)
chat-id temp-cofx)
"contact-update"
(protocol/send {:chat-id chat-id
:payload (v1.contact/map->ContactUpdate own-info)}
temp-cofx)
nil))
(:transport/chats db))))))

View File

@ -4,14 +4,18 @@
[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.handlers-macro :as handlers-macro]))
[status-im.utils.handlers-macro :as handlers-macro]
[status-im.utils.handlers :as handlers]
[status-im.data-store.transport :as transport-store]))
(defrecord NewContactKey [sym-key topic message]
message/StatusMessage
(send [this chat-id cofx]
(protocol/send-with-pubkey {:chat-id chat-id
:payload this}
cofx))
(let [success-event [:transport/set-contact-message-envelope-hash chat-id]]
(protocol/send-with-pubkey {:chat-id chat-id
:payload this
:success-event success-event}
cofx)))
(receive [this chat-id signature cofx]
(let [on-success (fn [sym-key sym-key-id]
(re-frame/dispatch [:contact/add-new-sym-key
@ -24,7 +28,8 @@
{:shh/add-new-sym-keys [{:web3 (get-in cofx [:db :web3])
:sym-key sym-key
:on-success on-success}]}
(protocol/init-chat chat-id topic)))))
(protocol/init-chat {:chat-id chat-id
:topic topic})))))
(defrecord ContactRequest [name profile-image address fcm-token]
message/StatusMessage
@ -40,19 +45,44 @@
(handlers-macro/merge-fx cofx
{:shh/get-new-sym-keys [{:web3 (:web3 db)
:on-success on-success}]}
(protocol/init-chat chat-id topic)))))
(protocol/init-chat {:chat-id chat-id
:topic topic
:resend? "contact-request"})))))
(defrecord ContactRequestConfirmed [name profile-image address fcm-token]
message/StatusMessage
(send [this chat-id cofx]
(handlers-macro/merge-fx cofx
(protocol/send {:chat-id chat-id
:payload this}))))
(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")]
(handlers-macro/merge-fx 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 {:chat-id chat-id
:payload this
:success-event success-event})))))
(defrecord ContactUpdate [name profile-image]
message/StatusMessage
(send [this _ {:keys [db] :as cofx}]
(let [public-keys (remove nil? (map :public-key (vals (:contacts/contacts db))))]
(handlers-macro/merge-fx cofx
(protocol/multi-send-by-pubkey {:public-keys public-keys
:payload this})))))
(let [public-keys (into #{} (remove nil? (map :public-key (vals (:contacts/contacts db)))))
recipients (filter #(public-keys (first %)) (:transport/chats db))]
(handlers-macro/merge-effects
cofx
(fn [[chat-id chat] temp-cofx]
(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]]
(handlers-macro/merge-fx temp-cofx
{:db (assoc-in db
[:transport/chats chat-id :resend?]
"contact-update")
:data-store/tx tx}
(protocol/send {:chat-id chat-id
:payload this
:success-event success-event}))))
recipients))))

View File

@ -33,7 +33,7 @@
:sym-key sym-key
:sym-key-id sym-key-id
:message message}]))}]}
(protocol/init-chat chat-id))))
(protocol/init-chat {:chat-id chat-id}))))
(defn- user-is-group-admin? [chat-id cofx]
(= (get-in cofx [:db :chats chat-id :group-admin])
@ -52,7 +52,7 @@
(defn- init-chat-if-new [chat-id cofx]
(if (nil? (get-in cofx [:db :transport/chats chat-id]))
(protocol/init-chat chat-id cofx)))
(protocol/init-chat {:chat-id chat-id} cofx)))
(defrecord GroupAdminUpdate [chat-name participants]
message/StatusMessage

View File

@ -14,12 +14,14 @@
(defn init-chat
"Initialises chat on protocol layer.
2 arities are provided, 2arg arity initialises the chat with topic derived from first argument (`chat-id`),
using the 3arg arity, you can specify the topic as well (second argument)"
([chat-id cofx]
(init-chat chat-id (transport.utils/get-topic chat-id) cofx))
([chat-id topic {:keys [db] :as cofx}]
{:db (assoc-in db [:transport/chats chat-id] (transport.db/create-chat topic))}))
If topic is not passed as argument it is derived from `chat-id`"
[{:keys [chat-id topic resend?]
:or {topic (transport.utils/get-topic chat-id)}}
{:keys [db]}]
{:db (assoc-in db
[:transport/chats chat-id]
(transport.db/create-chat {:topic topic
:resend? resend?}))})
#_(defn requires-ack [message-id chat-id {:keys [db] :as cofx}]
{:db (update-in db [:transport/chats chat-id :pending-ack] conj message-id)})

View File

@ -23,7 +23,7 @@
{:shh/generate-sym-key-from-password {:web3 (:web3 db)
:password chat-id
:on-success on-success}}
(protocol/init-chat chat-id)))))
(protocol/init-chat {:chat-id chat-id})))))
(handlers/register-handler-fx
::add-new-sym-key

View File

@ -1,15 +1,15 @@
(ns status-im.ui.screens.contacts.events
(:require [re-frame.core :as re-frame]
[status-im.utils.handlers :as handlers]
[status-im.utils.handlers-macro :as handlers-macro]
[status-im.utils.contacts :as utils.contacts]
[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.data-store.contacts :as contacts-store]
[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]))
(re-frame/reg-cofx

View File

@ -4,12 +4,12 @@
status-im.commands.handlers.jail
status-im.commands.events.loading
status-im.network.events
status-im.transport.handlers
[status-im.transport.handlers :as transport.handlers]
status-im.protocol.handlers
status-im.ui.screens.accounts.events
status-im.ui.screens.accounts.login.events
status-im.ui.screens.accounts.recover.events
status-im.ui.screens.contacts.events
[status-im.ui.screens.contacts.events :as contacts]
status-im.ui.screens.group.chat-settings.events
status-im.ui.screens.group.events
[status-im.ui.screens.navigation :as navigation]
@ -412,6 +412,7 @@
{:db (assoc db
:peers-summary peers-summary
:peers-count peers-count)}
(transport.handlers/resend-contact-messages previous-summary)
(inbox/peers-summary-change-fx previous-summary)))))
(handlers/register-handler-fx

View File

@ -52,7 +52,10 @@
:my-profile/update-picture
(fn [{:keys [db]} [this-event base64-image]]
(if base64-image
{:db (assoc-in db [:my-profile/profile :photo-path] (str "data:image/jpeg;base64," base64-image))}
{:db (-> db
(assoc-in [:my-profile/profile :photo-path]
(str "data:image/jpeg;base64," base64-image))
(assoc :my-profile/editing? true))}
{:open-image-picker this-event})))
(defn clean-name [db edit-view]