diff --git a/resources/images/icons2/12x12/face-id@2x.png b/resources/images/icons2/12x12/face-id@2x.png new file mode 100644 index 0000000000..f9b5d8023b Binary files /dev/null and b/resources/images/icons2/12x12/face-id@2x.png differ diff --git a/resources/images/icons2/12x12/face-id@3x.png b/resources/images/icons2/12x12/face-id@3x.png new file mode 100644 index 0000000000..99f905cf10 Binary files /dev/null and b/resources/images/icons2/12x12/face-id@3x.png differ diff --git a/resources/images/icons2/16x16/scanning@2x.png b/resources/images/icons2/16x16/scanning@2x.png new file mode 100644 index 0000000000..509b02716a Binary files /dev/null and b/resources/images/icons2/16x16/scanning@2x.png differ diff --git a/resources/images/icons2/16x16/scanning@3x.png b/resources/images/icons2/16x16/scanning@3x.png new file mode 100644 index 0000000000..b8ea24dead Binary files /dev/null and b/resources/images/icons2/16x16/scanning@3x.png differ diff --git a/src/status_im/contexts/wallet/add_address_to_watch/view.cljs b/src/status_im/contexts/wallet/add_address_to_watch/view.cljs index cce40d2d64..0de89e906a 100644 --- a/src/status_im/contexts/wallet/add_address_to_watch/view.cljs +++ b/src/status_im/contexts/wallet/add_address_to_watch/view.cljs @@ -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 diff --git a/src/status_im/contexts/wallet/create_account/edit_derivation_path/component_spec.cljs b/src/status_im/contexts/wallet/create_account/edit_derivation_path/component_spec.cljs deleted file mode 100644 index 4ff8fd23f2..0000000000 --- a/src/status_im/contexts/wallet/create_account/edit_derivation_path/component_spec.cljs +++ /dev/null @@ -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)))))) diff --git a/src/status_im/contexts/wallet/create_account/edit_derivation_path/style.cljs b/src/status_im/contexts/wallet/create_account/edit_derivation_path/style.cljs index 614901223c..f9ce232cc9 100644 --- a/src/status_im/contexts/wallet/create_account/edit_derivation_path/style.cljs +++ b/src/status_im/contexts/wallet/create_account/edit_derivation_path/style.cljs @@ -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}) diff --git a/src/status_im/contexts/wallet/create_account/edit_derivation_path/view.cljs b/src/status_im/contexts/wallet/create_account/edit_derivation_path/view.cljs index 46ff3ebaac..1dbd641fab 100644 --- a/src/status_im/contexts/wallet/create_account/edit_derivation_path/view.cljs +++ b/src/status_im/contexts/wallet/create_account/edit_derivation_path/view.cljs @@ -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)) + diff --git a/src/status_im/contexts/wallet/create_account/events.cljs b/src/status_im/contexts/wallet/create_account/events.cljs index 76982e2025..07ae02ae89 100644 --- a/src/status_im/contexts/wallet/create_account/events.cljs +++ b/src/status_im/contexts/wallet/create_account/events.cljs @@ -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) diff --git a/src/status_im/contexts/wallet/create_account/events_test.cljs b/src/status_im/contexts/wallet/create_account/events_test.cljs index f795c0c4ef..4ced4d4b38 100644 --- a/src/status_im/contexts/wallet/create_account/events_test.cljs +++ b/src/status_im/contexts/wallet/create_account/events_test.cljs @@ -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)))) diff --git a/src/status_im/contexts/wallet/create_account/view.cljs b/src/status_im/contexts/wallet/create_account/view.cljs index 7d9b506d72..c03586d60c 100644 --- a/src/status_im/contexts/wallet/create_account/view.cljs +++ b/src/status_im/contexts/wallet/create_account/view.cljs @@ -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 [_] diff --git a/src/status_im/core_spec.cljs b/src/status_im/core_spec.cljs index 673095197d..866c783af3 100644 --- a/src/status_im/core_spec.cljs +++ b/src/status_im/core_spec.cljs @@ -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])) diff --git a/src/status_im/subs/wallet/wallet.cljs b/src/status_im/subs/wallet/wallet.cljs index 8a33e4931c..9a7a9833ef 100644 --- a/src/status_im/subs/wallet/wallet.cljs +++ b/src/status_im/subs/wallet/wallet.cljs @@ -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] diff --git a/translations/en.json b/translations/en.json index c67f0468b0..9e59f594ec 100644 --- a/translations/en.json +++ b/translations/en.json @@ -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",