feat: support edge case flow in the wallet send flow when token is not available on receiver preferred networks (#19674)

feat: implement wallet send flow edge case when selected token is not supported on receiver's preferred networks

Signed-off-by: Brian Sztamfater <brian@status.im>
This commit is contained in:
Brian Sztamfater 2024-05-15 14:11:26 -03:00 committed by GitHub
parent 8f0840e913
commit 0145429852
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 829 additions and 402 deletions

View File

@ -229,15 +229,16 @@
(defn make-network-item
"This function generates props for quo/category component item"
[{:keys [network-name color on-change networks state label-props]}]
[{:keys [network-name color on-change networks state label-props type]}]
(cond-> {:title (string/capitalize (name network-name))
:image :icon-avatar
:image-props {:icon (resources/get-network network-name)
:size :size-20}
:action :selector
:action-props {:type (if (= :default state)
:filled-checkbox
:checkbox)
:action-props {:type (or type
(if (= :default state)
:filled-checkbox
:checkbox))
:customization-color color
:checked? (contains? networks network-name)
:on-change on-change}}

View File

@ -3,6 +3,8 @@
[status-im.constants :as constants]
[utils.number]))
(def ^:private last-comma-followed-by-text-to-end-regex #",\s(?=[^,]+$)")
(def id->network
{constants/ethereum-mainnet-chain-id constants/mainnet-network-name
constants/ethereum-goerli-chain-id constants/mainnet-network-name
@ -113,3 +115,13 @@
(as-> prefix $
(string/split $ ":")
(map short-name->network $)))
(defn network-ids->formatted-text
[network-ids]
(let [network-names (->> network-ids
(map id->network)
(map name)
(map string/capitalize)
(string/join ", "))
formatted-text (string/replace network-names last-comma-followed-by-text-to-end-regex " and ")]
formatted-text))

View File

@ -43,3 +43,21 @@
(seq [:mainnet]) "eth"
(seq [:mainnet :optimism]) "eth:opt"
(seq [:mainnet :optimism :arbitrum]) "eth:opt:arb1"))
(deftest test-network-ids->formatted-text
(testing "Empty network-ids should return an empty string"
(is (= "" (utils/network-ids->formatted-text []))))
(testing "Single network-id should return the capitalized name of that network"
(is (= "Mainnet" (utils/network-ids->formatted-text [constants/ethereum-mainnet-chain-id]))))
(testing "Two network-ids should return a comma-separated string with 'and' for the last item"
(is (= "Mainnet and Optimism"
(utils/network-ids->formatted-text [constants/ethereum-mainnet-chain-id
constants/optimism-mainnet-chain-id]))))
(testing "Multiple network-ids should return a comma-separated string with 'and' for the last item"
(is (= "Mainnet, Optimism and Arbitrum"
(utils/network-ids->formatted-text [constants/ethereum-mainnet-chain-id
constants/optimism-mainnet-chain-id
constants/arbitrum-mainnet-chain-id])))))

View File

@ -39,32 +39,38 @@
token-decimals (if collectible 0 (:decimals token))
native-token? (and token (= token-display-name "ETH"))
routes-available? (pos? (count chosen-route))
token-networks (:networks token)
token-networks-ids (when token-networks (mapv #(:chain-id %) token-networks))
from-network-amounts-by-chain (send-utils/network-amounts-by-chain {:route chosen-route
:token-decimals
token-decimals
:native-token?
native-token?
:to? false})
:receiver? false})
from-network-values-for-ui (send-utils/network-values-for-ui from-network-amounts-by-chain)
to-network-amounts-by-chain (send-utils/network-amounts-by-chain {:route chosen-route
:token-decimals
token-decimals
:native-token?
native-token?
:to? true})
:receiver? true})
to-network-values-for-ui (send-utils/network-values-for-ui to-network-amounts-by-chain)
sender-network-values (if routes-available?
(send-utils/network-amounts from-network-values-for-ui
disabled-from-chain-ids
receiver-networks
false)
(send-utils/network-amounts
{:network-values from-network-values-for-ui
:disabled-chain-ids disabled-from-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:receiver? false})
(send-utils/reset-network-amounts-to-zero
sender-network-values))
receiver-network-values (if routes-available?
(send-utils/network-amounts to-network-values-for-ui
disabled-from-chain-ids
receiver-networks
true)
(send-utils/network-amounts
{:network-values to-network-values-for-ui
:disabled-chain-ids disabled-from-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:receiver? true})
(send-utils/reset-network-amounts-to-zero
receiver-network-values))
network-links (when routes-available?
@ -147,6 +153,7 @@
(assoc-in [:wallet :ui :send :recipient] (or recipient address))
(assoc-in [:wallet :ui :send :to-address] to-address)
(assoc-in [:wallet :ui :send :address-prefix] prefix)
(assoc-in [:wallet :ui :send :receiver-preferred-networks] receiver-networks)
(assoc-in [:wallet :ui :send :receiver-networks] receiver-networks))
:fx [(when (and collectible-tx? one-collectible?)
[:dispatch [:wallet/get-suggested-routes {:amount 1}]])
@ -176,26 +183,44 @@
;; `token` is a map extracted from the sender, but in the wallet home page we don't know the
;; sender yet, so we only provide the `token-symbol`, later in
;; `:wallet/select-from-account` the `token` key will be set.
{:db (cond-> db
:always (update-in [:wallet :ui :send] dissoc :collectible)
:always (assoc-in [:wallet :ui :send :token-display-name] (:symbol token))
token (assoc-in [:wallet :ui :send :token] token)
token-symbol (assoc-in [:wallet :ui :send :token-symbol] token-symbol))
:fx [[:dispatch [:wallet/clean-suggested-routes]]
[:dispatch
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id :wallet-send-flow}]]]}))
(let [{token-networks :networks} token
receiver-networks (get-in db [:wallet :ui :send :receiver-networks])
token-networks-ids (mapv #(:chain-id %) token-networks)
token-not-supported-in-receiver-networks? (not (some (set receiver-networks)
token-networks-ids))]
{:db (cond-> db
:always (update-in [:wallet :ui :send] dissoc :collectible)
:always (assoc-in [:wallet :ui :send :token-display-name]
(:symbol token))
:always (assoc-in
[:wallet :ui :send
:token-not-supported-in-receiver-networks?]
token-not-supported-in-receiver-networks?)
token (assoc-in [:wallet :ui :send :token] token)
token-symbol (assoc-in [:wallet :ui :send :token-symbol]
token-symbol))
:fx [[:dispatch [:wallet/clean-suggested-routes]]
[:dispatch
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id :wallet-send-flow}]]]})))
(rf/reg-event-fx
:wallet/edit-token-to-send
(fn [{:keys [db]} [token]]
{:db (-> db
(assoc-in [:wallet :ui :send :token] token)
(assoc-in [:wallet :ui :send :token-display-name] token))
:fx [[:dispatch [:hide-bottom-sheet]]
[:dispatch [:wallet/clean-suggested-routes]]]}))
(let [{token-networks :networks} token
receiver-networks (get-in db [:wallet :ui :send :receiver-networks])
token-networks-ids (mapv #(:chain-id %) token-networks)
token-not-supported-in-receiver-networks? (not (some (set receiver-networks)
token-networks-ids))]
{:db (-> db
(assoc-in [:wallet :ui :send :token] token)
(assoc-in [:wallet :ui :send :token-display-name] token)
(assoc-in [:wallet :ui :send :token-not-supported-in-receiver-networks?]
token-not-supported-in-receiver-networks?))
:fx [[:dispatch [:hide-bottom-sheet]]
[:dispatch [:wallet/clean-suggested-routes]]]})))
(rf/reg-event-fx :wallet/clean-selected-token
(fn [{:keys [db]}]
@ -307,18 +332,21 @@
balances-per-chain
:disabled-chain-ids
disabled-from-chain-ids}))
token-networks-ids (when token (mapv #(:chain-id %) (:networks token)))
sender-network-values (when token-available-networks-for-suggested-routes
(send-utils/loading-network-amounts
token-available-networks-for-suggested-routes
disabled-from-chain-ids
receiver-networks
false))
{:valid-networks token-available-networks-for-suggested-routes
:disabled-chain-ids disabled-from-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:receiver? false}))
receiver-network-values (when token-available-networks-for-suggested-routes
(send-utils/loading-network-amounts
token-available-networks-for-suggested-routes
disabled-from-chain-ids
receiver-networks
true))
{:valid-networks token-available-networks-for-suggested-routes
:disabled-chain-ids disabled-from-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:receiver? true}))
request-params [transaction-type-param
from-address
to-address

View File

@ -76,7 +76,8 @@
:wallet/wallet-send-to-values-by-chain {1 (money/bignumber "250")}
:wallet/wallet-send-sender-network-values nil
:wallet/wallet-send-receiver-network-values nil
:wallet/wallet-send-network-links nil})
:wallet/wallet-send-network-links nil
:wallet/wallet-send-receiver-preferred-networks [1]})
(h/describe "Send > input amount screen"
(h/setup-restorable-re-frame)

View File

@ -1,4 +1,5 @@
(ns status-im.contexts.wallet.send.input-amount.style)
(ns status-im.contexts.wallet.send.input-amount.style
(:require [quo.foundations.colors :as colors]))
(def screen
{:flex 1})
@ -38,3 +39,24 @@
{:height 40
:width "100%"
:align-items :center})
(defn token-not-available-container
[theme]
{:height 90
:flex-direction :row
:background-color (colors/resolve-color :danger theme 5)
:border-color (colors/resolve-color :danger theme 10)
:border-width 1
:border-radius 12
:margin-horizontal 20
:padding 12})
(def token-not-available-content-container
{:margin-left 8
:align-items :flex-start})
(defn token-not-available-text
[theme]
{:height 36
:flex 1
:color (colors/resolve-color :danger theme)})

