feat(wallet): Add a new saved address with ENS (#20384)

This commit adds a feature to add new saved addresses with ENS

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
This commit is contained in:
Mohamed Javid 2024-06-07 19:47:06 +05:30 committed by GitHub
parent cedd4900d5
commit a3e713bbf0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 139 additions and 94 deletions

View File

@ -8,6 +8,7 @@
[status-im.common.floating-button-page.view :as floating-button-page] [status-im.common.floating-button-page.view :as floating-button-page]
[status-im.contexts.settings.wallet.saved-addresses.add-address-to-save.style :as style] [status-im.contexts.settings.wallet.saved-addresses.add-address-to-save.style :as style]
[status-im.contexts.wallet.common.validation :as validation] [status-im.contexts.wallet.common.validation :as validation]
[utils.debounce :as debounce]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
@ -18,7 +19,7 @@
(defn- validate-input (defn- validate-input
[account-addresses saved-addresses user-input] [account-addresses saved-addresses user-input]
(cond (cond
(or (nil? user-input) (= user-input "")) (string/blank? user-input)
nil nil
(contains? saved-addresses user-input) (contains? saved-addresses user-input)
@ -89,7 +90,7 @@
(defn- existing-saved-address (defn- existing-saved-address
[{:keys [address]}] [{:keys [address]}]
(let [{:keys [name customization-color chain-short-names ens has-ens?]} (let [{:keys [name customization-color chain-short-names ens ens?]}
(rf/sub [:wallet/saved-address-by-address (rf/sub [:wallet/saved-address-by-address
address])] address])]
[rn/view {:style style/existing-saved-address-container} [rn/view {:style style/existing-saved-address-container}
@ -103,43 +104,65 @@
:active-state? true :active-state? true
:user-props {:name name :user-props {:name name
:address (str chain-short-names address) :address (str chain-short-names address)
:ens (when has-ens? ens) :ens (when ens? ens)
:customization-color customization-color :customization-color customization-color
:blur? true} :blur? true}
:container-style style/saved-address-item}]])) :container-style style/saved-address-item}]]))
(defn view (defn view
[] []
(let [profile-color (rf/sub [:profile/customization-color]) (let [profile-color (rf/sub [:profile/customization-color])
accounts-addresses (rf/sub [:wallet/addresses]) accounts-addresses (rf/sub [:wallet/addresses])
saved-addresses-addresses (rf/sub [:wallet/saved-addresses-addresses]) saved-addresses-addresses (rf/sub [:wallet/saved-addresses-addresses])
[input-value set-input-value] (rn/use-state nil) [address-or-ens set-address-or-ens] (rn/use-state "")
[error set-error] (rn/use-state nil) [ens-address set-ens-address] (rn/use-state "")
error? (some? error) [error set-error] (rn/use-state nil)
validate #(validate-input accounts-addresses error? (some? error)
saved-addresses-addresses ens-name? (validation/ens-name? address-or-ens)
%) address (if ens-name? ens-address address-or-ens)
clear-input (rn/use-callback button-disabled? (or (string/blank? address-or-ens)
(fn [] (and ens-name? (string/blank? ens-address))
(set-input-value nil) error?)
(set-error nil))) validate #(validate-input accounts-addresses
on-change-text (rn/use-callback saved-addresses-addresses
(fn [new-value] %)
(let [lowercase-value (string/lower-case new-value)] clear-input (rn/use-callback
(set-error (validate lowercase-value)) (fn []
(set-input-value lowercase-value)))) (set-address-or-ens "")
paste-into-input (rn/use-callback #(clipboard/get-string (set-ens-address "")
(fn [clipboard-text] (set-error nil)))
(on-change-text clipboard-text)))) on-ens-resolve (rn/use-callback
on-press-continue (rn/use-callback (fn [resolved-address]
(fn [] (set-error (validate resolved-address))
(rf/dispatch [:wallet/set-address-to-save (set-ens-address resolved-address)))
{:address input-value}]) on-change-text (rn/use-callback
(rf/dispatch (fn [new-value]
[:navigate-to-within-stack (let [trimmed-value (string/trim new-value)]
[:screen/settings.save-address (set-error (validate (string/lower-case trimmed-value)))
:screen/settings.add-address-to-save]])) (set-address-or-ens trimmed-value)
[input-value])] (set-ens-address "")
(when (validation/ens-name? trimmed-value)
(debounce/debounce-and-dispatch
[:wallet/resolve-ens
{:ens new-value
:on-success on-ens-resolve
:on-error #(set-error :ens-not-registered)}]
300)))))
paste-into-input (rn/use-callback #(clipboard/get-string
(fn [clipboard-text]
(on-change-text clipboard-text))))
on-press-continue (rn/use-callback
(fn []
(rf/dispatch
[:wallet/set-address-to-save
{:address address
:ens address-or-ens
:ens? ens-name?}])
(rf/dispatch
[:navigate-to-within-stack
[:screen/settings.save-address
:screen/settings.add-address-to-save]]))
[address ens-name? address-or-ens])]
(rn/use-unmount #(rf/dispatch [:wallet/clear-address-to-save])) (rn/use-unmount #(rf/dispatch [:wallet/clear-address-to-save]))
[quo/overlay {:type :shell} [quo/overlay {:type :shell}
[floating-button-page/view [floating-button-page/view
@ -153,8 +176,7 @@
:accessibility-label :add-address-to-save-page-nav}] :accessibility-label :add-address-to-save-page-nav}]
:footer [quo/button :footer [quo/button
{:customization-color profile-color {:customization-color profile-color
:disabled? (or (string/blank? input-value) :disabled? button-disabled?
error?)
:on-press on-press-continue} :on-press on-press-continue}
(i18n/label :t/continue)]} (i18n/label :t/continue)]}
[quo/page-top [quo/page-top
@ -164,7 +186,7 @@
:description :text :description :text
:description-text (i18n/label :t/add-address-to-save-description)}] :description-text (i18n/label :t/add-address-to-save-description)}]
[address-input [address-input
{:input-value input-value {:input-value address-or-ens
:on-change-text on-change-text :on-change-text on-change-text
:paste-into-input paste-into-input :paste-into-input paste-into-input
:clear-input clear-input}] :clear-input clear-input}]
@ -172,4 +194,4 @@
[:<> [:<>
[error-view {:error error}] [error-view {:error error}]
(when (= error :existing-saved-address) (when (= error :existing-saved-address)
[existing-saved-address {:address input-value}])])]])) [existing-saved-address {:address address}])])]]))

