chore: Revise code for wallet settings (#20439)

* fix: use correct error message when displaying seed phrase as incorrect

* tidy: cleanup naming of events and effects

* tweak: show incorrect-seed-phrase error for any import error

* fix: show correct error message for incorrect private key

* chore: move import-private-key and import-seed-phrase namespaces

* chore: move encrypted-qr namespace

* chore: move scan-qr namespace

* tidy: rename key-pair to keypair inside for wallet settings

* tweak: use :open-modal navigation instead of :navigate-to-within-stack

* tweak: rename screen for importing missing key-pair by seed-phrase

* tidy: refactor code
This commit is contained in:
Sean Hagstrom 2024-06-13 14:13:26 +01:00 committed by GitHub
parent e0c7ecc6df
commit dc335cc333
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 120 additions and 110 deletions

View File

@ -2,6 +2,7 @@
(:require
[clojure.string :as string]
[legacy.status-im.ethereum.mnemonic :as mnemonic]
[oops.core :as oops]
[quo.core :as quo]
[quo.foundations.colors :as colors]
[react-native.core :as rn]
@ -96,35 +97,36 @@
(defn recovery-phrase-screen
[{:keys [keypair title recovering-keypair? render-controls]}]
(reagent/with-let [keyboard-shown? (reagent/atom false)
keyboard-show-listener (.addListener rn/keyboard
"keyboardDidShow"
#(reset! keyboard-shown? true))
keyboard-hide-listener (.addListener rn/keyboard
"keyboardDidHide"
#(reset! keyboard-shown? false))
invalid-seed-phrase? (reagent/atom false)
input-ref (reagent/atom nil)
focus-input (fn []
(let [ref @input-ref]
(when ref
(.focus ref))))
set-invalid-seed-phrase #(reset! invalid-seed-phrase? true)
seed-phrase (reagent/atom "")
on-change-seed-phrase (fn [new-phrase]
(when @invalid-seed-phrase?
(reset! invalid-seed-phrase? false))
(reset! seed-phrase new-phrase))
on-submit (fn []
(swap! seed-phrase clean-seed-phrase)
(if recovering-keypair?
(rf/dispatch [:wallet/seed-phrase-entered
(security/mask-data
@seed-phrase)
set-invalid-seed-phrase])
(rf/dispatch [:onboarding/seed-phrase-entered
(security/mask-data @seed-phrase)
set-invalid-seed-phrase])))]
(reagent/with-let [keyboard-shown? (reagent/atom false)
keyboard-show-listener (.addListener rn/keyboard
"keyboardDidShow"
#(reset! keyboard-shown? true))
keyboard-hide-listener (.addListener rn/keyboard
"keyboardDidHide"
#(reset! keyboard-shown? false))
invalid-seed-phrase? (reagent/atom false)
incorrect-seed-phrase? (reagent/atom false)
input-ref (reagent/atom nil)
focus-input #(some-> @input-ref
(oops/ocall "focus"))
set-incorrect-seed-phrase #(reset! incorrect-seed-phrase? true)
set-invalid-seed-phrase #(reset! invalid-seed-phrase? true)
seed-phrase (reagent/atom "")
on-change-seed-phrase (fn [new-phrase]
(when @invalid-seed-phrase?
(reset! invalid-seed-phrase? false)
(reset! incorrect-seed-phrase? false))
(reset! seed-phrase new-phrase))
on-submit (fn []
(swap! seed-phrase clean-seed-phrase)
(if recovering-keypair?
(rf/dispatch [:wallet/seed-phrase-entered
(security/mask-data
@seed-phrase)
set-invalid-seed-phrase])
(rf/dispatch [:onboarding/seed-phrase-entered
(security/mask-data @seed-phrase)
set-invalid-seed-phrase])))]
(let [words-coll (mnemonic/passphrase->words @seed-phrase)
last-word (peek words-coll)
pick-suggested-word (fn [pressed-word]
@ -143,16 +145,18 @@
suggestions-state (cond
(or error-in-words?
words-exceeded?
@invalid-seed-phrase?) :error
@invalid-seed-phrase?
@incorrect-seed-phrase?) :error
(string/blank? @seed-phrase) :info
(string/ends-with? @seed-phrase " ") :empty
:else :words)
suggestions-text (cond
upper-case? (i18n/label :t/seed-phrase-words-uppercase)
words-exceeded? (i18n/label :t/seed-phrase-words-exceeded)
error-in-words? (i18n/label :t/seed-phrase-error)
@invalid-seed-phrase? (i18n/label :t/seed-phrase-invalid)
:else (i18n/label :t/seed-phrase-info))
upper-case? (i18n/label :t/seed-phrase-words-uppercase)
words-exceeded? (i18n/label :t/seed-phrase-words-exceeded)
error-in-words? (i18n/label :t/seed-phrase-error)
@invalid-seed-phrase? (i18n/label :t/seed-phrase-invalid)
@incorrect-seed-phrase? (i18n/label :t/seed-phrase-incorrect)
:else (i18n/label :t/seed-phrase-info))
error-state? (= suggestions-state :error)
button-disabled? (or error-state?
(not (constants/seed-phrase-valid-length word-count))
@ -166,13 +170,13 @@
:word-count word-count
:ref #(reset! input-ref %)}
(if (fn? render-controls)
(render-controls {:submit-disabled? button-disabled?
:keyboard-shown? @keyboard-shown?
:container-style (style/continue-button @keyboard-shown?)
:prepare-seed-phrase secure-clean-seed-phrase
:focus-input focus-input
:seed-phrase (security/mask-data @seed-phrase)
:set-invalid-seed-phrase set-invalid-seed-phrase})
(render-controls {:submit-disabled? button-disabled?
:keyboard-shown? @keyboard-shown?
:container-style (style/continue-button @keyboard-shown?)
:prepare-seed-phrase secure-clean-seed-phrase
:focus-input focus-input
:seed-phrase (security/mask-data @seed-phrase)
:set-incorrect-seed-phrase set-incorrect-seed-phrase})
[quo/button
{:container-style (style/continue-button @keyboard-shown?)
:type :primary

View File

@ -35,7 +35,7 @@
:blur? true
:action :arrow}
{:title (i18n/label :t/wallet)
:on-press #(rf/dispatch [:navigate-to-within-stack [:screen/settings.wallet :settings]])
:on-press #(rf/dispatch [:open-modal :screen/settings.wallet])
:image-props :i/wallet
:image :icon
:blur? true

View File

@ -167,9 +167,9 @@
(rf/reg-event-fx :wallet/verify-private-key-for-keypair verify-private-key-for-keypair)
(defn import-keypair-by-seed-phrase
(defn import-missing-keypair-by-seed-phrase
[_ [{:keys [keypair-key-uid seed-phrase password on-success on-error]}]]
{:fx [[:import-keypair-by-seed-phrase
{:fx [[:effects.wallet/import-missing-keypair-by-seed-phrase
{:keypair-key-uid keypair-key-uid
:seed-phrase seed-phrase
:password password
@ -178,24 +178,26 @@
#{keypair-key-uid}])
(rf/call-continuation on-success))
:on-error (fn [error]
(rf/dispatch [:wallet/import-keypair-by-seed-phrase-failed error])
(rf/dispatch [:wallet/import-missing-keypair-by-seed-phrase-failed error])
(log/error "failed to import missing keypair with seed phrase"
{:error error})
(rf/call-continuation on-error error))}]]})
(rf/reg-event-fx :wallet/import-keypair-by-seed-phrase import-keypair-by-seed-phrase)
(rf/reg-event-fx :wallet/import-missing-keypair-by-seed-phrase import-missing-keypair-by-seed-phrase)
(defn import-keypair-by-seed-phrase-failed
(defn import-missing-keypair-by-seed-phrase-failed
[_ [error]]
(let [error-type (-> error ex-message keyword)
error-data (ex-data error)]
(when-not (and (= error-type :import-keypair-by-seed-phrase/import-error)
(= (:hint error-data) :incorrect-seed-phrase-for-keypair))
(when-not (= error-type :import-missing-keypair-by-seed-phrase/import-error)
{:fx [[:dispatch
[:toasts/upsert
{:type :negative
:theme :dark
:text (:error error-data)}]]]})))
(rf/reg-event-fx :wallet/import-keypair-by-seed-phrase-failed import-keypair-by-seed-phrase-failed)
(rf/reg-event-fx :wallet/import-missing-keypair-by-seed-phrase-failed
import-missing-keypair-by-seed-phrase-failed)
(defn import-missing-keypair-by-private-key
[_ [{:keys [keypair-key-uid private-key password on-success on-error]}]]

View File

@ -1,7 +1,7 @@
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.actions.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[status-im.contexts.settings.wallet.keypairs-and-accounts.remove.view :as remove-key-pair]
[status-im.contexts.settings.wallet.keypairs-and-accounts.remove.view :as remove-keypair]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
@ -14,20 +14,22 @@
[(:key-uid keypair)]])
[keypair])
on-show-qr (rn/use-callback #(rf/dispatch [:open-modal
:screen/settings.encrypted-key-pair-qr
:screen/settings.encrypted-keypair-qr
keypair])
[keypair])
on-remove-keypair (rn/use-callback #(rf/dispatch
[:show-bottom-sheet
{:theme :dark
:content (fn []
[remove-key-pair/view keypair])}])
[remove-keypair/view keypair])}])
[keypair])
on-rename-keypair (rn/use-callback #(rf/dispatch [:open-modal :screen/settings.rename-keypair
keypair])
[keypair])
on-import-seed-phrase (rn/use-callback
#(rf/dispatch [:open-modal :screen/settings.import-seed-phrase keypair])
#(rf/dispatch [:open-modal
:screen/settings.missing-keypair.import-seed-phrase
keypair])
[keypair])
on-import-private-key (rn/use-callback
#(rf/dispatch [:open-modal

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.encrypted-qr.countdown.view
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.encrypted-qr.countdown.view
(:require
[quo.core :as quo]
[quo.foundations.colors :as colors]

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.encrypted-qr.style
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.encrypted-qr.style
(:require
[quo.foundations.colors :as colors]
[react-native.safe-area :as safe-area]))

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.encrypted-qr.view
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.encrypted-qr.view
(:require
[quo.core :as quo]
[quo.foundations.colors :as colors]
@ -7,8 +7,10 @@
[status-im.common.qr-codes.view :as qr-codes]
[status-im.common.resources :as resources]
[status-im.common.standard-authentication.core :as standard-auth]
[status-im.contexts.settings.wallet.keypairs-and-accounts.encrypted-qr.countdown.view :as countdown]
[status-im.contexts.settings.wallet.keypairs-and-accounts.encrypted-qr.style :as style]
[status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.encrypted-qr.countdown.view
:as countdown]
[status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.encrypted-qr.style
:as style]
[status-im.contexts.syncing.utils :as sync-utils]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.import-private-key.style)
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.import-private-key.style)
(def form-container
{:row-gap 8

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.import-private-key.view
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.import-private-key.view
(:require
[clojure.string :as string]
[quo.core :as quo]
@ -7,7 +7,8 @@
[react-native.safe-area :as safe-area]
[status-im.common.floating-button-page.view :as floating-button-page]
[status-im.common.standard-authentication.core :as standard-auth]
[status-im.contexts.settings.wallet.keypairs-and-accounts.import-private-key.style :as style]
[status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.import-private-key.style
:as style]
[status-im.contexts.wallet.common.validation :as validation]
[utils.debounce :as debounce]
[utils.i18n :as i18n]
@ -68,8 +69,7 @@
[on-change])
on-import-error (rn/use-callback
(fn [_error]
(rf/dispatch [:hide-bottom-sheet])
(show-invalid)))
(rf/dispatch [:hide-bottom-sheet])))
on-import-success (rn/use-callback
(fn []
(rf/dispatch [:hide-bottom-sheet])
@ -134,5 +134,5 @@
(case flow-state
:correct-private-key (i18n/label :t/correct-private-key)
:invalid-private-key (i18n/label :t/invalid-private-key)
:incorrect-private-key (i18n/label :t/incorrect-private-key {:name (:name keypair)})
:incorrect-private-key (i18n/label :t/incorrect-private-key)
nil)])]]]))

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.import-seed-phrase.view
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.import-seed-phrase.view
(:require
[quo.core :as quo]
[react-native.core :as rn]
@ -14,16 +14,17 @@
container-style
prepare-seed-phrase
seed-phrase
set-invalid-seed-phrase
set-incorrect-seed-phrase
focus-input]}]
(let [keypair (rf/sub [:get-screen-params])
customization-color (rf/sub [:profile/customization-color])
show-errors (rn/use-callback
#(js/setTimeout
(fn []
(focus-input)
(reagent/next-tick set-invalid-seed-phrase))
600))
(fn [_error]
(js/setTimeout
(fn []
(focus-input)
(reagent/next-tick set-incorrect-seed-phrase))
600)))
on-import-error (rn/use-callback
(fn [_error]
(rf/dispatch [:hide-bottom-sheet])
@ -34,7 +35,7 @@
(rf/dispatch [:navigate-back])))
on-auth-success (rn/use-callback
(fn [password]
(rf/dispatch [:wallet/import-keypair-by-seed-phrase
(rf/dispatch [:wallet/import-missing-keypair-by-seed-phrase
{:keypair-key-uid (:key-uid keypair)
:seed-phrase (prepare-seed-phrase seed-phrase)
:password password

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.scan-qr.view
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.scan-qr.view
(:require
[react-native.core :as rn]
[status-im.common.scan-qr-code.view :as scan-qr-code]

View File

@ -159,9 +159,7 @@
:ens address-or-ens
:ens? ens-name?}])
(rf/dispatch
[:navigate-to-within-stack
[:screen/settings.save-address
:screen/settings.add-address-to-save]]))
[:open-modal :screen/settings.save-address]))
[address ens-name? address-or-ens])]
(rn/use-unmount #(rf/dispatch [:wallet/clear-address-to-save]))
[quo/overlay {:type :shell}

View File

@ -66,8 +66,7 @@
(defn- add-address-to-save
[]
(rf/dispatch [:navigate-to-within-stack
[:screen/settings.add-address-to-save :screen/settings.saved-addresses]]))
(rf/dispatch [:open-modal :screen/settings.add-address-to-save]))
(defn view
[]

View File

@ -8,11 +8,11 @@
(defn open-saved-addresses-settings-modal
[]
(rf/dispatch [:navigate-to-within-stack [:screen/settings.saved-addresses :settings]]))
(rf/dispatch [:open-modal :screen/settings.saved-addresses]))
(defn open-keypairs-and-accounts-settings-modal
[]
(rf/dispatch [:navigate-to-within-stack [:screen/settings.keypairs-and-accounts :settings]]))
(rf/dispatch [:open-modal :screen/settings.keypairs-and-accounts]))
(defn basic-settings-options
[]
@ -38,7 +38,7 @@
(defn open-network-settings-modal
[]
(rf/dispatch [:navigate-to-within-stack [:screen/settings.network-settings :settings]]))
(rf/dispatch [:open-modal :screen/settings.network-settings]))
(defn advanced-settings-options
[]

View File

@ -79,7 +79,7 @@
:on-success (fn [value]
(resolver {:value value}))}))))
(defn import-keypair-by-seed-phrase
(defn import-missing-keypair-by-seed-phrase
[keypair-key-uid seed-phrase password]
(-> (validate-mnemonic seed-phrase)
(promesa/then
@ -87,20 +87,20 @@
(if (not= keypair-key-uid key-uid)
(promesa/rejected
(ex-info
(error-message :import-keypair-by-seed-phrase/import-error)
(error-message :import-missing-keypair-by-seed-phrase/import-error)
{:hint :incorrect-seed-phrase-for-keypair}))
(make-seed-phrase-fully-operable seed-phrase password))))
(promesa/catch
(fn [error]
(promesa/rejected
(ex-info
(error-message :import-keypair-by-seed-phrase/import-error)
(error-message :import-missing-keypair-by-seed-phrase/import-error)
(ex-data error)))))))
(rf/reg-fx
:import-keypair-by-seed-phrase
:effects.wallet/import-missing-keypair-by-seed-phrase
(fn [{:keys [keypair-key-uid seed-phrase password on-success on-error]}]
(-> (import-keypair-by-seed-phrase keypair-key-uid seed-phrase password)
(-> (import-missing-keypair-by-seed-phrase keypair-key-uid seed-phrase password)
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-error)))))

