Long tap on route to lock value sent from this network (#20413)

This commit is contained in:
Volodymyr Kozieiev 2024-06-18 15:42:29 +01:00 committed by GitHub
parent 24b77811d8
commit f761d87b2a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 285 additions and 114 deletions

View File

@ -31,7 +31,7 @@
:else (string/capitalize (name network))))
(defn view-internal
[{:keys [network status amount container-style on-press] :as args}]
[{:keys [network status amount container-style on-press on-long-press] :as args}]
(let [theme (quo.theme/use-theme)]
(if (= status :edit)
[network-bridge-add (assoc args :theme theme)]
@ -39,7 +39,8 @@
{:style (merge (style/container network status theme) container-style)
:accessible true
:accessibility-label :container
:on-press on-press}
:on-press on-press
:on-long-press on-long-press}
(if (= status :loading)
[rn/view
{:style (style/loading-skeleton theme)

View File

@ -127,3 +127,6 @@
[state]
(set-input-value state ""))
(defn empty-value?
[state]
(string/blank? (:value state)))

View File

@ -269,3 +269,15 @@
(some #(when (= token-symbol (:symbol %))
%)
address-tokens)))
(defn make-limit-label-crypto
[amount currency]
(str amount
" "
(some-> currency
name
string/upper-case)))
(defn make-limit-label-fiat
[amount currency-symbol]
(str currency-symbol amount))

View File

@ -36,7 +36,8 @@
receiver-network-values (get-in db [:wallet :ui :send :receiver-network-values])
sender-network-values (get-in db [:wallet :ui :send :sender-network-values])
tx-type (get-in db [:wallet :ui :send :tx-type])
disabled-from-chain-ids (or (get-in db [:wallet :ui :send :disabled-from-chain-ids]) [])
disabled-from-chain-ids (get-in db [:wallet :ui :send :disabled-from-chain-ids] [])
from-locked-amounts (get-in db [:wallet :ui :send :from-locked-amounts] {})
token-decimals (if collectible 0 (:decimals token))
native-token? (and token (= token-display-name "ETH"))
routes-available? (pos? (count chosen-route))
@ -68,6 +69,7 @@
:disabled-chain-ids disabled-from-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:from-locked-amounts from-locked-amounts
:tx-type tx-type
:receiver? false})
(send-utils/reset-loading-network-amounts-to-zero
@ -144,6 +146,10 @@
(fn [{:keys [db]}]
{:db (update-in db [:wallet :ui :send] dissoc :disabled-from-chain-ids)}))
(rf/reg-event-fx :wallet/clean-from-locked-amounts
(fn [{:keys [db]}]
{:db (update-in db [:wallet :ui :send] dissoc :from-locked-amounts)}))
(rf/reg-event-fx
:wallet/select-send-address
(fn [{:keys [db]} [{:keys [address recipient stack-id start-flow?]}]]
@ -332,6 +338,14 @@
(fn [{:keys [db]} [chain-ids]]
{:db (assoc-in db [:wallet :ui :send :disabled-from-chain-ids] chain-ids)}))
(rf/reg-event-fx :wallet/lock-from-amount
(fn [{:keys [db]} [chain-id amount]]
{:db (assoc-in db [:wallet :ui :send :from-locked-amounts chain-id] amount)}))
(rf/reg-event-fx :wallet/unlock-from-amount
(fn [{:keys [db]} [chain-id]]
{:db (update-in db [:wallet :ui :send :from-locked-amounts] dissoc chain-id)}))
(rf/reg-event-fx :wallet/reset-network-amounts-to-zero
(fn [{:keys [db]}]
(let [sender-network-values (get-in db [:wallet :ui :send :sender-network-values])
@ -361,6 +375,7 @@
to-address (get-in db [:wallet :ui :send :to-address])
receiver-networks (get-in db [:wallet :ui :send :receiver-networks])
disabled-from-chain-ids (or (get-in db [:wallet :ui :send :disabled-from-chain-ids]) [])
from-locked-amounts (or (get-in db [:wallet :ui :send :from-locked-amounts]) {})
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
networks ((if test-networks-enabled? :test :prod)
(get-in db [:wallet :networks]))
@ -371,7 +386,8 @@
to-token-id ""
network-preferences (if token [] [(get-in collectible [:id :contract-id :chain-id])])
gas-rates constants/gas-rate-medium
amount-in (send-utils/amount-in-hex amount (if token token-decimal 0))
to-hex (fn [v] (send-utils/amount-in-hex v (if token token-decimal 0)))
amount-in (to-hex amount)
from-address wallet-address
disabled-from-chain-ids disabled-from-chain-ids
disabled-to-chain-ids (if (= transaction-type :tx/bridge)
@ -380,7 +396,7 @@
(not (some #(= chain-id %)
receiver-networks)))
network-chain-ids))
from-locked-amount {}
from-locked-amount (update-vals from-locked-amounts to-hex)
transaction-type-param (case transaction-type
:tx/collectible-erc-721 constants/send-type-erc-721-transfer
:tx/collectible-erc-1155 constants/send-type-erc-1155-transfer

View File

@ -81,6 +81,7 @@
:has-error false}}
:market-values-per-currency {:usd {:price 10}}}
:wallet/wallet-send-disabled-from-chain-ids []
:wallet/wallet-send-from-locked-amounts {}
:wallet/wallet-send-from-values-by-chain {1 (money/bignumber "250")}
:wallet/wallet-send-to-values-by-chain {1 (money/bignumber "250")}
:wallet/wallet-send-sender-network-values nil

View File

@ -21,18 +21,6 @@
[utils.number :as number]
[utils.re-frame :as rf]))
(defn- make-limit-label-crypto
[amount currency]
(str amount
" "
(some-> currency
name
string/upper-case)))
(defn- make-limit-label-fiat
[amount currency-symbol]
(str currency-symbol amount))
(defn- estimated-fees
[{:keys [loading-routes? fees amount]}]
[rn/view {:style style/estimated-fees-container}
@ -134,6 +122,16 @@
:on-button-press #(rf/dispatch [:show-bottom-sheet
{:content buy-token/view}])}])
(defn- fetch-routes
[{:keys [amount bounce-duration-ms token valid-input?]}]
(if valid-input?
(debounce/debounce-and-dispatch
[:wallet/get-suggested-routes
{:amount amount
:updated-token token}]
bounce-duration-ms)
(rf/dispatch [:wallet/clean-suggested-routes])))
(defn view
;; crypto-decimals, limit-crypto and initial-crypto-currency? args are needed
;; for component tests only
@ -168,6 +166,7 @@
send-enabled-networks (rf/sub [:wallet/wallet-send-enabled-networks])
enabled-from-chain-ids (rf/sub
[:wallet/wallet-send-enabled-from-chain-ids])
send-from-locked-amounts (rf/sub [:wallet/wallet-send-from-locked-amounts])
{token-balance :total-balance
available-balance :available-balance
:as token-by-symbol} (rf/sub [:wallet/token-by-symbol
@ -217,7 +216,7 @@
input-state))
(<= input-num-value 0)
(> input-num-value current-limit))
amount (if crypto-currency?
amount-in-crypto (if crypto-currency?
input-amount
(number/remove-trailing-zeroes
(.toFixed (/ input-amount conversion-rate)
@ -291,7 +290,31 @@
show-no-routes? (and
(or no-routes-found? limit-insufficient?)
(not-empty sender-network-values)
(not not-enough-asset?))]
(not not-enough-asset?))
request-fetch-routes (fn [bounce-duration-ms]
(fetch-routes
{:amount amount-in-crypto
:valid-input? valid-input?
:bounce-duration-ms bounce-duration-ms
:token token}))
swap-between-fiat-and-crypto (fn [swap-to-crypto-currency?]
(set-just-toggled-mode? true)
(set-crypto-currency swap-to-crypto-currency?)
(set-input-state
(fn [input-state]
(controlled-input/set-input-value
input-state
(let [value (controlled-input/input-value
input-state)
new-value (if swap-to-crypto-currency?
(.toFixed (/ value
conversion-rate)
crypto-decimals)
(.toFixed (* value
conversion-rate)
12))]
(number/remove-trailing-zeroes
new-value))))))]
(rn/use-mount
(fn []
(let [dismiss-keyboard-fn #(when (= % "active") (rn/dismiss-keyboard!))
@ -311,6 +334,10 @@
(clear-input!)
(rf/dispatch [:wallet/clean-suggested-routes]))
[current-address])
(rn/use-effect
(fn []
(request-fetch-routes 0))
[send-from-locked-amounts])
[rn/view
{:style style/screen
:accessibility-label (str "container"
@ -330,33 +357,21 @@
:title (i18n/label
:t/send-limit
{:limit (if crypto-currency?
(make-limit-label-crypto current-limit token-symbol)
(make-limit-label-fiat current-limit currency-symbol))})
(utils/make-limit-label-crypto current-limit token-symbol)
(utils/make-limit-label-fiat current-limit currency-symbol))})
:conversion conversion-rate
:show-keyboard? false
:value input-amount
:on-swap (fn [swap-to-crypto-currency?]
(set-just-toggled-mode? true)
(set-crypto-currency swap-to-crypto-currency?)
(set-input-state
(fn [input-state]
(controlled-input/set-input-value
input-state
(let [value (controlled-input/input-value input-state)
new-value (if swap-to-crypto-currency?
(.toFixed (/ value conversion-rate)
crypto-decimals)
(.toFixed (* value conversion-rate) 12))]
(number/remove-trailing-zeroes new-value))))))
:on-swap swap-between-fiat-and-crypto
:on-token-press show-select-asset-sheet}]
[routes/view
{:token token-by-symbol
:input-value input-amount
:value amount
:valid-input? valid-input?
:token-not-supported-in-receiver-networks? token-not-supported-in-receiver-networks?
:lock-fetch-routes? just-toggled-mode?
:current-screen-id current-screen-id}]
:current-screen-id current-screen-id
:request-fetch-routes request-fetch-routes}]
(when (and (not loading-routes?)
sender-network-values
token-not-supported-in-receiver-networks?)
@ -379,20 +394,13 @@
(and (not should-try-again?) confirm-disabled?))
:on-press (cond
should-try-again?
#(let [input-amount (controlled-input/input-value
input-state)
amount (if crypto-currency?
input-amount
(.toFixed (* token-balance
conversion-rate)
2))]
(rf/dispatch [:wallet/get-suggested-routes
{:amount amount
:updated-token token-by-symbol}]))
#(rf/dispatch [:wallet/get-suggested-routes
{:amount amount-in-crypto
:updated-token token-by-symbol}])
sending-to-unpreferred-networks?
#(show-unpreferred-networks-alert on-confirm)
:else
#(on-confirm amount))}
#(on-confirm amount-in-crypto))}
(when should-try-again?
{:type :grey}))}]
[quo/numbered-keyboard

View File

@ -1,5 +1,4 @@
(ns status-im.contexts.wallet.send.routes.style
(:require [quo.foundations.colors :as colors]))
(ns status-im.contexts.wallet.send.routes.style)
(def routes-container
{:padding-horizontal 20
@ -36,25 +35,15 @@
inverted?
(assoc :transform [{:scaleY -1}])))
(def empty-container
{:flex-grow 1
:align-items :center
:justify-content :center})
(def disclaimer
{:margin-horizontal 20
:margin-top 20
:margin-bottom 8})
(defn warning-container
[color theme]
{:flex-direction :row
:border-width 1
:border-color (colors/resolve-color color theme 10)
:background-color (colors/resolve-color color theme 5)
:margin-horizontal 20
:margin-top 4
:margin-bottom 8
:padding-left 12
:padding-vertical 11
:border-radius 12})
(def input-container
{:margin-top 8
:margin-bottom 12})
(def warning-text
{:margin-left 8
:margin-right 12
:padding-right 12})
(defn keyboard-container
[bottom]
{:padding-bottom bottom})

View File

@ -1,30 +1,26 @@
(ns status-im.contexts.wallet.send.routes.view
(:require
[clojure.string :as string]
[quo.core :as quo]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.common.controlled-input.utils :as controlled-input]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
[status-im.contexts.wallet.send.routes.style :as style]
[status-im.contexts.wallet.send.utils :as send-utils]
[status-im.contexts.wallet.sheets.network-preferences.view :as network-preferences]
[utils.debounce :as debounce]
[utils.i18n :as i18n]
[utils.number :as number]
[utils.re-frame :as rf]))
(def row-height 44)
(def space-between-rows 11)
(def network-link-linear-height 10)
(def network-link-1x-height 56)
(def network-link-2x-height 111)
(defn- fetch-routes
[{:keys [amount bounce-duration-ms token valid-input?]}]
(if valid-input?
(debounce/debounce-and-dispatch
[:wallet/get-suggested-routes
{:amount amount
:updated-token token}]
bounce-duration-ms)
(rf/dispatch [:wallet/clean-suggested-routes])))
(defn- open-preferences
[]
@ -89,8 +85,143 @@
(rf/dispatch [:wallet/update-receiver-networks
chain-ids]))}]))}]))
(defn- edit-amount
[{:keys [chain-id token-symbol]}]
(rf/dispatch
[:show-bottom-sheet
{:content
(fn []
(let [{:keys [network-name] :as network-details} (rf/sub [:wallet/network-details-by-chain-id
chain-id])
{fiat-currency :currency} (rf/sub [:profile/profile])
{token-decimals :decimals
:as
token} (rf/sub [:wallet/wallet-send-token])
currency (rf/sub [:profile/currency])
currency-symbol (rf/sub [:profile/currency-symbol])
send-from-locked-amounts (rf/sub
[:wallet/wallet-send-from-locked-amounts])
{account-color :color} (rf/sub [:wallet/current-viewing-account])
locked-amount (get send-from-locked-amounts chain-id)
network-name-str (string/capitalize (name network-name))
[input-state set-input-state] (rn/use-state (cond-> controlled-input/init-state
locked-amount
(controlled-input/set-input-value
locked-amount)))
[crypto-currency? set-crypto-currency] (rn/use-state true)
conversion-rate (-> token
:market-values-per-currency
currency
:price)
{token-balance :total-balance} (rf/sub [:wallet/token-by-symbol
(str token-symbol)
[chain-id]])
current-crypto-limit (utils/get-standard-crypto-format
token
token-balance)
current-fiat-limit (.toFixed (* token-balance conversion-rate) 2)
current-limit (if crypto-currency?
current-crypto-limit
current-fiat-limit)
crypto-decimals token-decimals
input-amount (controlled-input/input-value input-state)
[is-amount-locked? set-is-amount-locked] (rn/use-state (some? locked-amount))
bottom (safe-area/get-bottom)
amount-in-crypto (if crypto-currency?
input-amount
(number/remove-trailing-zeroes
(.toFixed (/ input-amount
conversion-rate)
crypto-decimals)))
swap-between-fiat-and-crypto (fn [swap-to-crypto-currency?]
(set-crypto-currency swap-to-crypto-currency?)
(set-input-state
(fn [input-state]
(controlled-input/set-input-value
input-state
(let [value (controlled-input/input-value
input-state)
new-value (if
swap-to-crypto-currency?
(.toFixed
(/ value
conversion-rate)
crypto-decimals)
(.toFixed
(* value
conversion-rate)
12))]
(number/remove-trailing-zeroes
new-value))))))
lock-or-unlock-amount (fn []
(if is-amount-locked?
(rf/dispatch [:wallet/lock-from-amount
chain-id
amount-in-crypto])
(rf/dispatch [:wallet/unlock-from-amount
chain-id]))
(rf/dispatch [:hide-bottom-sheet]))]
(rn/use-effect
(fn []
(set-input-state #(controlled-input/set-upper-limit % current-limit)))
[current-limit])
[:<>
[quo/drawer-top
{:title (i18n/label :t/send-from-network {:network network-name-str})
:description (i18n/label :t/define-amount-sent-from-network {:network network-name-str})}]
[quo/token-input
{:container-style style/input-container
:token token-symbol
:currency fiat-currency
:currency-symbol currency-symbol
:crypto-decimals (min token-decimals 6)
:error? (controlled-input/input-error input-state)
:networks [network-details]
:title (i18n/label
:t/send-limit
{:limit (if crypto-currency?
(utils/make-limit-label-crypto current-limit token-symbol)
(utils/make-limit-label-fiat current-limit currency-symbol))})
:conversion conversion-rate
:show-keyboard? false
:value (controlled-input/input-value input-state)
:on-swap swap-between-fiat-and-crypto}]
[quo/disclaimer
{:on-change (fn [checked?]
(set-is-amount-locked checked?))
:checked? is-amount-locked?
:container-style style/disclaimer
:icon (if is-amount-locked?
:i/locked
:i/unlocked)
:customization-color account-color}
(i18n/label :t/dont-auto-recalculate-network {:network network-name-str})]
[quo/bottom-actions
{:actions :one-action
:button-one-label (i18n/label :t/update)
:button-one-props {:on-press lock-or-unlock-amount
:customization-color account-color
:disabled? (or (controlled-input/empty-value? input-state)
(controlled-input/input-error input-state))}}]
[quo/numbered-keyboard
{:container-style (style/keyboard-container bottom)
:left-action :dot
:delete-key? true
:on-press (fn [c]
(let [new-text (str input-amount c)
max-decimals (if crypto-currency? crypto-decimals 2)
regex-pattern (str "^\\d*\\.?\\d{0," max-decimals "}$")
regex (re-pattern regex-pattern)]
(when (re-matches regex new-text)
(set-is-amount-locked true)
(set-input-state #(controlled-input/add-character % c)))))
:on-delete (fn []
(set-input-state controlled-input/delete-last))
:on-long-press-delete (fn []
(set-input-state controlled-input/delete-all))}]]))}]))
(defn render-network-values
[{:keys [network-values token-symbol on-press receiver? loading-routes?
[{:keys [network-values token-symbol on-press on-long-press receiver? loading-routes?
token-not-supported-in-receiver-networks?]}]
[rn/view
(map-indexed (fn [index {:keys [chain-id total-amount type]}]
@ -98,22 +229,27 @@
{:key (str (if receiver? "to" "from") "-" chain-id)
:style {:margin-top (if (pos? index) 11 7.5)}}
[quo/network-bridge
{:amount (if (= type :not-available)
(i18n/label :t/not-available)
(str total-amount " " token-symbol))
:network (network-utils/id->network chain-id)
:status (cond (and (= type :not-available)
loading-routes?
token-not-supported-in-receiver-networks?)
:loading
(= type :not-available)
:disabled
:else type)
:on-press #(when (not loading-routes?)
(cond
(= type :edit)
(open-preferences)
on-press (on-press chain-id total-amount)))}]])
{:amount (if (= type :not-available)
(i18n/label :t/not-available)
(str total-amount " " token-symbol))
:network (network-utils/id->network chain-id)
:status (cond (and (= type :not-available)
loading-routes?
token-not-supported-in-receiver-networks?)
:loading
(= type :not-available)
:disabled
:else type)
:on-press #(when (not loading-routes?)
(cond
(= type :edit)
(open-preferences)
on-press (on-press chain-id total-amount)))
:on-long-press #(when (not loading-routes?)
(cond
(= type :add)
(open-preferences)
on-long-press (on-long-press chain-id)))}]])
network-values)])
(defn render-network-links
@ -176,7 +312,7 @@
:text (i18n/label :t/at-least-one-network-must-be-activated)}]))))
(defn view
[{:keys [token theme input-value value valid-input?
[{:keys [token theme input-value valid-input? request-fetch-routes
lock-fetch-routes? on-press-to-network current-screen-id
token-not-supported-in-receiver-networks?]}]
(let [token-symbol (:symbol token)
@ -202,19 +338,11 @@
(when (and active-screen?
(> (count token-available-networks-for-suggested-routes) 0)
(not lock-fetch-routes?))
(fetch-routes
{:amount value
:valid-input? valid-input?
:bounce-duration-ms 2000
:token token})))
(request-fetch-routes 2000)))
[input-value valid-input?])
(rn/use-effect
#(when (and active-screen? (> (count token-available-networks-for-suggested-routes) 0))
(fetch-routes
{:amount value
:valid-input? valid-input?
:bounce-duration-ms 0
:token token}))
(request-fetch-routes 0))
[disabled-from-chain-ids])
[rn/scroll-view {:content-container-style style/routes-container}
(when show-routes?
@ -234,6 +362,11 @@
chain-id-to-disable
disabled-from-chain-ids
token-available-networks-for-suggested-routes))
:on-long-press (fn [chain-id]
(edit-amount
{:chain-id chain-id
:token-symbol token-symbol}))
:receiver? false
:theme theme
:loading-routes? loading-routes?
@ -249,8 +382,5 @@
:loading-routes? loading-routes?
:theme theme
:token-not-supported-in-receiver-networks? token-not-supported-in-receiver-networks?
:on-save #(fetch-routes
{:amount input-value
:valid-input? valid-input?
:bounce-duration-ms 0})}]]]))
:on-save #(request-fetch-routes 0)}]]]))