View File

@ -55,7 +55,7 @@
:address "0x2" :address "0x2"
:mixedcase-address "0x2" :mixedcase-address "0x2"
:chain-short-names "eth:arb1:oeth:" :chain-short-names "eth:arb1:oeth:"
:has-ens? false :ens? false
:network-preferences-names `(:mainnet :arbitrum :optimism) :network-preferences-names `(:mainnet :arbitrum :optimism)
:name "Bob" :name "Bob"
:created-at 1716826714 :created-at 1716826714
@ -75,7 +75,7 @@
:mixedcase-address "0x2" :mixedcase-address "0x2"
:chain-short-names "eth:arb1:oeth:" :chain-short-names "eth:arb1:oeth:"
:network-preferences-names `(:mainnet :arbitrum :optimism) :network-preferences-names `(:mainnet :arbitrum :optimism)
:has-ens? false :ens? false
:name "Bob" :name "Bob"
:created-at 1716826714 :created-at 1716826714
:ens "" :ens ""
@ -86,7 +86,7 @@
:mixedcase-address "0x1" :mixedcase-address "0x1"
:chain-short-names "eth:arb1:oeth:" :chain-short-names "eth:arb1:oeth:"
:network-preferences-names `(:mainnet :arbitrum :optimism) :network-preferences-names `(:mainnet :arbitrum :optimism)
:has-ens? false :ens? false
:name "Amy" :name "Amy"
:created-at 1716826806 :created-at 1716826806
:ens "" :ens ""

View File

@ -35,7 +35,7 @@
(defn view (defn view
[] []
(let [{:keys [address]} (rf/sub [:wallet/saved-address]) (let [{:keys [address ens ens?]} (rf/sub [:wallet/saved-address])
[network-prefixes address-without-prefix] (utils/split-prefix-and-address address) [network-prefixes address-without-prefix] (utils/split-prefix-and-address address)
[address-label set-address-label] (rn/use-state "") [address-label set-address-label] (rn/use-state "")
[address-color set-address-color] (rn/use-state (rand-nth colors/account-colors)) [address-color set-address-color] (rn/use-state (rand-nth colors/account-colors))
@ -46,6 +46,13 @@
selected-networks) selected-networks)
[selected-networks]) [selected-networks])
placeholder (i18n/label :t/address-name) placeholder (i18n/label :t/address-name)
address-text (rn/use-callback
(fn []
[quo/address-text
{:full-address? true
:address (str chain-short-names address-without-prefix)
:format :long}])
[address-without-prefix chain-short-names])
open-network-preferences (rn/use-callback open-network-preferences (rn/use-callback
(fn [] (fn []
(rf/dispatch (rf/dispatch
@ -68,11 +75,29 @@
:on-error :on-error
[:wallet/add-saved-address-failed] [:wallet/add-saved-address-failed]
:name address-label :name address-label
:ens ens
:address address-without-prefix :address address-without-prefix
:customization-color address-color :customization-color address-color
:chain-short-names chain-short-names}])) :chain-short-names chain-short-names}]))
[address-without-prefix chain-short-names address-label [address-without-prefix chain-short-names address-label
address-color])] address-color])
data-item-props (rn/use-memo
#(cond-> {:status :default
:size :default
:subtitle-type :default
:label :none
:blur? true
:icon-right? true
:right-icon :i/advanced
:card? true
:title (i18n/label :t/address)
:subtitle ens
:custom-subtitle address-text
:on-press open-network-preferences
:container-style style/data-item}
ens?
(dissoc :custom-subtitle))
[ens ens? open-network-preferences address-text])]
[quo/overlay {:type :shell} [quo/overlay {:type :shell}
[floating-button-page/view [floating-button-page/view
{:footer-container-padding 0 {:footer-container-padding 0
@ -125,22 +150,4 @@
[quo/divider-line [quo/divider-line
{:blur? true {:blur? true
:container-style style/color-picker-bottom-divider}] :container-style style/color-picker-bottom-divider}]
[quo/data-item [quo/data-item data-item-props]]]))
{:status :default
:size :default
:subtitle-type :default
:label :none
:blur? true
:icon-right? true
:right-icon :i/advanced
:card? true
:title (i18n/label :t/address)
:custom-subtitle (rn/use-callback
(fn []
[quo/address-text
{:full-address? true
:address (str chain-short-names address-without-prefix)
:format :long}])
[selected-networks address])
:on-press open-network-preferences
:container-style style/data-item}]]]))

