diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/NetworkManager.kt b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/NetworkManager.kt index 2d7ddc6158..740bb162c4 100644 --- a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/NetworkManager.kt +++ b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/NetworkManager.kt @@ -88,4 +88,16 @@ class NetworkManager(private val reactContext: ReactApplicationContext) : ReactC callback) } + @ReactMethod + fun inputConnectionStringForImportingKeypairsKeystores(connectionString: String, configJSON: String, callback: Callback) { + val jsonConfig = JSONObject(configJSON) + val receiverConfig = jsonConfig.getJSONObject("receiverConfig") + val keyStorePath = utils.pathCombine(utils.getNoBackupDirectory(), "/keystore") + receiverConfig.put("keystorePath", keyStorePath) + + utils.executeRunnableStatusGoMethod( + { Statusgo.inputConnectionStringForImportingKeypairsKeystores(connectionString, jsonConfig.toString()) }, + callback + ) + } } diff --git a/modules/react-native-status/ios/RCTStatus/NetworkManager.m b/modules/react-native-status/ios/RCTStatus/NetworkManager.m index 3701ec3acb..9e24aaf96d 100644 --- a/modules/react-native-status/ios/RCTStatus/NetworkManager.m +++ b/modules/react-native-status/ios/RCTStatus/NetworkManager.m @@ -124,4 +124,23 @@ RCT_EXPORT_METHOD(getConnectionStringForExportingKeypairsKeystores:(NSString *)c callback(@[result]); } +RCT_EXPORT_METHOD(inputConnectionStringForImportingKeypairsKeystores:(NSString *)cs + configJSON:(NSString *)configJSON + callback:(RCTResponseSenderBlock)callback) { + + NSData *configData = [configJSON dataUsingEncoding:NSUTF8StringEncoding]; + NSError *error; + NSMutableDictionary *configDict = [NSJSONSerialization JSONObjectWithData:configData options:NSJSONReadingMutableContainers error:&error]; + NSMutableDictionary *receiverConfig = configDict[@"receiverConfig"]; + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSURL *rootUrl =[[fileManager URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask] lastObject]; + NSURL *multiaccountKeystoreDir = [rootUrl URLByAppendingPathComponent:@"keystore"]; + NSString *keystoreDir = multiaccountKeystoreDir.path; + + [receiverConfig setValue:keystoreDir forKey:@"keystorePath"]; + NSString *modifiedConfigJSON = [Utils jsonStringWithPrettyPrint:NO fromDictionary:configDict]; + NSString *result = StatusgoInputConnectionStringForImportingKeypairsKeystores(cs, modifiedConfigJSON); + callback(@[result]); +} + @end diff --git a/src/native_module/core.cljs b/src/native_module/core.cljs index a0f0f93df1..ea9497723d 100644 --- a/src/native_module/core.cljs +++ b/src/native_module/core.cljs @@ -604,8 +604,27 @@ (defn get-connection-string-for-exporting-keypairs-keystores "Generates connection string form status-go for the purpose of exporting keypairs and keystores on sender side" - [config-json callback] - (log/info "[native-module] Fetching Export Keypairs Connection String" - {:fn :get-connection-string-for-exporting-keypairs-keystores - :config-json config-json}) - (.getConnectionStringForExportingKeypairsKeystores ^js (network) config-json callback)) + ([config-json] + (native-utils/promisify-native-module-call get-connection-string-for-exporting-keypairs-keystores + config-json)) + ([config-json callback] + (log/info "[native-module] Fetching Export Keypairs Connection String" + {:fn :get-connection-string-for-exporting-keypairs-keystores + :config-json config-json}) + (.getConnectionStringForExportingKeypairsKeystores ^js (network) config-json callback))) + +(defn input-connection-string-for-importing-keypairs-keystores + "Provides connection string to status-go for the purpose of importing keypairs and keystores on the receiver side" + ([connection-string config-json] + (native-utils/promisify-native-module-call input-connection-string-for-importing-keypairs-keystores + connection-string + config-json)) + ([connection-string config-json callback] + (log/info "[native-module] Sending Import Keypairs Connection String" + {:fn :input-connection-string-for-importing-keypairs-keystores + :config-json config-json + :connection-string connection-string}) + (.inputConnectionStringForImportingKeypairsKeystores ^js (network) + connection-string + config-json + callback))) diff --git a/src/quo/components/wallet/missing_keypairs/style.cljs b/src/quo/components/wallet/missing_keypairs/style.cljs index 10236e8a72..e0ae6175d3 100644 --- a/src/quo/components/wallet/missing_keypairs/style.cljs +++ b/src/quo/components/wallet/missing_keypairs/style.cljs @@ -13,7 +13,13 @@ {:top 1}) (def title-info-container - {:padding-left 8}) + {:padding-left 8 + :flex 1}) + +(def title-row + {:display :flex + :flex-direction :row + :justify-content :space-between}) (def title-container {:align-items :flex-start diff --git a/src/quo/components/wallet/missing_keypairs/view.cljs b/src/quo/components/wallet/missing_keypairs/view.cljs index bbfa655f11..0e465fdd34 100644 --- a/src/quo/components/wallet/missing_keypairs/view.cljs +++ b/src/quo/components/wallet/missing_keypairs/view.cljs @@ -1,5 +1,6 @@ (ns quo.components.wallet.missing-keypairs.view (:require + [quo.components.buttons.button.view :as button] [quo.components.icon :as icon] [quo.components.list-items.missing-keypair.view :as missing-keypair] [quo.components.markdown.text :as text] @@ -10,7 +11,7 @@ [utils.i18n :as i18n])) (defn title-view - [{:keys [keypairs blur?]}] + [{:keys [keypairs blur? on-import-press]}] (let [theme (quo.theme/use-theme)] [rn/view {:accessibility-label :title @@ -22,11 +23,18 @@ :color colors/warning-60}]] [rn/view {:style style/title-info-container} - [text/text - {:weight :medium - :style {:color colors/warning-60}} - (i18n/label :t/amount-missing-keypairs - {:amount (str (count keypairs))})] + [rn/view {:style style/title-row} + [text/text + {:weight :medium + :style {:color colors/warning-60}} + (i18n/label :t/amount-missing-keypairs + {:amount (str (count keypairs))})] + [button/button + {:type :outline + :background :blur + :size 24 + :on-press on-import-press} + (i18n/label :t/import)]] [text/text {:size :paragraph-2 :style (style/subtitle blur? theme)} diff --git a/src/status_im/contexts/settings/wallet/data_store.cljs b/src/status_im/contexts/settings/wallet/data_store.cljs new file mode 100644 index 0000000000..f24a3cdc97 --- /dev/null +++ b/src/status_im/contexts/settings/wallet/data_store.cljs @@ -0,0 +1,47 @@ +(ns status-im.contexts.settings.wallet.data-store) + +(defn extract-keypair-name + [db key-uids-set] + (when (= (count key-uids-set) 1) + (let [key-uid (first key-uids-set) + keypairs (get-in db [:wallet :keypairs])] + (->> (filter #(= (:key-uid %) key-uid) keypairs) + first + :name)))) + +(defn update-keypair + [keypairs key-uid update-fn] + (mapcat (fn [keypair] + (if (= (keypair :key-uid) key-uid) + (if-let [updated (update-fn keypair)] + [updated] + []) + [keypair])) + keypairs)) + +(defn make-accounts-fully-operable + [accounts key-uids-set] + (reduce-kv + (fn [acc k account] + (if (and (contains? key-uids-set (:key-uid account)) + (= (keyword (:operable account)) :no)) + (assoc acc k (assoc account :operable :fully)) + (assoc acc k account))) + {} + accounts)) + +(defn- make-keypairs-accounts-fully-operable + [accounts] + (map (fn [account] + (assoc account :operable :fully)) + accounts)) + +(defn make-keypairs-fully-operable + [keypairs key-uids-set] + (map (fn [keypair] + (if (contains? key-uids-set (:key-uid keypair)) + (update keypair + :accounts + make-keypairs-accounts-fully-operable) + keypair)) + keypairs)) diff --git a/src/status_im/contexts/settings/wallet/effects.cljs b/src/status_im/contexts/settings/wallet/effects.cljs new file mode 100644 index 0000000000..221feea3b4 --- /dev/null +++ b/src/status_im/contexts/settings/wallet/effects.cljs @@ -0,0 +1,38 @@ +(ns status-im.contexts.settings.wallet.effects + (:require [native-module.core :as native-module] + [promesa.core :as promesa] + [status-im.contexts.syncing.utils :as sync-utils] + [utils.re-frame :as rf] + [utils.security.core :as security] + [utils.transforms :as transforms])) + +(rf/reg-fx :effects.connection-string/export-keypair + (fn [{:keys [key-uid sha3-pwd keypair-key-uid on-success on-fail]}] + (let [config-map (transforms/clj->json {:senderConfig {:loggedInKeyUid key-uid + :keystorePath "" + :keypairsToExport [keypair-key-uid] + :password (security/safe-unmask-data + sha3-pwd)} + :serverConfig {:timeout 0}})] + (-> (native-module/get-connection-string-for-exporting-keypairs-keystores + config-map) + (promesa/then (fn [response] + (if (sync-utils/valid-connection-string? response) + (on-success response) + (on-fail (js/Error. + "generic-error: failed to get connection string"))))) + (promesa/catch on-fail))))) + +(rf/reg-fx :effects.connection-string/import-keypair + (fn [{:keys [key-uid sha3-pwd keypairs-key-uids connection-string on-success on-fail]}] + (let [config-map (transforms/clj->json {:receiverConfig + {:loggedInKeyUid key-uid + :keystorePath "" + :password (security/safe-unmask-data + sha3-pwd) + :keypairsToImport keypairs-key-uids}})] + (-> (native-module/input-connection-string-for-importing-keypairs-keystores + connection-string + config-map) + (promesa/then #(on-success keypairs-key-uids)) + (promesa/catch on-fail))))) diff --git a/src/status_im/contexts/settings/wallet/events.cljs b/src/status_im/contexts/settings/wallet/events.cljs index eedbdda354..c03e2dd40b 100644 --- a/src/status_im/contexts/settings/wallet/events.cljs +++ b/src/status_im/contexts/settings/wallet/events.cljs @@ -1,29 +1,16 @@ (ns status-im.contexts.settings.wallet.events (:require - [native-module.core :as native-module] - [status-im.contexts.syncing.utils :as sync-utils] + [status-im.contexts.settings.wallet.data-store :as data-store] [taoensso.timbre :as log] [utils.i18n :as i18n] - [utils.re-frame :as rf] - [utils.security.core :as security] - [utils.transforms :as transforms])) - -(defn- update-keypair - [keypairs key-uid update-fn] - (mapcat (fn [keypair] - (if (= (keypair :key-uid) key-uid) - (if-let [updated (update-fn keypair)] - [updated] - []) - [keypair])) - keypairs)) + [utils.re-frame :as rf])) (rf/reg-event-fx :wallet/rename-keypair-success (fn [{:keys [db]} [key-uid name]] {:db (update-in db [:wallet :keypairs] - #(update-keypair % key-uid (fn [keypair] (assoc keypair :name name)))) + #(data-store/update-keypair % key-uid (fn [keypair] (assoc keypair :name name)))) :fx [[:dispatch [:navigate-back]] [:dispatch [:toasts/upsert @@ -41,31 +28,28 @@ (rf/reg-event-fx :wallet/rename-keypair rename-keypair) -(defn get-key-pair-export-connection +(defn get-keypair-export-connection [{:keys [db]} [{:keys [sha3-pwd keypair-key-uid callback]}]] - (let [key-uid (get-in db [:profile/profile :key-uid]) - config-map (transforms/clj->json {:senderConfig {:loggedInKeyUid key-uid - :keystorePath "" - :keypairsToExport [keypair-key-uid] - :password (security/safe-unmask-data - sha3-pwd)} - :serverConfig {:timeout 0}}) - handle-connection (fn [response] - (when (sync-utils/valid-connection-string? response) - (callback response) - (rf/dispatch [:hide-bottom-sheet])))] - (native-module/get-connection-string-for-exporting-keypairs-keystores - config-map - handle-connection))) + (let [key-uid (get-in db [:profile/profile :key-uid])] + {:fx [[:effects.connection-string/export-keypair + {:key-uid key-uid + :sha3-pwd sha3-pwd + :keypair-key-uid keypair-key-uid + :on-success (fn [connect-string] + (callback connect-string) + (rf/dispatch [:hide-bottom-sheet])) + :on-fail (fn [error] + (rf/dispatch [:toasts/upsert + {:type :negative + :text (.-message error)}]))}]]})) -(rf/reg-event-fx :wallet/get-key-pair-export-connection get-key-pair-export-connection) +(rf/reg-event-fx :wallet/get-keypair-export-connection get-keypair-export-connection) -(rf/reg-event-fx - :wallet/remove-keypair-success +(rf/reg-event-fx :wallet/remove-keypair-success (fn [{:keys [db]} [key-uid]] {:db (update-in db [:wallet :keypairs] - #(update-keypair % key-uid (fn [_] nil))) + #(data-store/update-keypair % key-uid (fn [_] nil))) :fx [[:dispatch [:hide-bottom-sheet]] [:dispatch [:toasts/upsert @@ -79,6 +63,57 @@ [{:method "accounts_deleteKeypair" :params [key-uid] :on-success [:wallet/remove-keypair-success key-uid] - :on-error #(log/info "failed to remove keypair " %)}]]]}) + :on-error #(log/error "failed to remove keypair " {:error %})}]]]}) (rf/reg-event-fx :wallet/remove-keypair remove-keypair) + +(defn make-keypairs-accounts-fully-operable + [{:keys [db]} [key-uids-to-update]] + (let [key-uids-set (set key-uids-to-update) + keypair-name (data-store/extract-keypair-name db key-uids-set)] + {:db (-> db + (update-in [:wallet :accounts] #(data-store/make-accounts-fully-operable % key-uids-set)) + (update-in [:wallet :keypairs] #(data-store/make-keypairs-fully-operable % key-uids-set))) + :fx [[:dispatch + [:toasts/upsert + {:type :positive + :theme :dark + :text (if (= (count key-uids-to-update) 1) + (i18n/label :t/key-pair-imported-successfully {:name keypair-name}) + (i18n/label :t/key-pairs-successfully-imported + {:count (count key-uids-to-update)}))}]]]})) + +(rf/reg-event-fx :wallet/make-keypairs-accounts-fully-operable make-keypairs-accounts-fully-operable) + +(defn connection-string-for-import-keypair + [{:keys [db]} [{:keys [sha3-pwd keypairs-key-uids connection-string]}]] + (let [key-uid (get-in db [:profile/profile :key-uid])] + {:fx [[:effects.connection-string/import-keypair + {:key-uid key-uid + :sha3-pwd sha3-pwd + :keypairs-key-uids keypairs-key-uids + :connection-string connection-string + :on-success #(rf/dispatch [:wallet/make-keypairs-accounts-fully-operable %]) + :on-fail #(rf/dispatch [:toasts/upsert + {:type :negative + :theme :dark + :text %}])}]]})) + +(rf/reg-event-fx :wallet/connection-string-for-import-keypair connection-string-for-import-keypair) + +(defn success-keypair-qr-scan + [_ [connection-string keypairs-key-uids]] + {:fx [[:dispatch + [:standard-auth/authorize-with-password + {:blur? true + :theme :dark + :auth-button-label (i18n/label :t/confirm) + :on-auth-success (fn [password] + (rf/dispatch [:hide-bottom-sheet]) + (rf/dispatch + [:wallet/connection-string-for-import-keypair + {:connection-string connection-string + :keypairs-key-uids keypairs-key-uids + :sha3-pwd password}]))}]]]}) + +(rf/reg-event-fx :wallet/success-keypair-qr-scan success-keypair-qr-scan) diff --git a/src/status_im/contexts/settings/wallet/events_test.cljs b/src/status_im/contexts/settings/wallet/events_test.cljs index c37b7452bc..f74b20e7c2 100644 --- a/src/status_im/contexts/settings/wallet/events_test.cljs +++ b/src/status_im/contexts/settings/wallet/events_test.cljs @@ -1,20 +1,97 @@ (ns status-im.contexts.settings.wallet.events-test (:require - [cljs.test :refer-macros [deftest is]] + [cljs.test :refer-macros [deftest is testing]] matcher-combinators.test [status-im.contexts.settings.wallet.events :as sut])) -(def key-uid "0xfef454bb492ee4677594f8e05921c84f336fa811deb99b8d922477cc87a38b98") +(def mock-key-uid "key-1") +(defn mock-db + [keypairs accounts] + {:wallet {:keypairs keypairs + :accounts accounts} + :profile/profile {:key-uid "test-key-uid"}}) (deftest rename-keypair-test (let [new-keypair-name "key pair new" - cofx {:db {}} - expected {:fx [[:json-rpc/call - [{:method "accounts_updateKeypairName" - :params [key-uid new-keypair-name] - :on-success [:wallet/rename-keypair-success key-uid new-keypair-name] - :on-error fn?}]]]}] - (is (match? expected - (sut/rename-keypair cofx - [{:key-uid key-uid - :keypair-name new-keypair-name}]))))) + cofx {:db {}}] + (testing "rename-keypair" + (let [expected {:fx [[:json-rpc/call + [{:method "accounts_updateKeypairName" + :params [mock-key-uid new-keypair-name] + :on-success [:wallet/rename-keypair-success mock-key-uid + new-keypair-name] + :on-error fn?}]]]}] + (is (match? expected + (sut/rename-keypair cofx + [{:key-uid mock-key-uid + :keypair-name new-keypair-name}]))))))) + +(deftest get-keypair-export-connection-test + (let [cofx {:db (mock-db [] {})} + sha3-pwd "test-password" + user-key-uid "test-key-uid" + callback (fn [connect-string] (println "callback" connect-string))] + (testing "get-keypair-export-connection" + (let [expected {:fx [[:effects.connection-string/export-keypair + {:key-uid user-key-uid + :sha3-pwd sha3-pwd + :keypair-key-uid mock-key-uid + :on-success fn? + :on-fail fn?}]]}] + (is (match? expected + (sut/get-keypair-export-connection + cofx + [{:sha3-pwd sha3-pwd :keypair-key-uid mock-key-uid :callback callback}]))))))) + +(deftest remove-keypair-test + (let [cofx {:db {}}] + (testing "remove-keypair" + (let [expected {:fx [[:json-rpc/call + [{:method "accounts_deleteKeypair" + :params [mock-key-uid] + :on-success [:wallet/remove-keypair-success mock-key-uid] + :on-error fn?}]]]}] + (is (match? expected + (sut/remove-keypair cofx [mock-key-uid]))))))) + +(deftest make-keypairs-accounts-fully-operable-test + (let [db (mock-db [{:key-uid mock-key-uid + :accounts [{:key-uid mock-key-uid :operable "no"}]}] + {"0x1" {:key-uid mock-key-uid :operable "no"}}) + key-uids-to-update [mock-key-uid]] + (testing "make-keypairs-accounts-fully-operable" + (let [effects (sut/make-keypairs-accounts-fully-operable {:db db} [key-uids-to-update]) + result-db (:db effects) + updated-keypair (some #(when (= (:key-uid %) mock-key-uid) %) + (get-in result-db [:wallet :keypairs])) + updated-account (get-in result-db [:wallet :accounts "0x1"])] + (is (= (keyword (-> updated-keypair :accounts first :operable)) :fully)) + (is (= (keyword (:operable updated-account)) :fully)))))) + +(deftest connection-string-for-import-keypair-test + (let [cofx {:db (mock-db [] {})} + sha3-pwd "test-password" + user-key-uid "test-key-uid" + connection-string "test-connection-string"] + (testing "connection-string-for-import-keypair" + (let [expected {:fx [[:effects.connection-string/import-keypair + {:key-uid user-key-uid + :sha3-pwd sha3-pwd + :keypairs-key-uids [mock-key-uid] + :connection-string connection-string + :on-success fn? + :on-fail fn?}]]}] + (is (match? expected + (sut/connection-string-for-import-keypair cofx + [{:sha3-pwd sha3-pwd + :keypairs-key-uids [mock-key-uid] + :connection-string + connection-string}]))))))) + +(deftest success-keypair-qr-scan-test + (let [connection-string "valid-connection-string" + keypairs-key-uids ["keypair-uid"]] + (testing "success-keypair-qr-scan" + (let [effects (sut/success-keypair-qr-scan nil [connection-string keypairs-key-uids]) + fx (:fx effects)] + (is (some? fx)))))) diff --git a/src/status_im/contexts/settings/wallet/keypairs_and_accounts/encrypted_qr/view.cljs b/src/status_im/contexts/settings/wallet/keypairs_and_accounts/encrypted_qr/view.cljs index a54d4ff340..7e28c4b390 100644 --- a/src/status_im/contexts/settings/wallet/keypairs_and_accounts/encrypted_qr/view.cljs +++ b/src/status_im/contexts/settings/wallet/keypairs_and_accounts/encrypted_qr/view.cljs @@ -28,7 +28,7 @@ cleanup-clock (rn/use-callback #(set-code nil)) on-auth-success (rn/use-callback (fn [entered-password] (rf/dispatch - [:wallet/get-key-pair-export-connection + [:wallet/get-keypair-export-connection {:sha3-pwd entered-password :keypair-key-uid key-uid :callback validate-and-set-code}])) diff --git a/src/status_im/contexts/settings/wallet/keypairs_and_accounts/scan_qr/view.cljs b/src/status_im/contexts/settings/wallet/keypairs_and_accounts/scan_qr/view.cljs new file mode 100644 index 0000000000..d93b4541be --- /dev/null +++ b/src/status_im/contexts/settings/wallet/keypairs_and_accounts/scan_qr/view.cljs @@ -0,0 +1,23 @@ +(ns status-im.contexts.settings.wallet.keypairs-and-accounts.scan-qr.view + (:require + [react-native.core :as rn] + [status-im.common.scan-qr-code.view :as scan-qr-code] + [status-im.contexts.communities.events] + [status-im.contexts.syncing.utils :as sync-utils] + [utils.i18n :as i18n] + [utils.re-frame :as rf])) + +(defn view + [] + (let [keypairs-key-uids (rf/sub [:get-screen-params]) + on-success-scan (rn/use-callback (fn [scanned-text] + (rf/dispatch [:wallet/success-keypair-qr-scan scanned-text + keypairs-key-uids]) + [keypairs-key-uids]))] + [scan-qr-code/view + {:title (i18n/label :t/scan-key-pairs-qr-code) + :subtitle (i18n/label :t/find-it-in-setting) + :share-button? false + :validate-fn sync-utils/valid-connection-string? + :error-message (i18n/label :t/invalid-qr) + :on-success-scan on-success-scan}])) diff --git a/src/status_im/contexts/settings/wallet/keypairs_and_accounts/view.cljs b/src/status_im/contexts/settings/wallet/keypairs_and_accounts/view.cljs index 0a13a4b5e3..4f03f0aae7 100644 --- a/src/status_im/contexts/settings/wallet/keypairs_and_accounts/view.cljs +++ b/src/status_im/contexts/settings/wallet/keypairs_and_accounts/view.cljs @@ -93,7 +93,11 @@ profile-picture (rf/sub [:profile/image]) customization-color (rf/sub [:profile/customization-color]) {missing-keypairs :missing - operable-keypairs :operable} (rf/sub [:wallet/settings-keypairs-accounts])] + operable-keypairs :operable} (rf/sub [:wallet/settings-keypairs-accounts]) + on-import-press (rn/use-callback #(rf/dispatch [:open-modal + :screen/settings.scan-keypair-qr + (map :key-uid missing-keypairs)]) + [missing-keypairs])] [quo/overlay {:type :shell :container-style (style/page-wrapper (:top insets))} @@ -112,6 +116,7 @@ [quo/missing-keypairs {:blur? true :keypairs missing-keypairs + :on-import-press on-import-press :container-style style/missing-keypairs-container-style :on-options-press on-missing-keypair-options-press}]) [rn/flat-list diff --git a/src/status_im/contexts/syncing/events.cljs b/src/status_im/contexts/syncing/events.cljs index 5a9808e6b2..9dd40b3691 100644 --- a/src/status_im/contexts/syncing/events.cljs +++ b/src/status_im/contexts/syncing/events.cljs @@ -41,19 +41,12 @@ :VerifyTransactionChainID config/verify-transaction-chain-id}} log-config))) -(defn- extract-error - [json-str] - (-> json-str - transforms/json->clj - (get :error "") - not-empty)) - (defn- input-connection-string-callback [res] (log/info "[local-pairing] input-connection-string-for-bootstrapping callback" {:response res :event :syncing/input-connection-string-for-bootstrapping}) - (let [error (when (extract-error res) + (let [error (when (sync-utils/extract-error res) (str "generic-error: " res))] (when (some? error) (rf/dispatch [:toasts/upsert diff --git a/src/status_im/contexts/syncing/utils.cljs b/src/status_im/contexts/syncing/utils.cljs index 8805f5b325..f906ee02d9 100644 --- a/src/status_im/contexts/syncing/utils.cljs +++ b/src/status_im/contexts/syncing/utils.cljs @@ -1,7 +1,8 @@ (ns status-im.contexts.syncing.utils (:require [clojure.string :as string] - [status-im.constants :as constants])) + [status-im.constants :as constants] + [utils.transforms :as transforms])) (defn valid-connection-string? [connection-string] @@ -9,3 +10,10 @@ (string/starts-with? connection-string constants/local-pairing-connection-string-identifier))) + +(defn extract-error + [json-str] + (-> json-str + transforms/json->clj + (get :error "") + not-empty)) diff --git a/src/status_im/contexts/wallet/events.cljs b/src/status_im/contexts/wallet/events.cljs index c22b95c556..5ca07040eb 100644 --- a/src/status_im/contexts/wallet/events.cljs +++ b/src/status_im/contexts/wallet/events.cljs @@ -4,6 +4,7 @@ [clojure.string :as string] [react-native.platform :as platform] [status-im.constants :as constants] + [status-im.contexts.settings.wallet.effects] [status-im.contexts.settings.wallet.events] [status-im.contexts.wallet.common.utils.networks :as network-utils] [status-im.contexts.wallet.data-store :as data-store] diff --git a/src/status_im/navigation/screens.cljs b/src/status_im/navigation/screens.cljs index dd9cc8d479..8814546364 100644 --- a/src/status_im/navigation/screens.cljs +++ b/src/status_im/navigation/screens.cljs @@ -60,6 +60,7 @@ [status-im.contexts.settings.wallet.keypairs-and-accounts.encrypted-qr.view :as encrypted-key-pair-qr] [status-im.contexts.settings.wallet.keypairs-and-accounts.rename.view :as keypair-rename] + [status-im.contexts.settings.wallet.keypairs-and-accounts.scan-qr.view :as scan-keypair-qr] [status-im.contexts.settings.wallet.keypairs-and-accounts.view :as keypairs-and-accounts] [status-im.contexts.settings.wallet.network-settings.view :as network-settings] [status-im.contexts.settings.wallet.saved-addresses.view :as saved-addresses-settings] @@ -524,6 +525,10 @@ :options options/transparent-modal-screen-options :component keypairs-and-accounts/view} + {:name :screen/settings.scan-keypair-qr + :options options/transparent-modal-screen-options + :component scan-keypair-qr/view} + {:name :screen/settings.network-settings :options options/transparent-modal-screen-options :component network-settings/view} diff --git a/translations/en.json b/translations/en.json index 9c3c6ab0eb..560f6c0c0c 100644 --- a/translations/en.json +++ b/translations/en.json @@ -1292,6 +1292,7 @@ "scan-qr": "Scan QR", "scan-qr-code": "Scan QR code", "scan-with-status-app": "Scan with the Status app on another device", + "scan-key-pairs-qr-code": "Scan key pairs QR code", "invalid-qr": "Oops! This QR doesn’t work with Status", "search": "Search", "search-discover-communities": "Search communities or categories", @@ -2213,6 +2214,7 @@ "create-new-profile": "Create new profile", "add-existing-status-profile": "Add existing Status profile", "find-sync-code": "Find sync code", + "find-it-in-setting": "Find it in Settings on your other synced device", "sign-in-by-syncing": "Sign in by syncing", "synchronise-your-data-across-your-devices": "Synchronise your data across your devices", "scan-sync-qr-code": "Scan QR code", @@ -2452,6 +2454,8 @@ "no-other-accounts": "No other accounts", "here-is-a-cat-in-a-box-instead": "Here’s a cat in a box instead", "accounts-count": "{{count}} accounts", + "key-pairs-successfully-imported": "{{count}} key pairs successfully imported", + "key-pair-imported-successfully": "{{name}} key pair imported successfully", "n-m-people": "{{n}}/{{m}} people", "enter-eth": "Enter any ETH address or ENS name.", "eth-or-ens": "ETH address or ENS name.",