Keep username/photo-path synced across devices

We add syncing of account fields in pairing messages (only photo-path &
name for now). Also a sync message is sent each time we send a
contact-update, to keep other devices in sync. The change is compatible
with previous clients as it's just an accretion of transit.

Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
This commit is contained in:
Andrea Maria Piana 2018-11-14 14:51:20 +01:00
parent 6cf9e6136b
commit 08291a8396
No known key found for this signature in database
GPG Key ID: AA6CCA6DE0E06424
6 changed files with 163 additions and 110 deletions

View File

@ -47,6 +47,13 @@
(def merge-contacts (partial merge-with merge-contact))
(def account-mergeable-keys [:name :photo-path :last-updated])
(defn merge-account [local remote]
(if (> (:last-updated remote) (:last-updated local))
(merge local (select-keys remote account-mergeable-keys))
local))
(fx/defn upsert-installation [{:keys [db]} {:keys [installation-id] :as new-installation}]
(let [old-installation (get-in db [:pairing/installations installation-id])
updated-installation (merge old-installation new-installation)]
@ -68,18 +75,26 @@
(not (get-in db [:pairing/installations installation-id])))
(upsert-installation cofx new-installation))))))
(defn sync-installation-messages [{:keys [db]}]
(let [contacts (:contacts/contacts db)]
(map
(fn [batch]
(let [contacts-to-sync (reduce (fn [acc {:keys [public-key] :as contact}]
(assoc acc public-key (dissoc contact :photo-path)))
{}
batch)]
(transport.pairing/SyncInstallation. contacts-to-sync)))
(partition-all contact-batch-n (->> contacts
vals
(remove :dapp?))))))
(defn sync-installation-account-message [{:keys [db]}]
(let [account (-> db
:account/account
(select-keys account-mergeable-keys))]
(transport.pairing/SyncInstallation. {} account)))
(defn- contact-batch->sync-installation-message [batch]
(let [contacts-to-sync (reduce (fn [acc {:keys [public-key] :as contact}]
(assoc acc public-key (dissoc contact :photo-path)))
{}
batch)]
(transport.pairing/SyncInstallation. contacts-to-sync nil)))
(defn sync-installation-messages [{:keys [db] :as cofx}]
(let [contacts (:contacts/contacts db)
contact-batches (partition-all contact-batch-n (->> contacts
vals
(remove :dapp?)))]
(conj (mapv contact-batch->sync-installation-message contact-batches)
(sync-installation-account-message cofx))))
(defn enable [{:keys [db]} installation-id]
{:db (assoc-in db
@ -167,12 +182,15 @@
{}
contacts))
(defn handle-sync-installation [{:keys [db] :as cofx} {:keys [contacts]} sender]
(defn handle-sync-installation [{:keys [db] :as cofx} {:keys [contacts account]} sender]
(let [dev-mode? (get-in db [:account/account :dev-mode?])]
(when (and (config/pairing-enabled? dev-mode?)
(= sender (accounts.db/current-public-key cofx)))
(let [new-contacts (merge-contacts (:contacts/contacts db) (ensure-photo-path contacts))]
{:db (assoc db :contacts/contacts new-contacts)
(let [new-contacts (merge-contacts (:contacts/contacts db) (ensure-photo-path contacts))
new-account (merge-account (:account/account db) account)]
{:db (assoc db
:contacts/contacts new-contacts
:account/account new-account)
:data-store/tx [(data-store.contacts/save-contacts-tx (vals new-contacts))]}))))
(defn handle-pair-installation [{:keys [db] :as cofx} {:keys [installation-id device-type]} timestamp sender]

View File

@ -30,7 +30,8 @@
(let [sync-message (transport.pairing/SyncInstallation.
(select-keys
(get-in cofx [:db :contacts/contacts])
[chat-id]))]
[chat-id])
nil)]
(fx/merge cofx
(protocol/init-chat {:chat-id chat-id
:resend? "contact-request"})
@ -45,7 +46,8 @@
(let [sync-message (transport.pairing/SyncInstallation.
(select-keys
(get-in cofx [:db :contacts/contacts])
[chat-id]))
[chat-id])
nil)
success-event [:transport/contact-message-sent chat-id]
chat (get-in db [:transport/chats chat-id])
updated-chat (if chat
@ -60,3 +62,35 @@
:payload this
:success-event success-event})
(pairing/send-installation-message-fx sync-message)))))
(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})))))
(extend-type transport.contact/ContactUpdate
protocol/StatusMessage
(send [this _ {:keys [db] :as cofx}]
(let [contact-public-keys (reduce (fn [acc [_ {:keys [public-key dapp? pending?]}]]
(if (and (not dapp?)
(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)
sync-message (pairing/sync-installation-account-message cofx)
fxs (conj send-contact-update-fxs
(pairing/send-installation-message-fx sync-message))]
(apply fx/merge cofx fxs))))

View File

@ -18,35 +18,8 @@
(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}]
(let [contact-public-keys (reduce (fn [acc [_ {:keys [public-key dapp? pending?]}]]
(if (and (not dapp?)
(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)))

View File

@ -12,7 +12,7 @@
(log/warn "failed sync installation validation" (spec/explain :message/pair-installation this)))))
(defrecord SyncInstallation
[contacts]
[contacts account]
protocol/StatusMessage
(validate [this]
(if (spec/valid? :message/sync-installation this)

View File

@ -86,8 +86,8 @@
(deftype SyncInstallationHandler []
Object
(tag [this v] "p1")
(rep [this {:keys [contacts]}]
#js [contacts]))
(rep [this {:keys [contacts account]}]
#js [contacts account]))
(deftype PairInstallationHandler []
Object
@ -154,8 +154,8 @@
(contact/ContactUpdate. name profile-image address fcm-token))
"g5" (fn [[chat-id membership-updates message]]
(group-chat/GroupMembershipUpdate. chat-id membership-updates message))
"p1" (fn [[contacts]]
(pairing/SyncInstallation. contacts))
"p1" (fn [[contacts account]]
(pairing/SyncInstallation. contacts account))
"p2" (fn [[installation-id device-type]]
(pairing/PairInstallation. installation-id device-type))}}))

View File

@ -75,64 +75,85 @@
(deftest handle-sync-installation-test
(with-redefs [config/pairing-enabled? (constantly true)
identicon/identicon (constantly "generated")]
(let [old-contact-1 {:name "old-contact-one"
:public-key "contact-1"
:last-updated 0
:photo-path "old-contact-1"
:pending? true}
new-contact-1 {:name "new-contact-one"
:public-key "contact-1"
:last-updated 1
:photo-path "new-contact-1"
:pending? false}
old-contact-2 {:name "old-contact-2"
:public-key "contact-2"
:last-updated 0
:photo-path "old-contact-2"
:pending? false}
new-contact-2 {:name "new-contact-2"
:public-key "contact-2"
:last-updated 1
:photo-path "new-contact-2"
:pending? false}
contact-3 {:name "contact-3"
:public-key "contact-3"
:photo-path "contact-3"
:pending? false}
contact-4 {:name "contact-4"
:public-key "contact-4"
:pending? true}
local-contact-5 {:name "contact-5"
:photo-path "local"
:public-key "contact-5"
:pending? true
:last-updated 1}
remote-contact-5 {:name "contact-5"
:public-key "contact-5"
:photo-path "remote"
:pending? true
:last-updated 1}
cofx {:db {:account/account {:public-key "us"}
:contacts/contacts {"contact-1" old-contact-1
"contact-2" new-contact-2
"contact-3" contact-3
"contact-5" local-contact-5}}}
sync-message {:contacts {"contact-1" new-contact-1
"contact-2" old-contact-2
"contact-4" contact-4
"contact-5" remote-contact-5}}
expected {"contact-1" new-contact-1
"contact-2" new-contact-2
"contact-3" contact-3
"contact-4" (assoc contact-4
:photo-path "generated")
"contact-5" local-contact-5}]
(testing "not coming from us"
(is (not (pairing/handle-sync-installation cofx sync-message "not-us"))))
(testing "coming from us"
(is (= expected (get-in
(pairing/handle-sync-installation cofx sync-message "us")
[:db :contacts/contacts])))))))
(testing "syncing contacts"
(let [old-contact-1 {:name "old-contact-one"
:public-key "contact-1"
:last-updated 0
:photo-path "old-contact-1"
:pending? true}
new-contact-1 {:name "new-contact-one"
:public-key "contact-1"
:last-updated 1
:photo-path "new-contact-1"
:pending? false}
old-contact-2 {:name "old-contact-2"
:public-key "contact-2"
:last-updated 0
:photo-path "old-contact-2"
:pending? false}
new-contact-2 {:name "new-contact-2"
:public-key "contact-2"
:last-updated 1
:photo-path "new-contact-2"
:pending? false}
contact-3 {:name "contact-3"
:public-key "contact-3"
:photo-path "contact-3"
:pending? false}
contact-4 {:name "contact-4"
:public-key "contact-4"
:pending? true}
local-contact-5 {:name "contact-5"
:photo-path "local"
:public-key "contact-5"
:pending? true
:last-updated 1}
remote-contact-5 {:name "contact-5"
:public-key "contact-5"
:photo-path "remote"
:pending? true
:last-updated 1}
cofx {:db {:account/account {:public-key "us"}
:contacts/contacts {"contact-1" old-contact-1
"contact-2" new-contact-2
"contact-3" contact-3
"contact-5" local-contact-5}}}
sync-message {:contacts {"contact-1" new-contact-1
"contact-2" old-contact-2
"contact-4" contact-4
"contact-5" remote-contact-5}}
expected {"contact-1" new-contact-1
"contact-2" new-contact-2
"contact-3" contact-3
"contact-4" (assoc contact-4
:photo-path "generated")
"contact-5" local-contact-5}]
(testing "not coming from us"
(is (not (pairing/handle-sync-installation cofx sync-message "not-us"))))
(testing "coming from us"
(is (= expected (get-in
(pairing/handle-sync-installation cofx sync-message "us")
[:db :contacts/contacts]))))))
(testing "syncing account"
(let [old-account {:name "old-name"
:public-key "us"
:photo-path "old-photo-path"
:last-updated 0}
new-account {:name "new-name"
:public-key "us"
:photo-path "new-photo-path"
:last-updated 1}]
(testing "newer update"
(let [cofx {:db {:account/account old-account}}
sync-message {:account new-account}]
(is (= new-account (get-in (pairing/handle-sync-installation cofx sync-message "us")
[:db :account/account])))))
(testing "older update"
(let [cofx {:db {:account/account new-account}}
sync-message {:account old-account}]
(is (= new-account (get-in (pairing/handle-sync-installation cofx sync-message "us")
[:db :account/account])))))))))
(deftest handle-pair-installation-test
(with-redefs [config/pairing-enabled? (constantly true)]
@ -158,7 +179,10 @@
(deftest sync-installation-messages-test
(testing "it creates a sync installation message"
(let [cofx {:db {:account/account {:public-key "us"}
(let [cofx {:db {:account/account {:public-key "us"
:name "name"
:photo-path "photo-path"
:last-updated 1}
:contacts/contacts {"contact-1" {:name "contact-1"
:public-key "contact-1"}
"contact-2" {:name "contact-2"
@ -176,9 +200,13 @@
"contact-3" {:name "contact-3"
:public-key "contact-3"}
"contact-4" {:name "contact-4"
:public-key "contact-4"}})
:public-key "contact-4"}}
nil)
(transport.pairing/SyncInstallation. {"contact-5" {:name "contact-5"
:public-key "contact-5"}})]]
:public-key "contact-5"}} nil)
(transport.pairing/SyncInstallation. {} {:photo-path "photo-path"
:name "name"
:last-updated 1})]]
(is (= expected (pairing/sync-installation-messages cofx))))))
(deftest handle-bundles-added-test