Sync public chats

Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
This commit is contained in:
Andrea Maria Piana 2018-12-04 13:03:41 +01:00
parent 87e6c6cdee
commit 5f910a0bec
No known key found for this signature in database
GPG Key ID: AA6CCA6DE0E06424
11 changed files with 150 additions and 54 deletions

View File

@ -240,10 +240,11 @@
(fx/defn start-public-chat
"Starts a new public chat"
[cofx topic opts]
[cofx topic {:keys [dont-navigate?] :as opts}]
(fx/merge cofx
(add-public-chat topic)
(navigate-to-chat topic opts)
#(when-not dont-navigate?
(navigate-to-chat % topic opts))
(public-chat/join-public-chat topic)
(when platform/desktop?
(desktop.events/change-tab :home))))

View File

@ -671,7 +671,10 @@
(handlers/register-handler-fx
:chat.ui/start-public-chat
(fn [cofx [_ topic opts]]
(chat/start-public-chat cofx topic opts)))
(fx/merge
cofx
(chat/start-public-chat topic opts)
(pairing/sync-public-chat topic))))
(handlers/register-handler-fx
:chat.ui/remove-chat

View File

@ -6,15 +6,18 @@
[status-im.ui.screens.navigation :as navigation]
[status-im.utils.config :as config]
[status-im.utils.platform :as utils.platform]
[status-im.chat.models :as models.chat]
[status-im.accounts.db :as accounts.db]
[status-im.transport.message.protocol :as protocol]
[status-im.data-store.installations :as data-store.installations]
[status-im.native-module.core :as native-module]
[status-im.utils.identicon :as identicon]
[status-im.data-store.contacts :as data-store.contacts]
[status-im.data-store.accounts :as data-store.accounts]
[status-im.transport.message.pairing :as transport.pairing]))
(def contact-batch-n 4)
(def max-installations 2)
(defn- parse-response [response-js]
(-> response-js
@ -94,29 +97,41 @@
(not (get-in db [:pairing/installations installation-id])))
(fx/merge cofx
(upsert-installation new-installation)
#(when-not (get-in % [:db :pairing/prompt-user-pop-up])
#(when-not (or (get-in % [:db :pairing/prompt-user-pop-up])
(= :installations (:view-id db)))
(prompt-user-on-new-installation %))))))))
(defn sync-installation-account-message [{:keys [db]}]
(let [account (-> db
:account/account
(select-keys account-mergeable-keys))]
(transport.pairing/SyncInstallation. {} account)))
(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)))
(transport.pairing/SyncInstallation. contacts-to-sync {} {})))
(defn- chats->sync-installation-messages [{:keys [db]}]
(->> db
:chats
vals
(filter :public?)
(filter :is-active)
(map #(select-keys % [:chat-id :public?]))
(map #(transport.pairing/SyncInstallation. {} {} %))))
(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))))
(concat (mapv contact-batch->sync-installation-message contact-batches)
[(sync-installation-account-message cofx)]
(chats->sync-installation-messages cofx))))
(defn enable [{:keys [db]} installation-id]
{:db (assoc-in db
@ -154,8 +169,12 @@
(native-module/disable-installation installation-id
(partial handle-disable-installation-response installation-id)))
(defn enable-fx [_ installation-id]
{:pairing/enable-installation installation-id})
(defn enable-fx [cofx installation-id]
(if (< (count (filter :enabled? (get-in cofx [:db :pairing/installations]))) max-installations)
{:pairing/enable-installation installation-id}
{:utils/show-popup {:title (i18n/label :t/pairing-maximum-number-reached-title)
:content (i18n/label :t/pairing-maximum-number-reached-content)}}))
(defn disable-fx [_ installation-id]
{:pairing/disable-installation installation-id})
@ -184,6 +203,11 @@
(has-paired-installations? cofx))
(protocol/send payload nil cofx))))
(fx/defn sync-public-chat [cofx chat-id]
(let [sync-message (transport.pairing/SyncInstallation. {} {} {:public? true
:chat-id chat-id})]
(send-installation-message-fx cofx sync-message)))
(defn send-installation-messages [cofx]
;; The message needs to be broken up in chunks as we hit the whisper size limit
(let [sync-messages (sync-installation-messages cofx)
@ -204,16 +228,20 @@
{}
contacts))
(defn handle-sync-installation [{:keys [db] :as cofx} {:keys [contacts account]} sender]
(defn handle-sync-installation [{:keys [db] :as cofx} {:keys [contacts account chat]} 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))
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))]}))))
(fx/merge cofx
{:db (assoc db
:contacts/contacts new-contacts
:account/account new-account)
:data-store/base-tx [(data-store.accounts/save-account-tx new-account)]
:data-store/tx [(data-store.contacts/save-contacts-tx (vals new-contacts))]}
#(when (:public? chat)
(models.chat/start-public-chat % (:chat-id chat) {:dont-navigate? true})))))))
(defn handle-pair-installation [{:keys [db] :as cofx} {:keys [installation-id device-type]} timestamp sender]
(let [dev-mode? (get-in db [:account/account :dev-mode?])]

View File

@ -31,6 +31,7 @@
(select-keys
(get-in cofx [:db :contacts/contacts])
[chat-id])
nil
nil)]
(fx/merge cofx
(protocol/init-chat {:chat-id chat-id
@ -47,6 +48,7 @@
(select-keys
(get-in cofx [:db :contacts/contacts])
[chat-id])
nil
nil)
success-event [:transport/contact-message-sent chat-id]
chat (get-in db [:transport/chats chat-id])

View File

@ -12,7 +12,7 @@
(log/warn "failed sync installation validation" (spec/explain :message/pair-installation this)))))
(defrecord SyncInstallation
[contacts account]
[contacts account chat]
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 account]}]
#js [contacts account]))
(rep [this {:keys [contacts account chat]}]
#js [contacts account chat]))
(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 account]]
(pairing/SyncInstallation. contacts account))
"p1" (fn [[contacts account chat]]
(pairing/SyncInstallation. contacts account chat))
"p2" (fn [[installation-id device-type]]
(pairing/PairInstallation. installation-id device-type))}}))