View File

@ -12,5 +12,6 @@
:button-one-label (i18n/label :t/review-send)
:on-navigate-back (fn []
(rf/dispatch [:wallet/clean-disabled-from-networks])
(rf/dispatch [:wallet/clean-from-locked-amounts])
(rf/dispatch [:wallet/clean-send-amount])
(rf/dispatch [:navigate-back]))}])

View File

@ -119,7 +119,8 @@
network-amounts))
(defn network-amounts
[{:keys [network-values disabled-chain-ids receiver-networks token-networks-ids tx-type receiver?]}]
[{:keys [network-values disabled-chain-ids receiver-networks token-networks-ids tx-type receiver?
from-locked-amounts]}]
(let [disabled-set (set disabled-chain-ids)
receiver-networks-set (set receiver-networks)
network-values-keys (set (keys network-values))
@ -156,8 +157,9 @@
(map
(fn [[chain-id amount]]
{:chain-id chain-id
:total-amount amount
:total-amount (get from-locked-amounts chain-id amount)
:type (cond
(contains? from-locked-amounts chain-id) :locked
(contains? not-available-networks-set chain-id) :not-available
(or receiver? (not (contains? disabled-set chain-id))) :default
(and (not receiver?) (contains? disabled-set chain-id)) :disabled)}))

View File

@ -149,6 +149,11 @@
:<- [:wallet/wallet-send]
:-> :disabled-from-chain-ids)
(rf/reg-sub
:wallet/wallet-send-from-locked-amounts
:<- [:wallet/wallet-send]
:-> :from-locked-amounts)
(rf/reg-sub
:wallet/wallet-send-from-values-by-chain
:<- [:wallet/wallet-send]

View File

@ -2704,5 +2704,8 @@
"saved-address-network-preference-selection-description": "Only change if you know which networks the address owner is happy to to receive funds on",
"add-preferences": "Add preferences",
"buy-eth": "Buy ETH",
"not-enough-assets": "Not enough assets to pay gas fees"
"not-enough-assets": "Not enough assets to pay gas fees",
"send-from-network" : "Send from {{network}}",
"define-amount-sent-from-network" : "Define amount sent from {{network}} network",
"dont-auto-recalculate-network": "Don't auto recalculate {{network}}"
}