View File

@ -2,6 +2,8 @@
(:require
[clojure.string :as string]
[quo.core :as quo]
[quo.foundations.colors :as colors]
[quo.theme]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent]
@ -12,6 +14,7 @@
[status-im.contexts.wallet.send.input-amount.style :as style]
[status-im.contexts.wallet.send.routes.view :as routes]
[status-im.contexts.wallet.send.utils :as send-utils]
[status-im.contexts.wallet.sheets.unpreferred-networks-alert.view :as unpreferred-networks-alert]
[utils.address :as address]
[utils.i18n :as i18n]
[utils.money :as money]
@ -26,7 +29,7 @@
string/upper-case)))
(defn- estimated-fees
[{:keys [loading-suggested-routes? fees amount receiver]}]
[{:keys [loading-routes? fees amount receiver]}]
[rn/view {:style style/estimated-fees-container}
[rn/view {:style style/estimated-fees-content-container}
[quo/button
@ -35,20 +38,20 @@
:size 32
:inner-style {:opacity 1}
:accessibility-label :advanced-button
:disabled? loading-suggested-routes?
:disabled? loading-routes?
:on-press #(js/alert "Not implemented yet")}
:i/advanced]]
[quo/data-item
{:container-style style/fees-data-item
:label :none
:status (if loading-suggested-routes? :loading :default)
:status (if loading-routes? :loading :default)
:size :small
:title (i18n/label :t/fees)
:subtitle fees}]
[quo/data-item
{:container-style style/amount-data-item
:label :none
:status (if loading-suggested-routes? :loading :default)
:status (if loading-routes? :loading :default)
:size :small
:title (i18n/label :t/user-gets {:name receiver})
:subtitle amount}]])
@ -77,6 +80,36 @@
(rf/dispatch [:wallet/edit-token-to-send token])
(clear-input!))}]]))
(defn- token-not-available
[token-symbol receiver-networks token-networks]
(let [theme (quo.theme/use-theme)
add-token-networks (fn []
(let [chain-ids (concat receiver-networks
(mapv #(:chain-id %) token-networks))]
(rf/dispatch [:wallet/update-receiver-networks chain-ids])))]
[rn/view {:style (style/token-not-available-container theme)}
[rn/view
[quo/icon :i/alert
{:size 16
:color colors/danger-50}]]
[rn/view {:style style/token-not-available-content-container}
[quo/text
{:style (style/token-not-available-text theme)
:size :paragraph-2}
(i18n/label :t/token-not-available-on-receiver-networks {:token-symbol token-symbol})]
[quo/button
{:size 24
:customization-color colors/danger-50
:on-press add-token-networks}
(i18n/label :t/add-networks-token-can-be-sent-to {:token-symbol token-symbol})]]]))
(defn- show-unpreferred-networks-alert
[on-confirm]
(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[unpreferred-networks-alert/view
{:on-confirm on-confirm}])}]))
(defn view
;; crypto-decimals, limit-crypto and initial-crypto-currency? args are needed
;; for component tests only
@ -94,91 +127,112 @@
crypto-currency? (reagent/atom initial-crypto-currency?)
on-navigate-back on-navigate-back]
(fn []
(let [[input-state set-input-state] (rn/use-state controlled-input/init-state)
clear-input! #(set-input-state controlled-input/delete-all)
handle-on-confirm (fn []
(rf/dispatch [:wallet/set-token-amount-to-send
{:amount (controlled-input/input-value
input-state)
:stack-id current-screen-id}]))
{fiat-currency :currency} (rf/sub [:profile/profile])
(let [[input-state set-input-state] (rn/use-state controlled-input/init-state)
clear-input! #(set-input-state controlled-input/delete-all)
handle-on-confirm (fn []
(rf/dispatch [:wallet/set-token-amount-to-send
{:amount
(controlled-input/input-value
input-state)
:stack-id current-screen-id}]))
{fiat-currency :currency} (rf/sub [:profile/profile])
{token-symbol :symbol
token-networks :networks} (rf/sub [:wallet/wallet-send-token])
token-networks :networks} (rf/sub [:wallet/wallet-send-token])
{token-balance :total-balance
:as
token} (rf/sub
[:wallet/current-viewing-account-tokens-filtered
(str token-symbol)])
conversion-rate (-> token :market-values-per-currency :usd :price)
loading-routes? (rf/sub
[:wallet/wallet-send-loading-suggested-routes?])
token} (rf/sub
[:wallet/current-viewing-account-tokens-filtered
(str token-symbol)])
conversion-rate (-> token :market-values-per-currency :usd :price)
loading-routes? (rf/sub
[:wallet/wallet-send-loading-suggested-routes?])
route (rf/sub [:wallet/wallet-send-route])
to-address (rf/sub [:wallet/wallet-send-to-address])
route (rf/sub [:wallet/wallet-send-route])
to-address (rf/sub [:wallet/wallet-send-to-address])
on-confirm (or default-on-confirm handle-on-confirm)
crypto-decimals (or default-crypto-decimals
(utils/get-crypto-decimals-count token))
crypto-limit (or default-limit-crypto
(utils/get-standard-crypto-format
token
token-balance))
fiat-limit (.toFixed (* token-balance conversion-rate) 2)
current-limit (if @crypto-currency? crypto-limit fiat-limit)
valid-input? (not (or (string/blank? (controlled-input/input-value
input-state))
(<= (controlled-input/numeric-value input-state) 0)
(> (controlled-input/numeric-value input-state)
current-limit)))
current-currency (if @crypto-currency? token-symbol fiat-currency)
input-num-value (controlled-input/numeric-value input-state)
confirm-disabled? (or (nil? route)
(empty? route)
(string/blank? (controlled-input/input-value input-state))
(<= input-num-value 0)
(> input-num-value current-limit))
amount-text (str (controlled-input/input-value input-state)
" "
token-symbol)
first-route (first route)
native-currency-symbol (when-not confirm-disabled?
(get-in first-route [:from :native-currency-symbol]))
native-token (when native-currency-symbol
(rf/sub [:wallet/token-by-symbol
native-currency-symbol]))
fee-in-native-token (when-not confirm-disabled?
(send-utils/calculate-full-route-gas-fee route))
fee-in-crypto-formatted (when fee-in-native-token
(utils/get-standard-crypto-format
native-token
fee-in-native-token))
fee-in-fiat (when-not confirm-disabled?
(utils/calculate-token-fiat-value
{:currency fiat-currency
:balance fee-in-native-token
:token native-token}))
currency-symbol (rf/sub [:profile/currency-symbol])
fee-formatted (when fee-in-fiat
(utils/get-standard-fiat-format
fee-in-crypto-formatted
currency-symbol
fee-in-fiat))
show-select-asset-sheet #(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[select-asset-bottom-sheet
clear-input!])}])
loading-suggested-routes? (rf/sub
[:wallet/wallet-send-loading-suggested-routes?])
sender-network-values (rf/sub
[:wallet/wallet-send-sender-network-values])
suggested-routes (rf/sub [:wallet/wallet-send-suggested-routes])
routes (when suggested-routes
(or (:best suggested-routes) []))
no-routes-found? (and
(every-network-value-is-zero? sender-network-values)
(not (nil? routes))
(not loading-suggested-routes?))]
on-confirm (or default-on-confirm handle-on-confirm)
crypto-decimals (or default-crypto-decimals
(utils/get-crypto-decimals-count token))
crypto-limit (or default-limit-crypto
(utils/get-standard-crypto-format
token
token-balance))
fiat-limit (.toFixed (* token-balance conversion-rate) 2)
current-limit (if @crypto-currency? crypto-limit fiat-limit)
valid-input? (not (or (string/blank?
(controlled-input/input-value
input-state))
(<= (controlled-input/numeric-value
input-state)
0)
(> (controlled-input/numeric-value
input-state)
current-limit)))
current-currency (if @crypto-currency? token-symbol fiat-currency)
input-num-value (controlled-input/numeric-value input-state)
confirm-disabled? (or (nil? route)
(empty? route)
(string/blank? (controlled-input/input-value
input-state))
(<= input-num-value 0)
(> input-num-value current-limit))
amount-text (str (controlled-input/input-value input-state)
" "
token-symbol)
first-route (first route)
native-currency-symbol (when-not confirm-disabled?
(get-in first-route
[:from :native-currency-symbol]))
native-token (when native-currency-symbol
(rf/sub [:wallet/token-by-symbol
native-currency-symbol]))
fee-in-native-token (when-not confirm-disabled?
(send-utils/calculate-full-route-gas-fee route))
fee-in-crypto-formatted (when fee-in-native-token
(utils/get-standard-crypto-format
native-token
fee-in-native-token))
fee-in-fiat (when-not confirm-disabled?
(utils/calculate-token-fiat-value
{:currency fiat-currency
:balance fee-in-native-token
:token native-token}))
currency-symbol (rf/sub [:profile/currency-symbol])
fee-formatted (when fee-in-fiat
(utils/get-standard-fiat-format
fee-in-crypto-formatted
currency-symbol
fee-in-fiat))
show-select-asset-sheet #(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[select-asset-bottom-sheet
clear-input!])}])
sender-network-values (rf/sub
[:wallet/wallet-send-sender-network-values])
receiver-network-values (rf/sub
[:wallet/wallet-send-receiver-network-values])
token-not-supported-in-receiver-networks? (every? #(= (:type %) :not-available)
(filter #(not= (:type %) :add)
receiver-network-values))
suggested-routes (rf/sub [:wallet/wallet-send-suggested-routes])
routes (when suggested-routes
(or (:best suggested-routes) []))
no-routes-found? (and
(every-network-value-is-zero?
sender-network-values)
(not (nil? routes))
(not loading-routes?)
(not token-not-supported-in-receiver-networks?))
receiver-networks (rf/sub [:wallet/wallet-send-receiver-networks])
receiver-preferred-networks (rf/sub
[:wallet/wallet-send-receiver-preferred-networks])
receiver-preferred-networks-set (set receiver-preferred-networks)
sending-to-unpreferred-networks? (not (every? (fn [receiver-selected-network]
(contains?
receiver-preferred-networks-set
receiver-selected-network))
receiver-networks))]
(rn/use-mount
(fn []
(let [dismiss-keyboard-fn #(when (= % "active") (rn/dismiss-keyboard!))
@ -211,16 +265,21 @@
:on-swap #(reset! crypto-currency? %)
:on-token-press show-select-asset-sheet}]
[routes/view
{:token token
:input-value (controlled-input/input-value input-state)
:valid-input? valid-input?
:current-screen-id current-screen-id}]
{:token token
:input-value (controlled-input/input-value input-state)
:valid-input? valid-input?
:token-not-supported-in-receiver-networks? token-not-supported-in-receiver-networks?
:current-screen-id current-screen-id}]
(when (and (not loading-routes?)
sender-network-values
token-not-supported-in-receiver-networks?)
[token-not-available token-symbol receiver-networks token-networks])
(when (or loading-routes? (seq route))
[estimated-fees
{:loading-suggested-routes? loading-routes?
:fees fee-formatted
:amount amount-text
:receiver (address/get-shortened-key to-address)}])
{:loading-routes? loading-routes?
:fees fee-formatted
:amount amount-text
:receiver (address/get-shortened-key to-address)}])
(when no-routes-found?
[rn/view {:style style/no-routes-found-container}
[quo/info-message
@ -236,10 +295,14 @@
button-one-label)
:button-one-props (merge button-one-props
{:disabled? (and (not no-routes-found?) confirm-disabled?)
:on-press (if no-routes-found?
:on-press (cond
no-routes-found?
#(rf/dispatch [:wallet/get-suggested-routes
{:amount (controlled-input/input-value
input-state)}])
sending-to-unpreferred-networks?
#(show-unpreferred-networks-alert on-confirm)
:else
on-confirm)}
(when no-routes-found?
{:type :grey}))}]