View File

@ -77,11 +77,12 @@
[react/text {:style styles/qr-code-copy-text}
(i18n/label :copy-qr)]]]]]))
(defn installations-section [installations]
(defn installations-section [your-installation-id installations]
[react/view
[pairing.views/pair-this-device]
[pairing.views/sync-devices]
[react/view {:style pairing.styles/installation-list}
[pairing.views/your-device your-installation-id]
(for [installation installations]
^{:key (:installation-id installation)}
[react/view {:style {:margin-bottom 10}}
@ -176,10 +177,11 @@
[logging-display]])))
(views/defview installations []
(views/letsubs [installations [:pairing/installations]]
(views/letsubs [installations [:pairing/installations]
installation-id [:pairing/installation-id]]
[react/scroll-view
(when (config/pairing-enabled? true)
(installations-section installations))]))
(installations-section installation-id installations))]))
(views/defview backup-recovery-phrase []
[profile.recovery/backup-seed])

View File

@ -8,3 +8,7 @@
(->> installations
vals
(sort-by (comp unchecked-negate :last-paired)))))
(re-frame/reg-sub :pairing/installation-id
:<- [:get :account/account]
:installation-id)

View File

@ -2,13 +2,16 @@
(:require-macros [status-im.utils.views :as views])
(:require [re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[reagent.core :as reagent]
[status-im.utils.config :as config]
[status-im.ui.screens.main-tabs.styles :as main-tabs.styles]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.icons.vector-icons :as icons]
[status-im.ui.screens.home.styles :as home.styles]
[status-im.utils.platform :as utils.platform]
[status-im.utils.gfycat.core :as gfycat]
[status-im.ui.components.button.view :as buttons]
[status-im.ui.components.checkbox.view :as checkbox.views]
[status-im.ui.components.list.views :as list]
[status-im.ui.components.react :as react]
[status-im.ui.components.status-bar.view :as status-bar]
@ -17,6 +20,8 @@
[status-im.ui.screens.profile.components.views :as profile.components]
[status-im.ui.screens.pairing.styles :as styles]))
(def syncing (reagent/atom false))
(defn icon-style [{:keys [width height] :as style}]
(if utils.platform/desktop?
{:container-style {:width width
@ -26,25 +31,37 @@
style))
(defn synchronize-installations! []
(reset! syncing true)
;; Currently we don't know how long it takes, so we just disable for 10s, to avoid
;; spamming
(js/setTimeout #(reset! syncing false) 10000)
(re-frame/dispatch [:pairing.ui/synchronize-installation-pressed]))
(defn pair! []
(re-frame/dispatch [:pairing.ui/pair-devices-pressed]))
(defn enable-installation! [installation-id _]
(defn enable-installation! [installation-id]
(re-frame/dispatch [:pairing.ui/enable-installation-pressed installation-id]))
(defn disable-installation! [installation-id _]
(defn disable-installation! [installation-id]
(re-frame/dispatch [:pairing.ui/disable-installation-pressed installation-id]))
(defn footer []
[react/touchable-highlight {:on-press synchronize-installations!
(defn toggle-enabled! [installation-id enabled? _]
(if enabled?
(disable-installation! installation-id)
(enable-installation! installation-id)))
(defn footer [syncing]
[react/touchable-highlight {:on-press (when-not @syncing
synchronize-installations!)
:style main-tabs.styles/tabs-container}
[react/view
{:style styles/footer-content}
[react/text
{:style styles/footer-text}
(i18n/label :t/sync-all-devices)]]])
(if @syncing
(i18n/label :t/syncing-devices)
(i18n/label :t/sync-all-devices))]]])
(defn pair-this-device []
[react/touchable-highlight {:on-press pair!
@ -68,14 +85,30 @@
[icons/icon :icons/wnode (icon-style (styles/pairing-button-icon true))]]]
[react/view {:style styles/pairing-actions-text}
[react/view
[react/text {:style styles/pair-this-device-title} (i18n/label :t/sync-all-devices)]]]]])
[react/text {:style styles/pair-this-device-title}
(if @syncing
(i18n/label :t/syncing-devices)
(i18n/label :t/sync-all-devices))]]]]])
(defn your-device [installation-id]
[react/view {:style styles/installation-item}
[react/view {:style (styles/pairing-button true)}
[icons/icon (if utils.platform/desktop?
:icons/desktop
:icons/mobile)
(icon-style (styles/pairing-button-icon true))]]
[react/view {:style styles/pairing-actions-text}
[react/view
[react/text {:style styles/installation-item-name-text}
(str
(gfycat/generate-gfy installation-id)
" ("
(i18n/label :t/you)
")")]]]])
(defn render-row [{:keys [device-type enabled? installation-id]}]
[react/touchable-highlight
{:on-press (if enabled?
(partial disable-installation! installation-id)
(partial enable-installation! installation-id))
:accessibility-label :installation-item}
{:accessibility-label :installation-item}
[react/view {:style styles/installation-item}
[react/view {:style (styles/pairing-button enabled?)}
[icons/icon (if (= "desktop"
@ -86,28 +119,35 @@
[react/view {:style styles/pairing-actions-text}
[react/view
[react/text {:style styles/installation-item-name-text}
(gfycat/generate-gfy installation-id)]]
[react/view
[react/text {:style styles/installation-status}
(if enabled?
(i18n/label :t/syncing-enabled)
(i18n/label :t/syncing-disabled))]]]]])
(gfycat/generate-gfy installation-id)]]]
[react/view
(if utils.platform/ios?
;; On IOS switches seems to be broken, they take up value of dev-mode? (so if dev mode is on they all show to be on).
;; Replacing therefore with checkbox until I have more time to investigate
(checkbox.views/plain-checkbox {:checked? enabled?
:on-value-change (partial toggle-enabled! installation-id enabled?)})
[react/switch {:on-tint-color colors/blue
:value enabled?
:on-value-change (partial toggle-enabled! installation-id enabled?)}])]]])
(defn render-rows [installations]
(defn render-rows [installation-id installations]
[react/scroll-view {:style styles/wrapper}
[list/flat-list {:data installations
:default-separator? false
:key-fn :installation-id
:render-fn render-row}]])
[your-device installation-id]
(when (seq installations)
[list/flat-list {:data installations
:default-separator? false
:key-fn :installation-id
:render-fn render-row}])])
(defn installations-list [installations]
(defn installations-list [installation-id installations]
[react/view {:style styles/installation-list}
[react/view {:style styles/paired-devices-title}
[react/text (i18n/label :t/paired-devices)]]
(render-rows installations)])
(render-rows installation-id installations)])
(views/defview installations []
(views/letsubs [installations [:pairing/installations]]
(views/letsubs [installation-id [:pairing/installation-id]
installations [:pairing/installations]]
[react/view {:flex 1}
[status-bar/status-bar]
[toolbar/toolbar {}
@ -115,6 +155,5 @@
[toolbar/content-title (i18n/label :t/devices)]]
[react/scroll-view {:style {:background-color :white}}
[pair-this-device]
(when (seq installations)
[installations-list installations])]
(when (seq installations) [footer])]))
[installations-list installation-id installations]]
(when (seq installations) [footer syncing])]))

