[#21557] feat: show from account page in swap flow (#21611)

* [#21557] feat: show from account page in swap flow

* [#21557] feat: add disabled state to account-item

* [#21557] fix: apply suggestion

* [#21557] fix: add format address util and rename screen

* [#21557] fix: check and count account with token balance

* [#21557] fix: check for root screen
This commit is contained in:
Mohsen 2024-11-22 16:12:00 +03:00 committed by GitHub
parent 26ae330476
commit 21c2a525cf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 242 additions and 41 deletions

View File

@ -23,7 +23,7 @@
[:map
[:type {:optional true}
[:enum :default :tag :action :balance-neutral :balance-negative :balance-positive]]
[:state {:optional true} [:enum :default :selected :active]]
[:state {:optional true} [:enum :default :selected :active :disabled]]
[:blur? {:optional true} [:maybe :boolean]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:on-press {:optional true} [:maybe fn?]]

View File

@ -12,12 +12,13 @@
:else :transparent))
(defn container
[props]
[{:keys [state] :as props}]
{:height 56
:border-radius 12
:background-color (background-color props)
:flex-direction :row
:align-items :center
:opacity (if (= state :disabled) 0.3 1)
:padding-horizontal 12
:padding-vertical 6
:justify-content :space-between})

View File

@ -112,6 +112,7 @@
:pressed? pressed?})
:on-press-in on-press-in
:on-press on-press
:disabled (= state :disabled)
:on-press-out on-press-out
:accessibility-label :container}
[account-view props]

View File

@ -548,3 +548,27 @@
(:less-than-three-minutes constants/wallet-transaction-estimation) "1-3"
(:less-than-five-minutes constants/wallet-transaction-estimation) "3-5"
">5"))
(defn get-accounts-with-token-balance
[accounts token]
(let [operable-account (filter :operable? (vals accounts))
positive-balance-in-any-chain? (fn [{:keys [balances-per-chain]}]
(->> balances-per-chain
(map (comp :raw-balance val))
(some pos?)))
addresses-tokens-with-positive-balance (as-> operable-account $
(group-by :address $)
(update-vals $
#(filter positive-balance-in-any-chain?
(:tokens (first %)))))]
(if-let [asset-symbol (:symbol token)]
(let [addresses-with-asset (as-> addresses-tokens-with-positive-balance $
(update-vals $ #(set (map :symbol %)))
(keep (fn [[address token-symbols]]
(when (token-symbols asset-symbol) address))
$)
(set $))]
(filter #(addresses-with-asset (:address %)) operable-account))
(filter (fn [{:keys [tokens]}]
(some positive-balance-in-any-chain? tokens))
operable-account))))

View File

@ -7,6 +7,7 @@
[utils.number]))
(def ^:private last-comma-followed-by-text-to-end-regex #",\s(?=[^,]+$)")
(def ^:private max-network-prefixes 2)
(def id->network
{constants/ethereum-mainnet-chain-id constants/mainnet-network-name
@ -185,3 +186,11 @@
:related-chain-id related-chain-id
:layer layer)))
(sort-by (juxt :layer :short-name))))
(defn format-address
[address network-preferences]
(let [short-names (map network->short-name network-preferences)
prefix (when (<= (count short-names) max-network-prefixes)
(short-names->network-preference-prefix short-names))
transformed-address (str prefix address)]
transformed-address))

View File