View File

@ -57,14 +57,15 @@
[status-im.contexts.profile.settings.screens.password.change-password.view :as change-password]
[status-im.contexts.profile.settings.screens.password.view :as settings-password]
[status-im.contexts.profile.settings.view :as settings]
[status-im.contexts.settings.wallet.keypairs-and-accounts.encrypted-qr.view :as
encrypted-key-pair-qr]
[status-im.contexts.settings.wallet.keypairs-and-accounts.import-private-key.view :as
import-private-key]
[status-im.contexts.settings.wallet.keypairs-and-accounts.import-seed-phrase.view :as
import-seed-phrase]
[status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.encrypted-qr.view
:as encrypted-keypair-qr]
[status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.import-private-key.view
:as missing-keypairs.import-private-key]
[status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.import-seed-phrase.view
:as missing-keypairs.import-seed-phrase]
[status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.scan-qr.view
:as scan-keypair-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.add-address-to-save.view :as
@ -535,9 +536,9 @@
:options (assoc options/dark-screen :sheet? true)
:component keypair-rename/view}
{:name :screen/settings.encrypted-key-pair-qr
{:name :screen/settings.encrypted-keypair-qr
:options options/transparent-screen-options
:component encrypted-key-pair-qr/view}
:component encrypted-keypair-qr/view}
{:name :screen/settings.saved-addresses
:options options/transparent-modal-screen-options
@ -551,13 +552,13 @@
:options options/transparent-modal-screen-options
:component scan-keypair-qr/view}
{:name :screen/settings.import-seed-phrase
{:name :screen/settings.missing-keypair.import-seed-phrase
:options options/transparent-screen-options
:component import-seed-phrase/view}
:component missing-keypairs.import-seed-phrase/view}
{:name :screen/settings.missing-keypair-import-private-key
:options options/transparent-screen-options
:component import-private-key/view}
:component missing-keypairs.import-private-key/view}
{:name :screen/settings.network-settings
:options options/transparent-modal-screen-options

View File

@ -259,22 +259,22 @@
:wallet/settings-keypairs-accounts
:<- [:wallet/keypairs]
(fn [keypairs [_ format-options]]
(let [grouped-keypairs (group-by :lowest-operability keypairs)
operable-key-pair-ids (->> (concat (:fully grouped-keypairs)
(:partially grouped-keypairs))
(map :key-uid)
(into #{}))
missing-key-pair-ids (->> (map :key-uid (:no grouped-keypairs))
(into #{}))]
(let [grouped-keypairs (group-by :lowest-operability keypairs)
operable-keypair-ids (->> (concat (:fully grouped-keypairs)
(:partially grouped-keypairs))
(map :key-uid)
(into #{}))
missing-keypair-ids (->> (map :key-uid (:no grouped-keypairs))
(into #{}))]
{:operable (->> keypairs
(filter #(contains? operable-key-pair-ids (:key-uid %)))
(filter #(contains? operable-keypair-ids (:key-uid %)))
(map (fn [{:keys [accounts name type key-uid]}]
{:type (keyword type)
:name name
:key-uid key-uid
:accounts (format-settings-keypair-accounts accounts format-options)})))
:missing (->> keypairs
(filter #(contains? missing-key-pair-ids (:key-uid %)))
(filter #(contains? missing-keypair-ids (:key-uid %)))
(map (fn [{:keys [accounts name type key-uid]}]
{:type (keyword type)
:name name

View File

@ -1587,6 +1587,7 @@
"seed-phrase-words-uppercase": "Recovery phrase cannot contain uppercase characters",
"seed-phrase-error": "Recovery phrase contains invalid words",
"seed-phrase-invalid": "Invalid recovery phrase",
"seed-phrase-incorrect": "Recovery phrase does not match key pair",
"seed-phrase-info": "Enter 12, 18 or 24 words separated by spaces",
"word-count": "Word count",
"word-n": "Word #{{number}}",
@ -2659,7 +2660,7 @@
"import-private-key-info": "New addresses cannot be derived from an account imported from a private key. Import using a seed phrase if you wish to derive addresses.",
"invalid-private-key": "Its not a valid private key",
"correct-private-key": "Correct private key",
"incorrect-private-key": "This is not the private key for {{name}}",
"incorrect-private-key": "This is not the private key for this key pair",
"private-key-public-address": "Public address of private key",
"this-account-has-no-activity": "This account has no activity",
"this-address-has-activity": "This address has activity",