View File

@ -153,7 +153,15 @@
(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])))))))))
[:db :account/account])))))))
(testing "syncing public chats"
(let [cofx {:db {:account/account {:public-key "us"}}}]
(testing "a new chat"
(let [sync-message {:chat {:public? true
:chat-id "status"
:is-active true}}]
(is (get-in (pairing/handle-sync-installation cofx sync-message "us")
[:db :chats "status"]))))))))
(deftest handle-pair-installation-test
(with-redefs [config/pairing-enabled? (constantly true)]
@ -183,6 +191,9 @@
:name "name"
:photo-path "photo-path"
:last-updated 1}
:chats {"status" {:public? true
:is-active true
:chat-id "status"}}
:contacts/contacts {"contact-1" {:name "contact-1"
:public-key "contact-1"}
"contact-2" {:name "contact-2"
@ -201,12 +212,14 @@
:public-key "contact-3"}
"contact-4" {:name "contact-4"
:public-key "contact-4"}}
nil)
{} {})
(transport.pairing/SyncInstallation. {"contact-5" {:name "contact-5"
:public-key "contact-5"}} nil)
:public-key "contact-5"}} {} {})
(transport.pairing/SyncInstallation. {} {:photo-path "photo-path"
:name "name"
:last-updated 1})]]
:last-updated 1} {})
(transport.pairing/SyncInstallation. {} {} {:public? true
:chat-id "status"})]]
(is (= expected (pairing/sync-installation-messages cofx))))))
(deftest handle-bundles-added-test

View File

@ -9,10 +9,14 @@
"pair": "Pair devices",
"pair-this-device": "Pair this device",
"pair-this-device-description": "Pair your devices to sync contacts and chats between them",
"you": "you",
"syncing-enabled": "Syncing enabled",
"syncing-disabled": "Syncing disabled",
"sync-all-devices": "Sync all devices",
"syncing-devices": "Syncing...",
"paired-devices": "Paired devices",
"pairing-maximum-number-reached-title": "Max number of devices reached",
"pairing-maximum-number-reached-content": "Please disable one of your devices before enabling a new one.",
"pairing-new-installation-detected-title": "New device detected",
"pairing-new-installation-detected-content": "A new device has been detected.\nIn order to use your devices correctly, it's important to pair and enable them before using them.\nPlease go to the device section under settings to pair your devices.",
"pairing-go-to-installation": "Go to pairing settings",