@ -269,3 +269,68 @@
expected "0"]
(is (= (utils/token-balance-display-for-network token chain-id rounding-decimals)
expected)))))
(def mock-accounts
{:0xfc6327a092f6232e158a0dd1d6d967a2e1c65cd5
{:address "0xfc6327a092f6232e158a0dd1d6d967a2e1c65cd5"
:operable? true
:tokens [{:symbol "ETH"
:balances-per-chain {1 {:raw-balance 1000000
:balance 1.0}}}
{:symbol "USDC"
:balances-per-chain {1 {:raw-balance 0
:balance 0}}}]}
:0xbce36f66a8cd99f1d6489cb9585146e3f3b893be
{:address "0xbce36f66a8cd99f1d6489cb9585146e3f3b893be"
:operable? true
:tokens [{:symbol "ETH"
:balances-per-chain {1 {:raw-balance 0
:balance 0}}}]}})
(deftest get-accounts-with-token-balance-test
(testing "Positive token balance for a specific token"
(let [accounts mock-accounts
token {:symbol "ETH"}
expected [{:address "0xfc6327a092f6232e158a0dd1d6d967a2e1c65cd5"
:operable? true
:tokens [{:symbol "ETH"
:balances-per-chain {1 {:raw-balance 1000000
:balance 1.0}}}
{:symbol "USDC"
:balances-per-chain {1 {:raw-balance 0
:balance 0}}}]}]]
(is (= (utils/get-accounts-with-token-balance accounts token)
expected))))
(testing "No token symbol provided, return all tokens with positive balance"
(let [accounts mock-accounts
token {}
expected [{:address "0xfc6327a092f6232e158a0dd1d6d967a2e1c65cd5"
:operable? true
:tokens [{:symbol "ETH"
:balances-per-chain {1 {:raw-balance 1000000
:balance 1.0}}}
{:symbol "USDC"
:balances-per-chain {1 {:raw-balance 0
:balance 0}}}]}]]
(is (= (utils/get-accounts-with-token-balance accounts token)
expected))))
(testing "No matching token found for a specific token"
(let [accounts mock-accounts
token {:symbol "DAI"}
expected []]
(is (= (utils/get-accounts-with-token-balance accounts token)
expected))))
(testing "No operable accounts"
(let [accounts {:0xfc6327a092f6232e158a0dd1d6d967a2e1c65cd5
{:address "0xfc6327a092f6232e158a0dd1d6d967a2e1c65cd5"
:operable? false
:tokens [{:symbol "ETH"
:balances-per-chain {1 {:raw-balance 1000000
:balance 1.0}}}]}}
token {}
expected []]
(is (= (utils/get-accounts-with-token-balance accounts token)
expected)))))

View File

@ -14,11 +14,12 @@
[utils.number :as number]))
(rf/reg-event-fx :wallet.swap/start
(fn [{:keys [db]} [{:keys [network asset-to-receive open-new-screen?] :as data}]]
(fn [{:keys [db]} [{:keys [network asset-to-receive open-new-screen? from-account] :as data}]]
(let [{:keys [wallet]} db
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
view-id (:view-id db)
account (swap-utils/wallet-account wallet)
root-screen? (or (= view-id :wallet-stack) (nil? view-id))
account (or from-account (swap-utils/wallet-account wallet))
asset-to-pay (if (get-in data [:asset-to-pay :networks])
(:asset-to-pay data)
(swap-utils/select-asset-to-pay-by-symbol
@ -26,6 +27,10 @@
:account account
:test-networks-enabled? test-networks-enabled?
:token-symbol (get-in data [:asset-to-pay :symbol])}))
multi-account-balance? (-> (utils/get-accounts-with-token-balance (:accounts wallet)
asset-to-pay)
(count)
(> 1))
network' (or network
(swap-utils/select-network asset-to-pay))
start-point (if open-new-screen? :action-menu :swap-button)]
@ -35,7 +40,9 @@
(assoc-in [:wallet :ui :swap :network] network')
(assoc-in [:wallet :ui :swap :launch-screen] view-id)
(assoc-in [:wallet :ui :swap :start-point] start-point))
:fx (if network'
:fx (if (and multi-account-balance? root-screen? (not from-account))
[[:dispatch [:open-modal :screen/wallet.swap-select-account]]]
(if network'
[[:dispatch [:wallet/switch-current-viewing-account (:address account)]]
[:dispatch
(if open-new-screen?
@ -62,7 +69,9 @@
{:asset-to-pay asset-to-pay
:asset-to-receive asset-to-receive
:network network
:open-new-screen? open-new-screen?}]))}])}]]])})))
:open-new-screen?
open-new-screen?
:from-account from-account}]))}])}]]]))})))
(rf/reg-event-fx :wallet.swap/select-asset-to-pay
(fn [{:keys [db]} [{:keys [token]}]]
@ -527,3 +536,15 @@
[:dispatch [:wallet/navigate-to-account-within-stack address]])
[:dispatch [:wallet/fetch-activities-for-current-account]]
[:dispatch [:wallet/select-account-tab :activity]]]})))
(rf/reg-event-fx :wallet.swap/start-from-account
(fn [{:keys [db]} [account]]
(let [asset-to-pay (get-in db [:wallet :ui :swap :asset-to-pay])
asset-to-receive (get-in db [:wallet :ui :swap :asset-to-receive])]
{:fx [[:dispatch [:dismiss-modal :screen/wallet.swap-select-account]]
[:dispatch
[:wallet.swap/start
{:asset-to-pay asset-to-pay
:asset-to-receive asset-to-receive
:open-new-screen? true
:from-account account}]]]})))

View File

@ -0,0 +1,7 @@
(ns status-im.contexts.wallet.swap.select-account.style)
(def accounts-list
{:padding-bottom 12})
(def accounts-list-container
{:padding-horizontal 8})

View File

