From e5cee4b640b52e72bd19560e120291f9b1b34b66 Mon Sep 17 00:00:00 2001 From: Omar Basem Date: Mon, 18 Mar 2024 20:44:16 +0400 Subject: [PATCH] Wallet: Derivation Path screen (#19268) Wallet: Derivation Path screen (#19268) --- src/js/worklets/chat/messenger/messages.js | 24 ++- src/js/worklets/chat/messenger/navigation.js | 8 +- .../drawers/action_drawers/view.cljs | 17 +- src/status_im/constants.cljs | 5 + .../edit_derivation_path/component_spec.cljs | 8 + .../path_format_sheet/view.cljs | 39 ++++ .../edit_derivation_path/style.cljs | 2 +- .../edit_derivation_path/view.cljs | 174 ++++++++++-------- .../contexts/wallet/create_account/view.cljs | 11 +- src/status_im/feature_flags.cljs | 1 + src/status_im/subs/profile.cljs | 7 + translations/en.json | 9 +- 12 files changed, 205 insertions(+), 100 deletions(-) create mode 100644 src/status_im/contexts/wallet/create_account/edit_derivation_path/path_format_sheet/view.cljs diff --git a/src/js/worklets/chat/messenger/messages.js b/src/js/worklets/chat/messenger/messages.js index 7f23555b63..e6c6215ab5 100644 --- a/src/js/worklets/chat/messenger/messages.js +++ b/src/js/worklets/chat/messenger/messages.js @@ -1,5 +1,5 @@ import { useAnimatedReaction, withTiming, runOnJS } from 'react-native-reanimated'; -import { useState } from "react" +import { useState } from 'react'; export function messagesListOnScroll(distanceFromListTop, chatListScrollY, callback) { return function (event) { @@ -15,15 +15,19 @@ export function messagesListOnScroll(distanceFromListTop, chatListScrollY, callb } export function useMessagesScrolledToThreshold(distanceFromListTop, threshold) { - const [scrolledToThreshold, setScrolledToThreshold] = useState(false) + const [scrolledToThreshold, setScrolledToThreshold] = useState(false); - useAnimatedReaction(function () { - return distanceFromListTop.value <= threshold; - }, function (current) { - if(current !== scrolledToThreshold) { - runOnJS(setScrolledToThreshold)(current) - } - }, [scrolledToThreshold]) + useAnimatedReaction( + function () { + return distanceFromListTop.value <= threshold; + }, + function (current) { + if (current !== scrolledToThreshold) { + runOnJS(setScrolledToThreshold)(current); + } + }, + [scrolledToThreshold], + ); - return scrolledToThreshold + return scrolledToThreshold; } diff --git a/src/js/worklets/chat/messenger/navigation.js b/src/js/worklets/chat/messenger/navigation.js index f2031e4f3a..715213216c 100644 --- a/src/js/worklets/chat/messenger/navigation.js +++ b/src/js/worklets/chat/messenger/navigation.js @@ -20,10 +20,10 @@ export function navigationHeaderPosition(distanceFromListTop, isAllLoaded, topBa } export function navigationButtonsCompleteOpacity(isCalculationComplete) { - return useDerivedValue(function () { - 'worklet' - return isCalculationComplete.value ? withTiming(1) : 0 - }) + return useDerivedValue(function () { + 'worklet'; + return isCalculationComplete.value ? withTiming(1) : 0; + }); } export function interpolateNavigationViewOpacity(props) { diff --git a/src/quo/components/drawers/action_drawers/view.cljs b/src/quo/components/drawers/action_drawers/view.cljs index 51ead43ebc..b34acdbd6c 100644 --- a/src/quo/components/drawers/action_drawers/view.cljs +++ b/src/quo/components/drawers/action_drawers/view.cljs @@ -58,14 +58,15 @@ :on-press on-press} [rn/view {:style (style/row-container sub-label)} - [rn/view - {:accessibility-label :left-icon-for-action - :accessible true - :style (style/left-icon sub-label)} - [icon/icon icon - {:color (or icon-color (get-icon-color danger? theme)) - :no-color no-icon-color? - :size 20}]] + (when icon + [rn/view + {:accessibility-label :left-icon-for-action + :accessible true + :style (style/left-icon sub-label)} + [icon/icon icon + {:color (or icon-color (get-icon-color danger? theme)) + :no-color no-icon-color? + :size 20}]]) [rn/view {:style style/text-container} [text/text diff --git a/src/status_im/constants.cljs b/src/status_im/constants.cljs index a9b8c5a877..90dd28c0ea 100644 --- a/src/status_im/constants.cljs +++ b/src/status_im/constants.cljs @@ -187,6 +187,11 @@ ; EIP1581 Chat Key 0, the default whisper key (def ^:const path-whisper (str path-eip1581 "/0'/0")) +(def ^:const path-ropsten-testnet "m/44'/1'/0") +(def ^:const path-ledger "m/44'/60'/0'") +(def ^:const path-ledger-live "m/44'/60'") +(def ^:const path-keepkey "m/44'/60'") + (def ^:const path-default-wallet-keyword (keyword path-default-wallet)) (def ^:const path-whisper-keyword (keyword path-whisper)) (def ^:const path-wallet-root-keyword (keyword path-wallet-root)) 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 index 79b5387bd1..4ff8fd23f2 100644 --- 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 @@ -3,8 +3,14 @@ [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)) @@ -14,6 +20,7 @@ (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)) @@ -21,6 +28,7 @@ (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)) diff --git a/src/status_im/contexts/wallet/create_account/edit_derivation_path/path_format_sheet/view.cljs b/src/status_im/contexts/wallet/create_account/edit_derivation_path/path_format_sheet/view.cljs new file mode 100644 index 0000000000..6475f4b334 --- /dev/null +++ b/src/status_im/contexts/wallet/create_account/edit_derivation_path/path_format_sheet/view.cljs @@ -0,0 +1,39 @@ +(ns status-im.contexts.wallet.create-account.edit-derivation-path.path-format-sheet.view + (:require + [quo.core :as quo] + [status-im.constants :as constants] + [utils.i18n :as i18n] + [utils.re-frame :as rf])) + +(defn view + [] + (let [{:keys [customization-color]} (rf/sub [:get-screen-params])] + [:<> + [quo/drawer-top {:title (i18n/label :t/path-format)}] + [quo/action-drawer + [[{:accessibility-label :default-ethereum-format + :label (i18n/label :t/default-ethereum-format) + :state :selected + :customization-color customization-color + :sub-label constants/path-wallet-root} + {:accessibility-label :ropsten-testnet + :label (i18n/label :t/ropsten-testnet) + :sub-label constants/path-ropsten-testnet + :customization-color customization-color} + {:accessibility-label :ledger + :label (i18n/label :t/ledger) + :sub-label constants/path-ledger + :customization-color customization-color} + {:accessibility-label :ledger-live + :label (i18n/label :t/ledger-live) + :sub-label constants/path-ledger-live + :customization-color customization-color} + {:accessibility-label :keepkey + :label (i18n/label :t/keep-key) + :sub-label constants/path-keepkey + :customization-color customization-color} + {:icon :i/customize + :accessibility-label :custom + :label (i18n/label :t/custom) + :sub-label (i18n/label :t/type-your-path) + :add-divider? true}]]]])) 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 1bf011d421..614901223c 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 @@ -17,7 +17,7 @@ (def input-container {:padding-horizontal 20 - :padding-top 12}) + :padding-top 20}) (defn save-button-container [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 57399099e9..4255fc305f 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,12 +1,15 @@ (ns status-im.contexts.wallet.create-account.edit-derivation-path.view (:require [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])) @@ -25,7 +28,7 @@ (when on-reveal (on-reveal))) clear-action #(reset! state :empty) - derive-action #(reset! state :choose) + 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] @@ -38,83 +41,106 @@ (when on-reset (on-reset)))] (fn [{:keys [theme]}] - [rn/view - {:style (style/screen top)} - [quo/page-nav - {:background :blur - :icon-name :i/close - :on-press #(rf/dispatch [:navigate-back])}] - [quo/text - {:size :heading-1 - :weight :semi-bold - :style style/header} - (i18n/label :t/edit-derivation-path)] - [rn/view {:style style/tag} - [quo/context-tag - {:type :icon - :size 24 - :icon :i/placeholder - :style style/tag - :context "Alisher Card"}]] - [rn/view {:style style/temporal-placeholder} - [quo/text "Dropdown input will be here"] - [quo/text (i18n/label :t/path-format)]] - [quo/input - {:container-style style/input-container - :value @path-value - :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}] + (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}])] + [rn/view + {:style (style/screen top)} + [quo/page-nav + {:background :blur + :icon-name :i/close + :on-press #(rf/dispatch [:navigate-back])}] + [rn/view {:style {:padding-bottom 20}} + [quo/text + {:size :heading-1 + :weight :semi-bold + :style style/header} + (i18n/label :t/edit-derivation-path)] + [rn/view {:style style/tag} + [quo/context-tag + {:size 24 + :profile-picture profile-picture + :style style/tag + :full-name primary-name}]]] + [rn/pressable {:on-press show-path-format-sheet} + [quo/input + {:small? true + :editable false + :placeholder (i18n/label :t/search-assets) + :right-icon {:on-press show-path-format-sheet + :icon-name :i/dropdown + :style-fn (fn [] + {:color (colors/theme-colors colors/neutral-20 + colors/neutral-80 + theme) + :color-2 (colors/theme-colors colors/neutral-100 + colors/white + theme)})} + :label (i18n/label :t/path-format) + :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 - {:theme theme - :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}}] + (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 - {:theme theme - :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}}] + :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)]] + :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) + :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) - [rn/view {:style (style/save-button-container bottom)} - [quo/bottom-actions - {:theme theme - :actions :one-action - :button-one-label (i18n/label :t/save) - :button-one-props {:type :primary - :on-press #(js/alert "Save!") - :disabled? true}}]]]))) + [rn/view {:style (style/save-button-container bottom)} + [quo/bottom-actions + {:actions :one-action + :button-one-label (i18n/label :t/save) + :button-one-props {:type :primary + :on-press #(js/alert "Save!") + :disabled? true}}]] + (when-not (= @state :show) + [quo/numbered-keyboard + {:left-action :dot + :delete-key? true}])])))) (def view (quo.theme/with-theme view-internal)) + diff --git a/src/status_im/contexts/wallet/create_account/view.cljs b/src/status_im/contexts/wallet/create_account/view.cljs index 7aa5d9b46f..e263944599 100644 --- a/src/status_im/contexts/wallet/create_account/view.cljs +++ b/src/status_im/contexts/wallet/create_account/view.cljs @@ -14,6 +14,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 :refer [iphone-11-Pro-20-pixel-from-width]] @@ -32,7 +33,7 @@ :customization-color account-color}) :action (when-not keypair-name :button) :action-props {:on-press (fn [] - (rf/dispatch [:navigate-to :scrren/wallet.select-keypair])) + (rf/dispatch [:navigate-to :screen/wallet.select-keypair])) :button-text (i18n/label :t/edit) :alignment :flex-start} :description :text @@ -41,7 +42,13 @@ :image :icon :image-props :i/derivated-path :action :button - :action-props {:on-press #(js/alert "Coming soon!") + :action-props {:on-press (fn [] + (ff/alert ::ff/wallet.network-filter + #(rf/dispatch [:navigate-to + :screen/wallet.edit-derivation-path + {:customization-color account-color}]))) + + :button-text (i18n/label :t/edit) :icon-left :i/placeholder :alignment :flex-start} diff --git a/src/status_im/feature_flags.cljs b/src/status_im/feature_flags.cljs index f58d279259..a6cf042c3d 100644 --- a/src/status_im/feature_flags.cljs +++ b/src/status_im/feature_flags.cljs @@ -12,6 +12,7 @@ (reagent/atom {::wallet.edit-default-keypair true ::wallet.bridge-token (enabled-in-env? :FLAG_BRIDGE_TOKEN_ENABLED) + ::wallet.edit-derivation-path (enabled-in-env? :FLAG_EDIT_DERIVATION_PATH) ::wallet.remove-account (enabled-in-env? :FLAG_REMOVE_ACCOUNT_ENABLED) ::wallet.network-filter (enabled-in-env? :FLAG_NETWORK_FILTER_ENABLED) ::profile.new-contact-ui (enabled-in-env? :FLAG_NEW_CONTACT_UI_ENABLED) diff --git a/src/status_im/subs/profile.cljs b/src/status_im/subs/profile.cljs index 573a4d8cbc..0e6a4f3b2b 100644 --- a/src/status_im/subs/profile.cljs +++ b/src/status_im/subs/profile.cljs @@ -9,6 +9,7 @@ [re-frame.core :as re-frame] [status-im.common.pixel-ratio :as pixel-ratio] [status-im.constants :as constants] + [status-im.contexts.profile.utils :as profile.utils] [utils.address :as address] [utils.image-server :as image-server] [utils.security.core :as security])) @@ -129,6 +130,12 @@ (fn [{:keys [preferred-name]}] preferred-name)) +(re-frame/reg-sub + :profile/image + :<- [:profile/profile-with-image] + (fn [profile] + (profile.utils/photo profile))) + (re-frame/reg-sub :multiaccount/default-account :<- [:wallet/accounts] diff --git a/translations/en.json b/translations/en.json index 38d751f7fc..00e5926227 100644 --- a/translations/en.json +++ b/translations/en.json @@ -2547,5 +2547,12 @@ "provider-is-down": "The provider for the following chain(s) is down: {{chains}}", "unknown": "Unknown", "unsupported-file": "Unsupported file", - "cant-fetch-info": "Can't fetch info" + "cant-fetch-info": "Can't fetch info", + "default-ethereum-format": "Default Ethereum format", + "ropsten-testnet": "Ropsten Testnet", + "ledger": "Ledger", + "ledger-live": "Ledger live", + "keep-key": "KeepKey", + "type-your-path": "Type your own derivation path", + "default-format": "Default format" }