Wallet: Derivation Path - Scanning for Activity (#19482)
Wallet: Derivation Path - Scanning for Activity (#19482)
This commit is contained in:
parent
0e29149363
commit
74e84644aa
Binary file not shown.
After Width: | Height: | Size: 750 B |
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 595 B |
Binary file not shown.
After Width: | Height: | Size: 889 B |
|
@ -83,7 +83,7 @@
|
|||
:has-activity {:accessibility-label :account-has-activity
|
||||
:icon :i/done
|
||||
:type :success
|
||||
:message :t/this-address-has-activity}
|
||||
:message :t/address-activity}
|
||||
:no-activity {:accessibility-label :account-has-no-activity
|
||||
:icon :i/info
|
||||
:type :warning
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
(ns status-im.contexts.wallet.create-account.edit-derivation-path.component-spec
|
||||
(:require
|
||||
[status-im.contexts.wallet.create-account.edit-derivation-path.view :as edit-derivation-path]
|
||||
[test-helpers.component :as h]))
|
||||
|
||||
(def sub-mocks
|
||||
{:profile/profile {:public-key "123"}
|
||||
:contacts/contact-two-names-by-identity ["a"]
|
||||
:profile/image "image"})
|
||||
|
||||
(h/describe "Edit derivation path page"
|
||||
(h/test "Default render"
|
||||
(h/setup-subs sub-mocks)
|
||||
(h/render-with-theme-provider [edit-derivation-path/view {}])
|
||||
(h/is-truthy (h/get-by-translation-text :t/edit-derivation-path))
|
||||
(h/is-truthy (h/get-by-translation-text :t/path-format))
|
||||
(h/is-truthy (h/get-by-translation-text :t/derivation-path))
|
||||
(h/is-truthy (h/get-by-translation-text :t/reveal-address))
|
||||
(h/is-truthy (h/get-by-translation-text :t/save)))
|
||||
|
||||
|
||||
(h/test "Reveal address pressed"
|
||||
(h/setup-subs sub-mocks)
|
||||
(let [on-reveal (h/mock-fn)]
|
||||
(h/render-with-theme-provider [edit-derivation-path/view {:on-reveal on-reveal}])
|
||||
(h/fire-event :press (h/get-by-translation-text :t/reveal-address))
|
||||
(h/was-called on-reveal)
|
||||
(h/wait-for #(h/is-truthy (h/get-by-translation-text :t/address-activity)))))
|
||||
|
||||
(h/test "Reset button pressed"
|
||||
(h/setup-subs sub-mocks)
|
||||
(let [on-reset (h/mock-fn)]
|
||||
(h/render-with-theme-provider [edit-derivation-path/view {:on-reset on-reset}])
|
||||
(h/fire-event :press (h/get-by-translation-text :t/reset))
|
||||
(h/was-called on-reset)
|
||||
(h/wait-for #(h/is-truthy (h/get-by-translation-text :t/derive-addresses))))))
|
|
@ -30,9 +30,11 @@
|
|||
:padding-top 24})
|
||||
|
||||
(defn revealed-address
|
||||
[theme]
|
||||
[state theme]
|
||||
{:border-width 1
|
||||
:border-color (colors/resolve-color :success theme 40)
|
||||
:border-color (if (= state :scanning)
|
||||
(colors/theme-colors colors/neutral-20 colors/neutral-70 theme)
|
||||
(colors/resolve-color (if (= state :has-activity) :success :warning) theme 40))
|
||||
:border-style :dashed
|
||||
:border-radius 16
|
||||
:padding-horizontal 12
|
||||
|
@ -42,8 +44,6 @@
|
|||
{:margin-vertical 9
|
||||
:padding-left 2})
|
||||
|
||||
(def temporal-placeholder
|
||||
{:height 94
|
||||
:background-color colors/danger-50
|
||||
:align-items :center
|
||||
:justify-content :center})
|
||||
(defn keyboard
|
||||
[padding-bottom]
|
||||
{:padding-bottom padding-bottom})
|
||||
|
|
|
@ -1,51 +1,68 @@
|
|||
(ns status-im.contexts.wallet.create-account.edit-derivation-path.view
|
||||
(:require
|
||||
[clojure.string :as string]
|
||||
[quo.core :as quo]
|
||||
[quo.foundations.colors :as colors]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.contexts.wallet.common.temp :as temp]
|
||||
[status-im.contexts.wallet.common.utils :as utils]
|
||||
[status-im.contexts.wallet.create-account.edit-derivation-path.path-format-sheet.view :as
|
||||
path-format-sheet]
|
||||
[status-im.contexts.wallet.create-account.edit-derivation-path.style :as style]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
[utils.re-frame :as rf]
|
||||
[utils.security.core :as security]))
|
||||
|
||||
(defn- view-internal
|
||||
"States:
|
||||
default(filled)
|
||||
| -> (reveal-action) -> show
|
||||
| -> (clear-action) -> empty -> (derive-action) -> choose -> (choose-action) -> show"
|
||||
[{:keys [on-reset on-reveal]}]
|
||||
(let [top (safe-area/get-top)
|
||||
bottom (safe-area/get-bottom)
|
||||
state (reagent/atom :default)
|
||||
reveal-action (fn [_]
|
||||
(reset! state :show)
|
||||
(when on-reveal
|
||||
(on-reveal)))
|
||||
clear-action #(reset! state :empty)
|
||||
derive-action #(js/alert "To be implemented")
|
||||
choose-action #(reset! state :show)
|
||||
path-value (reagent/atom (utils/get-formatted-derivation-path 3))
|
||||
handle-path-change (fn [v]
|
||||
(reset! path-value v)
|
||||
(when (empty? v)
|
||||
(clear-action)))
|
||||
reset-path-value (fn [_]
|
||||
(reset! path-value "")
|
||||
(clear-action)
|
||||
(when on-reset
|
||||
(on-reset)))]
|
||||
[{:keys [on-reset]}]
|
||||
(let [top (safe-area/get-top)
|
||||
bottom (safe-area/get-bottom)
|
||||
input-focused? (reagent/atom false)
|
||||
path-value (reagent/atom "")
|
||||
input-ref (reagent/atom nil)
|
||||
reset-path-value (fn [_]
|
||||
(reset! path-value "")
|
||||
(when on-reset
|
||||
(on-reset)))]
|
||||
(fn [{:keys [theme]}]
|
||||
(let [{:keys [public-key]} (rf/sub [:profile/profile])
|
||||
primary-name (first (rf/sub [:contacts/contact-two-names-by-identity
|
||||
public-key]))
|
||||
profile-picture (rf/sub [:profile/image])
|
||||
show-path-format-sheet #(rf/dispatch [:show-bottom-sheet {:content path-format-sheet/view}])]
|
||||
(let [{:keys [public-key address]} (rf/sub [:profile/profile])
|
||||
{:keys [password current-derivation-path]} (rf/sub [:get-screen-params])
|
||||
primary-name (first (rf/sub
|
||||
[:contacts/contact-two-names-by-identity
|
||||
public-key]))
|
||||
profile-picture (rf/sub [:profile/image])
|
||||
show-path-format-sheet #(rf/dispatch [:show-bottom-sheet
|
||||
{:content path-format-sheet/view}])
|
||||
derivation-path (rf/sub [:wallet/derivation-path])
|
||||
state (rf/sub [:wallet/derivation-path-state])
|
||||
navigate-back-handler #(if @input-focused?
|
||||
(do
|
||||
(.blur ^js @input-ref)
|
||||
true)
|
||||
(rf/dispatch [:navigate-to
|
||||
:screen/wallet.create-account]))
|
||||
on-change-text #(rf/dispatch
|
||||
[:wallet/get-derived-addresses
|
||||
{:password (security/safe-unmask-data
|
||||
password)
|
||||
:paths [(string/replace @path-value
|
||||
#"\s"
|
||||
"")]
|
||||
:derived-from address}])]
|
||||
(rn/use-mount (fn []
|
||||
(reset! path-value current-derivation-path)
|
||||
(rf/dispatch [:wallet/get-derived-addresses
|
||||
{:password (security/safe-unmask-data password)
|
||||
:paths [(string/replace @path-value #"\s" "")]
|
||||
:derived-from address}])
|
||||
(rn/hw-back-add-listener navigate-back-handler)
|
||||
#(rn/hw-back-remove-listener navigate-back-handler)))
|
||||
[rn/view
|
||||
{:style (style/screen top)}
|
||||
[quo/page-nav
|
||||
|
@ -82,54 +99,33 @@
|
|||
:value (i18n/label :t/default-format)
|
||||
:container-style {:margin-horizontal 20}}]]
|
||||
[quo/input
|
||||
{:container-style style/input-container
|
||||
:value @path-value
|
||||
:editable false
|
||||
:label (i18n/label :t/derivation-path)
|
||||
:placeholder (utils/get-formatted-derivation-path 3)
|
||||
:button {:on-press reset-path-value
|
||||
:text (i18n/label :t/reset)}
|
||||
:on-change-text handle-path-change}]
|
||||
|
||||
(case @state
|
||||
:default
|
||||
[quo/bottom-actions
|
||||
{:actions :one-action
|
||||
:button-one-label (i18n/label :t/reveal-address)
|
||||
:button-one-props {:type :outline
|
||||
:icon-left :i/keycard-card
|
||||
:on-press reveal-action}}]
|
||||
|
||||
:empty
|
||||
[quo/bottom-actions
|
||||
{:actions :one-action
|
||||
:button-one-label (i18n/label :t/derive-addresses)
|
||||
:button-one-props {:type :outline
|
||||
:icon-left :i/keycard-card
|
||||
:on-press derive-action}}]
|
||||
|
||||
:show
|
||||
[rn/view {:style style/revealed-address-container}
|
||||
[rn/view {:style (style/revealed-address theme)}
|
||||
[quo/text
|
||||
{:weight :monospace}
|
||||
temp/address]]
|
||||
[quo/info-message
|
||||
{:type :success
|
||||
:icon :i/done
|
||||
:style style/info}
|
||||
(i18n/label :t/address-activity)]]
|
||||
|
||||
:choose
|
||||
[rn/view {:style style/temporal-placeholder}
|
||||
[quo/text "Dropdown input will be here"]
|
||||
[quo/button
|
||||
{:on-press (fn [_]
|
||||
(reset! path-value (utils/get-formatted-derivation-path 1))
|
||||
(choose-action))}
|
||||
"Choose"]]
|
||||
nil)
|
||||
|
||||
{:ref #(reset! input-ref %)
|
||||
:container-style style/input-container
|
||||
:value @path-value
|
||||
:on-focus #(reset! input-focused? true)
|
||||
:on-blur #(reset! input-focused? false)
|
||||
:show-soft-input-on-focus false
|
||||
:editable true
|
||||
:label (i18n/label :t/derivation-path)
|
||||
:placeholder (utils/get-formatted-derivation-path 3)
|
||||
:button {:on-press reset-path-value
|
||||
:text (i18n/label :t/reset)}}]
|
||||
[rn/view {:style style/revealed-address-container}
|
||||
[rn/view {:style (style/revealed-address state theme)}
|
||||
[quo/text
|
||||
{:weight :monospace}
|
||||
(:address derivation-path)]]
|
||||
[quo/info-message
|
||||
{:type (case state
|
||||
:has-activity :success
|
||||
:no-activity :warning
|
||||
:default)
|
||||
:icon (if (= state :scanning) :i/scanning :i/done)
|
||||
:style style/info}
|
||||
(i18n/label (case state
|
||||
:has-activity :t/address-activity
|
||||
:no-activity :t/address-no-activity
|
||||
:t/scanning))]]
|
||||
[rn/view {:style (style/save-button-container bottom)}
|
||||
[quo/bottom-actions
|
||||
{:actions :one-action
|
||||
|
@ -137,9 +133,17 @@
|
|||
:button-one-props {:type :primary
|
||||
:on-press #(js/alert "Save!")
|
||||
:disabled? true}}]]
|
||||
(when-not (= @state :show)
|
||||
(when @input-focused?
|
||||
[quo/numbered-keyboard
|
||||
{:left-action :dot
|
||||
:delete-key? true}])]))))
|
||||
{:left-action :dot
|
||||
:delete-key? true
|
||||
:container-style (style/keyboard bottom)
|
||||
:on-press (fn [value]
|
||||
(reset! path-value (str @path-value value))
|
||||
(on-change-text))
|
||||
:on-delete (fn []
|
||||
(reset! path-value (subs @path-value 0 (dec (count @path-value))))
|
||||
(on-change-text))}])]))))
|
||||
|
||||
(def view (quo.theme/with-theme view-internal))
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
(ns status-im.contexts.wallet.create-account.events
|
||||
(:require [status-im.contexts.wallet.data-store :as data-store]
|
||||
[utils.re-frame :as rf]))
|
||||
(:require [camel-snake-kebab.extras :as cske]
|
||||
[status-im.contexts.wallet.data-store :as data-store]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.transforms :as transforms]))
|
||||
|
||||
(defn get-keypairs-success
|
||||
[{:keys [db]} [keypairs]]
|
||||
|
@ -49,3 +51,23 @@
|
|||
{:db (update-in db [:wallet :ui :create-account] dissoc :new-keypair)})
|
||||
|
||||
(rf/reg-event-fx :wallet/clear-new-keypair clear-new-keypair)
|
||||
|
||||
(defn get-derived-addresses
|
||||
[{:keys [db]} [{:keys [password derived-from paths]}]]
|
||||
{:db (assoc-in db [:wallet :ui :create-account :derivation-path-state] :scanning)
|
||||
:json-rpc/call [{:method "wallet_getDerivedAddresses"
|
||||
:params [password derived-from paths]
|
||||
:on-success [:wallet/get-derived-addresses-success]}]})
|
||||
|
||||
(rf/reg-event-fx :wallet/get-derived-addresses get-derived-addresses)
|
||||
|
||||
(defn get-derived-addresses-success
|
||||
[{:keys [db]} [response]]
|
||||
(let [derived-address (first response)]
|
||||
{:db (-> db
|
||||
(assoc-in [:wallet :ui :create-account :derivation-path-state]
|
||||
(if (:has-activity derived-address) :has-activity :no-activity))
|
||||
(assoc-in [:wallet :ui :create-account :derivation-path]
|
||||
(cske/transform-keys transforms/->kebab-case-keyword derived-address)))}))
|
||||
|
||||
(rf/reg-event-fx :wallet/get-derived-addresses-success get-derived-addresses-success)
|
||||
|
|
|
@ -42,3 +42,14 @@
|
|||
expected-db {:wallet {:ui {:create-account {}}}}
|
||||
effects (events/clear-new-keypair {:db db})]
|
||||
(is (match? (:db effects) expected-db))))
|
||||
|
||||
(deftest get-derived-addresses-test
|
||||
(let [db {}
|
||||
password "test-password"
|
||||
derived-from "test-derive-from"
|
||||
paths ["path1"]
|
||||
event-args [{:password password :derived-from derived-from :paths paths}]
|
||||
expected-db (assoc-in db [:wallet :ui :create-account :derivation-path-state] :scanning)
|
||||
effects (events/get-derived-addresses {:db db} event-args)
|
||||
result-db (:db effects)]
|
||||
(is (match? result-db expected-db))))
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
[status-im.contexts.wallet.create-account.style :as style]
|
||||
[status-im.contexts.wallet.create-account.utils :as create-account.utils]
|
||||
[status-im.contexts.wallet.sheets.account-origin.view :as account-origin]
|
||||
[status-im.feature-flags :as ff]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.responsiveness :as responsiveness]
|
||||
|
@ -23,29 +24,39 @@
|
|||
|
||||
(defn- get-keypair-data
|
||||
[{:keys [title primary-keypair? new-keypair? derivation-path customization-color]}]
|
||||
[{:title title
|
||||
:image (if primary-keypair? :avatar :icon)
|
||||
:image-props (if primary-keypair?
|
||||
{:full-name (utils.string/get-initials title 1)
|
||||
:size :xxs
|
||||
:customization-color customization-color}
|
||||
:i/seed)
|
||||
:action (when-not new-keypair? :button)
|
||||
:action-props {:on-press #(rf/dispatch [:navigate-to :screen/wallet.select-keypair])
|
||||
:button-text (i18n/label :t/edit)
|
||||
:alignment :flex-start}
|
||||
:description :text
|
||||
:description-props {:text (i18n/label :t/on-device)}}
|
||||
{:title (i18n/label :t/derivation-path)
|
||||
:image :icon
|
||||
:image-props :i/derivated-path
|
||||
:action :button
|
||||
:action-props {:on-press #(js/alert "Coming soon!")
|
||||
:button-text (i18n/label :t/edit)
|
||||
:icon-left :i/placeholder
|
||||
:alignment :flex-start}
|
||||
:description :text
|
||||
:description-props {:text (string/replace derivation-path #"/" " / ")}}])
|
||||
(let [formatted-path (string/replace derivation-path #"/" " / ")
|
||||
on-auth-success (fn [password]
|
||||
(rf/dispatch [:navigate-to
|
||||
:screen/wallet.edit-derivation-path
|
||||
{:password password
|
||||
:current-derivation-path formatted-path}]))]
|
||||
[{:title title
|
||||
:image (if primary-keypair? :avatar :icon)
|
||||
:image-props (if primary-keypair?
|
||||
{:full-name (utils.string/get-initials title 1)
|
||||
:size :xxs
|
||||
:customization-color customization-color}
|
||||
:i/seed)
|
||||
:action (when-not new-keypair? :button)
|
||||
:action-props {:on-press #(rf/dispatch [:navigate-to :screen/wallet.select-keypair])
|
||||
:button-text (i18n/label :t/edit)
|
||||
:alignment :flex-start}
|
||||
:description :text
|
||||
:description-props {:text (i18n/label :t/on-device)}}
|
||||
{:title (i18n/label :t/derivation-path)
|
||||
:image :icon
|
||||
:image-props :i/derivated-path
|
||||
:action :button
|
||||
:action-props {:on-press #(if (ff/enabled? :ff/wallet.edit-derivation-path)
|
||||
(rf/dispatch [:standard-auth/authorize
|
||||
{:on-auth-success on-auth-success
|
||||
:auth-button-label (i18n/label :t/continue)}])
|
||||
(js/alert "Coming soon!"))
|
||||
:button-text (i18n/label :t/edit)
|
||||
:icon-left :i/face-id
|
||||
:alignment :flex-start}
|
||||
:description :text
|
||||
:description-props {:text formatted-path}}]))
|
||||
|
||||
(defn- f-view
|
||||
[_]
|
||||
|
|
|
@ -6,5 +6,4 @@
|
|||
[status-im.contexts.shell.share.wallet.component-spec]
|
||||
[status-im.contexts.wallet.add-address-to-watch.component-spec]
|
||||
[status-im.contexts.wallet.add-address-to-watch.confirm-address.component-spec]
|
||||
[status-im.contexts.wallet.create-account.edit-derivation-path.component-spec]
|
||||
[status-im.contexts.wallet.send.input-amount.component-spec]))
|
||||
|
|
|
@ -137,6 +137,11 @@
|
|||
:goerli-enabled? goerli-enabled?})
|
||||
selected-networks))))
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet/derivation-path-state
|
||||
:<- [:wallet/create-account]
|
||||
:-> :derivation-path-state)
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet/accounts
|
||||
:<- [:wallet]
|
||||
|
|
|
@ -2458,6 +2458,8 @@
|
|||
"edit-wallet-network-preferences-updated-message": "Account network preferences has been updated",
|
||||
"search-assets": "Search assets",
|
||||
"address-activity": "This address has activity",
|
||||
"address-no-activity": "This address has no activity",
|
||||
"scanning": "Scanning for activity...",
|
||||
"keypairs": "Keypairs",
|
||||
"keypairs-description": "Select keypair to derive your new account from",
|
||||
"confirm-account-origin": "Confirm account origin",
|
||||
|
@ -2481,7 +2483,6 @@
|
|||
"est-time": "Est. time",
|
||||
"user-gets": "{{name}} gets",
|
||||
"slide-to-send": "Slide to send",
|
||||
"this-address-has-activity": "This address has activity",
|
||||
"generate-new-keypair": "Generate new keypair",
|
||||
"import-using-phrase": "Import using recovery phrase",
|
||||
"import-from-keycard": "Import from Keycard",
|
||||
|
|
Loading…
Reference in New Issue