From 693eae9cf9793185193c486bce350d28951f5cc0 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Fri, 2 Nov 2018 14:48:45 +0100 Subject: [PATCH] Send your own messages Adds a `chat-id` field in `content` map. The reason it has been added to the map instead of augmenting transit is that it would simplify the calculation of `message-id`, which in this case is consistent for both old & new clients. `chat-id` also represents the `chat-id` with respect of the sender, as in 1-to-1 chats that is asymmetric. Signed-off-by: Andrea Maria Piana --- .env | 2 +- .env.jenkins | 2 +- .env.nightly | 2 +- Makefile | 6 +- STATUS_GO_VERSION | 2 +- .../status/ethereum/module/StatusModule.java | 41 +++++++- .../desktop/CMakeLists.txt | 2 +- .../react-native-status/desktop/rctstatus.cpp | 16 ++++ .../react-native-status/desktop/rctstatus.h | 2 + .../ios/RCTStatus/RCTStatus.m | 24 +++++ src/status_im/chat/commands/sending.cljs | 3 +- src/status_im/chat/models/input.cljs | 3 +- src/status_im/chat/models/message.cljs | 23 +++-- src/status_im/data_store/installations.cljs | 9 +- .../realm/schemas/account/core.cljs | 18 +++- .../realm/schemas/account/installation.cljs | 14 +++ .../realm/schemas/account/migrations.cljs | 4 +- src/status_im/events.cljs | 24 ++++- src/status_im/group_chats/core.cljs | 2 +- src/status_im/native_module/core.cljs | 4 + src/status_im/native_module/impl/module.cljs | 8 ++ src/status_im/pairing/core.cljs | 95 +++++++++++++++++-- src/status_im/transport/db.cljs | 5 + src/status_im/transport/impl/receive.cljs | 5 + src/status_im/transport/impl/send.cljs | 5 + src/status_im/transport/message/core.cljs | 11 ++- src/status_im/transport/message/pairing.cljs | 8 ++ src/status_im/transport/message/protocol.cljs | 19 ++-- src/status_im/transport/message/transit.cljs | 13 ++- src/status_im/ui/screens/pairing/styles.cljs | 4 +- src/status_im/ui/screens/pairing/views.cljs | 26 ++++- src/status_im/utils/config.cljs | 4 +- .../status_im/test/chat/models/message.cljs | 2 +- test/cljs/status_im/test/pairing/core.cljs | 26 ++++- translations/en.json | 2 + 35 files changed, 377 insertions(+), 59 deletions(-) diff --git a/.env b/.env index 2d7ecefa96..6082719493 100644 --- a/.env +++ b/.env @@ -9,7 +9,7 @@ POW_TIME=1 DEFAULT_NETWORK=mainnet_rpc DEBUG_WEBVIEW=1 GROUP_CHATS_ENABLED=1 -PAIRING_ENABLED=0 +PAIRING_ENABLED=1 CACHED_WEBVIEWS_ENABLED=1 EXTENSIONS=1 HARDWALLET_ENABLED=0 diff --git a/.env.jenkins b/.env.jenkins index 3fc3e084c7..9df85ce87b 100644 --- a/.env.jenkins +++ b/.env.jenkins @@ -13,4 +13,4 @@ MAINNET_WARNING_ENABLED=1 CACHED_WEBVIEWS_ENABLED=1 EXTENSIONS=1 PFS_ENCRYPTION_ENABLED=0 -PAIRING_ENABLED=0 +PAIRING_ENABLED=1 diff --git a/.env.nightly b/.env.nightly index a563961bfc..79137d0f71 100644 --- a/.env.nightly +++ b/.env.nightly @@ -9,7 +9,7 @@ POW_TIME=1 DEFAULT_NETWORK=mainnet_rpc DEBUG_WEBVIEW=1 GROUP_CHATS_ENABLED=1 -PAIRING_ENABLED=0 +PAIRING_ENABLED=1 MAINNET_WARNING_ENABLED=1 EXTENSIONS=1 PFS_ENCRYPTION_ENABLED=0 diff --git a/Makefile b/Makefile index 9d7ca55966..2bc4bac2ab 100644 --- a/Makefile +++ b/Makefile @@ -51,14 +51,14 @@ _prepare-mobile: ##@prepare Install mobile platform dependencies and prepare wor npm install $(STATUS_GO_IOS_ARCH): - cd $(RCTSTATUS_DIR) && curl -OL "$(GITHUB_URL)/v$(STATUS_GO_VER)/status-go-ios-$(STATUS_GO_VER).zip" + cd $(RCTSTATUS_DIR) && curl -OL "$(GITHUB_URL)/$(STATUS_GO_VER)/status-go-ios.zip" $(STATUS_GO_DRO_ARCH): mkdir -p $(ANDROID_LIBS_DIR) - cd $(ANDROID_LIBS_DIR) && curl -OL "$(GITHUB_URL)/v$(STATUS_GO_VER)/status-go-$(STATUS_GO_VER).aar" + cd $(ANDROID_LIBS_DIR) && curl -o status-go-$(STATUS_GO_VER).aar -OL "$(GITHUB_URL)/$(STATUS_GO_VER)/status-go-android.aar" prepare-ios: $(STATUS_GO_IOS_ARCH) _prepare-mobile ##@prepare Install and prepare iOS-specific dependencies - cd $(RCTSTATUS_DIR) && unzip -q -o status-go-ios-$(STATUS_GO_VER).zip + cd $(RCTSTATUS_DIR) && unzip -q -o status-go-ios.zip ifeq ($(OS),Darwin) cd ios && pod install endif diff --git a/STATUS_GO_VERSION b/STATUS_GO_VERSION index e4b8073b53..d5f5dded5c 100644 --- a/STATUS_GO_VERSION +++ b/STATUS_GO_VERSION @@ -1 +1 @@ -0.16.4-4-gaf1f6eb5 +v0.16.3-9-g27700aa2 diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java index 1d896a78e7..bed410cc62 100644 --- a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java +++ b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java @@ -706,7 +706,6 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL StatusThreadPoolExecutor.getInstance().execute(r); } - @ReactMethod public void signGroupMembership(final String content, final Callback callback) { Log.d(TAG, "signGroupMembership"); @@ -727,6 +726,46 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL StatusThreadPoolExecutor.getInstance().execute(r); } + @ReactMethod + public void enableInstallation(final String installationId, final Callback callback) { + Log.d(TAG, "enableInstallation"); + if (!checkAvailability()) { + callback.invoke(false); + return; + } + + Runnable r = new Runnable() { + @Override + public void run() { + String result = Statusgo.EnableInstallation(installationId); + + callback.invoke(result); + } + }; + + StatusThreadPoolExecutor.getInstance().execute(r); + } + + @ReactMethod + public void disableInstallation(final String installationId, final Callback callback) { + Log.d(TAG, "disableInstallation"); + if (!checkAvailability()) { + callback.invoke(false); + return; + } + + Runnable r = new Runnable() { + @Override + public void run() { + String result = Statusgo.DisableInstallation(installationId); + + callback.invoke(result); + } + }; + + StatusThreadPoolExecutor.getInstance().execute(r); + } + @Override public @Nullable Map getConstants() { diff --git a/modules/react-native-status/desktop/CMakeLists.txt b/modules/react-native-status/desktop/CMakeLists.txt index 4dc314d2d3..3c86a0bad9 100755 --- a/modules/react-native-status/desktop/CMakeLists.txt +++ b/modules/react-native-status/desktop/CMakeLists.txt @@ -38,7 +38,7 @@ file (STRINGS "../../../STATUS_GO_VERSION" STATUS_GO_VERSION) ExternalProject_Add(StatusGo_ep PREFIX ${StatusGo_PREFIX} SOURCE_DIR ${StatusGo_SOURCE_DIR} - URL https://github.com/status-im/status-go/releases/download/v${STATUS_GO_VERSION}/status-go-desktop-${STATUS_GO_VERSION}.zip + URL https://github.com/status-im/status-go/releases/download/${STATUS_GO_VERSION}/status-go-desktop.zip BUILD_BYPRODUCTS ${StatusGo_STATIC_LIB} CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/${CONFIGURE_SCRIPT} ${GO_ROOT_PATH} ${StatusGo_ROOT} ${StatusGo_SOURCE_DIR} BUILD_COMMAND "" diff --git a/modules/react-native-status/desktop/rctstatus.cpp b/modules/react-native-status/desktop/rctstatus.cpp index ef3c4bd212..0a0449f496 100644 --- a/modules/react-native-status/desktop/rctstatus.cpp +++ b/modules/react-native-status/desktop/rctstatus.cpp @@ -215,6 +215,22 @@ void RCTStatus::extractGroupMembershipSignatures(QString signatures, double call }, signatures, callbackId); } +void RCTStatus::enableInstallation(QString installationId, double callbackId) { + Q_D(RCTStatus); + QtConcurrent::run([&](QString installationId, double callbackId) { + const char* result = EnableInstallation(installationId.toUtf8().data()); + d->bridge->invokePromiseCallback(callbackId, QVariantList{result}); + }, installationId, callbackId); +} + +void RCTStatus::disableInstallation(QString installationId, double callbackId) { + Q_D(RCTStatus); + QtConcurrent::run([&](QString installationId, double callbackId) { + const char* result = DisableInstallation(installationId.toUtf8().data()); + d->bridge->invokePromiseCallback(callbackId, QVariantList{result}); + }, installationId, callbackId); +} + void RCTStatus::setAdjustResize() { } diff --git a/modules/react-native-status/desktop/rctstatus.h b/modules/react-native-status/desktop/rctstatus.h index c10343656c..0c425dda45 100644 --- a/modules/react-native-status/desktop/rctstatus.h +++ b/modules/react-native-status/desktop/rctstatus.h @@ -46,6 +46,8 @@ public: Q_INVOKABLE void signMessage(QString rpcParams, double callbackId); Q_INVOKABLE void signGroupMembership(QString content, double callbackId); Q_INVOKABLE void extractGroupMembershipSignatures(QString signatures, double callbackId); + Q_INVOKABLE void enableInstallation(QString installationId, double callbackId); + Q_INVOKABLE void disableInstallation(QString installationId, double callbackId); Q_INVOKABLE void setAdjustResize(); Q_INVOKABLE void setAdjustPan(); diff --git a/modules/react-native-status/ios/RCTStatus/RCTStatus.m b/modules/react-native-status/ios/RCTStatus/RCTStatus.m index ae94caffd4..7de89880f2 100644 --- a/modules/react-native-status/ios/RCTStatus/RCTStatus.m +++ b/modules/react-native-status/ios/RCTStatus/RCTStatus.m @@ -277,6 +277,30 @@ RCT_EXPORT_METHOD(extractGroupMembershipSignatures:(NSString *)content callback(@[[NSString stringWithUTF8String: result]]); } +//////////////////////////////////////////////////////////////////// +#pragma mark - EnableInstallation +//////////////////////////////////////////////////////////////////// enableInstallation +RCT_EXPORT_METHOD(enableInstallation:(NSString *)content + callback:(RCTResponseSenderBlock)callback) { +#if DEBUG + NSLog(@"EnableInstallation() method called"); +#endif + char * result = EnableInstallation((char *) [content UTF8String]); + callback(@[[NSString stringWithUTF8String: result]]); +} + +//////////////////////////////////////////////////////////////////// +#pragma mark - DisableInstallation +//////////////////////////////////////////////////////////////////// disableInstallation +RCT_EXPORT_METHOD(disableInstallation:(NSString *)content + callback:(RCTResponseSenderBlock)callback) { +#if DEBUG + NSLog(@"DisableInstallation() method called"); +#endif + char * result = DisableInstallation((char *) [content UTF8String]); + callback(@[[NSString stringWithUTF8String: result]]); +} + //////////////////////////////////////////////////////////////////// #pragma mark - only android methods //////////////////////////////////////////////////////////////////// diff --git a/src/status_im/chat/commands/sending.cljs b/src/status_im/chat/commands/sending.cljs index 7a06fc31ea..db061602d3 100644 --- a/src/status_im/chat/commands/sending.cljs +++ b/src/status_im/chat/commands/sending.cljs @@ -15,7 +15,8 @@ (protocol/enhance-send-parameters type parameter-map cofx))] {:chat-id chat-id :content-type constants/content-type-command - :content {:command-path command-path + :content {:chat-id chat-id + :command-path command-path :params (or new-parameter-map parameter-map)}})) (fx/defn validate-and-send diff --git a/src/status_im/chat/models/input.cljs b/src/status_im/chat/models/input.cljs index 25448b2492..a39ba49f50 100644 --- a/src/status_im/chat/models/input.cljs +++ b/src/status_im/chat/models/input.cljs @@ -126,7 +126,8 @@ {:db (assoc-in db [:chats current-chat-id :metadata :responding-to-message] nil)} (chat.message/send-message {:chat-id current-chat-id :content-type constants/content-type-text - :content (cond-> {:text input-text} + :content (cond-> {:chat-id current-chat-id + :text input-text} reply-to-message (assoc :response-to reply-to-message))}) (commands.input/set-command-reference nil) diff --git a/src/status_im/chat/models/message.cljs b/src/status_im/chat/models/message.cljs index fbe0a14de7..4ff5a3ac72 100644 --- a/src/status_im/chat/models/message.cljs +++ b/src/status_im/chat/models/message.cljs @@ -103,7 +103,8 @@ (update-in [:chats chat-id :messages] assoc message-id prepared-message) ;; this will increase last-clock-value twice when sending our own messages (update-in [:chats chat-id :last-clock-value] (partial utils.clocks/receive clock-value))) - (not current-chat?) + (and (not current-chat?) + (not= from (:current-public-key db))) (update-in [:chats chat-id :unviewed-messages] (fnil conj #{}) message-id)) :data-store/tx [(messages-store/save-message-tx prepared-message)]}] (if batch? @@ -193,18 +194,20 @@ :accumulated []} messages))) -(defn valid-chat-id? [cofx {:keys [chat-id from message-type]}] - "Validate chat-id and message-type" - (case message-type - :group-user-message (get-in cofx [:db :chats chat-id :contacts from]) - :public-group-user-message (get-in cofx [:db :chats chat-id :public?]) - :user-message (or (= (get-in cofx [:db :current-public-key]) from) - (= chat-id from)) - false)) +(defn extract-chat-id [cofx {:keys [chat-id from message-type]}] + "Validate and return a valid chat-id" + (cond + (and (= :group-user-message message-type) + (get-in cofx [:db :chats chat-id :contacts from])) chat-id + (and (= :public-group-user-message message-type) + (get-in cofx [:db :chats chat-id :public?])) chat-id + (and (= :user-message message-type) + (= (get-in cofx [:db :current-public-key]) from)) chat-id + (= :user-message message-type) from)) (fx/defn receive-many [{:keys [now] :as cofx} messages] - (let [valid-messages (filter (partial valid-chat-id? cofx) messages) + (let [valid-messages (keep #(when-let [chat-id (extract-chat-id cofx %)] (assoc % :chat-id chat-id)) messages) deduped-messages (filter-messages cofx valid-messages) chat->message (group-by :chat-id deduped-messages) chat-ids (keys chat->message) diff --git a/src/status_im/data_store/installations.cljs b/src/status_im/data_store/installations.cljs index a0caca3dde..7291d92cae 100644 --- a/src/status_im/data_store/installations.cljs +++ b/src/status_im/data_store/installations.cljs @@ -18,7 +18,12 @@ installation true))) -(defn confirm +(defn enable [installation-id] (save {:installation-id installation-id - :confirmed? true})) + :enabled? true})) + +(defn disable + [installation-id] + (save {:installation-id installation-id + :enabled? false})) diff --git a/src/status_im/data_store/realm/schemas/account/core.cljs b/src/status_im/data_store/realm/schemas/account/core.cljs index 843475d341..07b9992ed0 100644 --- a/src/status_im/data_store/realm/schemas/account/core.cljs +++ b/src/status_im/data_store/realm/schemas/account/core.cljs @@ -199,6 +199,19 @@ (def v20 v19) +(def v21 [chat/v8 + transport/v7 + transport-inbox-topic/v1 + contact/v2 + message/v7 + mailserver/v11 + user-status/v1 + membership-update/v1 + installation/v2 + local-storage/v1 + browser/v8 + dapp-permissions/v9]) + ;; put schemas ordered by version (def schemas [{:schema v1 :schemaVersion 1 @@ -259,4 +272,7 @@ :migration migrations/v19} {:schema v20 :schemaVersion 20 - :migration migrations/v20}]) + :migration migrations/v20} + {:schema v21 + :schemaVersion 21 + :migration migrations/v21}]) diff --git a/src/status_im/data_store/realm/schemas/account/installation.cljs b/src/status_im/data_store/realm/schemas/account/installation.cljs index 21c399823c..6d1b53439b 100644 --- a/src/status_im/data_store/realm/schemas/account/installation.cljs +++ b/src/status_im/data_store/realm/schemas/account/installation.cljs @@ -4,3 +4,17 @@ :primaryKey :installation-id :properties {:installation-id :string :confirmed? :bool}}) + +(def v2 {:name :installation + :primaryKey :installation-id + :properties {:installation-id :string + :device-type {:type :string + :optional true} + :last-paired {:type :int + :optional true} + :has-bundle? {:type :bool + :optional true + :default false} + :enabled? {:type :bool + :optional true + :default false}}}) diff --git a/src/status_im/data_store/realm/schemas/account/migrations.cljs b/src/status_im/data_store/realm/schemas/account/migrations.cljs index 59965cb6f2..6fed62cd6e 100644 --- a/src/status_im/data_store/realm/schemas/account/migrations.cljs +++ b/src/status_im/data_store/realm/schemas/account/migrations.cljs @@ -119,7 +119,7 @@ (log/debug "migrating v19 account database")) (defn v20 [old-realm new-realm] - (log/debug "migrating v19 account database") + (log/debug "migrating v20 account database") (some-> new-realm (.objects "message") (.filtered (str "content-type = \"text/plain\"")) @@ -128,3 +128,5 @@ new-content (message-content/enrich-content content)] (aset message "content" (pr-str new-content))))))) +(defn v21 [old-realm new-realm] + (log/debug "migrating v21 account database")) diff --git a/src/status_im/events.cljs b/src/status_im/events.cljs index 4b4d76a9c0..8a4b554e2d 100644 --- a/src/status_im/events.cljs +++ b/src/status_im/events.cljs @@ -1104,12 +1104,30 @@ (handlers/register-handler-fx :pairing.ui/pair-devices-pressed - [] (fn [cofx _] - (pairing/start cofx))) + (pairing/pair-installation cofx))) (handlers/register-handler-fx :pairing.ui/synchronize-installation-pressed - [] (fn [cofx _] (pairing/send-installation-message cofx))) + +(handlers/register-handler-fx + :pairing.ui/enable-installation-pressed + (fn [cofx [_ installation-id]] + (pairing/enable-fx cofx installation-id))) + +(handlers/register-handler-fx + :pairing.ui/disable-installation-pressed + (fn [cofx [_ installation-id]] + (pairing/disable-fx cofx installation-id))) + +(handlers/register-handler-fx + :pairing.callback/enable-installation-success + (fn [cofx [_ installation-id]] + (pairing/enable cofx installation-id))) + +(handlers/register-handler-fx + :pairing.callback/disable-installation-success + (fn [cofx [_ installation-id]] + (pairing/disable cofx installation-id))) diff --git a/src/status_im/group_chats/core.cljs b/src/status_im/group_chats/core.cljs index c37013f950..30f4631ee4 100644 --- a/src/status_im/group_chats/core.cljs +++ b/src/status_im/group_chats/core.cljs @@ -110,7 +110,7 @@ cofx {:shh/send-group-message {:web3 web3 :src current-public-key - :dsts (disj members current-public-key) + :dsts members :success-event [:transport/message-sent chat-id (transport.utils/message-id (:message payload)) diff --git a/src/status_im/native_module/core.cljs b/src/status_im/native_module/core.cljs index e001f2b42f..48bda83730 100644 --- a/src/status_im/native_module/core.cljs +++ b/src/status_im/native_module/core.cljs @@ -63,3 +63,7 @@ (def extract-group-membership-signatures native-module/extract-group-membership-signatures) (def sign-group-membership native-module/sign-group-membership) + +(def enable-installation native-module/enable-installation) + +(def disable-installation native-module/disable-installation) diff --git a/src/status_im/native_module/impl/module.cljs b/src/status_im/native_module/impl/module.cljs index 0a8394e31f..7237908224 100644 --- a/src/status_im/native_module/impl/module.cljs +++ b/src/status_im/native_module/impl/module.cljs @@ -142,6 +142,14 @@ (when status (call-module #(.signGroupMembership status content callback)))) +(defn enable-installation [installation-id callback] + (when status + (call-module #(.enableInstallation status installation-id callback)))) + +(defn disable-installation [installation-id callback] + (when status + (call-module #(.disableInstallation status installation-id callback)))) + (defn is24Hour [] (when status (.-is24Hour status))) diff --git a/src/status_im/pairing/core.cljs b/src/status_im/pairing/core.cljs index 73df019351..ee152fce58 100644 --- a/src/status_im/pairing/core.cljs +++ b/src/status_im/pairing/core.cljs @@ -1,18 +1,31 @@ (ns status-im.pairing.core (:require + [re-frame.core :as re-frame] [status-im.utils.fx :as fx] [status-im.utils.config :as config] + [status-im.utils.platform :as utils.platform] [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.transport.message.pairing :as transport.pairing])) -(defn start [cofx] +(defn- parse-response [response-js] + (-> response-js + js/JSON.parse + (js->clj :keywordize-keys true))) + +(defn pair-installation [cofx] + (let [installation-id (get-in cofx [:db :account/account :installation-id]) + device-type utils.platform/os] + (protocol/send (transport.pairing/PairInstallation. installation-id device-type) nil cofx))) + +(defn send-pair-installation [cofx payload] (let [{:keys [current-public-key web3]} (:db cofx)] {:shh/send-pairing-message {:web3 web3 :src current-public-key - :payload []}})) + :payload payload}})) (defn merge-contact [local remote] (let [[old-contact new-contact] (sort-by :last-updated [local remote])] @@ -28,20 +41,26 @@ (def merge-contacts (partial merge-with merge-contact)) -(defn handle-bundles-added [{:keys [db]} bundle] +(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)] + {:db (assoc-in db + [:pairing/installations installation-id] + updated-installation) + :data-store/tx [(data-store.installations/save updated-installation)]})) + +(defn handle-bundles-added [{:keys [db] :as cofx} 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}] + :has-bundle? true}] (when (and (= (:identity bundle) (:current-public-key db)) + (not= (get-in db [:account/account :installation-id]) installation-id) (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)]}))))) + (upsert-installation cofx new-installation)))))) (defn sync-installation-messages [{:keys [db]}] (let [contacts (:contacts/contacts db)] @@ -49,6 +68,56 @@ (fn [[k v]] (transport.pairing/SyncInstallation. {k (dissoc v :photo-path)})) contacts))) +(defn enable [{:keys [db]} installation-id] + {:db (assoc-in db + [:pairing/installations installation-id :enabled?] + true) + :data-store/tx [(data-store.installations/enable installation-id)]}) + +(defn disable [{:keys [db]} installation-id] + {:db (assoc-in db + [:pairing/installations installation-id :enabled?] + false) + :data-store/tx [(data-store.installations/disable installation-id)]}) + +(defn handle-enable-installation-response + "Callback to dispatch on enable signature response" + [installation-id response-js] + (let [{:keys [error]} (parse-response response-js)] + (if error + (re-frame/dispatch [:pairing.callback/enable-installation-failed error]) + (re-frame/dispatch [:pairing.callback/enable-installation-success installation-id])))) + +(defn handle-disable-installation-response + "Callback to dispatch on disable signature response" + [installation-id response-js] + (let [{:keys [error]} (parse-response response-js)] + (if error + (re-frame/dispatch [:pairing.callback/disable-installation-failed error]) + (re-frame/dispatch [:pairing.callback/disable-installation-success installation-id])))) + +(defn enable-installation! [installation-id] + (native-module/enable-installation installation-id + (partial handle-enable-installation-response installation-id))) + +(defn disable-installation! [installation-id] + (native-module/disable-installation installation-id + (partial handle-disable-installation-response installation-id))) + +(defn enable-fx [_ installation-id] + {:pairing/enable-installation installation-id}) + +(defn disable-fx [_ installation-id] + {:pairing/disable-installation installation-id}) + +(re-frame/reg-fx + :pairing/enable-installation + enable-installation!) + +(re-frame/reg-fx + :pairing/disable-installation + disable-installation!) + (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) @@ -67,6 +136,16 @@ {:db (assoc db :contacts/contacts new-contacts) :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] + (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])) + (not= (get-in db [:account/account :installation-id]) installation-id)) + (let [installation {:installation-id installation-id + :device-type device-type + :last-paired timestamp}] + (upsert-installation cofx installation))))) + (fx/defn load-installations [{:keys [db all-installations]}] {:db (assoc db :pairing/installations (reduce (fn [acc {:keys [installation-id] :as i}] diff --git a/src/status_im/transport/db.cljs b/src/status_im/transport/db.cljs index 2261502f30..8903862424 100644 --- a/src/status_im/transport/db.cljs +++ b/src/status_im/transport/db.cljs @@ -27,6 +27,9 @@ (spec/def :request/attemps int?) (spec/def :request/cursor :global/not-empty-string) +(spec/def :pairing/installation-id :global/not-empty-string) +(spec/def :pairing/device-type :global/not-empty-string) + (spec/def :transport.inbox.topic/last-request pos-int?) (spec/def :transport.inbox.topic/started-at pos-int?) (spec/def :transport.inbox.topic/chat-id (spec/or :keyword keyword? @@ -105,6 +108,8 @@ (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/pair-installation (spec/keys :req-un [:pairing/installation-id + :pairing/device-type])) (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] diff --git a/src/status_im/transport/impl/receive.cljs b/src/status_im/transport/impl/receive.cljs index f365276a11..4b56b31c52 100644 --- a/src/status_im/transport/impl/receive.cljs +++ b/src/status_im/transport/impl/receive.cljs @@ -31,3 +31,8 @@ protocol/StatusMessage (receive [this _ signature _ cofx] (pairing/handle-sync-installation cofx this signature))) + +(extend-type transport.pairing/PairInstallation + protocol/StatusMessage + (receive [this _ signature timestamp cofx] + (pairing/handle-pair-installation cofx this timestamp signature))) diff --git a/src/status_im/transport/impl/send.cljs b/src/status_im/transport/impl/send.cljs index e4237ee4c1..68cec39c96 100644 --- a/src/status_im/transport/impl/send.cljs +++ b/src/status_im/transport/impl/send.cljs @@ -9,3 +9,8 @@ protocol/StatusMessage (send [this chat-id cofx] (group-chats/send-membership-update cofx this chat-id))) + +(extend-type transport.pairing/PairInstallation + protocol/StatusMessage + (send [this _ cofx] + (pairing/send-pair-installation cofx this))) diff --git a/src/status_im/transport/message/core.cljs b/src/status_im/transport/message/core.cljs index 11851c4827..d69de9d94a 100644 --- a/src/status_im/transport/message/core.cljs +++ b/src/status_im/transport/message/core.cljs @@ -11,7 +11,7 @@ [taoensso.timbre :as log])) (fx/defn receive-message - [cofx now-in-s chat-id js-message] + [cofx now-in-s filter-chat-id js-message] (let [{:keys [payload sig timestamp ttl]} (js->clj js-message :keywordize-keys true) status-message (-> payload transport.utils/to-utf8 @@ -20,7 +20,14 @@ (try (when-let [valid-message (protocol/validate status-message)] (fx/merge (assoc cofx :js-obj js-message) - #(protocol/receive valid-message (or chat-id sig) sig timestamp %))) + #(protocol/receive valid-message + (or + filter-chat-id + (get-in valid-message [:content :chat-id]) + sig) + sig + timestamp + %))) (catch :default e nil))))) ; ignore unknown message types (defn- js-array->seq [array] diff --git a/src/status_im/transport/message/pairing.cljs b/src/status_im/transport/message/pairing.cljs index 1fc91f56b8..5a2f4c1086 100644 --- a/src/status_im/transport/message/pairing.cljs +++ b/src/status_im/transport/message/pairing.cljs @@ -3,6 +3,14 @@ [status-im.transport.message.protocol :as protocol] [taoensso.timbre :as log])) +(defrecord PairInstallation + [installation-id device-type] + protocol/StatusMessage + (validate [this] + (if (spec/valid? :message/pair-installation this) + this + (log/warn "failed sync installation validation" (spec/explain :message/pair-installation this))))) + (defrecord SyncInstallation [contacts] protocol/StatusMessage diff --git a/src/status_im/transport/message/protocol.cljs b/src/status_im/transport/message/protocol.cljs index 8aa5ef8f9b..91fb510984 100644 --- a/src/status_im/transport/message/protocol.cljs +++ b/src/status_im/transport/message/protocol.cljs @@ -80,12 +80,14 @@ (defrecord Message [content content-type message-type clock-value timestamp] StatusMessage (send [this chat-id cofx] - (let [params {:chat-id chat-id - :payload this - :success-event [:transport/message-sent - chat-id - (transport.utils/message-id this) - message-type]}] + (let [dev-mode? (get-in cofx [:db :account/account :dev-mode?]) + current-public-key (get-in cofx [:db :current-public-key]) + params {:chat-id chat-id + :payload this + :success-event [:transport/message-sent + chat-id + (transport.utils/message-id this) + message-type]}] (case message-type :public-group-user-message (if config/pfs-encryption-enabled? @@ -103,7 +105,10 @@ chat-id (:success-event params) this) - (send-with-pubkey cofx params))))) + (fx/merge cofx + #(when (config/pairing-enabled? dev-mode?) + (send-direct-message % current-public-key nil this)) + (send-with-pubkey params)))))) (receive [this chat-id signature _ cofx] {:chat-received-message/add-fx [(assoc (into {} this) diff --git a/src/status_im/transport/message/transit.cljs b/src/status_im/transport/message/transit.cljs index cebe01c8ad..88c95a378d 100644 --- a/src/status_im/transport/message/transit.cljs +++ b/src/status_im/transport/message/transit.cljs @@ -94,6 +94,12 @@ (rep [this {:keys [contacts]}] #js [contacts])) +(deftype PairInstallationHandler [] + Object + (tag [this v] "p2") + (rep [this {:keys [installation-id device-type]}] + #js [installation-id device-type])) + (def writer (transit/writer :json {:handlers {contact/NewContactKey (NewContactKeyHandler.) @@ -103,7 +109,8 @@ protocol/Message (MessageHandler.) protocol/MessagesSeen (MessagesSeenHandler.) group-chat/GroupMembershipUpdate (GroupMembershipUpdateHandler.) - pairing/SyncInstallation (SyncInstallationHandler.)}})) + pairing/SyncInstallation (SyncInstallationHandler.) + pairing/PairInstallation (PairInstallationHandler.)}})) ;; ;; Reader handlers @@ -156,7 +163,9 @@ "g5" (fn [[chat-id membership-updates message]] (group-chat/GroupMembershipUpdate. chat-id membership-updates message)) "p1" (fn [[contacts]] - (pairing/SyncInstallation. contacts))}})) + (pairing/SyncInstallation. contacts)) + "p2" (fn [[installation-id device-type]] + (pairing/PairInstallation. installation-id device-type))}})) (defn serialize "Serializes a record implementing the StatusMessage protocol using the custom writers" diff --git a/src/status_im/ui/screens/pairing/styles.cljs b/src/status_im/ui/screens/pairing/styles.cljs index 3d497720c0..94430a7a60 100644 --- a/src/status_im/ui/screens/pairing/styles.cljs +++ b/src/status_im/ui/screens/pairing/styles.cljs @@ -7,7 +7,9 @@ :background-color :white}) (def installation-item-inner - {:padding-horizontal 16}) + {:flex 1 + :flex-direction :row + :padding-horizontal 16}) (defstyle installation-item {:flex-direction :row diff --git a/src/status_im/ui/screens/pairing/views.cljs b/src/status_im/ui/screens/pairing/views.cljs index 5bdd5b48bd..fa5a362576 100644 --- a/src/status_im/ui/screens/pairing/views.cljs +++ b/src/status_im/ui/screens/pairing/views.cljs @@ -3,6 +3,8 @@ (:require [re-frame.core :as re-frame] [status-im.i18n :as i18n] [status-im.utils.config :as config] + [status-im.utils.gfycat.core :as gfycat] + [status-im.ui.components.button.view :as buttons] [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] @@ -14,19 +16,35 @@ [status-im.ui.screens.pairing.styles :as styles])) (defn synchronize-installation! [id] - (re-frame/dispatch [:pairing.ui/synchronize-installation-pressed 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]}] +(defn enable-installation! [installation-id _] + (re-frame/dispatch [:pairing.ui/enable-installation-pressed installation-id])) + +(defn disable-installation! [installation-id _] + (re-frame/dispatch [:pairing.ui/disable-installation-pressed installation-id])) + +(defn render-row [{:keys [device-type enabled? 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]]]]) + [react/view + [react/text {:style styles/installation-item-name-text} + (str (gfycat/generate-gfy installation-id) " - " (or device-type + "unknown"))]] + [react/view + (if enabled? + [buttons/primary-button + {:on-press (partial disable-installation! installation-id)} + (i18n/label :t/enabled)] + [buttons/secondary-button + {:on-press (partial enable-installation! installation-id)} + (i18n/label :t/disabled)])]]]]) (defn render-rows [installations] [react/view styles/wrapper diff --git a/src/status_im/utils/config.cljs b/src/status_im/utils/config.cljs index af4c69a99f..e777513a8d 100644 --- a/src/status_im/utils/config.cljs +++ b/src/status_im/utils/config.cljs @@ -22,9 +22,7 @@ (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)) + (or dev-mode? platform/desktop?))) (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))) diff --git a/test/cljs/status_im/test/chat/models/message.cljs b/test/cljs/status_im/test/chat/models/message.cljs index e76ca60a41..bf611501c2 100644 --- a/test/cljs/status_im/test/chat/models/message.cljs +++ b/test/cljs/status_im/test/chat/models/message.cljs @@ -153,7 +153,7 @@ (testing "our own message" (is (get-in (message/receive-many cofx [own-message]) [:db :chats "matching" :messages "1"]))) (testing "a message with non matching chat-id" - (is (= cofx (message/receive-many cofx [bad-chat-id-message])))))) + (is (get-in (message/receive-many cofx [bad-chat-id-message]) [:db :chats "not-matching" :messages "1"]))))) (deftest receive-send-seen (let [cofx {:db {:chats {"chat-id" {}} diff --git a/test/cljs/status_im/test/pairing/core.cljs b/test/cljs/status_im/test/pairing/core.cljs index 14641ca64d..8a28c37b83 100644 --- a/test/cljs/status_im/test/pairing/core.cljs +++ b/test/cljs/status_im/test/pairing/core.cljs @@ -113,6 +113,28 @@ (pairing/handle-sync-installation cofx sync-message "us") [:db :contacts/contacts]))))))) +(deftest handle-pair-installation-test + (with-redefs [config/pairing-enabled? (constantly true)] + (let [cofx {:db {:current-public-key "us" + :pairing/installations {"1" {:has-bundle? true + :installation-id "1"} + "2" {:has-bundle? false + :installation-id "2"}}}} + pair-message {:device-type "ios" + :installation-id "1"}] + (testing "not coming from us" + (is (not (pairing/handle-pair-installation cofx pair-message 1 "not-us")))) + (testing "coming from us" + (is (= {"1" {:has-bundle? true + :installation-id "1" + :last-paired 1 + :device-type "ios"} + "2" {:has-bundle? false + :installation-id "2"}} + (get-in + (pairing/handle-pair-installation cofx pair-message 1 "us") + [:db :pairing/installations]))))))) + (deftest sync-installation-messages-test (testing "it creates a sync installation message" (let [cofx {:db {:current-public-key "us" @@ -124,14 +146,14 @@ (deftest handle-bundles-added-test (with-redefs [config/pairing-enabled? (constantly true)] - (let [installation-1 {:confirmed? true + (let [installation-1 {:has-bundle? false :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-2" {:has-bundle? true :installation-id "installation-2"}}] (is (= expected (get-in (pairing/handle-bundles-added cofx new-installation) [:db :pairing/installations]))))) (testing "already existing installation" diff --git a/translations/en.json b/translations/en.json index 260a8fd382..4cc1ac72e0 100644 --- a/translations/en.json +++ b/translations/en.json @@ -6,6 +6,8 @@ "description": "Description", "devices": "Devices", "pair": "Pair devices", + "enabled": "enabled", + "disabled": "disabled", "currency-display-name-tzs": "Tanzanian Shilling", "currency-display-name-brl": "Brazil Real", "mainnet-network": "Main network",