chore(wallet): reset input on send flow when swapping accounts (#20099)

This commit is contained in:
Jamie Caprani 2024-05-24 13:31:34 +02:00 committed by GitHub
parent d2fb6c7ee0
commit 3a728fb112
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 272 additions and 266 deletions

View File

@ -8,8 +8,7 @@
(h/render-with-theme-provider component :light)) (h/render-with-theme-provider component :light))
(def props (def props
{:theme :light {:state :default
:state :default
:label "Mainnet" :label "Mainnet"
:network-image 873 :network-image 873
:customization-color :blue :customization-color :blue

View File

@ -40,7 +40,6 @@
[:catn [:catn
[:props [:props
[:map {:closed true} [:map {:closed true}
[:theme :schema.common/theme]
[:network-image :int] [:network-image :int]
[:label :string] [:label :string]
[:fiat-value :string] [:fiat-value :string]

View File

@ -23,11 +23,11 @@
:add-divider? true :add-divider? true
:on-press #(rf/dispatch [:navigate-to :screen/wallet.enter-seed-phrase :on-press #(rf/dispatch [:navigate-to :screen/wallet.enter-seed-phrase
{:recovering-keypair? true}])} {:recovering-keypair? true}])}
{:icon :i/key (when (ff/enabled? ::wallet.import-private-key)
:accessibility-label :import-private-key {:icon :i/key
:label (i18n/label :t/import-private-key) :accessibility-label :import-private-key
:on-press (when (ff/enabled? ::wallet.import-private-key) :label (i18n/label :t/import-private-key)
#(rf/dispatch [:navigate-to :screen/wallet.import-private-key]))}]]]) :on-press #(rf/dispatch [:navigate-to :screen/wallet.import-private-key])})]]])
(defn- parse-accounts (defn- parse-accounts
[given-accounts] [given-accounts]

View File

@ -18,6 +18,7 @@
:network-name :mainnet :network-name :mainnet
:chain-id 1 :chain-id 1
:related-chain-id 5}] :related-chain-id 5}]
:wallet/current-viewing-account-address "0x1"
:wallet/current-viewing-account {:path "m/44'/60'/0'/0/1" :wallet/current-viewing-account {:path "m/44'/60'/0'/0/1"
:emoji "💎" :emoji "💎"
:key-uid "0x2f5ea39" :key-uid "0x2f5ea39"

View File

@ -6,7 +6,6 @@
[quo.theme] [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]
[reagent.core :as reagent]
[status-im.common.controlled-input.utils :as controlled-input] [status-im.common.controlled-input.utils :as controlled-input]
[status-im.contexts.wallet.common.account-switcher.view :as account-switcher] [status-im.contexts.wallet.common.account-switcher.view :as account-switcher]
[status-im.contexts.wallet.common.asset-list.view :as asset-list] [status-im.contexts.wallet.common.asset-list.view :as asset-list]
@ -97,7 +96,7 @@
[rn/view [rn/view
[quo/icon :i/alert [quo/icon :i/alert
{:size 16 {:size 16
:color colors/danger-50}]] :color (colors/resolve-color :danger theme)}]]
[rn/view {:style style/token-not-available-content-container} [rn/view {:style style/token-not-available-content-container}
[quo/text [quo/text
{:style (style/token-not-available-text theme) {:style (style/token-not-available-text theme)
@ -105,7 +104,7 @@
(i18n/label :t/token-not-available-on-receiver-networks {:token-symbol token-symbol})] (i18n/label :t/token-not-available-on-receiver-networks {:token-symbol token-symbol})]
[quo/button [quo/button
{:size 24 {:size 24
:customization-color colors/danger-50 :customization-color (colors/resolve-color :danger theme)
:on-press add-token-networks} :on-press add-token-networks}
(i18n/label :t/add-networks-token-can-be-sent-to {:token-symbol token-symbol})]]])) (i18n/label :t/add-networks-token-can-be-sent-to {:token-symbol token-symbol})]]]))
@ -140,258 +139,266 @@
initial-crypto-currency? :initial-crypto-currency? initial-crypto-currency? :initial-crypto-currency?
:or {initial-crypto-currency? true}}] :or {initial-crypto-currency? true}}]
(let [_ (rn/dismiss-keyboard!) (let [_ (rn/dismiss-keyboard!)
bottom (safe-area/get-bottom) bottom (safe-area/get-bottom)
crypto-currency? (reagent/atom initial-crypto-currency?) [crypto-currency? set-crypto-currency] (rn/use-state initial-crypto-currency?)
on-navigate-back on-navigate-back] on-navigate-back on-navigate-back
(fn [] [input-state set-input-state] (rn/use-state controlled-input/init-state)
(let [[input-state set-input-state] (rn/use-state controlled-input/init-state) [just-toggled-mode? set-just-toggled-mode?] (rn/use-state false)
[just-toggled-mode? set-just-toggled-mode?] (rn/use-state false) clear-input! #(set-input-state controlled-input/delete-all)
clear-input! #(set-input-state controlled-input/delete-all) handle-on-confirm (fn []
handle-on-confirm (fn [] (rf/dispatch [:wallet/set-token-amount-to-send
(rf/dispatch [:wallet/set-token-amount-to-send {:amount
{:amount (controlled-input/input-value
(controlled-input/input-value input-state)
input-state) :stack-id current-screen-id}]))
:stack-id current-screen-id}])) {fiat-currency :currency} (rf/sub [:profile/profile])
{fiat-currency :currency} (rf/sub [:profile/profile]) {token-symbol :symbol
{token-symbol :symbol token-networks :networks
token-networks :networks token-decimals :decimals
token-decimals :decimals :as
:as token} (rf/sub [:wallet/wallet-send-token])
token} (rf/sub [:wallet/wallet-send-token]) send-enabled-networks (rf/sub [:wallet/wallet-send-enabled-networks])
send-enabled-networks (rf/sub [:wallet/wallet-send-enabled-networks]) enabled-from-chain-ids (rf/sub
enabled-from-chain-ids (rf/sub [:wallet/wallet-send-enabled-from-chain-ids])
[:wallet/wallet-send-enabled-from-chain-ids]) {token-balance :total-balance
{token-balance :total-balance available-balance :available-balance} (rf/sub [:wallet/token-by-symbol
available-balance :available-balance} (rf/sub [:wallet/token-by-symbol (str token-symbol)
(str token-symbol) enabled-from-chain-ids])
enabled-from-chain-ids]) currency-symbol (rf/sub [:profile/currency-symbol])
currency-symbol (rf/sub [:profile/currency-symbol]) currency (rf/sub [:profile/currency])
currency (rf/sub [:profile/currency]) conversion-rate (-> token
conversion-rate (-> token :market-values-per-currency currency :price) :market-values-per-currency
loading-routes? (rf/sub currency
[:wallet/wallet-send-loading-suggested-routes?]) :price)
route (rf/sub [:wallet/wallet-send-route]) loading-routes? (rf/sub
on-confirm (or default-on-confirm handle-on-confirm) [:wallet/wallet-send-loading-suggested-routes?])
crypto-decimals (or token-decimals default-crypto-decimals) route (rf/sub [:wallet/wallet-send-route])
current-crypto-limit (or default-limit-crypto on-confirm (or default-on-confirm handle-on-confirm)
(utils/get-standard-crypto-format crypto-decimals (or token-decimals default-crypto-decimals)
token current-crypto-limit (or default-limit-crypto
token-balance)) (utils/get-standard-crypto-format
available-crypto-limit (or default-limit-crypto token
(utils/get-standard-crypto-format token-balance))
token available-crypto-limit (or default-limit-crypto
available-balance)) (utils/get-standard-crypto-format
current-fiat-limit (.toFixed (* token-balance conversion-rate) 2) token
available-fiat-limit (.toFixed (* available-balance conversion-rate) 2) available-balance))
current-limit (if @crypto-currency? current-fiat-limit (.toFixed (* token-balance conversion-rate) 2)
current-crypto-limit available-fiat-limit (.toFixed (* available-balance conversion-rate) 2)
current-fiat-limit) current-limit (if crypto-currency?
available-limit (if @crypto-currency? current-crypto-limit
available-crypto-limit current-fiat-limit)
available-fiat-limit) available-limit (if crypto-currency?
valid-input? (not (or (string/blank? available-crypto-limit
(controlled-input/input-value available-fiat-limit)
input-state)) valid-input? (not (or (string/blank?
(<= (controlled-input/numeric-value (controlled-input/input-value
input-state) input-state))
0) (<= (controlled-input/numeric-value
(> (controlled-input/numeric-value input-state)
input-state) 0)
available-limit))) (> (controlled-input/numeric-value
input-num-value (controlled-input/numeric-value input-state) input-state)
input-amount (controlled-input/input-value input-state) available-limit)))
confirm-disabled? (or (nil? route) input-num-value (controlled-input/numeric-value input-state)
(empty? route) input-amount (controlled-input/input-value input-state)
(string/blank? (controlled-input/input-value confirm-disabled? (or (nil? route)
input-state)) (empty? route)
(<= input-num-value 0) (string/blank? (controlled-input/input-value
(> input-num-value current-limit)) input-state))
amount (if @crypto-currency? (<= input-num-value 0)
input-amount (> input-num-value current-limit))
(number/remove-trailing-zeroes amount (if crypto-currency?
(.toFixed (/ input-amount conversion-rate) input-amount
crypto-decimals))) (number/remove-trailing-zeroes
send-amount (rf/sub [:wallet/wallet-send-amount]) (.toFixed (/ input-amount conversion-rate)
amount-text (str (number/remove-trailing-zeroes crypto-decimals)))
(.toFixed (js/parseFloat send-amount) send-amount (rf/sub [:wallet/wallet-send-amount])
(min token-decimals 6))) amount-text (str (number/remove-trailing-zeroes
" " (.toFixed (js/parseFloat send-amount)
token-symbol) (min token-decimals 6)))
first-route (first route) " "
native-currency-symbol (when-not confirm-disabled? token-symbol)
(get-in first-route first-route (first route)
[:from :native-currency-symbol])) native-currency-symbol (when-not confirm-disabled?
native-token (when native-currency-symbol (get-in first-route
(rf/sub [:wallet/token-by-symbol [:from :native-currency-symbol]))
native-currency-symbol])) native-token (when native-currency-symbol
fee-in-native-token (when-not confirm-disabled? (rf/sub [:wallet/token-by-symbol
(send-utils/calculate-full-route-gas-fee route)) native-currency-symbol]))
fee-in-crypto-formatted (when fee-in-native-token fee-in-native-token (when-not confirm-disabled?
(utils/get-standard-crypto-format (send-utils/calculate-full-route-gas-fee route))
native-token fee-in-crypto-formatted (when fee-in-native-token
fee-in-native-token)) (utils/get-standard-crypto-format
fee-in-fiat (when-not confirm-disabled? native-token
(utils/calculate-token-fiat-value fee-in-native-token))
{:currency fiat-currency fee-in-fiat (when-not confirm-disabled?
:balance fee-in-native-token (utils/calculate-token-fiat-value
:token native-token})) {:currency fiat-currency
fee-formatted (when fee-in-fiat :balance fee-in-native-token
(utils/get-standard-fiat-format :token native-token}))
fee-in-crypto-formatted fee-formatted (when fee-in-fiat
currency-symbol (utils/get-standard-fiat-format
fee-in-fiat)) fee-in-crypto-formatted
show-select-asset-sheet #(rf/dispatch currency-symbol
[:show-bottom-sheet fee-in-fiat))
{:content (fn [] show-select-asset-sheet #(rf/dispatch
[select-asset-bottom-sheet [:show-bottom-sheet
clear-input!])}]) {:content (fn []
sender-network-values (rf/sub [select-asset-bottom-sheet
[:wallet/wallet-send-sender-network-values]) clear-input!])}])
receiver-network-values (rf/sub sender-network-values (rf/sub
[:wallet/wallet-send-receiver-network-values]) [:wallet/wallet-send-sender-network-values])
token-not-supported-in-receiver-networks? (every? #(= (:type %) :not-available) receiver-network-values (rf/sub
(filter #(not= (:type %) :add) [:wallet/wallet-send-receiver-network-values])
receiver-network-values)) token-not-supported-in-receiver-networks? (every? #(= (:type %) :not-available)
suggested-routes (rf/sub [:wallet/wallet-send-suggested-routes]) (filter #(not= (:type %) :add)
routes (when suggested-routes receiver-network-values))
(or (:best suggested-routes) [])) suggested-routes (rf/sub [:wallet/wallet-send-suggested-routes])
no-routes-found? (and routes (when suggested-routes
(every-network-value-is-zero? (or (:best suggested-routes) []))
sender-network-values) no-routes-found? (and
(not (nil? routes)) (every-network-value-is-zero?
(not loading-routes?) sender-network-values)
(not token-not-supported-in-receiver-networks?)) (not (nil? routes))
receiver-networks (rf/sub [:wallet/wallet-send-receiver-networks]) (not loading-routes?)
receiver-preferred-networks (rf/sub (not token-not-supported-in-receiver-networks?))
[:wallet/wallet-send-receiver-preferred-networks]) receiver-networks (rf/sub [:wallet/wallet-send-receiver-networks])
receiver-preferred-networks-set (set receiver-preferred-networks) receiver-preferred-networks (rf/sub
sending-to-unpreferred-networks? (not (every? (fn [receiver-selected-network] [:wallet/wallet-send-receiver-preferred-networks])
(contains? receiver-preferred-networks-set (set receiver-preferred-networks)
receiver-preferred-networks-set sending-to-unpreferred-networks? (not (every? (fn [receiver-selected-network]
receiver-selected-network)) (contains?
receiver-networks)) receiver-preferred-networks-set
input-error (controlled-input/input-error input-state) receiver-selected-network))
limit-insufficient? (> (controlled-input/numeric-value input-state) receiver-networks))
current-limit) input-error (controlled-input/input-error input-state)
should-try-again? (and (not limit-insufficient?) no-routes-found?)] limit-insufficient? (> (controlled-input/numeric-value input-state)
(rn/use-mount current-limit)
(fn [] should-try-again? (and (not limit-insufficient?) no-routes-found?)
(let [dismiss-keyboard-fn #(when (= % "active") (rn/dismiss-keyboard!)) current-address (rf/sub [:wallet/current-viewing-account-address])]
app-keyboard-listener (.addEventListener rn/app-state "change" dismiss-keyboard-fn)] (rn/use-mount
#(.remove app-keyboard-listener)))) (fn []
(rn/use-effect (let [dismiss-keyboard-fn #(when (= % "active") (rn/dismiss-keyboard!))
(fn [] app-keyboard-listener (.addEventListener rn/app-state "change" dismiss-keyboard-fn)]
(set-input-state #(controlled-input/set-upper-limit % current-limit))) #(.remove app-keyboard-listener))))
[current-limit]) (rn/use-effect
(rn/use-effect (fn []
(fn [] (set-input-state #(controlled-input/set-upper-limit % current-limit)))
(when input-error (debounce/clear-all)) [current-limit])
(when (and limit-insufficient? routes) (rf/dispatch [:wallet/reset-network-amounts-to-zero]))) (rn/use-effect
[input-error]) (fn []
[rn/view (when input-error (debounce/clear-all))
{:style style/screen (when (and limit-insufficient? routes) (rf/dispatch [:wallet/reset-network-amounts-to-zero])))
:accessibility-label (str "container" [input-error])
(when (controlled-input/input-error input-state) "-error"))} (rn/use-effect
[account-switcher/view (fn []
{:icon-name :i/arrow-left (clear-input!)
:on-press on-navigate-back (rf/dispatch [:wallet/clean-suggested-routes]))
:switcher-type :select-account}] [current-address])
[quo/token-input [rn/view
{:container-style style/input-container {:style style/screen
:token token-symbol :accessibility-label (str "container"
:currency fiat-currency (when (controlled-input/input-error input-state) "-error"))}
:currency-symbol currency-symbol [account-switcher/view
:crypto-decimals (min token-decimals 6) {:icon-name :i/arrow-left
:error? (controlled-input/input-error input-state) :on-press on-navigate-back
:networks (seq send-enabled-networks) :switcher-type :select-account}]
:title (i18n/label [quo/token-input
:t/send-limit {:container-style style/input-container
{:limit (if @crypto-currency? :token token-symbol
(make-limit-label-crypto current-limit token-symbol) :currency fiat-currency
(make-limit-label-fiat current-limit currency-symbol))}) :currency-symbol currency-symbol
:conversion conversion-rate :crypto-decimals (min token-decimals 6)
:show-keyboard? false :error? (controlled-input/input-error input-state)
:value input-amount :networks (seq send-enabled-networks)
:on-swap (fn [swap-to-crypto-currency?] :title (i18n/label
(set-just-toggled-mode? true) :t/send-limit
(reset! crypto-currency? swap-to-crypto-currency?) {:limit (if crypto-currency?
(set-input-state (make-limit-label-crypto current-limit token-symbol)
(fn [input-state] (make-limit-label-fiat current-limit currency-symbol))})
(controlled-input/set-input-value :conversion conversion-rate
input-state :show-keyboard? false
(let [value (controlled-input/input-value input-state) :value input-amount
new-value (if swap-to-crypto-currency? :on-swap (fn [swap-to-crypto-currency?]
(.toFixed (/ value conversion-rate) (set-just-toggled-mode? true)
crypto-decimals) (set-crypto-currency swap-to-crypto-currency?)
(.toFixed (* value conversion-rate) 12))] (set-input-state
(number/remove-trailing-zeroes new-value)))))) (fn [input-state]
:on-token-press show-select-asset-sheet}] (controlled-input/set-input-value
[routes/view input-state
{:token token (let [value (controlled-input/input-value input-state)
:input-value input-amount new-value (if swap-to-crypto-currency?
:value amount (.toFixed (/ value conversion-rate)
:valid-input? valid-input? crypto-decimals)
:token-not-supported-in-receiver-networks? token-not-supported-in-receiver-networks? (.toFixed (* value conversion-rate) 12))]
:lock-fetch-routes? just-toggled-mode? (number/remove-trailing-zeroes new-value))))))
:current-screen-id current-screen-id}] :on-token-press show-select-asset-sheet}]
(when (and (not loading-routes?) [routes/view
sender-network-values {:token token
token-not-supported-in-receiver-networks?) :input-value input-amount
[token-not-available token-symbol receiver-networks token-networks]) :value amount
(when (or loading-routes? fee-formatted) :valid-input? valid-input?
[estimated-fees :token-not-supported-in-receiver-networks? token-not-supported-in-receiver-networks?
{:loading-routes? loading-routes? :lock-fetch-routes? just-toggled-mode?
:fees fee-formatted :current-screen-id current-screen-id}]
:amount amount-text}]) (when (and (not loading-routes?)
(when (and (or no-routes-found? limit-insufficient?) (not-empty sender-network-values)) sender-network-values
[no-routes-found]) token-not-supported-in-receiver-networks?)
[quo/bottom-actions [token-not-available token-symbol receiver-networks token-networks])
{:actions :one-action (when (or loading-routes? fee-formatted)
:button-one-label (if should-try-again? [estimated-fees
(i18n/label :t/try-again) {:loading-routes? loading-routes?
button-one-label) :fees fee-formatted
:button-one-props (merge (when-not should-try-again? button-one-props) :amount amount-text}])
{:disabled? (or loading-routes? (when (and (or no-routes-found? limit-insufficient?) (not-empty sender-network-values))
(and (not should-try-again?) confirm-disabled?)) [no-routes-found])
:on-press (cond [quo/bottom-actions
should-try-again? {:actions :one-action
#(let [input-amount (controlled-input/input-value :button-one-label (if should-try-again?
input-state) (i18n/label :t/try-again)
amount (if @crypto-currency? button-one-label)
input-amount :button-one-props (merge (when-not should-try-again? button-one-props)
(.toFixed (* token-balance {:disabled? (or loading-routes?
conversion-rate) (and (not should-try-again?) confirm-disabled?))
2))] :on-press (cond
(rf/dispatch [:wallet/get-suggested-routes should-try-again?
{:amount amount}])) #(let [input-amount (controlled-input/input-value
sending-to-unpreferred-networks? input-state)
#(show-unpreferred-networks-alert on-confirm) amount (if crypto-currency?
:else input-amount
on-confirm)} (.toFixed (* token-balance
(when should-try-again? conversion-rate)
{:type :grey}))}] 2))]
[quo/numbered-keyboard (rf/dispatch [:wallet/get-suggested-routes
{:container-style (style/keyboard-container bottom) {:amount amount}]))
:left-action :dot sending-to-unpreferred-networks?
:delete-key? true #(show-unpreferred-networks-alert on-confirm)
:on-press (fn [c] :else
(let [new-text (str input-amount c) on-confirm)}
max-decimals (if @crypto-currency? crypto-decimals 2) (when should-try-again?
regex-pattern (str "^\\d*\\.?\\d{0," max-decimals "}$") {:type :grey}))}]
regex (re-pattern regex-pattern)] [quo/numbered-keyboard
(when (and (not loading-routes?) {:container-style (style/keyboard-container bottom)
(re-matches regex new-text)) :left-action :dot
(debounce/clear-all) :delete-key? true
(set-just-toggled-mode? false) :on-press (fn [c]
(set-input-state #(controlled-input/add-character % c))))) (let [new-text (str input-amount c)
:on-delete (fn [] max-decimals (if crypto-currency? crypto-decimals 2)
(when-not loading-routes? regex-pattern (str "^\\d*\\.?\\d{0," max-decimals "}$")
(debounce/clear-all) regex (re-pattern regex-pattern)]
(set-just-toggled-mode? false) (when (and (not loading-routes?)
(set-input-state controlled-input/delete-last))) (re-matches regex new-text))
:on-long-press-delete (fn [] (debounce/clear-all)
(when-not loading-routes? (set-just-toggled-mode? false)
(debounce/clear-all) (set-input-state #(controlled-input/add-character % c)))))
(set-just-toggled-mode? false) :on-delete (fn []
(set-input-state controlled-input/delete-all)))}]])))) (when-not loading-routes?
(debounce/clear-all)
(set-just-toggled-mode? false)
(set-input-state controlled-input/delete-last)))
:on-long-press-delete (fn []
(when-not loading-routes?
(debounce/clear-all)
(set-just-toggled-mode? false)
(set-input-state controlled-input/delete-all)))}]]))