feat(swap): implement network selection drawer (#20368)

This commit is contained in:
Brian Sztamfater 2024-07-03 09:15:07 -03:00 committed by GitHub
parent 99506c810d
commit 2e8776d0a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 273 additions and 13 deletions

View File

@ -44,13 +44,14 @@
[:label :string] [:label :string]
[:fiat-value :string] [:fiat-value :string]
[:token-value :string] [:token-value :string]
[:container-style {:optional true} [:maybe :map]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]] [:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:state {:optional true} [:enum :pressed :active :disabled :default]] [:state {:optional true} [:enum :pressed :active :disabled :default]]
[:on-press {:optional true} [:maybe fn?]]]]] [:on-press {:optional true} [:maybe fn?]]]]]
:any]) :any])
(defn- view-internal (defn- view-internal
[{:keys [on-press state customization-color] [{:keys [on-press container-style state customization-color]
:as props :as props
:or {customization-color :blue}}] :or {customization-color :blue}}]
(let [theme (quo.theme/use-theme) (let [theme (quo.theme/use-theme)
@ -59,7 +60,8 @@
on-press-out (rn/use-callback #(set-pressed false)) on-press-out (rn/use-callback #(set-pressed false))
internal-state (if pressed? :pressed state)] internal-state (if pressed? :pressed state)]
[rn/pressable [rn/pressable
{:style (style/container internal-state customization-color theme) {:style (merge (style/container internal-state customization-color theme)
container-style)
:on-press-in (when-not (= state :disabled) on-press-in) :on-press-in (when-not (= state :disabled) on-press-in)
:on-press-out (when-not (= state :disabled) on-press-out) :on-press-out (when-not (= state :disabled) on-press-out)
:on-press (when-not (= state :disabled) on-press) :on-press (when-not (= state :disabled) on-press)

View File

@ -505,6 +505,9 @@
(def ^:const optimism-network-name :optimism) (def ^:const optimism-network-name :optimism)
(def ^:const arbitrum-network-name :arbitrum) (def ^:const arbitrum-network-name :arbitrum)
(def ^:const layer-1-network 1)
(def ^:const layer-2-network 2)
(def ^:const default-network-names [mainnet-network-name optimism-network-name arbitrum-network-name]) (def ^:const default-network-names [mainnet-network-name optimism-network-name arbitrum-network-name])
(def ^:const default-network-count (count default-network-names)) (def ^:const default-network-count (count default-network-names))

View File

@ -0,0 +1,17 @@
(ns status-im.contexts.wallet.sheets.network-selection.style)
(defn network-list-container
[mainnet?]
{:margin-horizontal 8
:padding-vertical (when mainnet? 8)})
(def header-container
{:height 62
:padding-horizontal 20})
(def context-tag
{:margin-top 4})
(def divider-label
{:padding-top 0
:padding-bottom 0})

View File

@ -0,0 +1,55 @@
(ns status-im.contexts.wallet.sheets.network-selection.view
(:require [quo.core :as quo]
[quo.foundations.resources :as quo.resources]
[react-native.core :as rn]
[status-im.constants :as constants]
[status-im.contexts.wallet.sheets.network-selection.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- network-item
[{:keys [network on-select-network]}]
(let [{:keys [network-name
chain-id]} network
{balance-in-crypto :crypto
balance-in-fiat :fiat} (rf/sub [:wallet/swap-asset-to-pay-network-balance chain-id])
mainnet? (= network-name constants/mainnet-network-name)]
[quo/network-list
{:label (name network-name)
:network-image (quo.resources/get-network network-name)
:token-value balance-in-crypto
:fiat-value balance-in-fiat
:on-press #(on-select-network network)
:container-style (style/network-list-container mainnet?)}]))
(defn view
[{:keys [on-select-network]}]
(let [token-symbol (rf/sub [:wallet/swap-asset-to-pay-token-symbol])
{mainnet-network :mainnet-network
layer-2-networks :layer-2-networks} (rf/sub [:wallet/swap-asset-to-pay-networks])
render-fn (rn/use-callback (fn [network]
[network-item
{:network network
:on-select-network
on-select-network}]))]
[:<>
[rn/view {:style style/header-container}
[quo/text
{:size :heading-2
:weight :semi-bold}
(i18n/label :t/select-network)]
[quo/context-tag
{:type :token
:size 24
:token token-symbol
:container-style style/context-tag}]]
(when mainnet-network
[network-item
{:network mainnet-network
:on-select-network on-select-network}])
[quo/divider-label {:container-style style/divider-label}
(i18n/label :t/layer-2)]
[rn/flat-list
{:data (vec layer-2-networks)
:render-fn render-fn
:scroll-enabled false}]]))

View File

@ -1,18 +1,35 @@
(ns status-im.contexts.wallet.swap.events (ns status-im.contexts.wallet.swap.events
(:require [re-frame.core :as rf])) (:require [re-frame.core :as rf]
[status-im.contexts.wallet.sheets.network-selection.view :as network-selection]))
(rf/reg-event-fx :wallet.swap/start (rf/reg-event-fx :wallet.swap/start
(fn [{:keys [_db]}] (fn [{:keys [_db]}]
{:fx [[:dispatch [:open-modal :screen/wallet.swap-select-asset-to-pay]]]})) {:fx [[:dispatch [:open-modal :screen/wallet.swap-select-asset-to-pay]]]}))
(rf/reg-event-fx :wallet.swap/select-asset-to-pay (rf/reg-event-fx :wallet.swap/select-asset-to-pay
(fn [{:keys [db]} token] (fn [{:keys [db]} [{:keys [token network]}]]
{:db (assoc-in db [:wallet :ui :swap :asset-to-pay] token) {:db (-> db
:fx [[:dispatch (assoc-in [:wallet :ui :swap :asset-to-pay] token)
[:toasts/upsert (assoc-in [:wallet :ui :swap :network] network))
{:id :swap-error :fx [(if network
:type :negative [:dispatch
:text "Not implemented yet"}]]]})) [:toasts/upsert
{:id :swap-error
:type :negative
:text "Not implemented yet"}]]
[:dispatch
[:show-bottom-sheet
{:content (fn []
[network-selection/view
{:token-symbol (:symbol token)
:on-select-network (fn [network]
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch
[:wallet.swap/select-asset-to-pay
{:token token
:network network
:stack-id
:screen/wallet.swap-select-asset-to-pay}]))}])}]])]}))
(rf/reg-event-fx :wallet.swap/clean-asset-to-pay (rf/reg-event-fx :wallet.swap/clean-asset-to-pay
(fn [{:keys [db]}] (fn [{:keys [db]}]

View File

@ -21,9 +21,12 @@
(defn- assets-view (defn- assets-view
[search-text on-change-text] [search-text on-change-text]
(let [on-token-press (fn [token] (let [on-token-press (fn [token]
(rf/dispatch [:wallet.swap/select-asset-to-pay (let [token-networks (:networks token)]
{:token token (rf/dispatch [:wallet.swap/select-asset-to-pay
:stack-id :screen/wallet.swap-select-asset-to-pay}]))] {:token token
:network (when (= (count token-networks) 1)
(first token-networks))
:stack-id :screen/wallet.swap-select-asset-to-pay}])))]
[:<> [:<>
[search-input search-text on-change-text] [search-input search-text on-change-text]
[asset-list/view [asset-list/view

View File

@ -21,6 +21,7 @@
status-im.subs.wallet.networks status-im.subs.wallet.networks
status-im.subs.wallet.saved-addresses status-im.subs.wallet.saved-addresses
status-im.subs.wallet.send status-im.subs.wallet.send
status-im.subs.wallet.swap
status-im.subs.wallet.wallet status-im.subs.wallet.wallet
status-im.subs.wallet.wallet-connect)) status-im.subs.wallet.wallet-connect))

View File

@ -0,0 +1,54 @@
(ns status-im.subs.wallet.swap
(:require [re-frame.core :as rf]
[status-im.constants :as constants]
[status-im.contexts.wallet.common.utils :as utils]
[utils.money :as money]))
(rf/reg-sub
:wallet/swap
:<- [:wallet/ui]
:-> :swap)
(rf/reg-sub
:wallet/swap-asset-to-pay
:<- [:wallet/swap]
:-> :asset-to-pay)
(rf/reg-sub
:wallet/swap-asset-to-pay-token-symbol
:<- [:wallet/swap-asset-to-pay]
:-> :symbol)
(rf/reg-sub
:wallet/swap-asset-to-pay-networks
:<- [:wallet/swap-asset-to-pay]
(fn [token]
(let [{token-networks :networks} token
grouped-networks (group-by :layer
token-networks)
mainnet-network (first (get grouped-networks constants/layer-1-network))
layer-2-networks (get grouped-networks constants/layer-2-network)]
{:mainnet-network mainnet-network
:layer-2-networks layer-2-networks})))
(rf/reg-sub
:wallet/swap-asset-to-pay-network-balance
:<- [:wallet/swap-asset-to-pay]
:<- [:profile/currency]
:<- [:profile/currency-symbol]
:<- [:wallet/swap-asset-to-pay-token-symbol]
(fn [[token currency currency-symbol token-symbol] [_ chain-id]]
(let [{:keys [balances-per-chain
decimals]} token
balance-for-chain (get balances-per-chain chain-id)
total-balance (money/token->unit (:raw-balance balance-for-chain) decimals)
fiat-value (utils/calculate-token-fiat-value
{:currency currency
:balance total-balance
:token token})
crypto-formatted (utils/get-standard-crypto-format token total-balance)
fiat-formatted (utils/get-standard-fiat-format crypto-formatted
currency-symbol
fiat-value)]
{:crypto (str crypto-formatted " " token-symbol)
:fiat fiat-formatted})))

View File

@ -0,0 +1,107 @@
(ns status-im.subs.wallet.swap-test
(:require
[cljs.test :refer [is testing]]
[re-frame.db :as rf-db]
[status-im.subs.root]
[status-im.subs.wallet.collectibles]
[test-helpers.unit :as h]
[utils.re-frame :as rf]))
(def networks
{:mainnet-network
{:full-name "Mainnet"
:network-name :mainnet
:chain-id 1
:related-chain-id 5
:layer 1}
:layer-2-networks
[{:full-name "Optimism"
:network-name :optimism
:chain-id 10
:related-chain-id 420
:layer 2}
{:full-name "Arbitrum"
:network-name :arbitrum
:chain-id 42161
:related-chain-id 421613
:layer 2}]})
(def swap-data
{:asset-to-pay
{:description "Status Network Token (SNT)"
:decimals 18
:symbol "SNT"
:name "Status"
:total-balance 1
:balances-per-chain
{1
{:raw-balance 1000000000000000000
:balance "1"
:chain-id 1}
10
{:raw-balance 0
:balance "0"
:chain-id 10}
42161
{:raw-balance 0
:balance "0"
:chain-id 42161}}
:networks (concat [(networks :mainnet-network)] (networks :layer-2-networks))
:chain-id 0
:market-values-per-currency
{:usd
{:change-24hour -0.00109422754667007
:change-pct-day -5.57352274163899
:change-pct-24hour -4.177805426737527
:high-day 0.0271858672171352
:market-cap 170783296.1155821
:has-error false
:change-pct-hour -0.0160462113709363
:low-day 0.02473516779550377
:price 0.0251}}
:asset-website-url "https://status.im/"
:available-balance 1
:token-list-id ""
:built-on "ETH"
:verified true}
:network nil})
(h/deftest-sub :wallet/swap
[sub-name]
(testing "Return the wallet/ui/swap node"
(swap! rf-db/app-db assoc-in
[:wallet :ui :swap]
swap-data)
(is (match? swap-data (rf/sub [sub-name])))))
(h/deftest-sub :wallet/swap-asset-to-pay
[sub-name]
(testing "Return swap asset-to-pay"
(swap! rf-db/app-db assoc-in
[:wallet :ui :swap]
swap-data)
(is (match? (swap-data :asset-to-pay) (rf/sub [sub-name])))))
(h/deftest-sub :wallet/swap-asset-to-pay-token-symbol
[sub-name]
(testing "Return asset-to-pay token symbol"
(swap! rf-db/app-db assoc-in
[:wallet :ui :swap]
swap-data)
(is (match? "SNT" (rf/sub [sub-name])))))
(h/deftest-sub :wallet/swap-asset-to-pay-networks
[sub-name]
(testing "Return the available networks for the swap asset-to-pay"
(swap! rf-db/app-db assoc-in
[:wallet :ui :swap]
swap-data)
(is (match? networks (rf/sub [sub-name])))))
(h/deftest-sub :wallet/swap-asset-to-pay-network-balance
[sub-name]
(testing "Return swap asset-to-pay"
(swap! rf-db/app-db assoc-in
[:wallet :ui :swap]
swap-data)
(is (match? {:crypto "1 SNT" :fiat "$0.03"} (rf/sub [sub-name 1])))))

View File

@ -2627,6 +2627,7 @@
"derivation-path-header": "Derivation path", "derivation-path-header": "Derivation path",
"derivation-path-desc": "Derivation paths are the routes your Status Wallet uses to generate addresses from your private key.", "derivation-path-desc": "Derivation paths are the routes your Status Wallet uses to generate addresses from your private key.",
"select-networks": "Select networks", "select-networks": "Select networks",
"select-network": "Select network",
"generating-keypair": "Generating key pair...", "generating-keypair": "Generating key pair...",
"keypair-name": "Key pair name", "keypair-name": "Key pair name",
"keypair-name-description": "Name key pair for your own personal reference", "keypair-name-description": "Name key pair for your own personal reference",