From 2618a55dbcbe394f278df93cba976d43702e2b03 Mon Sep 17 00:00:00 2001 From: Nikolay Date: Mon, 13 May 2024 12:39:42 +0300 Subject: [PATCH] [#18817] Import private key: UI for key pair name (#19747) --- .../standard_title/style.cljs | 1 + .../add_account/create_account/events.cljs | 15 +++ .../import_private_key/view.cljs | 109 +++++++++++------- .../create_account/key_pair_name/style.cljs | 12 ++ .../create_account/key_pair_name/view.cljs | 94 +++++++++++++++ .../new_keypair/check_your_backup/view.cljs | 3 +- .../new_keypair/keypair_name/style.cljs | 13 --- .../new_keypair/keypair_name/view.cljs | 75 ------------ src/status_im/navigation/screens.cljs | 8 +- src/status_im/subs/wallet/wallet.cljs | 16 ++- translations/en.json | 1 + 11 files changed, 209 insertions(+), 138 deletions(-) create mode 100644 src/status_im/contexts/wallet/add_account/create_account/key_pair_name/style.cljs create mode 100644 src/status_im/contexts/wallet/add_account/create_account/key_pair_name/view.cljs delete mode 100644 src/status_im/contexts/wallet/add_account/create_account/new_keypair/keypair_name/style.cljs delete mode 100644 src/status_im/contexts/wallet/add_account/create_account/new_keypair/keypair_name/view.cljs diff --git a/src/quo/components/text_combinations/standard_title/style.cljs b/src/quo/components/text_combinations/standard_title/style.cljs index 2f4f9c7a52..f4b6364fc8 100644 --- a/src/quo/components/text_combinations/standard_title/style.cljs +++ b/src/quo/components/text_combinations/standard_title/style.cljs @@ -3,6 +3,7 @@ (def container {:flex-direction :row + :flex 1 :justify-content :space-between}) (def right-counter diff --git a/src/status_im/contexts/wallet/add_account/create_account/events.cljs b/src/status_im/contexts/wallet/add_account/create_account/events.cljs index 9c25e72aec..a6ce746868 100644 --- a/src/status_im/contexts/wallet/add_account/create_account/events.cljs +++ b/src/status_im/contexts/wallet/add_account/create_account/events.cljs @@ -90,3 +90,18 @@ (cske/transform-keys transforms/->kebab-case-keyword derived-address)))})) (rf/reg-event-fx :wallet/get-derived-addresses-success get-derived-addresses-success) + +(rf/reg-event-fx + :wallet/set-private-key + (fn [{:keys [db]} [value]] + {:db (assoc-in db [:wallet :ui :create-account :private-key] (security/mask-data value))})) + +(rf/reg-event-fx + :wallet/set-public-address + (fn [{:keys [db]} [value]] + {:db (assoc-in db [:wallet :ui :create-account :public-address] value)})) + +(rf/reg-event-fx + :wallet/clear-private-key-data + (fn [{:keys [db]} _] + {:db (update-in db [:wallet :ui :create-account] dissoc :private-key :public-address)})) diff --git a/src/status_im/contexts/wallet/add_account/create_account/import_private_key/view.cljs b/src/status_im/contexts/wallet/add_account/create_account/import_private_key/view.cljs index dca9fec6df..e7f14a2e5f 100644 --- a/src/status_im/contexts/wallet/add_account/create_account/import_private_key/view.cljs +++ b/src/status_im/contexts/wallet/add_account/create_account/import_private_key/view.cljs @@ -1,5 +1,6 @@ (ns status-im.contexts.wallet.add-account.create-account.import-private-key.view (:require + [clojure.string :as string] [quo.core :as quo] [quo.theme :as theme] [react-native.clipboard :as clipboard] @@ -12,45 +13,50 @@ [utils.re-frame :as rf])) (defn- address-input - [{:keys [input-value set-input-value set-flow-state error?]}] - (let [check-address (rn/use-callback - (debounce/debounce (fn [v] - (if (empty? v) - (set-flow-state nil) - ;; TODO check for validation - (if-not (v/private-key? v) - (set-flow-state :invalid-private-key) - ;; TODO add real requests - (do - (set-flow-state :scanning) - (js/setTimeout - set-flow-state - 400 - (rand-nth [:active-address :inactive-address])))))) - 500)) - on-change (rn/use-callback - (fn [v] - (set-input-value v) - (check-address v))) - on-paste (rn/use-callback - (fn [] - (clipboard/get-string - (fn [clipboard] - (when-not (empty? clipboard) - (on-change clipboard))))))] + [{:keys [input-value set-flow-state error?]}] + (let [check-address + (rn/use-callback + (debounce/debounce + (fn [v] + (if (empty? v) + (set-flow-state nil) + ;; TODO check for validation + (if-not (v/private-key? v) + (set-flow-state :invalid-private-key) + ;; TODO add real requests + (do + (set-flow-state :scanning) + ;; TODO get real address + ;; Should be fixed in #18819 + (rf/dispatch [:wallet/set-public-address + "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"]) + (js/setTimeout set-flow-state 400 (rand-nth [:active-address :inactive-address])))))) + 500)) + + on-change (rn/use-callback + (fn [v] + (rf/dispatch [:wallet/set-private-key v]) + (check-address v))) + on-paste (rn/use-callback + (fn [] + (clipboard/get-string + (fn [clipboard] + (when-not (empty? clipboard) + (on-change clipboard))))))] [quo/input - {:accessibility-label :add-address-to-watch + {:accessibility-label :import-private-key :placeholder (i18n/label :t/enter-private-key-placeholder) :container-style style/input :label (i18n/label :t/private-key) :type :password :error? error? :return-key-type :done + :auto-focus true :on-change-text on-change :button (when (empty? input-value) {:on-press on-paste :text (i18n/label :t/paste)}) - :value input-value}])) + :default-value input-value}])) (defn- activity-indicator [state] @@ -84,13 +90,29 @@ :style style/indicator) (i18n/label message)]))) +(defn on-unmount + [] + (rf/dispatch [:wallet/clear-private-key-data])) + +(defn navigate-back + [] + (rf/dispatch [:navigate-back])) + +(defn navigate-to-keypair-import + [] + (rf/dispatch [:navigate-to + :screen/wallet.keypair-name + {:workflow :import-private-key}])) + (defn view [] - (let [theme (theme/use-theme) - customization-color (rf/sub [:profile/customization-color]) - [input-value set-input-value] (rn/use-state "") - [flow-state set-flow-state] (rn/use-state) - error? (= :invalid-private-key flow-state)] + (let [theme (theme/use-theme) + customization-color (rf/sub [:profile/customization-color]) + private-key (rf/sub [:wallet/import-private-key]) + public-address (rf/sub [:wallet/public-address]) + [flow-state set-flow-state] (rn/use-state nil) + error? (= :invalid-private-key flow-state)] + (rn/use-unmount on-unmount) [rn/view {:flex 1} [floating-button-page/view {:customization-color customization-color @@ -98,7 +120,7 @@ {:background :white :type :no-title :icon-name :i/close - :on-press #(rf/dispatch [:navigate-back])}] + :on-press navigate-back}] :footer [:<> (when-not (#{:active-address :inactive-address} flow-state) [quo/information-box @@ -108,8 +130,8 @@ (i18n/label :t/import-private-key-info)]) [quo/button {:customization-color customization-color - :disabled? (or (empty? input-value) error?) - :on-press #()} + :disabled? (or (string/blank? private-key) error?) + :on-press navigate-to-keypair-import} (i18n/label :t/continue)]]} [quo/page-top {:container-style style/page-top @@ -117,17 +139,16 @@ :description :text :description-text (i18n/label :t/enter-private-key)}] [address-input - {:input-value input-value - :set-input-value set-input-value - :set-flow-state set-flow-state - :error? error?}] + {:input-value private-key + :set-flow-state set-flow-state + :error? error?}] (when (#{:scanning :active-address :inactive-address} flow-state) [rn/view {:style style/key-section} [quo/section-label {:section (i18n/label :t/private-key-public-address) :container-style style/section-label}] - ;; TODO Get real address - [rn/view {:style (style/public-address flow-state theme)} - [quo/text "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"]]]) - (when (seq input-value) + (when (seq public-address) + [rn/view {:style (style/public-address flow-state theme)} + [quo/text public-address]])]) + (when (seq private-key) [activity-indicator flow-state])]])) diff --git a/src/status_im/contexts/wallet/add_account/create_account/key_pair_name/style.cljs b/src/status_im/contexts/wallet/add_account/create_account/key_pair_name/style.cljs new file mode 100644 index 0000000000..c880a1ee1e --- /dev/null +++ b/src/status_im/contexts/wallet/add_account/create_account/key_pair_name/style.cljs @@ -0,0 +1,12 @@ +(ns status-im.contexts.wallet.add-account.create-account.key-pair-name.style) + +(def page-top + {:margin-top 2}) + +(def input + {:padding-top 12 + :padding-horizontal 20}) + +(def error-container + {:padding-horizontal 20 + :padding-vertical 8}) diff --git a/src/status_im/contexts/wallet/add_account/create_account/key_pair_name/view.cljs b/src/status_im/contexts/wallet/add_account/create_account/key_pair_name/view.cljs new file mode 100644 index 0000000000..a2c3cf1137 --- /dev/null +++ b/src/status_im/contexts/wallet/add_account/create_account/key_pair_name/view.cljs @@ -0,0 +1,94 @@ +(ns status-im.contexts.wallet.add-account.create-account.key-pair-name.view + (:require + [clojure.string :as string] + [quo.core :as quo] + [react-native.core :as rn] + [status-im.common.floating-button-page.view :as floating-button-page] + [status-im.common.not-implemented :as not-implemented] + [status-im.common.validation.general :as validators] + [status-im.contexts.wallet.add-account.create-account.key-pair-name.style :as style] + [utils.i18n :as i18n] + [utils.re-frame :as rf])) + +(def ^:private key-pair-name-max-length 15) +(def ^:private key-pair-name-min-length 5) + +(def error-messages + {:too-long (i18n/label :t/key-name-error-length) + :too-short (i18n/label :t/key-name-error-too-short {:count key-pair-name-min-length}) + :emoji (i18n/label :t/key-name-error-emoji) + :special-char (i18n/label :t/key-name-error-special-char)}) + +(defn navigate-back + [] + (rf/dispatch [:navigate-back])) + +(defn view + [] + (let [{:keys [workflow]} (rf/sub [:get-screen-params]) + customization-color (rf/sub [:profile/customization-color]) + [key-pair-name set-key-pair-name] (rn/use-state "") + [error set-error] (rn/use-state nil) + on-change-text (rn/use-callback + (fn [value] + (set-key-pair-name value) + (cond + (> (count value) key-pair-name-max-length) + (set-error :too-long) + + (< 0 (count value) key-pair-name-min-length) + (set-error :too-short) + + (validators/has-emojis? value) + (set-error :emoji) + + (validators/has-special-characters? value) + (set-error :special-char) + + :else (set-error nil)))) + on-continue (rn/use-callback + (fn [_] + (case workflow + ;; TODO issue #19759. Implement creation account from + ;; private key + :import-private-key + (not-implemented/alert) + + :new-key-pair + (rf/dispatch [:wallet/new-keypair-continue + {:keypair-name key-pair-name}]) + (js/alert "Unknown workflow"))) + [workflow key-pair-name]) + disabled? (or (some? error) (string/blank? key-pair-name))] + [rn/view {:style {:flex 1}} + [floating-button-page/view + {:customization-color customization-color + :header [quo/page-nav + {:icon-name :i/arrow-left + :on-press navigate-back + :accessibility-label :top-bar}] + :footer [quo/button + {:customization-color customization-color + :disabled? disabled? + :on-press on-continue} + (i18n/label :t/continue)]} + [quo/page-top + {:title (i18n/label :t/keypair-name) + :description-text (i18n/label :t/keypair-name-description) + :description :text + :container-style style/page-top}] + [quo/input + {:container-style style/input + :placeholder (i18n/label :t/keypair-name-input-placeholder) + :label (i18n/label :t/keypair-name) + :char-limit key-pair-name-max-length + :auto-focus true + :on-change-text on-change-text + :error error}] + (when error + [quo/info-message + {:type :error + :size :default + :icon :i/info + :container-style style/error-container} + (get error-messages error)])]])) diff --git a/src/status_im/contexts/wallet/add_account/create_account/new_keypair/check_your_backup/view.cljs b/src/status_im/contexts/wallet/add_account/create_account/new_keypair/check_your_backup/view.cljs index be3c382074..a7a5b47e06 100644 --- a/src/status_im/contexts/wallet/add_account/create_account/new_keypair/check_your_backup/view.cljs +++ b/src/status_im/contexts/wallet/add_account/create_account/new_keypair/check_your_backup/view.cljs @@ -81,7 +81,8 @@ (reset! show-error? false) (when (= @quiz-index questions-count) (rf/dispatch [:navigate-to - :screen/wallet.keypair-name]))) + :screen/wallet.keypair-name + {:workflow :new-key-pair}]))) (do (when (> @incorrect-count 0) (rf/dispatch [:show-bottom-sheet diff --git a/src/status_im/contexts/wallet/add_account/create_account/new_keypair/keypair_name/style.cljs b/src/status_im/contexts/wallet/add_account/create_account/new_keypair/keypair_name/style.cljs deleted file mode 100644 index e59b907d25..0000000000 --- a/src/status_im/contexts/wallet/add_account/create_account/new_keypair/keypair_name/style.cljs +++ /dev/null @@ -1,13 +0,0 @@ -(ns status-im.contexts.wallet.add-account.create-account.new-keypair.keypair-name.style) - -(def header-container - {:margin-horizontal 20 - :margin-top 12 - :margin-bottom 20}) - -(def bottom-action - {:margin-horizontal -20}) - -(def error-container - {:margin-left 20 - :margin-vertical 8}) diff --git a/src/status_im/contexts/wallet/add_account/create_account/new_keypair/keypair_name/view.cljs b/src/status_im/contexts/wallet/add_account/create_account/new_keypair/keypair_name/view.cljs deleted file mode 100644 index 6b48ce3567..0000000000 --- a/src/status_im/contexts/wallet/add_account/create_account/new_keypair/keypair_name/view.cljs +++ /dev/null @@ -1,75 +0,0 @@ -(ns status-im.contexts.wallet.add-account.create-account.new-keypair.keypair-name.view - (:require - [quo.core :as quo] - [react-native.core :as rn] - [status-im.common.floating-button-page.view :as floating-button-page] - [status-im.common.validation.general :as validators] - [status-im.contexts.wallet.add-account.create-account.new-keypair.keypair-name.style :as style] - [utils.i18n :as i18n] - [utils.re-frame :as rf])) - -(def keypair-name-max-length 15) -(def keypair-name-min-length 5) - -(def error-messages - {:length (i18n/label :t/key-name-error-length) - :emoji (i18n/label :t/key-name-error-emoji) - :special-char (i18n/label :t/key-name-error-special-char)}) - -(defn navigate-back [] (rf/dispatch [:navigate-back])) - -(defn view - [] - (let [[keypair-name set-keypair-name] (rn/use-state "") - customization-color (rf/sub [:profile/customization-color]) - [error set-error] (rn/use-state false) - on-change-text (rn/use-callback (fn [value] - (set-keypair-name value) - (cond - (> (count value) keypair-name-max-length) - (set-error :length) - (validators/has-emojis? value) (set-error - :emoji) - (validators/has-special-characters? value) - (set-error :special-char) - :else (set-error nil)))) - on-continue (rn/use-callback #(rf/dispatch [:wallet/new-keypair-continue - {:keypair-name - keypair-name}]) - [keypair-name])] - [rn/view {:style {:flex 1}} - [floating-button-page/view - {:header [quo/page-nav - {:icon-name :i/arrow-left - :on-press navigate-back - :accessibility-label :top-bar}] - :footer [quo/bottom-actions - {:actions :one-action - :button-one-label (i18n/label :t/continue) - :button-one-props {:disabled? (or (pos? error) - (< (count keypair-name) - keypair-name-min-length) - (> (count keypair-name) - keypair-name-max-length)) - :customization-color customization-color - :on-press on-continue} - :container-style style/bottom-action}]} - [quo/text-combinations - {:container-style style/header-container - :title (i18n/label :t/keypair-name) - :description (i18n/label :t/keypair-name-description)}] - [quo/input - {:container-style {:margin-horizontal 20} - :placeholder (i18n/label :t/keypair-name-input-placeholder) - :label (i18n/label :t/keypair-name) - :char-limit keypair-name-max-length - :auto-focus true - :on-change-text on-change-text - :error error}] - (when error - [quo/info-message - {:type :error - :size :default - :icon :i/info - :container-style style/error-container} - (get error-messages error)])]])) diff --git a/src/status_im/navigation/screens.cljs b/src/status_im/navigation/screens.cljs index 553ca53c99..3441576c01 100644 --- a/src/status_im/navigation/screens.cljs +++ b/src/status_im/navigation/screens.cljs @@ -79,12 +79,12 @@ wallet-edit-derivation-path] [status-im.contexts.wallet.add-account.create-account.import-private-key.view :as wallet-import-private-key] + [status-im.contexts.wallet.add-account.create-account.key-pair-name.view :as + wallet-key-pair-name] [status-im.contexts.wallet.add-account.create-account.new-keypair.backup-recovery-phrase.view :as wallet-backup-recovery-phrase] [status-im.contexts.wallet.add-account.create-account.new-keypair.check-your-backup.view :as wallet-check-your-backup] - [status-im.contexts.wallet.add-account.create-account.new-keypair.keypair-name.view :as - wallet-keypair-name] [status-im.contexts.wallet.add-account.create-account.select-keypair.view :as wallet-select-keypair] [status-im.contexts.wallet.add-account.create-account.view :as wallet-create-account] [status-im.contexts.wallet.bridge.bridge-to.view :as wallet-bridge-to] @@ -428,8 +428,8 @@ :component wallet-check-your-backup/view} {:name :screen/wallet.keypair-name - :options {:insets {:top? true :bottom? true}} - :component wallet-keypair-name/view} + :options {:insets {:top? true}} + :component wallet-key-pair-name/view} {:name :screen/wallet.enter-seed-phrase :component enter-seed-phrase/view} diff --git a/src/status_im/subs/wallet/wallet.cljs b/src/status_im/subs/wallet/wallet.cljs index d89cf853ae..fb50234188 100644 --- a/src/status_im/subs/wallet/wallet.cljs +++ b/src/status_im/subs/wallet/wallet.cljs @@ -5,7 +5,8 @@ [status-im.contexts.wallet.common.utils :as utils] [status-im.contexts.wallet.common.utils.networks :as network-utils] [status-im.subs.wallet.add-account.address-to-watch] - [utils.number])) + [utils.number] + [utils.security.core :as security])) (defn- filter-networks [chain-ids network-details] @@ -421,3 +422,16 @@ {:tokens tokens :currency currency :currency-symbol currency-symbol}))) + +(rf/reg-sub + :wallet/import-private-key + :<- [:wallet/create-account] + (fn [create-account] + (some-> create-account + :private-key + security/unmask))) + +(rf/reg-sub + :wallet/public-address + :<- [:wallet/create-account] + :-> :public-address) diff --git a/translations/en.json b/translations/en.json index 7d21a57d84..6494da800d 100644 --- a/translations/en.json +++ b/translations/en.json @@ -2593,6 +2593,7 @@ "key-name-error-length": "Key name too long", "key-name-error-emoji": "Emojis are not allowed", "key-name-error-special-char": "Special characters are not allowed", + "key-name-error-too-short": "Key pair name must be at least {{count}} characters", "display": "Display", "testnet-mode-enabled": "Testnet mode enabled", "online-community-member": "Online",