View File

@ -1,14 +1,11 @@
(ns status-im.contexts.wallet.send.routes.view
(:require
[clojure.string :as string]
[quo.core :as quo]
[quo.foundations.colors :as colors]
[quo.foundations.resources :as resources]
[react-native.core :as rn]
[reagent.core :as reagent]
[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.re-frame :as rf]))
@ -19,19 +16,6 @@
(def network-link-1x-height 56)
(def network-link-2x-height 111)
(defn- make-network-item
[{:keys [network-name chain-id] :as _network}
{:keys [title color on-change network-preferences] :as _options}]
{:title (or title (string/capitalize (name network-name)))
:image :icon-avatar
:image-props {:icon (resources/get-network network-name)
:size :size-20}
:action :selector
:action-props {:type :checkbox
:customization-color color
:checked? (some #(= % chain-id) @network-preferences)
:on-change on-change}})
(defn fetch-routes
[amount valid-input? bounce-duration-ms]
(if valid-input?
@ -40,82 +24,93 @@
bounce-duration-ms)
(rf/dispatch [:wallet/clean-suggested-routes])))
(defn networks-drawer
[{:keys [on-save theme]}]
(let [network-details (rf/sub [:wallet/network-details])
{:keys [color]} (rf/sub [:wallet/current-viewing-account])
selected-networks (rf/sub [:wallet/wallet-send-receiver-networks])
prefix (rf/sub [:wallet/wallet-send-address-prefix])
prefix-seq (string/split prefix #":")
grouped-details (group-by #(contains? (set prefix-seq) (:short-name %)) network-details)
preferred (get grouped-details true [])
not-preferred (get grouped-details false [])
network-preferences (reagent/atom selected-networks)
toggle-network (fn [{:keys [chain-id]}]
(swap! network-preferences
(fn [preferences]
(if (some #(= % chain-id) preferences)
(vec (remove #(= % chain-id) preferences))
(conj preferences chain-id)))))]
(fn []
[rn/view
[quo/drawer-top {:title (i18n/label :t/edit-receiver-networks)}]
[quo/category
{:list-type :settings
:label (i18n/label :t/preferred-by-receiver)
:data (mapv (fn [network]
(make-network-item network
{:color color
:network-preferences network-preferences
:on-change #(toggle-network network)}))
preferred)}]
(when (pos? (count not-preferred))
[quo/category
{:list-type :settings
:label (i18n/label :t/not-preferred-by-receiver)
:data (mapv (fn [network]
(make-network-item network
{:color color
:network-preferences network-preferences
:on-change #(toggle-network network)}))
not-preferred)}])
(when (not= selected-networks @network-preferences)
[rn/view {:style (style/warning-container color theme)}
[quo/icon :i/info {:color (colors/resolve-color color theme)}]
[quo/text
{:size :paragraph-2
:style style/warning-text} (i18n/label :t/receiver-networks-warning)]])
[quo/bottom-actions
{:actions :one-action
:button-one-label (i18n/label :t/apply-changes)
:button-one-props {:disabled? (or (= selected-networks @network-preferences)
(empty? @network-preferences))
:on-press (fn []
(rf/dispatch [:wallet/update-receiver-networks
@network-preferences])
(rf/dispatch [:hide-bottom-sheet])
(on-save))
:customization-color color}}]])))
(defn- open-preferences
[]
(rf/dispatch
[:show-bottom-sheet
{:content
(fn []
(let [receiver-networks (rf/sub [:wallet/wallet-send-receiver-networks])
receiver-preferred-networks (rf/sub
[:wallet/wallet-send-receiver-preferred-networks])
{token-symbol :symbol
token-networks :networks} (rf/sub [:wallet/wallet-send-token])
token-chain-ids-set (set (mapv #(:chain-id %) token-networks))
[selected-receiver-networks
set-selected-receiver-networks] (rn/use-state receiver-networks)
receiver-preferred-networks-set (set receiver-preferred-networks)
receiver-selected-preferred-networks (filter #(contains?
receiver-preferred-networks-set
%)
selected-receiver-networks)
receiver-selected-non-preferred-networks (filter #(not (contains?
receiver-preferred-networks-set
%))
selected-receiver-networks)
not-available-preferred-networks (filter (fn [preferred-chain-id]
(not (contains? token-chain-ids-set
preferred-chain-id)))
receiver-selected-preferred-networks)
not-available-non-preferred-networks (filter (fn [preferred-chain-id]
(not (contains?
token-chain-ids-set
preferred-chain-id)))
receiver-selected-non-preferred-networks)
first-section-warning-label (when (not-empty not-available-preferred-networks)
(i18n/label
:t/token-not-available-on-networks
{:token-symbol token-symbol
:networks
(network-utils/network-ids->formatted-text
not-available-preferred-networks)}))
second-section-warning-label (when (not-empty
not-available-non-preferred-networks)
(i18n/label
:t/token-not-available-on-networks
{:token-symbol token-symbol
:networks
(network-utils/network-ids->formatted-text
not-available-non-preferred-networks)}))]
[network-preferences/view
{:title (i18n/label :t/edit-receiver-networks)
:first-section-label (i18n/label :t/preferred-by-receiver)
:second-section-label (i18n/label :t/not-preferred-by-receiver)
:selected-networks (set (map network-utils/id->network
receiver-networks))
:receiver-preferred-networks receiver-preferred-networks
:button-label (i18n/label :t/apply-changes)
:first-section-warning-label first-section-warning-label
:second-section-warning-label second-section-warning-label
:on-change #(set-selected-receiver-networks %)
:on-save (fn [chain-ids]
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch [:wallet/update-receiver-networks
chain-ids]))}]))}]))
(defn render-network-values
[{:keys [network-values token-symbol on-press theme on-save to? loading-suggested-routes?]}]
[{:keys [network-values token-symbol on-press receiver? loading-routes?
token-not-supported-in-receiver-networks?]}]
[rn/view
(map-indexed (fn [index {:keys [chain-id total-amount type]}]
[rn/view
{:key (str (if to? "to" "from") "-" chain-id)
{:key (str (if receiver? "to" "from") "-" chain-id)
:style {:margin-top (if (pos? index) 11 7.5)}}
[quo/network-bridge
{:amount (str total-amount " " token-symbol)
{:amount (if (= type :not-available)
(i18n/label :t/not-available)
(str total-amount " " token-symbol))
:network (network-utils/id->network chain-id)
:status type
:on-press #(when (not loading-suggested-routes?)
: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 :add)
(rf/dispatch [:show-bottom-sheet
{:content (fn []
[networks-drawer
{:theme theme
:on-save on-save}])}])
(open-preferences)
on-press (on-press chain-id total-amount)))}]])
network-values)])
@ -180,12 +175,13 @@
(defn view
[{:keys [token theme input-value valid-input?
on-press-to-network current-screen-id]}]
on-press-to-network current-screen-id
token-not-supported-in-receiver-networks?]}]
(let [token-symbol (:symbol token)
nav-current-screen-id (rf/sub [:view-id])
active-screen? (= nav-current-screen-id current-screen-id)
loading-suggested-routes? (rf/sub
[:wallet/wallet-send-loading-suggested-routes?])
loading-routes? (rf/sub
[:wallet/wallet-send-loading-suggested-routes?])
sender-network-values (rf/sub
[:wallet/wallet-send-sender-network-values])
receiver-network-values (rf/sub
@ -220,23 +216,27 @@
:container-style style/section-label-right}]])
[rn/view {:style style/routes-inner-container}
[render-network-values
{:token-symbol token-symbol
:network-values sender-network-values
:on-press #(disable-chain %1
disabled-from-chain-ids
token-available-networks-for-suggested-routes)
:to? false
:theme theme
:loading-suggested-routes? loading-suggested-routes?}]
{:token-symbol token-symbol
:network-values sender-network-values
:on-press (fn [chain-id-to-disable]
(disable-chain
chain-id-to-disable
disabled-from-chain-ids
token-available-networks-for-suggested-routes))
:receiver? false
:theme theme
:loading-routes? loading-routes?
:token-not-supported-in-receiver-networks? false}]
[render-network-links
{:network-links network-links
:sender-network-values sender-network-values}]
[render-network-values
{:token-symbol token-symbol
:network-values receiver-network-values
:on-press on-press-to-network
:to? true
:loading-suggested-routes? loading-suggested-routes?
:theme theme
:on-save #(fetch-routes input-value valid-input? 0)}]]]))
{:token-symbol token-symbol
:network-values receiver-network-values
:on-press on-press-to-network
:receiver? true
:loading-routes? loading-routes?
:theme theme
:token-not-supported-in-receiver-networks? token-not-supported-in-receiver-networks?
:on-save #(fetch-routes input-value valid-input? 0)}]]]))

View File

@ -43,21 +43,22 @@
(money/wei->ether (reduce money/add (map calculate-gas-fee route))))
(defn network-amounts-by-chain
[{:keys [route token-decimals native-token? to?]}]
(reduce (fn [acc path]
(let [amount-hex (if to? (:amount-in path) (:amount-out path))
amount-units (native-module/hex-to-number
(utils.hex/normalize-hex amount-hex))
amount (money/with-precision
(if native-token?
(money/wei->ether amount-units)
(money/token->unit amount-units
token-decimals))
6)
chain-id (if to? (get-in path [:to :chain-id]) (get-in path [:from :chain-id]))]
(update acc chain-id money/add amount)))
{}
route))
[{:keys [route token-decimals native-token? receiver?]}]
(reduce
(fn [acc path]
(let [amount-hex (if receiver? (:amount-in path) (:amount-out path))
amount-units (native-module/hex-to-number
(utils.hex/normalize-hex amount-hex))
amount (money/with-precision
(if native-token?
(money/wei->ether amount-units)
(money/token->unit amount-units
token-decimals))
6)
chain-id (if receiver? (get-in path [:to :chain-id]) (get-in path [:from :chain-id]))]
(update acc chain-id money/add amount)))
{}
route))
(defn network-values-for-ui
[amounts]
@ -93,62 +94,103 @@
network-amounts))
(defn network-amounts
[network-values disabled-chain-ids receiver-networks to?]
(let [disabled-set (set disabled-chain-ids)
receiver-networks-set (set receiver-networks)
network-values-keys (set (keys network-values))
routes-found? (pos? (count network-values-keys))
updated-network-values (when routes-found?
(reduce (fn [acc k]
(if (or (contains? network-values-keys k)
(and to?
(not (contains? receiver-networks-set k))))
acc
(assoc acc k (money/bignumber "0"))))
network-values
disabled-chain-ids))]
(cond-> (->> updated-network-values
(map
(fn [[k v]]
{:chain-id k
:total-amount v
:type (if (or to? (not (contains? disabled-set k))) :default :disabled)}))
(sort-by #(get network-priority-score (network-utils/id->network (:chain-id %))))
(filter
#(or (and to?
(or (contains? receiver-networks-set (:chain-id %))
(money/greater-than (:total-amount %) (money/bignumber "0"))))
(not to?)))
(vec))
(and to?
[{:keys [network-values disabled-chain-ids receiver-networks token-networks-ids receiver?]}]
(let [disabled-set (set disabled-chain-ids)
receiver-networks-set (set receiver-networks)
network-values-keys (set (keys network-values))
routes-found? (pos? (count network-values-keys))
token-networks-ids-set (set token-networks-ids)
not-available-networks (if receiver?
(filter #(not (token-networks-ids-set %))
receiver-networks)
[])
not-available-networks-set (set not-available-networks)
network-values-with-disabled-chains (when routes-found?
(reduce
(fn [acc k]
(if (or (contains? network-values-keys k)
(and receiver?
(not (contains? receiver-networks-set
k))))
acc
(assoc acc k (money/bignumber "0"))))
network-values
disabled-chain-ids))
network-values-with-not-available-chains (if (and receiver? routes-found?)
(let [network-values-keys
(set (keys
network-values-with-disabled-chains))]
(reduce
(fn [acc k]
(if (not (contains? network-values-keys k))
(assoc acc k nil)
acc))
network-values-with-disabled-chains
not-available-networks))
network-values-with-disabled-chains)]
(cond-> (->>
network-values-with-not-available-chains
(map
(fn [[chain-id amount]]
{:chain-id chain-id
:total-amount amount
:type (cond
(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)}))
(sort-by (fn [network-amount]
(get network-priority-score
(network-utils/id->network (:chain-id network-amount)))))
(filter
(fn [network-amount]
(or (and receiver?
(or (contains? receiver-networks-set (:chain-id network-amount))
(money/greater-than (:total-amount network-amount) (money/bignumber "0"))))
(not receiver?))))
(vec))
(and receiver?
routes-found?
(< (count updated-network-values) available-networks-count))
(< (count network-values-with-not-available-chains) available-networks-count))
(conj {:type :add}))))
(defn loading-network-amounts
[valid-networks disabled-chain-ids receiver-networks to?]
(let [disabled-set (set disabled-chain-ids)
receiver-networks-set (set receiver-networks)
receiver-networks-count (count receiver-networks)
valid-networks (concat valid-networks disabled-chain-ids)]
[{:keys [valid-networks disabled-chain-ids receiver-networks token-networks-ids receiver?]}]
(let [disabled-set (set disabled-chain-ids)
receiver-networks-set (set receiver-networks)
receiver-networks-count (count receiver-networks)
token-networks-ids-set (set token-networks-ids)
valid-networks-set (set valid-networks)
not-available-networks (if receiver?
(filter #(not (token-networks-ids-set %)) receiver-networks)
[])
not-available-networks-set (set not-available-networks)
valid-networks (concat valid-networks
disabled-chain-ids
(when receiver?
(filter #(not (valid-networks-set %))
not-available-networks)))]
(cond-> (->> valid-networks
(map (fn [k]
(cond-> {:chain-id k
:type (if (or to?
(not (contains? disabled-set k)))
:loading
:disabled)}
(and (not to?) (contains? disabled-set k))
(assoc :total-amount (money/bignumber "0")))))
(sort-by (fn [item]
(map
(fn [chain-id]
(cond->
{:chain-id chain-id
:type (cond
(contains? not-available-networks-set chain-id) :not-available
(or receiver?
(not (contains? disabled-set chain-id))) :loading
(and (not receiver?) (contains? disabled-set chain-id)) :disabled)}
(and (not receiver?) (contains? disabled-set chain-id))
(assoc :total-amount (money/bignumber "0")))))
(sort-by (fn [network-amount]
(get network-priority-score
(network-utils/id->network (:chain-id item)))))
(network-utils/id->network (:chain-id network-amount)))))
(filter
#(or (and to? (contains? receiver-networks-set (:chain-id %)))
(and (not to?)
(not (contains? disabled-chain-ids (:chain-id %))))))
(fn [network-amount]
(or (and receiver? (contains? receiver-networks-set (:chain-id network-amount)))
(and (not receiver?)
(not (contains? disabled-chain-ids (:chain-id network-amount)))))))
(vec))
(and to? (< receiver-networks-count available-networks-count)) (conj {:type :add}))))
(and receiver? (< receiver-networks-count available-networks-count)) (conj {:type :add}))))
(defn network-links
[route from-values-by-chain to-values-by-chain]

View File

@ -38,11 +38,11 @@
:to {:chain-id "2"}}]
token-decimals 18
native-token? true
to? true
receiver? true
result (utils/network-amounts-by-chain {:route route
:token-decimals token-decimals
:native-token? native-token?
:to? to?})
:receiver? receiver?})
expected {"1" (money/bignumber "1")
"2" (money/bignumber "1")}]
(doseq [[chain-id exp-value] expected]
@ -56,11 +56,11 @@
:to {:chain-id "1"}}]
token-decimals 18
native-token? true
to? true
receiver? true
result (utils/network-amounts-by-chain {:route route
:token-decimals token-decimals
:native-token? native-token?
:to? to?})
:receiver? receiver?})
expected {"1" (money/bignumber "2")}]
(doseq [[chain-id exp-value] expected]
(is (money/equal-to (get result chain-id) exp-value)))))
@ -72,11 +72,11 @@
:from {:chain-id "2"}}]
token-decimals 6
native-token? false
to? false
receiver? false
result (utils/network-amounts-by-chain {:route route
:token-decimals token-decimals
:native-token? native-token?
:to? to?})
:receiver? receiver?})
expected {"1" (money/bignumber "2")
"2" (money/bignumber "2")}]
(doseq [[chain-id exp-value] expected]
@ -268,12 +268,13 @@
(is (every? identity comparisons)))))
(deftest test-network-amounts
(testing "Handles disabled and receiver networks correctly when to? is true"
(testing "Handles disabled and receiver networks correctly when receiver? is true"
(let [network-values {"1" (money/bignumber "100")
"10" (money/bignumber "200")}
disabled-chain-ids ["1"]
receiver-networks ["10"]
to? true
token-networks-ids ["1" "10" "42161"]
receiver? true
expected [{:chain-id "1"
:total-amount (money/bignumber "100")
:type :default}
@ -281,39 +282,44 @@
:total-amount (money/bignumber "200")
:type :default}
{:type :add}]
result (utils/network-amounts network-values
disabled-chain-ids
receiver-networks
to?)]
result (utils/network-amounts {:network-values network-values
:disabled-chain-ids disabled-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:receiver? receiver?})]
(is (every? identity (map #(map/deep-compare %1 %2) expected result)))))
(testing "Adds default amount for non-disabled non-receiver networks when to? is false"
(testing "Adds default amount for non-disabled non-receiver networks when receiver? is false"
(let [network-values {"1" (money/bignumber "100")}
disabled-chain-ids ["10"]
receiver-networks []
to? false
token-networks-ids ["1" "10" "42161"]
receiver? false
expected [{:chain-id "1"
:total-amount (money/bignumber "100")
:type :default}
{:chain-id "10"
:total-amount (money/bignumber "0")
:type :disabled}]
result (utils/network-amounts network-values
disabled-chain-ids
receiver-networks
to?)]
result (utils/network-amounts {:network-values network-values
:disabled-chain-ids disabled-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:receiver? receiver?})]
(is (every? identity (map #(map/deep-compare %1 %2) expected result)))))
(testing "Handles empty inputs correctly"
(let [network-values {}
disabled-chain-ids []
receiver-networks []
to? true
token-networks-ids []
receiver? true
expected []
result (utils/network-amounts network-values
disabled-chain-ids
receiver-networks
to?)]
result (utils/network-amounts {:network-values network-values
:disabled-chain-ids disabled-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:receiver? receiver?})]
(is (= expected result))))
(testing "Processes case with multiple network interactions"
@ -322,7 +328,8 @@
"42161" (money/bignumber "500")}
disabled-chain-ids ["1" "42161"]
receiver-networks ["10"]
to? true
token-networks-ids ["1" "10" "42161"]
receiver? true
expected [{:chain-id "1"
:total-amount (money/bignumber "300")
:type :default}
@ -332,10 +339,50 @@
{:chain-id "42161"
:total-amount (money/bignumber "500")
:type :default}]
result (utils/network-amounts network-values
disabled-chain-ids
receiver-networks
to?)]
result (utils/network-amounts {:network-values network-values
:disabled-chain-ids disabled-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:receiver? receiver?})]
(is (every? identity (map #(map/deep-compare %1 %2) expected result)))))
(testing "Does not assign :not-available type when receiver? is false"
(let [network-values {"1" (money/bignumber "100")}
disabled-chain-ids ["10"]
receiver-networks ["1"]
token-networks-ids ["1" "10"]
receiver? false
expected [{:chain-id "1"
:total-amount (money/bignumber "100")
:type :default}
{:chain-id "10"
:total-amount (money/bignumber "0")
:type :disabled}]
result (utils/network-amounts {:network-values network-values
:disabled-chain-ids disabled-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:receiver? receiver?})]
(is (every? identity (map #(map/deep-compare %1 %2) expected result)))))
(testing
"Assigns :not-available type to networks not available in token-networks-ids when receiver? is true"
(let [network-values {"1" (money/bignumber "100")}
disabled-chain-ids []
receiver-networks ["1" "10"]
token-networks-ids ["1"]
receiver? false
expected [{:chain-id "1"
:total-amount (money/bignumber "100")
:type :default}
{:chain-id "10"
:total-amount nil
:type :not-available}]
result (utils/network-amounts {:network-values network-values
:disabled-chain-ids disabled-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:receiver? receiver?})]
(is (every? identity (map #(map/deep-compare %1 %2) expected result))))))
(deftest test-loading-network-amounts
@ -343,63 +390,109 @@
(let [valid-networks ["1" "10" "42161"]
disabled-chain-ids ["42161"]
receiver-networks ["1" "10"]
to? true
token-networks-ids ["1" "10" "42161"]
receiver? true
expected [{:chain-id "1" :type :loading}
{:chain-id "10" :type :loading}
{:type :add}]
result (utils/loading-network-amounts valid-networks
disabled-chain-ids
receiver-networks
to?)
result (utils/loading-network-amounts {:valid-networks valid-networks
:disabled-chain-ids disabled-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:receiver? receiver?})
comparisons (map #(map/deep-compare %1 %2)
expected
result)]
(is (every? identity comparisons))))
(testing "Assigns :disabled type with zero total-amount to disabled networks when to? is false"
(testing "Assigns :disabled type with zero total-amount to disabled networks when receiver? is false"
(let [valid-networks ["1" "10" "42161"]
disabled-chain-ids ["10" "42161"]
receiver-networks ["1"]
to? false
token-networks-ids ["1" "10" "42161"]
receiver? false
expected [{:chain-id "1" :type :loading}
{:chain-id "10" :type :disabled :total-amount (money/bignumber "0")}
{:chain-id "42161" :type :disabled :total-amount (money/bignumber "0")}]
result (utils/loading-network-amounts valid-networks
disabled-chain-ids
receiver-networks
to?)
result (utils/loading-network-amounts {:valid-networks valid-networks
:disabled-chain-ids disabled-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:receiver? receiver?})
comparisons (map #(map/deep-compare %1 %2)
expected
result)]
(is (every? identity comparisons))))
(testing "Filters out networks not in receiver networks when to? is true"
(testing "Filters out networks not in receiver networks when receiver? is true"
(let [valid-networks ["1" "10" "42161" "59144"]
disabled-chain-ids ["10"]
receiver-networks ["1" "42161"]
to? true
token-networks-ids ["1" "10" "42161"]
receiver? true
expected [{:chain-id "1" :type :loading}
{:chain-id "42161" :type :loading}]
result (utils/loading-network-amounts valid-networks
disabled-chain-ids
receiver-networks
to?)
result (utils/loading-network-amounts {:valid-networks valid-networks
:disabled-chain-ids disabled-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:receiver? receiver?})
comparisons (map #(map/deep-compare %1 %2)
expected
result)]
(is (every? identity comparisons))))
(testing "Appends :add type if receiver network count is less than available networks and to? is true"
(testing
"Appends :add type if receiver network count is less than available networks and receiver? is true"
(let [valid-networks ["1" "10" "42161"]
disabled-chain-ids ["10"]
receiver-networks ["1"]
to? true
token-networks-ids ["1" "10" "42161"]
receiver? true
expected [{:chain-id "1" :type :loading}
{:type :add}]
result (utils/loading-network-amounts valid-networks
disabled-chain-ids
receiver-networks
to?)
result (utils/loading-network-amounts {:valid-networks valid-networks
:disabled-chain-ids disabled-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:receiver? receiver?})
comparisons (map #(map/deep-compare %1 %2)
expected
result)]
(is (every? identity comparisons))))
(testing
"Assigns :not-available type to networks not available in token-networks-ids when receiver? is false"
(let [valid-networks ["42161"]
disabled-chain-ids []
receiver-networks ["1"]
token-networks-ids ["42161"]
receiver? false
expected [{:chain-id "42161" :type :loading}]
result (utils/loading-network-amounts {:valid-networks valid-networks
:disabled-chain-ids disabled-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:receiver? receiver?})
comparisons (map #(map/deep-compare %1 %2)
expected
result)]
(is (every? identity comparisons))))
(testing
"Assigns :not-available type to networks not available in token-networks-ids when receiver? is true"
(let [valid-networks ["42161"]
disabled-chain-ids []
receiver-networks ["1"]
token-networks-ids ["42161"]
receiver? true
expected [{:chain-id "1" :type :not-available}
{:type :add}]
result (utils/loading-network-amounts {:valid-networks valid-networks
:disabled-chain-ids disabled-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:receiver? receiver?})
comparisons (map #(map/deep-compare %1 %2)
expected
result)]

View File

@ -12,3 +12,26 @@
(def data-item
{:margin-horizontal 20
:margin-vertical 8})
(defn warning-container
[sending-to-unpreferred-networks?]
{:flex-direction :row
:margin-horizontal 20
:margin-bottom (when sending-to-unpreferred-networks? 8)
:align-items :center})
(defn sending-to-unpreferred-networks-alert-container
[theme]
{:height 76
:flex-direction :row
:background-color (colors/resolve-color :blue theme 5)
:border-color (colors/resolve-color :blue theme 10)
:border-width 1
:border-radius 12
:margin-horizontal 20
:padding 10})
(def sending-to-unpreferred-networks-text
{:flex 1
:height 54.6
:margin-left 8})

View File

@ -3,6 +3,7 @@
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.blur :as blur]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im.constants :as constants]
[status-im.contexts.wallet.common.utils :as utils]
@ -11,12 +12,14 @@
[utils.re-frame :as rf]))
(defn view
[{:keys [selected-networks account watch-only?]}]
[{:keys [title first-section-label second-section-label selected-networks
receiver-preferred-networks account watch-only?]}]
(let [state (reagent/atom :default)
{:keys [color address
network-preferences-names]} (or account (rf/sub [:wallet/current-viewing-account]))
initial-network-preferences-names (or selected-networks network-preferences-names)
network-preferences-names-state (reagent/atom #{})
receiver? (boolean (not-empty receiver-preferred-networks))
network-preferences-names-state (reagent/atom (if receiver? selected-networks #{}))
toggle-network (fn [network-name]
(reset! state :changed)
(let [contains-network? (contains?
@ -34,15 +37,30 @@
(if (= @state :default)
initial-network-preferences-names
@network-preferences-names-state))]
(fn [{:keys [on-save blur? button-label]}]
(let [theme (quo.theme/use-theme)
network-details (rf/sub [:wallet/network-details])
mainnet (first network-details)
layer-2-networks (rest network-details)
current-networks (filter (fn [network]
(contains? (get-current-preferences-names)
(:network-name network)))
network-details)]
(fn [{:keys [on-save on-change blur? button-label first-section-warning-label
second-section-warning-label]}]
(let [theme (quo.theme/use-theme)
network-details (rf/sub [:wallet/network-details])
first-section-networks (filter (fn [network]
(if receiver-preferred-networks
(some (fn [chain-id]
(= (:chain-id network) chain-id))
receiver-preferred-networks)
(= (:network-name network) :mainnet)))
network-details)
second-section-networks (remove (fn [network]
(some (fn [chain-id]
(= (:chain-id network) chain-id))
(map :chain-id first-section-networks)))
network-details)
current-networks (filter (fn [network]
(contains? (get-current-preferences-names)
(:network-name network)))
network-details)
sending-to-unpreferred-networks? (and receiver?
(some #(contains? @network-preferences-names-state
(:network-name %))
second-section-networks))]
[:<>
;; quo/overlay isn't compatible with sheets
(when blur?
@ -51,52 +69,103 @@
:blur-amount 20
:blur-radius 25}])
[quo/drawer-top
{:title (i18n/label :t/network-preferences)
:description (if watch-only?
(i18n/label :t/network-preferences-desc-1)
(i18n/label :t/network-preferences-desc-2))
{:title (or title (i18n/label :t/network-preferences))
:description (when-not receiver?
(if watch-only?
(i18n/label :t/network-preferences-desc-1)
(i18n/label :t/network-preferences-desc-2)))
:blur? blur?}]
[quo/data-item
{:status :default
:size :default
:description :default
:label :none
:blur? blur?
:card? true
:title (i18n/label :t/address)
:custom-subtitle (fn []
[quo/address-text
{:networks current-networks
:address address
:blur? blur?
:format :long}])
:container-style (merge style/data-item
{:background-color (colors/theme-colors colors/neutral-2_5
colors/neutral-90
theme)})}]
(when-not receiver?
[quo/data-item
{:status :default
:size :default
:description :default
:label :none
:blur? blur?
:card? true
:title (i18n/label :t/address)
:custom-subtitle (fn []
[quo/address-text
{:networks current-networks
:address address
:blur? blur?
:format :long}])
:container-style (merge style/data-item
{:background-color (colors/theme-colors colors/neutral-2_5
colors/neutral-90
theme)})}])
[quo/category
{:list-type :settings
:blur? blur?
:data [(utils/make-network-item {:state @state
:network-name (:network-name mainnet)
:color color
:blur? blur?
:networks (get-current-preferences-names)
:on-change #(toggle-network (:network-name
mainnet))})]}]
[quo/category
{:list-type :settings
:blur? blur?
:label (i18n/label :t/layer-2)
:label (when first-section-label first-section-label)
:data (mapv (fn [network]
(utils/make-network-item {:state @state
:network-name (:network-name network)
:color color
:blur? blur?
:networks (get-current-preferences-names)
:on-change #(toggle-network (:network-name
network))}))
layer-2-networks)}]
(utils/make-network-item
{:state @state
:network-name (:network-name network)
:color color
:normal-checkbox? receiver?
:networks (get-current-preferences-names)
:type :checkbox
:on-change (fn []
(toggle-network (:network-name
network))
(when on-change
(let [chain-ids (map :chain-id current-networks)]
(on-change chain-ids))))}))
first-section-networks)}]
(when first-section-warning-label
[rn/view
{:style (style/warning-container false)}
[quo/icon :i/alert
{:size 16
:color colors/danger-50}]
[quo/text
{:size :paragraph-2
:style {:margin-left 4
:color colors/danger-50}}
first-section-warning-label]])
(when (not-empty second-section-networks)
[quo/category
{:list-type :settings
:blur? blur?
:label (or second-section-label (i18n/label :t/layer-2))
:data (mapv (fn [network]
(utils/make-network-item
{:state @state
:network-name (:network-name network)
:color color
:normal-checkbox? receiver?
:networks (get-current-preferences-names)
:type :checkbox
:on-change (fn []
(toggle-network (:network-name
network))
(when on-change
(let [chain-ids (map :chain-id current-networks)]
(on-change chain-ids))))}))
second-section-networks)}])
(when second-section-warning-label
[rn/view
{:style (style/warning-container sending-to-unpreferred-networks?)}
[quo/icon :i/alert
{:size 16
:color colors/danger-50}]
[quo/text
{:size :paragraph-2
:style {:margin-left 4
:color colors/danger-50}}
second-section-warning-label]])
(when sending-to-unpreferred-networks?
[rn/view {:style (style/sending-to-unpreferred-networks-alert-container theme)}
[rn/view
[quo/icon :i/alert
{:size 16
:color (colors/resolve-color :blue theme)
:container-style {:margin-top 2}}]]
[quo/text
{:style style/sending-to-unpreferred-networks-text
:size :paragraph-2}
(i18n/label :t/sending-to-networks-the-receiver-does-not-prefer)]])
[quo/bottom-actions
{:actions :one-action
:blur? blur?

View File

@ -0,0 +1,10 @@
(ns status-im.contexts.wallet.sheets.unpreferred-networks-alert.style)
(def sending-to-unpreferred-networks-title
{:margin-horizontal 20})
(def sending-to-unpreferred-networks-text
{:flex 1
:height 66
:margin-horizontal 20
:margin-vertical 4})

View File

@ -0,0 +1,28 @@
(ns status-im.contexts.wallet.sheets.unpreferred-networks-alert.view
(:require [quo.core :as quo]
[re-frame.core :as rf]
[status-im.contexts.wallet.sheets.unpreferred-networks-alert.style :as style]
[utils.i18n :as i18n]))
(defn view
[{:keys [on-confirm]}]
[:<>
[quo/text
{:style style/sending-to-unpreferred-networks-title
:size :heading-2
:weight :semi-bold}
(i18n/label :t/sending-to-unpreferred-networks)]
[quo/text
{:style style/sending-to-unpreferred-networks-text
:size :paragraph-1}
(i18n/label :t/sending-to-networks-the-receiver-does-not-prefer)]
[quo/bottom-actions
{:actions :two-actions
:button-two-label (i18n/label :t/cancel)
:button-two-props {:on-press #(rf/dispatch [:hide-bottom-sheet])
:type :grey}
:button-one-label (i18n/label :t/proceed-anyway)
:button-one-props {:on-press (fn []
(rf/dispatch [:hide-bottom-sheet])
(when on-confirm (on-confirm)))
:customization-color :danger}}]])

View File

@ -42,3 +42,8 @@
(= sender current-viewing-account-address))
activities)]
(set (map :recipient users-sent-transactions)))))
(rf/reg-sub
:wallet/send-token-not-supported-in-receiver-networks?
:<- [:wallet/wallet-send]
:-> :token-not-supported-in-receiver-networks?)

View File

@ -88,6 +88,11 @@
:<- [:wallet/wallet-send]
:-> :receiver-networks)
(rf/reg-sub
:wallet/wallet-send-receiver-preferred-networks
:<- [:wallet/wallet-send]
:-> :receiver-preferred-networks)
(rf/reg-sub
:wallet/wallet-send-route
:<- [:wallet/wallet-send]

View File

@ -2624,7 +2624,14 @@
"this-account-has-no-activity": "This account has no activity",
"this-address-has-activity": "This address has activity",
"scanning-for-activity": "Scanning for activity...",
"send-community-link": "Send community link",
"at-least-one-network-must-be-activated": "At least 1 network must be activated",
"status-is-a-secure-messaging-app": "Status is a secure messaging app, crypto wallet and web3 browser built with the state of the art technology"
"send-community-link": "Send community link",
"status-is-a-secure-messaging-app": "Status is a secure messaging app, crypto wallet and web3 browser built with the state of the art technology",
"token-not-available-on-receiver-networks": "{{token-symbol}} is not available on the recipients desired networks. Proceed with caution.",
"add-networks-token-can-be-sent-to": "Add networks {{token-symbol}} can be sent to",
"not-available": "Not available",
"token-not-available-on-networks": "{{token-symbol}} is not available on {{networks}}.",
"sending-to-networks-the-receiver-does-not-prefer": "Sending to networks the receiver does not prefer may result in recipient having difficulty accessing the sent tokens.",
"proceed-anyway": "Proceed anyway",
"sending-to-unpreferred-networks": "Sending to unpreferred networks"
}