Wallet: network receiver preferences (#18583)

* wallet: network receiver preferences
This commit is contained in:
Omar Basem 2024-01-25 15:45:45 +04:00 committed by GitHub
parent 8999b2b9e8
commit 1fb6c60f72
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 212 additions and 62 deletions

View File

@ -4,7 +4,7 @@
(defn container
[network state theme]
{:flex 1
{:width 136
:height 44
:border-width 1
:border-radius 12

View File

@ -10,8 +10,12 @@
[react-native.core :as rn]))
(defn network-bridge-add
[{:keys [network state theme]}]
[rn/view {:style (merge (style/container network state theme) (style/add-container theme))}
[{:keys [network state theme container-style on-press]}]
[rn/pressable
{:style (merge (style/container network state theme)
(style/add-container theme)
container-style)
:on-press on-press}
[icon/icon :i/add-circle {:size 12 :no-color true}]])
(defn- network->text
@ -21,13 +25,14 @@
:else (string/capitalize (name network))))
(defn view-internal
[{:keys [theme network status amount container-style] :as args}]
[{:keys [theme network status amount container-style on-press] :as args}]
(if (= status :add)
[network-bridge-add args]
[rn/view
[rn/pressable
{:style (merge (style/container network status theme) container-style)
:accessible true
:accessibility-label :container}
:accessibility-label :container
:on-press on-press}
(if (= status :loading)
[rn/view
{:style (style/loading-skeleton theme)

View File

@ -185,6 +185,11 @@
constants/arbitrum-chain-id :arbitrum
constants/arbitrum-test-chain-id :arbitrum})
(def short-name->id
{:eth constants/mainnet-chain-id
:opt constants/optimism-chain-id
:arb1 constants/arbitrum-chain-id})
(defn get-standard-fiat-format
[crypto-value currency-symbol fiat-value]
(if (string/includes? crypto-value "<")

View File

@ -2,6 +2,7 @@
(:require
[camel-snake-kebab.core :as csk]
[camel-snake-kebab.extras :as cske]
[clojure.string :as string]
[status-im.constants :as constants]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.send.utils :as send-utils]
@ -57,16 +58,23 @@
(rf/reg-event-fx :wallet/select-send-address
(fn [{:keys [db]} [{:keys [address token recipient stack-id]}]]
(let [[prefix to-address] (utils/split-prefix-and-address address)]
(let [[prefix to-address] (utils/split-prefix-and-address address)
prefix-seq (string/split prefix #":")
selected-networks (mapv #(utils/short-name->id (keyword %)) prefix-seq)]
{:db (-> db
(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 :address-prefix] prefix)
(assoc-in [:wallet :ui :send :selected-networks] selected-networks))
:fx [[:navigate-to-within-stack
(if token
[:wallet-send-input-amount stack-id]
[:wallet-select-asset stack-id])]]})))
(rf/reg-event-fx :wallet/update-receiver-networks
(fn [{:keys [db]} [selected-networks]]
{:db (assoc-in db [:wallet :ui :send :selected-networks] selected-networks)}))
(rf/reg-event-fx :wallet/send-select-token
(fn [{:keys [db]} [{:keys [token stack-id]}]]
{:db (assoc-in db [:wallet :ui :send :token] token)
@ -92,10 +100,11 @@
(let [wallet-address (get-in db [:wallet :current-viewing-account-address])
token (get-in db [:wallet :ui :send :token])
account-address (get-in db [:wallet :ui :send :send-account-address])
selected-networks (get-in db [:wallet :ui :send :selected-networks])
to-address (or account-address (get-in db [:wallet :ui :send :to-address]))
token-decimal (:decimals token)
token-id (:symbol token)
network-preferences []
network-preferences selected-networks
gas-rates constants/gas-rate-medium
amount-in (send-utils/amount-in-hex amount token-decimal)
from-address wallet-address

View File

@ -44,7 +44,8 @@
:market-values-per-currency {:usd {:price 10}}}
:wallet/wallet-send-loading-suggested-routes? false
:wallet/wallet-send-route {:route []}
:wallet/wallet-send-suggested-routes {:candidates []}})
:wallet/wallet-send-suggested-routes {:candidates []}
:wallet/wallet-send-selected-networks []})
(h/describe "Send > input amount screen"
(h/setup-restorable-re-frame)

View File

@ -54,13 +54,6 @@
(normalize-input current v)
current))
(defn- find-affordable-networks
[{:keys [balances-per-chain]} input-value]
(->> balances-per-chain
(filter (fn [[_ {:keys [balance]}]]
(>= (js/parseFloat balance) input-value)))
(map first)))
(defn- reset-input-error
[new-value prev-value input-error]
(reset! input-error
@ -129,21 +122,22 @@
(<= input-num-value 0)
(> input-num-value (:amount @current-limit)))
amount (str @input-value " " token-symbol)
{:keys [color]} (rf/sub [:wallet/current-viewing-account])]
{:keys [color]} (rf/sub [:wallet/current-viewing-account])
fetch-routes (fn []
(rf/dispatch [:wallet/clean-suggested-routes])
(when-not (or
(empty? @input-value)
(<= input-num-value 0)
(> input-num-value (:amount @current-limit)))
(debounce/debounce-and-dispatch [:wallet/get-suggested-routes
@input-value]
100)))]
(rn/use-effect
(fn []
(let [dismiss-keyboard-fn #(when (= % "active") (rn/dismiss-keyboard!))
app-keyboard-listener (.addEventListener rn/app-state "change" dismiss-keyboard-fn)]
#(.remove app-keyboard-listener))))
(rn/use-effect (fn []
(rf/dispatch [:wallet/clean-suggested-routes])
(when-not (or
(empty? @input-value)
(<= input-num-value 0)
(> input-num-value (:amount @current-limit)))
(debounce/debounce-and-dispatch [:wallet/get-suggested-routes @input-value]
100)))
[@input-value])
(rn/use-effect #(fetch-routes) [@input-value])
[rn/view
{:style style/screen
:accessibility-label (str "container" (when @input-error "-error"))}
@ -166,10 +160,11 @@
:on-change-text (fn [text]
(handle-on-change text))}]
[routes/view
{:amount amount
:routes suggested-routes
:loading-networks (find-affordable-networks token @input-value)
:networks (:networks token)}]
{:amount amount
:routes suggested-routes
:token token
:input-value @input-value
:fetch-routes fetch-routes}]
[quo/bottom-actions
{:actions :1-action
:button-one-label (i18n/label :t/confirm)

View File

@ -1,4 +1,5 @@
(ns status-im.contexts.wallet.send.routes.style)
(ns status-im.contexts.wallet.send.routes.style
(:require [quo.foundations.colors :as colors]))
(def routes-container
{:padding-horizontal 20
@ -30,3 +31,26 @@
{:flex-grow 1
:align-items :center
:justify-content :center})
(def add-network
{:margin-top 8
:align-self :flex-end
:left 12})
(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 warning-text
{:margin-left 8
:margin-right 12
:padding-right 12})

View File

@ -1,39 +1,134 @@
(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]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.send.routes.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn route-item
[{:keys [amount from-network to-network status]}]
[rn/view {:style style/routes-inner-container}
[quo/network-bridge
{:amount amount
:network from-network
:status status}]
(if (= status :default)
[quo/network-link
{:shape :linear
:source from-network
:destination to-network
:container-style style/network-link}]
[rn/view {:style {:width 73}}])
[quo/network-bridge
{:amount amount
:network to-network
:status status
:container-style {:right 12}}]])
(defn- find-affordable-networks
[{:keys [balances-per-chain]} input-value selected-networks]
(->> balances-per-chain
(filter (fn [[_ {:keys [balance chain-id]}]]
(and
(>= (js/parseFloat balance) input-value)
(some #(= % chain-id) selected-networks))))
(map first)))
(defn view
[{:keys [amount routes loading-networks]}]
(let [loading-suggested-routes? (rf/sub [:wallet/wallet-send-loading-suggested-routes?])
candidates (:candidates routes)]
(if (or (and (not-empty loading-networks) loading-suggested-routes?) (not-empty candidates))
(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 networks-drawer
[{:keys [fetch-routes theme]}]
(let [network-details (rf/sub [:wallet/network-details])
{:keys [color]} (rf/sub [:wallet/current-viewing-account])
selected-networks (rf/sub [:wallet/wallet-send-selected-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
{:button-one-label (i18n/label :t/apply-changes)
:button-one-props {:disabled? (= selected-networks @network-preferences)
:on-press (fn []
(rf/dispatch [:wallet/update-receiver-networks
@network-preferences])
(rf/dispatch [:hide-bottom-sheet])
(fetch-routes))
:customization-color color}}]])))
(defn route-item
[{:keys [amount from-network to-network status theme fetch-routes]}]
(if (= status :add)
[quo/network-bridge
{:status :add
:container-style style/add-network
:on-press #(rf/dispatch [:show-bottom-sheet
{:content (fn [] [networks-drawer
{:theme theme
:fetch-routes fetch-routes}])}])}]
[rn/view {:style style/routes-inner-container}
[quo/network-bridge
{:amount amount
:network from-network
:status status}]
(if (= status :default)
[quo/network-link
{:shape :linear
:source from-network
:destination to-network
:container-style style/network-link}]
[rn/view {:style {:width 73}}])
[quo/network-bridge
{:amount amount
:network to-network
:status status
:container-style {:right 12}}]]))
(defn- view-internal
[{:keys [amount routes token input-value theme fetch-routes]}]
(let [selected-networks (rf/sub [:wallet/wallet-send-selected-networks])
loading-networks (find-affordable-networks token input-value selected-networks)
loading-suggested-routes? (rf/sub [:wallet/wallet-send-loading-suggested-routes?])
best-routes (:best routes)
data (if loading-suggested-routes? loading-networks best-routes)]
(if (or (and (not-empty loading-networks) loading-suggested-routes?) (not-empty best-routes))
[rn/flat-list
{:data (if loading-suggested-routes? loading-networks candidates)
{:data (if (and (< (count data) 3) (pos? (count data)))
(concat data [{:status :add}])
data)
:content-container-style style/routes-container
:header [rn/view {:style style/routes-header-container}
[quo/section-label
@ -45,7 +140,12 @@
:render-fn (fn [item]
[route-item
{:amount amount
:status (if loading-suggested-routes? :loading :default)
:theme theme
:fetch-routes fetch-routes
:status (cond
(= (:status item) :add) :add
loading-suggested-routes? :loading
:else :default)
:from-network (if loading-suggested-routes?
(utils/id->network item)
(utils/id->network (get-in item [:from :chain-id])))
@ -54,5 +154,7 @@
(utils/id->network (get-in item
[:to :chain-id])))}])}]
[rn/view {:style style/empty-container}
(when (and (not (nil? candidates)) (not loading-suggested-routes?))
(when (and (not (nil? best-routes)) (not loading-suggested-routes?))
[quo/text (i18n/label :t/no-routes-found)])])))
(def view (quo.theme/with-theme view-internal))

View File

@ -48,6 +48,11 @@
:<- [:wallet/wallet-send]
:-> :address-prefix)
(rf/reg-sub
:wallet/wallet-send-selected-networks
:<- [:wallet/wallet-send]
:-> :selected-networks)
(rf/reg-sub
:wallet/wallet-send-route
:<- [:wallet/wallet-send]

View File

@ -2453,11 +2453,10 @@
"share-details": "Share details",
"what-are-you-waiting-for": "What are you waiting for?",
"no-relevant-tokens": "No relevant tokens",
"on-the-web": "On the web",
"sending-with-ellipsis": "Sending...",
"sending-with-elipsis": "Sending...",
"transaction-confirmed": "Transaction confirmed!",
"transacation-finalised": "Transaction finalised!",
"no-relevant-tokens": "No relevant tokens",
"from-label": "From",
"to-label": "To",
"oops-wrong-word": "Oops! Wrong word",
@ -2469,5 +2468,10 @@
"remove-account-title": "Remove account",
"remove-account-desc": "The account will be removed from all of your synced devices. Make sure you have a backup of your keypair or recovery phrase and derivation path (if its not default).",
"derivation-path-copied": "Derivation path copied",
"remove-account-confirmation": "I have taken note of the derivation path"
"remove-account-confirmation": "I have taken note of the derivation path",
"edit-receiver-networks": "Edit receiver networks",
"preferred-by-receiver": "Preferred by receiver",
"not-preferred-by-receiver": "Not preferred by receiver",
"apply-changes": "Apply changes",
"receiver-networks-warning": "Changing these settings may result in sending tokens to networks the recipient doesn't use"
}