Add pairing
Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
This commit is contained in:
parent
ca8a3037d8
commit
073dddcee0
3
.env
3
.env
|
@ -8,7 +8,8 @@ POW_TARGET=0.002
|
|||
POW_TIME=1
|
||||
DEFAULT_NETWORK=mainnet_rpc
|
||||
DEBUG_WEBVIEW=1
|
||||
GROUP_CHATS_ENABLED=1
|
||||
GROUP_CHATS_ENABLED=0
|
||||
PAIRING_ENABLED=0
|
||||
CACHED_WEBVIEWS_ENABLED=1
|
||||
EXTENSIONS=1
|
||||
HARDWALLET_ENABLED=0
|
||||
|
|
|
@ -8,8 +8,9 @@ POW_TARGET=0.002
|
|||
POW_TIME=1
|
||||
DEFAULT_NETWORK=mainnet_rpc
|
||||
DEBUG_WEBVIEW=1
|
||||
GROUP_CHATS_ENABLED=1
|
||||
GROUP_CHATS_ENABLED=0
|
||||
MAINNET_WARNING_ENABLED=1
|
||||
CACHED_WEBVIEWS_ENABLED=1
|
||||
EXTENSIONS=1
|
||||
PFS_ENCRYPTION_ENABLED=0
|
||||
PAIRING_ENABLED=0
|
||||
|
|
|
@ -9,6 +9,7 @@ POW_TIME=1
|
|||
DEFAULT_NETWORK=mainnet_rpc
|
||||
DEBUG_WEBVIEW=1
|
||||
GROUP_CHATS_ENABLED=0
|
||||
PAIRING_ENABLED=0
|
||||
MAINNET_WARNING_ENABLED=1
|
||||
EXTENSIONS=1
|
||||
PFS_ENCRYPTION_ENABLED=0
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.16.3
|
||||
0.16.3-3-g37e4ef01
|
||||
|
|
|
@ -14021,7 +14021,7 @@
|
|||
}
|
||||
},
|
||||
"web3": {
|
||||
"version": "git+https://github.com/status-im/web3.js.git#b1c2e2b75f6a190b320dda4be7931d3680ecb727",
|
||||
"version": "git+https://github.com/status-im/web3.js.git#5fd0b24fd10ce55183a5253a370840540a07db1e",
|
||||
"requires": {
|
||||
"bignumber.js": "github:status-im/bignumber.js#cc066a0a3d6bfe0c436c9957f4ea8344bf963c89",
|
||||
"crypto-js": "3.1.8",
|
||||
|
|
|
@ -84,7 +84,7 @@
|
|||
"string_decoder": "0.10.31",
|
||||
"text-encoding": "^0.6.4",
|
||||
"url": "0.10.3",
|
||||
"web3": "https://github.com/status-im/web3.js.git#feature/chat-api",
|
||||
"web3": "https://github.com/status-im/web3.js.git#features/pairing-message",
|
||||
"web3-utils": "1.0.0-beta.36"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -10966,7 +10966,7 @@
|
|||
}
|
||||
},
|
||||
"web3": {
|
||||
"version": "git+https://github.com/status-im/web3.js.git#b1c2e2b75f6a190b320dda4be7931d3680ecb727",
|
||||
"version": "git+https://github.com/status-im/web3.js.git#5fd0b24fd10ce55183a5253a370840540a07db1e",
|
||||
"requires": {
|
||||
"bignumber.js": "github:status-im/bignumber.js#cc066a0a3d6bfe0c436c9957f4ea8344bf963c89",
|
||||
"crypto-js": "3.1.8",
|
||||
|
|
|
@ -66,7 +66,7 @@
|
|||
"string_decoder": "0.10.31",
|
||||
"text-encoding": "^0.6.4",
|
||||
"url": "0.10.3",
|
||||
"web3": "https://github.com/status-im/web3.js.git#feature/chat-api",
|
||||
"web3": "https://github.com/status-im/web3.js.git#features/pairing-message",
|
||||
"web3-utils": "1.0.0-beta.36"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
(ns status-im.chat.subs
|
||||
(:require [clojure.string :as string]
|
||||
[re-frame.core :refer [reg-sub subscribe]]
|
||||
[status-im.utils.config :as utils.config]
|
||||
[status-im.chat.constants :as chat.constants]
|
||||
[status-im.chat.commands.core :as commands]
|
||||
[status-im.chat.commands.input :as commands.input]
|
||||
|
@ -76,9 +77,13 @@
|
|||
platform/ios? kb-height
|
||||
:default 0)))
|
||||
|
||||
(defn active-chats [[contacts chats]]
|
||||
(reduce (fn [acc [chat-id {:keys [is-active] :as chat}]]
|
||||
(if is-active
|
||||
(defn active-chats [[contacts chats {:keys [dev-mode?]}]]
|
||||
(reduce (fn [acc [chat-id {:keys [group-chat public? is-active] :as chat}]]
|
||||
(if (and is-active
|
||||
;; not a group chat
|
||||
(or (not (and group-chat (not public?)))
|
||||
;; if it's a group chat
|
||||
(utils.config/group-chats-enabled? dev-mode?)))
|
||||
(assoc acc chat-id (if-let [contact (get contacts chat-id)]
|
||||
(-> chat
|
||||
(assoc :name (:name contact))
|
||||
|
@ -93,6 +98,7 @@
|
|||
:get-active-chats
|
||||
:<- [:get-contacts]
|
||||
:<- [:get-chats]
|
||||
:<- [:get-current-account]
|
||||
active-chats)
|
||||
|
||||
(reg-sub
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
status-im.data-store.chats
|
||||
status-im.data-store.messages
|
||||
status-im.data-store.contacts
|
||||
status-im.data-store.installations
|
||||
status-im.data-store.transport
|
||||
status-im.data-store.browser
|
||||
status-im.data-store.accounts
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
(ns status-im.data-store.installations
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.data-store.realm.core :as core]))
|
||||
|
||||
(re-frame/reg-cofx
|
||||
:data-store/get-all-installations
|
||||
(fn [coeffects _]
|
||||
(assoc coeffects :all-installations (-> @core/account-realm
|
||||
(core/get-all :installation)
|
||||
(core/all-clj :installation)))))
|
||||
|
||||
(defn save
|
||||
"Returns tx function for saving a installation"
|
||||
[installation]
|
||||
(fn [realm]
|
||||
(core/create realm
|
||||
:installation
|
||||
installation
|
||||
true)))
|
||||
|
||||
(defn confirm
|
||||
[installation-id]
|
||||
(save {:installation-id installation-id
|
||||
:confirmed? true}))
|
|
@ -11,6 +11,7 @@
|
|||
[status-im.data-store.realm.schemas.account.dapp-permissions :as dapp-permissions]
|
||||
[status-im.data-store.realm.schemas.account.request :as request]
|
||||
[status-im.data-store.realm.schemas.account.membership-update :as membership-update]
|
||||
[status-im.data-store.realm.schemas.account.installation :as installation]
|
||||
[status-im.data-store.realm.schemas.account.migrations :as migrations]
|
||||
[taoensso.timbre :as log]))
|
||||
|
||||
|
@ -183,6 +184,19 @@
|
|||
|
||||
(def v18 v17)
|
||||
|
||||
(def v19 [chat/v8
|
||||
transport/v7
|
||||
transport-inbox-topic/v1
|
||||
contact/v2
|
||||
message/v7
|
||||
mailserver/v11
|
||||
user-status/v1
|
||||
membership-update/v1
|
||||
installation/v1
|
||||
local-storage/v1
|
||||
browser/v8
|
||||
dapp-permissions/v9])
|
||||
|
||||
;; put schemas ordered by version
|
||||
(def schemas [{:schema v1
|
||||
:schemaVersion 1
|
||||
|
@ -237,4 +251,7 @@
|
|||
:migration migrations/v17}
|
||||
{:schema v18
|
||||
:schemaVersion 18
|
||||
:migration migrations/v18}])
|
||||
:migration migrations/v18}
|
||||
{:schema v19
|
||||
:schemaVersion 19
|
||||
:migration migrations/v19}])
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
(ns status-im.data-store.realm.schemas.account.installation)
|
||||
|
||||
(def v1 {:name :installation
|
||||
:primaryKey :installation-id
|
||||
:properties {:installation-id :string
|
||||
:confirmed? :bool}})
|
|
@ -113,3 +113,6 @@
|
|||
(.objects "transport-inbox-topic")
|
||||
(.map (fn [inbox-topic _ _]
|
||||
(aset inbox-topic "last-request" 1)))))
|
||||
|
||||
(defn v19 [old-realm new-realm]
|
||||
(log/debug "migrating v19 account database"))
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
[status-im.mailserver.core :as mailserver]
|
||||
[status-im.network.core :as network]
|
||||
[status-im.notifications.core :as notifications]
|
||||
[status-im.pairing.core :as pairing]
|
||||
[status-im.privacy-policy.core :as privacy-policy]
|
||||
[status-im.protocol.core :as protocol]
|
||||
[status-im.qr-scanner.core :as qr-scanner]
|
||||
|
@ -95,6 +96,7 @@
|
|||
(re-frame/inject-cofx :data-store/deduplication-ids)
|
||||
(re-frame/inject-cofx :data-store/get-local-storage-data)
|
||||
(re-frame/inject-cofx :data-store/get-all-contacts)
|
||||
(re-frame/inject-cofx :data-store/get-all-installations)
|
||||
(re-frame/inject-cofx :data-store/get-all-mailservers)
|
||||
(re-frame/inject-cofx :data-store/transport)
|
||||
(re-frame/inject-cofx :data-store/transport-inbox-topics)
|
||||
|
@ -1092,3 +1094,17 @@
|
|||
:search/filter-changed
|
||||
(fn [cofx [_ search-filter]]
|
||||
(search/filter-changed cofx search-filter)))
|
||||
|
||||
;; pairing module
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:pairing.ui/pair-devices-pressed
|
||||
[]
|
||||
(fn [cofx _]
|
||||
(pairing/start cofx)))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:pairing.ui/synchronize-installation-pressed
|
||||
[]
|
||||
(fn [cofx _]
|
||||
(pairing/send-installation-message cofx)))
|
||||
|
|
|
@ -304,7 +304,8 @@
|
|||
message
|
||||
membership-updates] :as membership-update}
|
||||
sender-signature]
|
||||
(when (and config/group-chats-enabled?
|
||||
(let [dev-mode? (get-in cofx [:db :account/account :dev-mode?])]
|
||||
(when (and (config/group-chats-enabled? dev-mode?)
|
||||
(valid-chat-id? chat-id (-> membership-updates first :from)))
|
||||
(let [previous-chat (get-in cofx [:db :chats chat-id])]
|
||||
(fx/merge cofx
|
||||
|
@ -313,7 +314,7 @@
|
|||
;; don't allow anything but group messages
|
||||
(instance? protocol/Message message)
|
||||
(= :group-user-message (:message-type message)))
|
||||
(protocol/receive message chat-id sender-signature nil %))))))
|
||||
(protocol/receive message chat-id sender-signature nil %)))))))
|
||||
|
||||
(defn handle-sign-success
|
||||
"Upsert chat and send signed payload to group members"
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
[status-im.contact.core :as contact]
|
||||
[status-im.models.dev-server :as models.dev-server]
|
||||
[status-im.protocol.core :as protocol]
|
||||
[status-im.pairing.core :as pairing]
|
||||
[status-im.models.transactions :as transactions]
|
||||
[status-im.models.wallet :as models.wallet]
|
||||
[status-im.native-module.core :as status]
|
||||
|
@ -149,7 +150,7 @@
|
|||
(let [{:universal-links/keys [url]
|
||||
:keys [accounts/accounts accounts/create contacts/contacts networks/networks
|
||||
network network-status peers-count peers-summary view-id navigation-stack
|
||||
status-module-initialized? device-UUID semaphores]
|
||||
pairing/installations status-module-initialized? device-UUID semaphores]
|
||||
:node/keys [status]
|
||||
:or {network (get app-db :network)}} db
|
||||
current-account (get accounts address)
|
||||
|
@ -206,6 +207,7 @@
|
|||
(initialize-account-db address)
|
||||
(protocol/initialize-protocol address)
|
||||
(contact/load-contacts)
|
||||
(pairing/load-installations)
|
||||
#(when (dev-mode? %)
|
||||
(models.dev-server/start))
|
||||
(chat-loading/initialize-chats)
|
||||
|
|
|
@ -100,7 +100,11 @@
|
|||
:RequireTopics (get-topics network)
|
||||
:InstallationID installation-id
|
||||
:PFSEnabled (or config/pfs-encryption-enabled?
|
||||
config/group-chats-enabled?))
|
||||
;; We don't check dev-mode? here as
|
||||
;; otherwise we would have to restart the node
|
||||
;; when the user enables it
|
||||
(config/group-chats-enabled? true)
|
||||
(config/pairing-enabled? true)))
|
||||
|
||||
(and
|
||||
config/bootnodes-settings-enabled?
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
(ns status-im.pairing.core
|
||||
(:require
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.config :as config]
|
||||
[status-im.transport.message.protocol :as protocol]
|
||||
[status-im.data-store.installations :as data-store.installations]
|
||||
[status-im.utils.identicon :as identicon]
|
||||
[status-im.data-store.contacts :as data-store.contacts]
|
||||
[status-im.transport.message.pairing :as transport.pairing]))
|
||||
|
||||
(defn start [cofx]
|
||||
(let [{:keys [current-public-key web3]} (:db cofx)]
|
||||
{:shh/send-pairing-message {:web3 web3
|
||||
:src current-public-key
|
||||
:payload []}}))
|
||||
|
||||
(defn merge-contact [local remote]
|
||||
(let [[old-contact new-contact] (sort-by :last-updated [local remote])]
|
||||
(-> local
|
||||
(merge new-contact)
|
||||
(assoc :photo-path
|
||||
(or (:photo-path new-contact)
|
||||
(:photo-path old-contact)
|
||||
(identicon/identicon (:whisper-identity local))))
|
||||
(assoc :pending? (boolean
|
||||
(and (:pending? local true)
|
||||
(:pending? remote true)))))))
|
||||
|
||||
(def merge-contacts (partial merge-with merge-contact))
|
||||
|
||||
(defn handle-bundles-added [{:keys [db]} bundle]
|
||||
(let [dev-mode? (get-in db [:account/account :dev-mode?])]
|
||||
(when (config/pairing-enabled? dev-mode?)
|
||||
(let [installation-id (:installationID bundle)
|
||||
new-installation {:installation-id installation-id
|
||||
:confirmed? false}]
|
||||
(when
|
||||
(and (= (:identity bundle)
|
||||
(:current-public-key db))
|
||||
(not (get-in db [:pairing/installations installation-id])))
|
||||
{:db (assoc-in db
|
||||
[:pairing/installations installation-id]
|
||||
new-installation)
|
||||
:data-store/tx [(data-store.installations/save new-installation)]})))))
|
||||
|
||||
(defn sync-installation-messages [{:keys [db]}]
|
||||
(let [contacts (:contacts/contacts db)]
|
||||
(map
|
||||
(fn [[k v]] (transport.pairing/SyncInstallation. {k (dissoc v :photo-path)}))
|
||||
contacts)))
|
||||
|
||||
(defn send-installation-message [cofx]
|
||||
;; The message needs to be broken up in chunks as we hit the whisper size limit
|
||||
(let [{:keys [current-public-key web3]} (:db cofx)
|
||||
sync-messages (sync-installation-messages cofx)]
|
||||
{:shh/send-direct-message
|
||||
(map #(hash-map :web3 web3
|
||||
:src current-public-key
|
||||
:dst current-public-key
|
||||
:payload %) sync-messages)}))
|
||||
|
||||
(defn handle-sync-installation [{:keys [db] :as cofx} {:keys [contacts]} sender]
|
||||
(let [dev-mode? (get-in db [:account/account :dev-mode?])]
|
||||
(when (and (config/pairing-enabled? dev-mode?)
|
||||
(= sender (get-in cofx [:db :current-public-key])))
|
||||
(let [new-contacts (merge-contacts (:contacts/contacts db) contacts)]
|
||||
{:db (assoc db :contacts/contacts new-contacts)
|
||||
:data-store/tx [(data-store.contacts/save-contacts-tx (vals new-contacts))]}))))
|
||||
|
||||
(fx/defn load-installations [{:keys [db all-installations]}]
|
||||
{:db (assoc db :pairing/installations (reduce
|
||||
(fn [acc {:keys [installation-id] :as i}]
|
||||
(assoc acc installation-id i))
|
||||
{}
|
||||
all-installations))})
|
|
@ -3,6 +3,7 @@
|
|||
[status-im.accounts.login.core :as accounts.login]
|
||||
[status-im.init.core :as init]
|
||||
[status-im.node.core :as node]
|
||||
[status-im.pairing.core :as pairing]
|
||||
[status-im.transport.inbox :as inbox]
|
||||
[status-im.transport.message.core :as transport.message]
|
||||
[status-im.utils.fx :as fx]
|
||||
|
@ -55,6 +56,7 @@
|
|||
"module.initialized" (status-module-initialized cofx)
|
||||
"envelope.sent" (transport.message/update-envelope-status cofx (:hash event) :sent)
|
||||
"envelope.expired" (transport.message/update-envelope-status cofx (:hash event) :sent)
|
||||
"bundles.added" (pairing/handle-bundles-added cofx event)
|
||||
"mailserver.request.completed" (when (accounts.db/logged-in? cofx)
|
||||
(inbox/update-inbox-topics cofx {:request-id (:requestID event)
|
||||
:cursor (:cursor event)}))
|
||||
|
|
|
@ -104,6 +104,7 @@
|
|||
(spec/def :message/message-seen (spec/keys :req-un [:message/ids]))
|
||||
|
||||
(spec/def :message/group-membership-update (spec/keys :req-un [:group-chat/membership-updates :group-chat/chat-id]))
|
||||
(spec/def :message/sync-installation (spec/keys :req-un [:contacts/contacts]))
|
||||
|
||||
(spec/def :message/message-common (spec/keys :req-un [::content-type ::message-type ::clock-value ::timestamp]))
|
||||
(spec/def :message.text/content (spec/keys :req-un [:message.content/text]
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
(ns status-im.transport.impl.receive
|
||||
(:require [status-im.group-chats.core :as group-chats]
|
||||
[status-im.contact.core :as contact]
|
||||
[status-im.pairing.core :as pairing]
|
||||
[status-im.transport.message.contact :as transport.contact]
|
||||
[status-im.transport.message.group-chat :as transport.group-chat]
|
||||
[status-im.transport.message.pairing :as transport.pairing]
|
||||
[status-im.transport.message.protocol :as protocol]))
|
||||
|
||||
(extend-type transport.group-chat/GroupMembershipUpdate
|
||||
|
@ -24,3 +26,8 @@
|
|||
protocol/StatusMessage
|
||||
(receive [this _ signature timestamp cofx]
|
||||
(contact/receive-contact-update signature timestamp this cofx)))
|
||||
|
||||
(extend-type transport.pairing/SyncInstallation
|
||||
protocol/StatusMessage
|
||||
(receive [this _ signature _ cofx]
|
||||
(pairing/handle-sync-installation cofx this signature)))
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
(ns status-im.transport.impl.send
|
||||
(:require [status-im.group-chats.core :as group-chats]
|
||||
[status-im.pairing.core :as pairing]
|
||||
[status-im.transport.message.pairing :as transport.pairing]
|
||||
[status-im.transport.message.group-chat :as transport.group-chat]
|
||||
[status-im.transport.message.protocol :as protocol]))
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
(ns status-im.transport.message.pairing
|
||||
(:require [cljs.spec.alpha :as spec]
|
||||
[status-im.transport.message.protocol :as protocol]
|
||||
[taoensso.timbre :as log]))
|
||||
|
||||
(defrecord SyncInstallation
|
||||
[contacts]
|
||||
protocol/StatusMessage
|
||||
(validate [this]
|
||||
(if (spec/valid? :message/sync-installation this)
|
||||
this
|
||||
(log/warn "failed sync installation validation" (spec/explain :message/sync-installation this)))))
|
|
@ -3,6 +3,7 @@
|
|||
(:require [status-im.transport.message.contact :as contact]
|
||||
[status-im.transport.message.protocol :as protocol]
|
||||
[status-im.transport.message.group-chat :as group-chat]
|
||||
[status-im.transport.message.pairing :as pairing]
|
||||
[status-im.constants :as constants]
|
||||
[cognitect.transit :as transit]))
|
||||
|
||||
|
@ -87,6 +88,12 @@
|
|||
(rep [this {:keys [chat-id membership-updates message]}]
|
||||
#js [chat-id membership-updates message]))
|
||||
|
||||
(deftype SyncInstallationHandler []
|
||||
Object
|
||||
(tag [this v] "p1")
|
||||
(rep [this {:keys [contacts]}]
|
||||
#js [contacts]))
|
||||
|
||||
(def writer (transit/writer :json
|
||||
{:handlers
|
||||
{contact/NewContactKey (NewContactKeyHandler.)
|
||||
|
@ -95,7 +102,8 @@
|
|||
contact/ContactUpdate (ContactUpdateHandler.)
|
||||
protocol/Message (MessageHandler.)
|
||||
protocol/MessagesSeen (MessagesSeenHandler.)
|
||||
group-chat/GroupMembershipUpdate (GroupMembershipUpdateHandler.)}}))
|
||||
group-chat/GroupMembershipUpdate (GroupMembershipUpdateHandler.)
|
||||
pairing/SyncInstallation (SyncInstallationHandler.)}}))
|
||||
|
||||
;;
|
||||
;; Reader handlers
|
||||
|
@ -146,7 +154,9 @@
|
|||
"c6" (fn [[name profile-image address fcm-token]]
|
||||
(contact/ContactUpdate. name profile-image address fcm-token))
|
||||
"g5" (fn [[chat-id membership-updates message]]
|
||||
(group-chat/GroupMembershipUpdate. chat-id membership-updates message))}}))
|
||||
(group-chat/GroupMembershipUpdate. chat-id membership-updates message))
|
||||
"p1" (fn [[contacts]]
|
||||
(pairing/SyncInstallation. contacts))}}))
|
||||
|
||||
(defn serialize
|
||||
"Serializes a record implementing the StatusMessage protocol using the custom writers"
|
||||
|
|
|
@ -82,6 +82,21 @@
|
|||
direct-message
|
||||
(handle-response success-event error-event)))))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:shh/send-pairing-message
|
||||
(fn [params]
|
||||
(let [{:keys [web3 payload src dsts success-event error-event]
|
||||
:or {error-event :protocol/send-status-message-error}} params
|
||||
message (clj->js {:sig src
|
||||
:payload (-> payload
|
||||
transit/serialize
|
||||
transport.utils/from-utf8)})]
|
||||
(.. web3
|
||||
-shh
|
||||
(sendPairingMessage
|
||||
message
|
||||
(handle-response success-event error-event))))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:shh/send-group-message
|
||||
(fn [params]
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
[status-im.ui.components.toolbar.view :as toolbar]
|
||||
[status-im.utils.config :as config]))
|
||||
|
||||
(defn- options-list [{:keys [anon-id]}]
|
||||
(defn- options-list [{:keys [dev-mode?]}]
|
||||
[react/view action-button.styles/actions-list
|
||||
[action-button/action-button
|
||||
{:label (i18n/label :t/start-new-chat)
|
||||
|
@ -23,14 +23,14 @@
|
|||
:on-press #(re-frame/dispatch [:navigate-to :new-chat])}]
|
||||
[action-button/action-separator]
|
||||
;; Hide behind flag (false by default), till everything is fixed in group chats
|
||||
(when config/group-chats-enabled?
|
||||
(when (config/group-chats-enabled? dev-mode?)
|
||||
[action-button/action-button
|
||||
{:label (i18n/label :t/start-group-chat)
|
||||
:accessibility-label :start-group-chat-button
|
||||
:icon :icons/contacts
|
||||
:icon-opts {:color colors/blue}
|
||||
:on-press #(re-frame/dispatch [:contact.ui/start-group-chat-pressed])}])
|
||||
(when config/group-chats-enabled?
|
||||
(when (config/group-chats-enabled? dev-mode?)
|
||||
[action-button/action-separator])
|
||||
[action-button/action-button
|
||||
{:label (i18n/label :t/new-public-group-chat)
|
||||
|
@ -69,4 +69,4 @@
|
|||
[status-bar/status-bar]
|
||||
[toolbar/simple-toolbar (i18n/label :t/new)]
|
||||
[common/separator]
|
||||
[options-list (assoc account :anon-id device-UUID)]]))
|
||||
[options-list account]]))
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
:tab-bar-visible? true
|
||||
:navigation-stack '()
|
||||
:contacts/contacts {}
|
||||
:pairing/installations {}
|
||||
:qr-codes {}
|
||||
:group/selected-contacts #{}
|
||||
:chats {}
|
||||
|
@ -198,6 +199,7 @@
|
|||
:contacts/click-handler
|
||||
:contacts/click-action
|
||||
:contacts/click-params
|
||||
:pairing/installations
|
||||
:commands/stored-command
|
||||
:group/selected-contacts
|
||||
:accounts/accounts
|
||||
|
|
|
@ -92,7 +92,7 @@
|
|||
[react/image {:style styles/suggested-contact-image
|
||||
:source {:uri (:photo-path c)}}]
|
||||
[react/text {:style styles/new-contact-subtitle} (:name c)]]]))]]
|
||||
(when config/group-chats-enabled? group-chat-section)
|
||||
(when (config/group-chats-enabled? true) group-chat-section)
|
||||
^{:key "publicchat"}
|
||||
[react/view {:style styles/new-contact-title}
|
||||
[react/text {:style styles/new-contact-title-text
|
||||
|
|
|
@ -172,3 +172,6 @@
|
|||
:margin-top 36
|
||||
:margin-bottom 16
|
||||
:font-size 16})
|
||||
|
||||
(def pair-button
|
||||
{:margin-left 32})
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.utils.build :as build]
|
||||
[status-im.utils.config :as config]
|
||||
[status-im.utils.utils :as utils]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.i18n :as i18n]
|
||||
|
@ -11,6 +12,7 @@
|
|||
[status-im.utils.gfycat.core :as gfy]
|
||||
[clojure.string :as string]
|
||||
[status-im.ui.screens.offline-messaging-settings.views :as offline-messaging.views]
|
||||
[status-im.ui.screens.pairing.views :as pairing.views]
|
||||
[status-im.ui.components.qr-code-viewer.views :as qr-code-viewer]
|
||||
[status-im.ui.screens.desktop.main.tabs.profile.styles :as styles]
|
||||
[status-im.ui.screens.profile.user.views :as profile]
|
||||
|
@ -74,11 +76,24 @@
|
|||
[react/text {:style styles/qr-code-copy-text}
|
||||
(i18n/label :copy-qr)]]]]]))
|
||||
|
||||
(defn installations-section [installations]
|
||||
[react/view
|
||||
[react/view {:style styles/title-separator}]
|
||||
[react/text {:style styles/mailserver-title} (i18n/label :devices)]
|
||||
[react/touchable-highlight {:style styles/pair-button
|
||||
:on-press pairing.views/pair!}
|
||||
[react/text (i18n/label :pair)]]
|
||||
(for [installation installations]
|
||||
^{:key (:installation-id installation)}
|
||||
[react/view {:style {:margin-vertical 8}}
|
||||
[pairing.views/render-row installation]])])
|
||||
|
||||
(views/defview advanced-settings []
|
||||
(views/letsubs [current-wnode-id [:settings/current-wnode]
|
||||
installations [:pairing/installations]
|
||||
wnodes [:settings/fleet-wnodes]]
|
||||
(let [render-fn (offline-messaging.views/render-row current-wnode-id)]
|
||||
[react/view
|
||||
[react/scroll-view
|
||||
[react/text {:style styles/advanced-settings-title
|
||||
:font :medium}
|
||||
(i18n/label :advanced-settings)]
|
||||
|
@ -88,7 +103,9 @@
|
|||
(for [node (vals wnodes)]
|
||||
^{:key (:id node)}
|
||||
[react/view {:style {:margin-vertical 8}}
|
||||
[render-fn node]])]])))
|
||||
[render-fn node]])]
|
||||
(when (config/pairing-enabled? true)
|
||||
(installations-section installations))])))
|
||||
|
||||
(views/defview backup-recovery-phrase []
|
||||
[profile.recovery/backup-seed])
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
(ns status-im.ui.screens.pairing.styles
|
||||
(:require [status-im.ui.components.colors :as colors])
|
||||
(:require-macros [status-im.utils.styles :refer [defstyle]]))
|
||||
|
||||
(def wrapper
|
||||
{:flex 1
|
||||
:background-color :white})
|
||||
|
||||
(def installation-item-inner
|
||||
{:padding-horizontal 16})
|
||||
|
||||
(defstyle installation-item
|
||||
{:flex-direction :row
|
||||
:background-color :white
|
||||
:align-items :center
|
||||
:padding-horizontal 16
|
||||
:ios {:height 64}
|
||||
:android {:height 56}})
|
||||
|
||||
(defstyle installation-item-name-text
|
||||
{:color colors/black
|
||||
:ios {:font-size 17
|
||||
:letter-spacing -0.2
|
||||
:line-height 20}
|
||||
:android {:font-size 16}})
|
|
@ -0,0 +1,7 @@
|
|||
(ns status-im.ui.screens.pairing.subs
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.utils.ethereum.core :as ethereum]))
|
||||
|
||||
(re-frame/reg-sub :pairing/installations
|
||||
:<- [:get :pairing/installations]
|
||||
vals)
|
|
@ -0,0 +1,47 @@
|
|||
(ns status-im.ui.screens.pairing.views
|
||||
(:require-macros [status-im.utils.views :as views])
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.i18n :as i18n]
|
||||
[status-im.utils.config :as config]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.ui.components.icons.vector-icons :as vector-icons]
|
||||
[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]
|
||||
[status-im.ui.components.toolbar.view :as toolbar]
|
||||
[status-im.ui.components.toolbar.actions :as toolbar.actions]
|
||||
[status-im.ui.screens.profile.components.views :as profile.components]
|
||||
[status-im.ui.screens.pairing.styles :as styles]))
|
||||
|
||||
(defn synchronize-installation! [id]
|
||||
(re-frame/dispatch [:pairing.ui/synchronize-installation-pressed id]))
|
||||
|
||||
(defn pair! []
|
||||
(re-frame/dispatch [:pairing.ui/pair-devices-pressed]))
|
||||
|
||||
(defn render-row [{:keys [installation-id]}]
|
||||
[react/touchable-highlight
|
||||
{:on-press #(synchronize-installation! installation-id)
|
||||
:accessibility-label :installation-item}
|
||||
[react/view styles/installation-item
|
||||
[react/view styles/installation-item-inner
|
||||
[react/text {:style styles/installation-item-name-text}
|
||||
installation-id]]]])
|
||||
|
||||
(defn render-rows [installations]
|
||||
[react/view styles/wrapper
|
||||
[list/flat-list {:data installations
|
||||
:default-separator? false
|
||||
:key-fn :installation-id
|
||||
:render-fn render-row}]])
|
||||
|
||||
(views/defview installations []
|
||||
(views/letsubs [installations [:pairing/installations]]
|
||||
[react/view {:flex 1}
|
||||
[status-bar/status-bar]
|
||||
[toolbar/toolbar {}
|
||||
toolbar/default-nav-back
|
||||
[toolbar/content-title (i18n/label :t/devices)]
|
||||
[toolbar/actions
|
||||
[(toolbar.actions/add false pair!)]]]
|
||||
(render-rows installations)]))
|
|
@ -61,3 +61,6 @@
|
|||
{:font-size 15
|
||||
:letter-spacing -0.2
|
||||
:color colors/blue})
|
||||
|
||||
(def pair-button
|
||||
{:margin-left 32})
|
||||
|
|
|
@ -175,6 +175,13 @@
|
|||
{:label-kw :t/bootnodes
|
||||
:action-fn #(re-frame/dispatch [:navigate-to :bootnodes-settings])
|
||||
:accessibility-label :bootnodes-settings-button}])
|
||||
(when (config/pairing-enabled? dev-mode?)
|
||||
[profile.components/settings-item-separator])
|
||||
(when (config/pairing-enabled? dev-mode?)
|
||||
[profile.components/settings-item
|
||||
{:label-kw :t/devices
|
||||
:action-fn #(re-frame/dispatch [:navigate-to :installations])
|
||||
:accessibility-label :pairing-settings-button}])
|
||||
(when dev-mode?
|
||||
[profile.components/settings-switch-item
|
||||
{:label-kw :t/web3-opt-in
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
status-im.ui.screens.fleet-settings.subs
|
||||
status-im.ui.screens.offline-messaging-settings.subs
|
||||
status-im.ui.screens.bootnodes-settings.subs
|
||||
status-im.ui.screens.pairing.subs
|
||||
status-im.ui.screens.currency-settings.subs
|
||||
status-im.ui.screens.browser.subs
|
||||
status-im.ui.screens.add-new.new-chat.subs
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
[status-im.ui.screens.offline-messaging-settings.edit-mailserver.views :refer [edit-mailserver]]
|
||||
[status-im.ui.screens.extensions.add.views :refer [edit-extension show-extension]]
|
||||
[status-im.ui.screens.bootnodes-settings.views :refer [bootnodes-settings]]
|
||||
[status-im.ui.screens.pairing.views :refer [installations]]
|
||||
[status-im.ui.screens.bootnodes-settings.edit-bootnode.views :refer [edit-bootnode]]
|
||||
[status-im.ui.screens.currency-settings.views :refer [currency-settings]]
|
||||
[status-im.ui.screens.help-center.views :refer [help-center]]
|
||||
|
@ -270,6 +271,7 @@
|
|||
:profile-photo-capture profile-photo-capture
|
||||
:about-app about-app/about-app
|
||||
:bootnodes-settings bootnodes-settings
|
||||
:installations installations
|
||||
:edit-bootnode edit-bootnode
|
||||
:offline-messaging-settings offline-messaging-settings
|
||||
:edit-mailserver edit-mailserver
|
||||
|
|
|
@ -17,7 +17,14 @@
|
|||
|
||||
(def bootnodes-settings-enabled? (enabled? (get-config :BOOTNODES_SETTINGS_ENABLED "1")))
|
||||
(def rpc-networks-only? (enabled? (get-config :RPC_NETWORKS_ONLY "1")))
|
||||
(def group-chats-enabled? (enabled? (get-config :GROUP_CHATS_ENABLED "0")))
|
||||
(defn group-chats-enabled? [dev-mode?]
|
||||
(and (enabled? (get-config :GROUP_CHATS_ENABLED "0"))
|
||||
(or dev-mode? platform/desktop?)))
|
||||
(defn pairing-enabled? [dev-mode?]
|
||||
(and (enabled? (get-config :PAIRING_ENABLED "0"))
|
||||
(or dev-mode? platform/desktop?)
|
||||
;; Hard disable as desktop ignores build parameters
|
||||
false))
|
||||
(def mainnet-warning-enabled? (enabled? (get-config :MAINNET_WARNING_ENABLED 0)))
|
||||
(def pfs-encryption-enabled? (enabled? (get-config :PFS_ENCRYPTION_ENABLED "0")))
|
||||
(def in-app-notifications-enabled? (enabled? (get-config :IN_APP_NOTIFICATIONS_ENABLED 0)))
|
||||
|
|
|
@ -118,4 +118,4 @@
|
|||
(testing "it returns only chats with is-active"
|
||||
(is (= {1 active-chat-1
|
||||
2 active-chat-2}
|
||||
(s/active-chats [{} chats]))))))
|
||||
(s/active-chats [{} chats {}]))))))
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
(is (= 3 (group-chats/get-last-clock-value {:db {:chats {chat-id initial-message}}} chat-id))))
|
||||
|
||||
(deftest handle-group-membership-update
|
||||
(with-redefs [config/group-chats-enabled? true]
|
||||
(with-redefs [config/group-chats-enabled? (constantly true)]
|
||||
(testing "a brand new chat"
|
||||
(let [actual (->
|
||||
(group-chats/handle-membership-update {:db {}} initial-message admin)
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
(is (= "id" (:InstallationID actual))))))
|
||||
(testing "pfs & group chats disabled"
|
||||
(with-redefs [config/pfs-encryption-enabled? false
|
||||
config/group-chats-enabled? false]
|
||||
config/group-chats-enabled? (constantly false)]
|
||||
(testing "the user is not logged in"
|
||||
(let [actual (parse-node-config (node/start cofx nil))]
|
||||
(is (not (:PFSEnabled actual)))))
|
||||
|
@ -37,7 +37,7 @@
|
|||
(let [actual (parse-node-config (node/start cofx address))]
|
||||
(is (:PFSEnabled actual))))))
|
||||
(testing "group chats is enabled"
|
||||
(with-redefs [config/group-chats-enabled? true]
|
||||
(with-redefs [config/group-chats-enabled? (constantly true)]
|
||||
(testing "the user is not logged in"
|
||||
(let [actual (parse-node-config (node/start cofx nil))]
|
||||
(is (not (:PFSEnabled actual)))))
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
(ns status-im.test.pairing.core
|
||||
(:require [cljs.test :refer-macros [deftest is testing]]
|
||||
[status-im.transport.message.pairing :as transport.pairing]
|
||||
[status-im.utils.config :as config]
|
||||
[status-im.pairing.core :as pairing]))
|
||||
|
||||
(deftest merge-contact-test
|
||||
(testing "vanilla contacts"
|
||||
(let [contact-1 {:pending? false
|
||||
:this-should-be-kept true
|
||||
:last-updated 1
|
||||
:name "name-v1"
|
||||
:photo-path "photo-v1"}
|
||||
contact-2 {:pending? false
|
||||
:last-updated 2
|
||||
:name "name-v2"
|
||||
:photo-path "photo-v2"}
|
||||
expected {:pending? false
|
||||
:this-should-be-kept true
|
||||
:last-updated 2
|
||||
:name "name-v2"
|
||||
:photo-path "photo-v2"}]
|
||||
(is (= expected (pairing/merge-contact contact-1 contact-2)))))
|
||||
(testing "without last-updated"
|
||||
(let [contact-1 {:pending? false
|
||||
:name "name-v1"
|
||||
:photo-path "photo-v1"}
|
||||
contact-2 {:pending? false
|
||||
:last-updated 2
|
||||
:name "name-v2"
|
||||
:photo-path "photo-v2"}
|
||||
expected {:pending? false
|
||||
:last-updated 2
|
||||
:name "name-v2"
|
||||
:photo-path "photo-v2"}]
|
||||
(is (= expected (pairing/merge-contact contact-1 contact-2)))))
|
||||
(testing "nil contact"
|
||||
(let [contact-1 nil
|
||||
contact-2 {:pending? false
|
||||
:last-updated 2
|
||||
:name "name-v2"
|
||||
:photo-path "photo-v2"}
|
||||
expected {:pending? false
|
||||
:last-updated 2
|
||||
:name "name-v2"
|
||||
:photo-path "photo-v2"}]
|
||||
(is (= expected (pairing/merge-contact contact-1 contact-2)))))
|
||||
(testing "not pending in one device"
|
||||
(let [contact-1 {:pending? false
|
||||
:last-updated 1
|
||||
:name "name-v1"
|
||||
:photo-path "photo-v1"}
|
||||
contact-2 {:pending? true
|
||||
:last-updated 2
|
||||
:name "name-v2"
|
||||
:photo-path "photo-v2"}
|
||||
expected {:pending? false
|
||||
:last-updated 2
|
||||
:name "name-v2"
|
||||
:photo-path "photo-v2"}]
|
||||
(is (= expected (pairing/merge-contact contact-1 contact-2)))))
|
||||
(testing "pending in one device and nil"
|
||||
(let [contact-1 nil
|
||||
contact-2 {:pending? true
|
||||
:last-updated 2
|
||||
:name "name-v2"
|
||||
:photo-path "photo-v2"}
|
||||
expected {:pending? true
|
||||
:last-updated 2
|
||||
:name "name-v2"
|
||||
:photo-path "photo-v2"}]
|
||||
(is (= expected (pairing/merge-contact contact-1 contact-2))))))
|
||||
|
||||
(deftest handle-sync-installation-test
|
||||
(with-redefs [config/pairing-enabled? (constantly true)]
|
||||
(let [old-contact-1 {:name "old-contact-one"
|
||||
:last-updated 0
|
||||
:photo-path "old-contact-1"
|
||||
:pending? true}
|
||||
new-contact-1 {:name "new-contact-one"
|
||||
:last-updated 1
|
||||
:photo-path "new-contact-1"
|
||||
:pending? false}
|
||||
old-contact-2 {:name "old-contact-2"
|
||||
:last-updated 0
|
||||
:photo-path "old-contact-2"
|
||||
:pending? false}
|
||||
new-contact-2 {:name "new-contact-2"
|
||||
:last-updated 1
|
||||
:photo-path "new-contact-2"
|
||||
:pending? false}
|
||||
contact-3 {:name "contact-3"
|
||||
:photo-path "contact-3"
|
||||
:pending? false}
|
||||
contact-4 {:name "contact-4"
|
||||
:photo-path "contact-4"
|
||||
:pending? true}
|
||||
cofx {:db {:current-public-key "us"
|
||||
:contacts/contacts {"contact-1" old-contact-1
|
||||
"contact-2" new-contact-2
|
||||
"contact-3" contact-3}}}
|
||||
sync-message {:contacts {"contact-1" new-contact-1
|
||||
"contact-2" old-contact-2
|
||||
"contact-4" contact-4}}
|
||||
expected {"contact-1" new-contact-1
|
||||
"contact-2" new-contact-2
|
||||
"contact-3" contact-3
|
||||
"contact-4" contact-4}]
|
||||
(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])))))))
|
||||
|
||||
(deftest sync-installation-messages-test
|
||||
(testing "it creates a sync installation message"
|
||||
(let [cofx {:db {:current-public-key "us"
|
||||
:contacts/contacts {"contact-1" {:name "contact-1"}
|
||||
"contact-2" {:name "contact-2"}}}}
|
||||
expected [(transport.pairing/SyncInstallation. {"contact-1" {:name "contact-1"}})
|
||||
(transport.pairing/SyncInstallation. {"contact-2" {:name "contact-2"}})]]
|
||||
(is (= expected (pairing/sync-installation-messages cofx))))))
|
||||
|
||||
(deftest handle-bundles-added-test
|
||||
(with-redefs [config/pairing-enabled? (constantly true)]
|
||||
(let [installation-1 {:confirmed? true
|
||||
:installation-id "installation-1"}
|
||||
cofx {:db {:current-public-key "us"
|
||||
:pairing/installations {"installation-1" installation-1}}}]
|
||||
(testing "new installations"
|
||||
(let [new-installation {:identity "us" :installationID "installation-2"}
|
||||
expected {"installation-1" installation-1
|
||||
"installation-2" {:confirmed? false
|
||||
:installation-id "installation-2"}}]
|
||||
(is (= expected (get-in (pairing/handle-bundles-added cofx new-installation) [:db :pairing/installations])))))
|
||||
(testing "already existing installation"
|
||||
(let [old-installation {:identity "us" :installationID "installation-1"}]
|
||||
(is (not (pairing/handle-bundles-added cofx old-installation)))))
|
||||
(testing "not from us"
|
||||
(let [new-installation {:identity "not-us" :installationID "does-not-matter"}]
|
||||
(is (not (pairing/handle-bundles-added cofx new-installation))))))))
|
|
@ -11,6 +11,7 @@
|
|||
[status-im.test.wallet.transactions.views]
|
||||
[status-im.test.mailserver.core]
|
||||
[status-im.test.group-chats.core]
|
||||
[status-im.test.pairing.core]
|
||||
[status-im.test.node.core]
|
||||
[status-im.test.models.bootnode]
|
||||
[status-im.test.models.account]
|
||||
|
@ -78,6 +79,7 @@
|
|||
'status-im.test.extensions.core
|
||||
'status-im.test.mailserver.core
|
||||
'status-im.test.group-chats.core
|
||||
'status-im.test.pairing.core
|
||||
'status-im.test.node.core
|
||||
'status-im.test.models.bootnode
|
||||
'status-im.test.models.account
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
"confirm": "Confirm",
|
||||
"public-chat": "Public chat",
|
||||
"description": "Description",
|
||||
"devices": "Devices",
|
||||
"pair": "Pair devices",
|
||||
"currency-display-name-tzs": "Tanzanian Shilling",
|
||||
"currency-display-name-brl": "Brazil Real",
|
||||
"mainnet-network": "Main network",
|
||||
|
|
Loading…
Reference in New Issue