View File

@ -2,6 +2,7 @@
(:require (:require
[clojure.string :as string] [clojure.string :as string]
[quo.core :as quo] [quo.core :as quo]
[quo.foundations.colors :as colors]
[react-native.core :as rn] [react-native.core :as rn]
[react-native.platform :as platform] [react-native.platform :as platform]
[status-im.common.not-implemented :as not-implemented] [status-im.common.not-implemented :as not-implemented]
@ -52,10 +53,11 @@
open-remove-confirmation-sheet (rn/use-callback open-remove-confirmation-sheet (rn/use-callback
#(rf/dispatch #(rf/dispatch
[:show-bottom-sheet [:show-bottom-sheet
{:theme :dark {:theme :dark
:shell? true :shell? true
:content (fn [] :blur-background colors/bottom-sheet-background-blur
[remove-address/view opts])}]) :content (fn []
[remove-address/view opts])}])
[opts]) [opts])
open-show-address-qr (rn/use-callback open-show-address-qr (rn/use-callback
#(rf/dispatch [:open-modal #(rf/dispatch [:open-modal

View File

@ -1,6 +1,7 @@
(ns status-im.contexts.settings.wallet.saved-addresses.view (ns status-im.contexts.settings.wallet.saved-addresses.view
(:require (:require
[quo.core :as quo] [quo.core :as quo]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme] [quo.theme :as quo.theme]
[react-native.core :as rn] [react-native.core :as rn]
[react-native.safe-area :as safe-area] [react-native.safe-area :as safe-area]
@ -20,27 +21,29 @@
:container-style style/empty-container-style}])) :container-style style/empty-container-style}]))
(defn- saved-address (defn- saved-address
[{:keys [name address chain-short-names customization-color has-ens? ens network-preferences-names]}] [{:keys [name address chain-short-names customization-color ens? ens network-preferences-names]}]
(let [full-address (str chain-short-names address) (let [full-address (str chain-short-names address)
on-press-saved-address (rn/use-callback on-press-saved-address (rn/use-callback
#(rf/dispatch #(rf/dispatch
[:show-bottom-sheet [:show-bottom-sheet
{:theme :dark {:theme :dark
:shell? true :shell? true
:content (fn [] :blur-background colors/bottom-sheet-background-blur
[address-options/view :content (fn []
{:address address [address-options/view
:chain-short-names chain-short-names {:address address
:full-address full-address :chain-short-names chain-short-names
:name name :full-address full-address
:network-preferences-names network-preferences-names :name name
:customization-color customization-color}])}]) :network-preferences-names
network-preferences-names
:customization-color customization-color}])}])
[address chain-short-names full-address name customization-color])] [address chain-short-names full-address name customization-color])]
[quo/saved-address [quo/saved-address
{:blur? true {:blur? true
:user-props {:name name :user-props {:name name
:address full-address :address full-address
:ens (when has-ens? ens) :ens (when ens? ens)
:customization-color customization-color :customization-color customization-color
:blur? true} :blur? true}
:container-style {:margin-horizontal 8} :container-style {:margin-horizontal 8}
@ -88,12 +91,13 @@
:icon :i/add :icon :i/add
:container-style style/title-container}] :container-style style/title-container}]
[rn/section-list [rn/section-list
{:key-fn :title {:key-fn :title
:sticky-section-headers-enabled false :shows-vertical-scroll-indicator false
:render-section-header-fn header :sticky-section-headers-enabled false
:render-section-footer-fn footer :render-section-header-fn header
:sections saved-addresses :render-section-footer-fn footer
:render-fn saved-address :sections saved-addresses
:separator [rn/view {:style {:height 4}}] :render-fn saved-address
:content-container-style {:flex-grow 1} :separator [rn/view {:style {:height 4}}]
:empty-component [empty-state]}]])) :content-container-style {:flex-grow 1}
:empty-component [empty-state]}]]))