@ -0,0 +1,52 @@
(ns status-im.contexts.wallet.swap.select-account.view
(:require
[quo.core :as quo]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.common.events-helper :as events-helper]
[status-im.common.floating-button-page.view :as floating-button-page]
[status-im.contexts.wallet.swap.select-account.style :as style]
[utils.i18n :as i18n]
[utils.money :as money]
[utils.re-frame :as rf]))
(defn- on-account-press
[account]
(rf/dispatch [:wallet.swap/start-from-account account]))
(defn- render-fn
[item _ _]
(let [has-balance (money/above-zero? (:asset-pay-balance item))]
[quo/account-item
{:type (if has-balance :tag :default)
:on-press #(on-account-press item)
:state (if has-balance :default :disabled)
:token-props {:symbol (:asset-pay-symbol item)
:value (:asset-pay-balance item)}
:account-props (assoc item
:address (:formatted-address item)
:full-address? true)}]))
(defn- on-close
[]
(rf/dispatch [:wallet/clean-current-viewing-account])
(events-helper/navigate-back))
(defn view
[]
(let [accounts (rf/sub [:wallet/accounts-with-balances])]
[floating-button-page/view
{:footer-container-padding 0
:header [quo/page-nav
{:margin-top (safe-area/get-top)
:icon-name :i/close
:on-press on-close}]}
[quo/page-top
{:title (i18n/label :t/from-label)
:title-accessibility-label :title-label}]
[rn/flat-list
{:style style/accounts-list
:content-container-style style/accounts-list-container
:data accounts
:render-fn render-fn
:shows-horizontal-scroll-indicator false}]]))

View File

@ -134,6 +134,7 @@
[status-im.contexts.wallet.send.send-amount.view :as wallet-send-input-amount]
[status-im.contexts.wallet.send.transaction-confirmation.view :as wallet-transaction-confirmation]
[status-im.contexts.wallet.send.transaction-progress.view :as wallet-transaction-progress]
[status-im.contexts.wallet.swap.select-account.view :as wallet-swap-select-account]
[status-im.contexts.wallet.swap.select-asset-to-pay.view :as wallet-swap-select-asset-to-pay]
[status-im.contexts.wallet.swap.set-spending-cap.view :as wallet-swap-set-spending-cap]
[status-im.contexts.wallet.swap.setup-swap.view :as wallet-swap-setup-swap]
@ -635,6 +636,12 @@
:insets {:top? true}}
:component wallet-swap-select-asset-to-pay/view}
{:name :screen/wallet.swap-select-account
:metrics {:track? true}
:options {:modalPresentationStyle :overCurrentContext
:insets {:bottom? true}}
:component wallet-swap-select-account/view}
{:name :screen/wallet.setup-swap
:metrics {:track? true
:alias-id :wallet-swap.input-amount-to-swap}

View File

@ -4,8 +4,6 @@
[utils.money :as money]
[utils.number :as number]))
(def max-network-prefixes 2)
(re-frame/reg-sub
:wallet/networks
:<- [:wallet]
@ -53,12 +51,7 @@
(re-frame/reg-sub
:wallet/account-address
(fn [_ [_ address network-preferences]]
(let [short-names (map network-utils/network->short-name network-preferences)
prefix (when (<= (count short-names) max-network-prefixes)
(network-utils/short-names->network-preference-prefix
short-names))
transformed-address (str prefix address)]
transformed-address)))
(network-utils/format-address address network-preferences)))
(re-frame/reg-sub
:wallet/network-values

View File

@ -3,6 +3,7 @@
[re-frame.core :as rf]
[status-im.constants :as constants]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
[status-im.contexts.wallet.send.utils :as send-utils]
[utils.money :as money]
[utils.number :as number]))
@ -315,3 +316,23 @@
:token token-for-fees})]
fee-in-fiat)))
(rf/reg-sub
:wallet/accounts-with-balances
:<- [:wallet/operable-accounts]
:<- [:wallet/swap-asset-to-pay]
(fn [[accounts asset-to-pay]]
(let [token-symbol (:symbol asset-to-pay)]
(map
(fn [account]
(let [tokens (:tokens account)
filtered-tokens (filter #(= (:symbol %) token-symbol) tokens)
asset-pay-balance (utils/calculate-total-token-balance filtered-tokens)
formatted-address (network-utils/format-address (:address account)
(:network-preferences-names account))]
(assoc account
:formatted-address formatted-address
:asset-pay-balance (utils/sanitized-token-amount-to-display
asset-pay-balance
constants/min-token-decimals-to-display)
:asset-pay-symbol token-symbol)))
accounts))))