diff --git a/src/native_module/core.cljs b/src/native_module/core.cljs index 7785d22400f0..ea9497723df7 100644 --- a/src/native_module/core.cljs +++ b/src/native_module/core.cljs @@ -604,20 +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 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)) + ([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/view.cljs b/src/quo/components/wallet/missing_keypairs/view.cljs index 63f75047868c..0e465fdd3443 100644 --- a/src/quo/components/wallet/missing_keypairs/view.cljs +++ b/src/quo/components/wallet/missing_keypairs/view.cljs @@ -11,7 +11,7 @@ [utils.i18n :as i18n])) (defn title-view - [{:keys [keypairs blur? on-import-click]}] + [{:keys [keypairs blur? on-import-press]}] (let [theme (quo.theme/use-theme)] [rn/view {:accessibility-label :title @@ -33,7 +33,7 @@ {:type :outline :background :blur :size 24 - :on-press on-import-click} + :on-press on-import-press} (i18n/label :t/import)]] [text/text {:size :paragraph-2 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 000000000000..f24a3cdc9750 --- /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/events.cljs b/src/status_im/contexts/settings/wallet/events.cljs index 8a7222d3bdcd..97adb73d105d 100644 --- a/src/status_im/contexts/settings/wallet/events.cljs +++ b/src/status_im/contexts/settings/wallet/events.cljs @@ -1,6 +1,8 @@ (ns status-im.contexts.settings.wallet.events (:require [native-module.core :as native-module] + [promesa.core :as promesa] + [status-im.contexts.settings.wallet.data-store :as data-store] [status-im.contexts.syncing.events :as syncing-events] [status-im.contexts.syncing.utils :as sync-utils] [taoensso.timbre :as log] @@ -9,22 +11,12 @@ [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)) - (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 @@ -42,31 +34,44 @@ (rf/reg-event-fx :wallet/rename-keypair rename-keypair) -(defn get-key-pair-export-connection +(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 "generic-error: failed to get connection string")))) + (promesa/catch on-fail))))) + +(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))) - -(rf/reg-event-fx :wallet/get-key-pair-export-connection get-key-pair-export-connection) - -(rf/reg-event-fx - :wallet/remove-keypair-success + (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 error}]))}]]})) + +(rf/reg-event-fx :wallet/get-keypair-export-connection get-keypair-export-connection) + +(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 @@ -84,85 +89,80 @@ (rf/reg-event-fx :wallet/remove-keypair remove-keypair) -(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-accounts - [accounts key-uids-set] - (into {} - (map (fn [[k account]] - (if (and (contains? key-uids-set (:key-uid account)) - (= (:operable account) :no)) - [k (assoc account :operable :fully)] - [k account])) - accounts))) - -(defn update-keypairs - [keypairs key-uids-set] - (map (fn [keypair] - (if (contains? key-uids-set (:key-uid keypair)) - (update keypair - :accounts - (fn [accounts] - (map (fn [account] - (if (and (contains? key-uids-set (:key-uid account)) - (= (:operable account) ":no")) - (assoc account :operable ":fully") - account)) - accounts))) - keypair)) - keypairs)) - -(rf/reg-event-fx :wallet/make-key-pairs-fully-operable - (fn [{:keys [db]} [key-uids-to-update]] - (let [key-uids-set (set key-uids-to-update) - keypair-name (extract-keypair-name db key-uids-set)] - {:db (-> db - (update-in [:wallet :accounts] update-accounts key-uids-set) - (update-in [:wallet :keypairs] update-keypairs 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)}))}]]]}))) - -(defn- input-connection-string-for-importing-keypairs-keystores-callback - [res keypairs-key-uids] - (log/info "[local-pairing] input-connection-string-for-importing-keypairs-keystores callback" - {:response res - :event :settings/input-connection-string-for-importing-keypairs-keystores-callback}) - (let [error (when (syncing-events/extract-error res) - (str "generic-error: " res))] - (when-not (some? error) - (rf/dispatch [:wallet/make-key-pairs-fully-operable keypairs-key-uids])) - (when (some? error) - (rf/dispatch [:toasts/upsert - {:type :negative - :text error}])))) - -(defn connection-string-for-key-pair-import +(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) + +(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 (.stringify js/JSON + (clj->js + {: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 (fn [res] + (let [error (when (syncing-events/extract-error res) + (str "generic-error: " res))] + (if-not (some? error) + (on-success) + (on-fail error))))) + (promesa/catch #(log/error "error import-keypair/connection-string " %)))))) + +(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]) - config-map (.stringify js/JSON - (clj->js - {: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 - #(input-connection-string-for-importing-keypairs-keystores-callback % keypairs-key-uids)))) - -(rf/reg-event-fx :wallet/connection-string-for-key-pair-import connection-string-for-key-pair-import) + (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 + keypairs-key-uids]) + :on-fail #(rf/dispatch [:toasts/upsert + {:type :negative + :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 [(if (sync-utils/valid-connection-string? connection-string) + [: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}]))}]] + [:dispatch + [:toasts/upsert + {:type :negative + :theme :dark + :text (i18n/label :t/invalid-qr)}]])]}) + +(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 c37b7452bcd4..62793973f36d 100644 --- a/src/status_im/contexts/settings/wallet/events_test.cljs +++ b/src/status_im/contexts/settings/wallet/events_test.cljs @@ -1,20 +1,81 @@ (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 +(deftest test-rename-keypair (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] + :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 key-uid + [{:key-uid mock-key-uid :keypair-name new-keypair-name}]))))) + +(deftest test-get-keypair-export-connection + (let [db (mock-db [] {}) + sha3-pwd "test-password" + keypair-key-uid "test-keypair-uid" + callback (fn [connect-string] (println "callback" connect-string))] + (testing "test-get-keypair-export-connection" + (let [effects (sut/get-keypair-export-connection + {:db db} + [{:sha3-pwd sha3-pwd :keypair-key-uid keypair-key-uid :callback callback}]) + fx (:fx effects)] + (is (some? fx)))))) + +(deftest test-remove-keypair + (let [db (mock-db [{:key-uid mock-key-uid :name "Key 1"}] {})] + (testing "remove-keypair" + (let [effects (sut/remove-keypair {:db db} [{:key-uid mock-key-uid}]) + fx (:fx effects)] + (is (some? fx)))))) + +(deftest test-make-keypairs-accounts-fully-operable + (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 test-connection-string-for-import-keypair + (let [db (mock-db [] {}) + sha3-pwd "test-password" + keypairs-key-uids ["test-keypair-uid"] + connection-string "test-connection-string"] + (testing "connection-string-for-import-keypair" + (let [effects (sut/connection-string-for-import-keypair + {:db db} + [{:sha3-pwd sha3-pwd + :keypairs-key-uids keypairs-key-uids + :connection-string connection-string}]) + fx (:fx effects)] + (is (some? fx)))))) + +(deftest test-success-keypair-qr-scan + (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 a54d4ff3406f..7e28c4b390de 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 index 187f66036d17..3038ae968afa 100644 --- 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 @@ -3,45 +3,16 @@ [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.debounce :as debounce] [utils.i18n :as i18n] [utils.re-frame :as rf])) -(defn show-invalid-qr-toast - [] - (debounce/debounce-and-dispatch - [:toasts/upsert - {:type :negative - :theme :dark - :text (i18n/label :t/invalid-qr)}] - 300)) - -(defn- on-valid-connection-received - [connection-string keypairs-key-uids] - (rf/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-key-pair-import - {:connection-string connection-string - :keypairs-key-uids keypairs-key-uids - :sha3-pwd password}]))}])) - (defn view [] - (let [keypairs (rf/sub [:get-screen-params]) - keypairs-key-uids (rn/use-memo #(map :key-uid keypairs) [keypairs]) + (let [keypairs-key-uids (rf/sub [:get-screen-params]) on-success-scan (rn/use-callback (fn [scanned-text] - (if (sync-utils/valid-connection-string? scanned-text) - (on-valid-connection-received scanned-text - keypairs-key-uids) - (show-invalid-qr-toast))) - [keypairs-key-uids])] + (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) 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 5a8d6c95ff89..4f03f0aae75b 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 @@ -94,9 +94,9 @@ customization-color (rf/sub [:profile/customization-color]) {missing-keypairs :missing operable-keypairs :operable} (rf/sub [:wallet/settings-keypairs-accounts]) - on-import-click (rn/use-callback #(rf/dispatch [:open-modal - :screen/settings.scan-key-pair-qr - missing-keypairs]) + 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 @@ -116,7 +116,7 @@ [quo/missing-keypairs {:blur? true :keypairs missing-keypairs - :on-import-click on-import-click + :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/navigation/screens.cljs b/src/status_im/navigation/screens.cljs index 360383a43839..88145463646b 100644 --- a/src/status_im/navigation/screens.cljs +++ b/src/status_im/navigation/screens.cljs @@ -60,7 +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-key-pair-qr] + [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] @@ -525,9 +525,9 @@ :options options/transparent-modal-screen-options :component keypairs-and-accounts/view} - {:name :screen/settings.scan-key-pair-qr + {:name :screen/settings.scan-keypair-qr :options options/transparent-modal-screen-options - :component scan-key-pair-qr/view} + :component scan-keypair-qr/view} {:name :screen/settings.network-settings :options options/transparent-modal-screen-options