View File

@ -1,6 +1,6 @@
(ns status-im.contexts.wallet.common.validation (ns status-im.contexts.wallet.common.validation
(:require [status-im.constants :as constants])) (:require [status-im.constants :as constants]))
(defn ens-name? [s] (re-find constants/regx-ens s)) (defn ens-name? [s] (boolean (re-find constants/regx-ens s)))
(defn eth-address? [s] (re-find constants/regx-multichain-address s)) (defn eth-address? [s] (re-find constants/regx-multichain-address s))
(defn private-key? [s] (re-find constants/regx-private-key s)) (defn private-key? [s] (re-find constants/regx-private-key s))

View File

@ -143,7 +143,7 @@
(-> saved-address (-> saved-address
(assoc :network-preferences-names (assoc :network-preferences-names
(network-utils/network-preference-prefix->network-names (:chain-short-names saved-address))) (network-utils/network-preference-prefix->network-names (:chain-short-names saved-address)))
(assoc :has-ens? (not (string/blank? (:ens saved-address)))))) (assoc :ens? (not (string/blank? (:ens saved-address))))))
(defn rpc->saved-address (defn rpc->saved-address
[saved-address] [saved-address]

View File

@ -526,3 +526,13 @@
:on-error #(log/info "failed to fetch crypto on ramps" :on-error #(log/info "failed to fetch crypto on ramps"
{:error % {:error %
:event :wallet/get-crypto-on-ramps})}]]]})) :event :wallet/get-crypto-on-ramps})}]]]}))
(rf/reg-event-fx
:wallet/resolve-ens
(fn [{db :db} [{:keys [ens on-success on-error]}]]
(let [chain-id (network-utils/network->chain-id db constants/mainnet-network-name)]
{:fx [[:json-rpc/call
[{:method "ens_addressOf"
:params [chain-id ens]
:on-success on-success
:on-error on-error}]]]})))

View File

@ -9,7 +9,7 @@
:address "0x1" :address "0x1"
:mixedcase-address "0x1" :mixedcase-address "0x1"
:chain-short-names "eth:arb1:oeth:" :chain-short-names "eth:arb1:oeth:"
:has-ens? false :ens? false
:network-preferences-names #{:arbitrum :network-preferences-names #{:arbitrum
:optimism :optimism
:mainnet} :mainnet}
@ -22,7 +22,7 @@
:address "0x2" :address "0x2"
:mixedcase-address "0x2" :mixedcase-address "0x2"
:chain-short-names "eth:" :chain-short-names "eth:"
:has-ens? false :ens? false
:network-preferences-names #{:mainnet} :network-preferences-names #{:mainnet}
:name "Bob" :name "Bob"
:created-at 1716828825 :created-at 1716828825
@ -33,7 +33,7 @@
:address "0x1" :address "0x1"
:mixedcase-address "0x1" :mixedcase-address "0x1"
:chain-short-names "eth:" :chain-short-names "eth:"
:has-ens? false :ens? false
:network-preferences-names #{:mainnet} :network-preferences-names #{:mainnet}
:name "Alice" :name "Alice"
:created-at 1716826745 :created-at 1716826745
@ -44,7 +44,7 @@
:address "0x2" :address "0x2"
:mixedcase-address "0x2" :mixedcase-address "0x2"
:chain-short-names "eth:arb1:oeth:" :chain-short-names "eth:arb1:oeth:"
:has-ens? false :ens? false
:network-preferences-names #{:arbitrum :network-preferences-names #{:arbitrum
:optimism :optimism
:mainnet} :mainnet}
@ -61,7 +61,7 @@
:address "0x1" :address "0x1"
:mixedcase-address "0x1" :mixedcase-address "0x1"
:chain-short-names "eth:" :chain-short-names "eth:"
:has-ens? false :ens? false
:network-preferences-names #{:mainnet} :network-preferences-names #{:mainnet}
:name "Alice" :name "Alice"
:created-at 1716826745 :created-at 1716826745
@ -74,7 +74,7 @@
:address "0x2" :address "0x2"
:mixedcase-address "0x2" :mixedcase-address "0x2"
:chain-short-names "eth:arb1:oeth:" :chain-short-names "eth:arb1:oeth:"
:has-ens? false :ens? false
:network-preferences-names #{:arbitrum :network-preferences-names #{:arbitrum
:optimism :optimism
:mainnet} :mainnet}