fix(swap): refetch swap proposal when switching account (#21518)
Signed-off-by: Brian Sztamfater <brian@status.im>
This commit is contained in:
parent
ca22b67d0e
commit
a7d330ecb3
|
@ -192,6 +192,18 @@
|
||||||
(number/small-number-threshold display-decimals)
|
(number/small-number-threshold display-decimals)
|
||||||
(str amount-fixed-decimals))))
|
(str amount-fixed-decimals))))
|
||||||
|
|
||||||
|
(defn token-balance-display-for-network
|
||||||
|
"Formats a token balance for a specific chain and rounds it to a specified number of decimals.
|
||||||
|
If the balance is less than the smallest representable value based on rounding decimals,
|
||||||
|
a threshold value is displayed instead."
|
||||||
|
[token chain-id rounding-decimals]
|
||||||
|
(let [token-decimals (:decimals token)
|
||||||
|
display-decimals (min token-decimals rounding-decimals)]
|
||||||
|
(-> (get-in token [:balances-per-chain chain-id :raw-balance] 0)
|
||||||
|
(number/convert-to-whole-number token-decimals)
|
||||||
|
money/bignumber
|
||||||
|
(sanitized-token-amount-to-display display-decimals))))
|
||||||
|
|
||||||
(defn calculate-balance-from-tokens
|
(defn calculate-balance-from-tokens
|
||||||
[{:keys [currency tokens chain-ids]}]
|
[{:keys [currency tokens chain-ids]}]
|
||||||
(->> tokens
|
(->> tokens
|
||||||
|
|
|
@ -195,3 +195,77 @@
|
||||||
(is (= (utils/sanitized-token-amount-to-display 0.00000123 6) "0.000001"))
|
(is (= (utils/sanitized-token-amount-to-display 0.00000123 6) "0.000001"))
|
||||||
(is (= (utils/sanitized-token-amount-to-display nil 2) "0"))
|
(is (= (utils/sanitized-token-amount-to-display nil 2) "0"))
|
||||||
(is (= (utils/sanitized-token-amount-to-display "invalid" 2) "0"))))
|
(is (= (utils/sanitized-token-amount-to-display "invalid" 2) "0"))))
|
||||||
|
|
||||||
|
(deftest token-balance-display-for-network-test
|
||||||
|
(testing "Standard balance with rounding decimals"
|
||||||
|
(let [token {:decimals 18
|
||||||
|
:balances-per-chain {1 {:raw-balance "1000000000000000000"}}}
|
||||||
|
chain-id 1
|
||||||
|
rounding-decimals 2
|
||||||
|
expected "1"]
|
||||||
|
(is (= (utils/token-balance-display-for-network token chain-id rounding-decimals)
|
||||||
|
expected))))
|
||||||
|
|
||||||
|
(testing "Balance with more decimals than specified rounding"
|
||||||
|
(let [token {:decimals 18
|
||||||
|
:balances-per-chain {1 {:raw-balance "123456789000000000"}}}
|
||||||
|
chain-id 1
|
||||||
|
rounding-decimals 3
|
||||||
|
expected "0.123"]
|
||||||
|
(is (= (utils/token-balance-display-for-network token chain-id rounding-decimals)
|
||||||
|
expected))))
|
||||||
|
|
||||||
|
(testing "Very small balance displayed as threshold (<0.000001)"
|
||||||
|
(let [token {:decimals 18
|
||||||
|
:balances-per-chain {1 {:raw-balance "1"}}}
|
||||||
|
chain-id 1
|
||||||
|
rounding-decimals 6
|
||||||
|
expected "<0.000001"]
|
||||||
|
(is (= (utils/token-balance-display-for-network token chain-id rounding-decimals)
|
||||||
|
expected))))
|
||||||
|
|
||||||
|
(testing
|
||||||
|
"Very small balance displayed without threshold when token decimals are equal than reounding decimals"
|
||||||
|
(let [token {:decimals 18
|
||||||
|
:balances-per-chain {1 {:raw-balance "1"}}}
|
||||||
|
chain-id 1
|
||||||
|
rounding-decimals 18
|
||||||
|
expected "0.000000000000000001"]
|
||||||
|
(is (= (utils/token-balance-display-for-network token chain-id rounding-decimals)
|
||||||
|
expected))))
|
||||||
|
|
||||||
|
(testing
|
||||||
|
"Very small balance displayed without threshold when token decimals are lower than reounding decimals"
|
||||||
|
(let [token {:decimals 18
|
||||||
|
:balances-per-chain {1 {:raw-balance "1"}}}
|
||||||
|
chain-id 1
|
||||||
|
rounding-decimals 21
|
||||||
|
expected "0.000000000000000001"]
|
||||||
|
(is (= (utils/token-balance-display-for-network token chain-id rounding-decimals)
|
||||||
|
expected))))
|
||||||
|
|
||||||
|
(testing "Zero balance displayed correctly"
|
||||||
|
(let [token {:decimals 18
|
||||||
|
:balances-per-chain {1 {:raw-balance "0"}}}
|
||||||
|
chain-id 1
|
||||||
|
rounding-decimals 2
|
||||||
|
expected "0"]
|
||||||
|
(is (= (utils/token-balance-display-for-network token chain-id rounding-decimals)
|
||||||
|
expected))))
|
||||||
|
|
||||||
|
(testing "Balance with trailing zeroes removed"
|
||||||
|
(let [token {:decimals 8
|
||||||
|
:balances-per-chain {1 {:raw-balance "123400000"}}}
|
||||||
|
chain-id 1
|
||||||
|
rounding-decimals 6
|
||||||
|
expected "1.234"]
|
||||||
|
(is (= (utils/token-balance-display-for-network token chain-id rounding-decimals)
|
||||||
|
expected))))
|
||||||
|
|
||||||
|
(testing "Balance when chain data is missing"
|
||||||
|
(let [token {:decimals 18}
|
||||||
|
chain-id 1
|
||||||
|
rounding-decimals 2
|
||||||
|
expected "0"]
|
||||||
|
(is (= (utils/token-balance-display-for-network token chain-id rounding-decimals)
|
||||||
|
expected)))))
|
||||||
|
|
|
@ -2,11 +2,13 @@
|
||||||
(:require [quo.core :as quo]
|
(:require [quo.core :as quo]
|
||||||
quo.theme
|
quo.theme
|
||||||
[react-native.gesture :as gesture]
|
[react-native.gesture :as gesture]
|
||||||
[status-im.contexts.wallet.common.utils :as wallet.utils]
|
[status-im.contexts.wallet.common.utils :as utils]
|
||||||
[status-im.contexts.wallet.sheets.select-account.style :as style]
|
[status-im.contexts.wallet.sheets.select-account.style :as style]
|
||||||
[utils.i18n :as i18n]
|
[utils.i18n :as i18n]
|
||||||
[utils.re-frame :as rf]))
|
[utils.re-frame :as rf]))
|
||||||
|
|
||||||
|
(def ^:private rounding-decimals 4)
|
||||||
|
|
||||||
(defn list-item
|
(defn list-item
|
||||||
[{:keys [account selected-account-address]}]
|
[{:keys [account selected-account-address]}]
|
||||||
(let [{:keys [color address]} account]
|
(let [{:keys [color address]} account]
|
||||||
|
@ -25,12 +27,14 @@
|
||||||
token (->> tokens
|
token (->> tokens
|
||||||
(filter #(= (:symbol %) asset-symbol))
|
(filter #(= (:symbol %) asset-symbol))
|
||||||
first)
|
first)
|
||||||
token-balance (get-in token [:balances-per-chain (:chain-id network) :balance])]
|
chain-id (:chain-id network)
|
||||||
|
token-balance-display (utils/token-balance-display-for-network token
|
||||||
|
chain-id
|
||||||
|
rounding-decimals)]
|
||||||
[quo/account-item
|
[quo/account-item
|
||||||
{:type (if (= address selected-account-address) :default :tag)
|
{:type (if (= address selected-account-address) :default :tag)
|
||||||
:token-props {:symbol asset-symbol
|
:token-props {:symbol asset-symbol
|
||||||
:value (wallet.utils/cut-fiat-balance (or (js/parseFloat token-balance) 0)
|
:value token-balance-display}
|
||||||
4)}
|
|
||||||
:account-props (assoc account :customization-color color)
|
:account-props (assoc account :customization-color color)
|
||||||
:customization-color color
|
:customization-color color
|
||||||
:state (if (= address selected-account-address) :selected :default)
|
:state (if (= address selected-account-address) :selected :default)
|
||||||
|
|
|
@ -247,13 +247,12 @@
|
||||||
|
|
||||||
(rf/reg-event-fx
|
(rf/reg-event-fx
|
||||||
:wallet/clean-swap-proposal
|
:wallet/clean-swap-proposal
|
||||||
(fn [{:keys [db]} [{:keys [clean-approval-transaction?]}]]
|
(fn [{:keys [db]} [{:keys [clean-amounts? clean-approval-transaction?]}]]
|
||||||
(let [keys-to-dissoc (cond-> [:amount
|
(let [keys-to-dissoc (cond-> [:last-request-uuid
|
||||||
:amount-hex
|
|
||||||
:last-request-uuid
|
|
||||||
:swap-proposal
|
:swap-proposal
|
||||||
:error-response
|
:error-response
|
||||||
:loading-swap-proposal?]
|
:loading-swap-proposal?]
|
||||||
|
clean-amounts? (conj :amount :amount-hex)
|
||||||
clean-approval-transaction? (conj :approval-transaction-id :approved-amount))]
|
clean-approval-transaction? (conj :approval-transaction-id :approved-amount))]
|
||||||
{:db (apply update-in db [:wallet :ui :swap] dissoc keys-to-dissoc)
|
{:db (apply update-in db [:wallet :ui :swap] dissoc keys-to-dissoc)
|
||||||
:fx [[:dispatch [:wallet/stop-get-swap-proposal]]]})))
|
:fx [[:dispatch [:wallet/stop-get-swap-proposal]]]})))
|
||||||
|
|
|
@ -26,13 +26,14 @@
|
||||||
[utils.string :as utils.string]))
|
[utils.string :as utils.string]))
|
||||||
|
|
||||||
(def ^:private default-text-for-unfocused-input "0.00")
|
(def ^:private default-text-for-unfocused-input "0.00")
|
||||||
(def ^:private default-token-symbol "ETH")
|
|
||||||
|
|
||||||
(defn- on-close
|
(defn- on-close
|
||||||
[start-point]
|
[start-point]
|
||||||
(when (= start-point :action-menu)
|
(when (= start-point :action-menu)
|
||||||
(rf/dispatch [:centralized-metrics/track :metric/swap-closed]))
|
(rf/dispatch [:centralized-metrics/track :metric/swap-closed]))
|
||||||
(rf/dispatch [:wallet/clean-swap-proposal {:clean-approval-transaction? true}])
|
(rf/dispatch [:wallet/clean-swap-proposal
|
||||||
|
{:clean-amounts? true
|
||||||
|
:clean-approval-transaction? true}])
|
||||||
(events-helper/navigate-back))
|
(events-helper/navigate-back))
|
||||||
|
|
||||||
(defn- fetch-swap-proposal
|
(defn- fetch-swap-proposal
|
||||||
|
@ -43,7 +44,8 @@
|
||||||
:clean-approval-transaction? clean-approval-transaction?}]
|
:clean-approval-transaction? clean-approval-transaction?}]
|
||||||
100)
|
100)
|
||||||
(rf/dispatch [:wallet/clean-swap-proposal
|
(rf/dispatch [:wallet/clean-swap-proposal
|
||||||
{:clean-approval-transaction? clean-approval-transaction?}])))
|
{:clean-amounts? true
|
||||||
|
:clean-approval-transaction? clean-approval-transaction?}])))
|
||||||
|
|
||||||
(defn- data-item
|
(defn- data-item
|
||||||
[{:keys [title subtitle size subtitle-icon subtitle-color on-press loading?]}]
|
[{:keys [title subtitle size subtitle-icon subtitle-color on-press loading?]}]
|
||||||
|
@ -90,47 +92,31 @@
|
||||||
[{:keys [input-state on-max-press on-input-focus on-token-press on-approve-press input-focused?]}]
|
[{:keys [input-state on-max-press on-input-focus on-token-press on-approve-press input-focused?]}]
|
||||||
(let [account-color (rf/sub [:wallet/current-viewing-account-color])
|
(let [account-color (rf/sub [:wallet/current-viewing-account-color])
|
||||||
network (rf/sub [:wallet/swap-network])
|
network (rf/sub [:wallet/swap-network])
|
||||||
pay-token-symbol (:symbol (rf/sub [:wallet/swap-asset-to-pay]))
|
pay-token-symbol (rf/sub [:wallet/swap-asset-to-pay-symbol])
|
||||||
currency (rf/sub [:profile/currency])
|
pay-token-decimals (rf/sub [:wallet/swap-asset-to-pay-decimals])
|
||||||
loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])
|
loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])
|
||||||
swap-proposal (rf/sub [:wallet/swap-proposal-without-fees])
|
swap-proposal (rf/sub [:wallet/swap-proposal-without-fees])
|
||||||
approval-required (rf/sub [:wallet/swap-proposal-approval-required])
|
approval-required (rf/sub [:wallet/swap-proposal-approval-required])
|
||||||
approval-amount-required (rf/sub [:wallet/swap-proposal-approval-amount-required])
|
approval-amount-required (rf/sub [:wallet/swap-proposal-approval-amount-required])
|
||||||
currency-symbol (rf/sub [:profile/currency-symbol])
|
|
||||||
approval-transaction-status (rf/sub [:wallet/swap-approval-transaction-status])
|
approval-transaction-status (rf/sub [:wallet/swap-approval-transaction-status])
|
||||||
approval-transaction-id (rf/sub [:wallet/swap-approval-transaction-id])
|
approval-transaction-id (rf/sub [:wallet/swap-approval-transaction-id])
|
||||||
approved-amount (rf/sub [:wallet/swap-approved-amount])
|
approved-amount (rf/sub [:wallet/swap-approved-amount])
|
||||||
error-response (rf/sub [:wallet/swap-error-response])
|
error-response (rf/sub [:wallet/swap-error-response])
|
||||||
asset-to-pay (rf/sub [:wallet/token-by-symbol
|
|
||||||
(or pay-token-symbol default-token-symbol)])
|
|
||||||
overlay-shown? (boolean (:sheets (rf/sub [:bottom-sheet])))
|
overlay-shown? (boolean (:sheets (rf/sub [:bottom-sheet])))
|
||||||
input-ref (rn/use-ref-atom nil)
|
input-ref (rn/use-ref-atom nil)
|
||||||
set-input-ref (rn/use-callback (fn [ref] (reset! input-ref ref)))
|
set-input-ref (rn/use-callback (fn [ref] (reset! input-ref ref)))
|
||||||
pay-input-num-value (controlled-input/value-numeric input-state)
|
pay-input-num-value (controlled-input/value-numeric input-state)
|
||||||
pay-input-amount (controlled-input/input-value input-state)
|
pay-input-amount (controlled-input/input-value input-state)
|
||||||
pay-token-decimals (:decimals asset-to-pay)
|
pay-token-balance-selected-chain (rf/sub [:wallet/swap-asset-to-pay-balance-for-chain
|
||||||
pay-token-balance-selected-chain (number/convert-to-whole-number
|
(:chain-id network)])
|
||||||
(get-in asset-to-pay
|
pay-token-fiat-value (rf/sub [:wallet/swap-asset-to-pay-amount-in-fiat
|
||||||
[:balances-per-chain
|
pay-input-num-value])
|
||||||
(:chain-id network) :raw-balance]
|
|
||||||
0)
|
|
||||||
pay-token-decimals)
|
|
||||||
pay-token-fiat-value (utils/formatted-token-fiat-value
|
|
||||||
{:currency currency
|
|
||||||
:currency-symbol currency-symbol
|
|
||||||
:balance (or pay-input-num-value 0)
|
|
||||||
:token asset-to-pay})
|
|
||||||
available-crypto-limit (money/bignumber
|
available-crypto-limit (money/bignumber
|
||||||
pay-token-balance-selected-chain)
|
pay-token-balance-selected-chain)
|
||||||
display-decimals (min pay-token-decimals
|
display-decimals (min pay-token-decimals
|
||||||
constants/min-token-decimals-to-display)
|
constants/min-token-decimals-to-display)
|
||||||
available-crypto-limit-display (number/remove-trailing-zeroes
|
available-crypto-limit-display (rf/sub [:wallet/swap-asset-to-pay-balance-for-chain-ui
|
||||||
(.toFixed available-crypto-limit display-decimals))
|
(:chain-id network)])
|
||||||
available-crypto-limit-display (if (and (= available-crypto-limit-display "0")
|
|
||||||
(money/greater-than available-crypto-limit
|
|
||||||
(money/bignumber 0)))
|
|
||||||
(number/small-number-threshold display-decimals)
|
|
||||||
available-crypto-limit-display)
|
|
||||||
approval-amount-required-num (when approval-amount-required
|
approval-amount-required-num (when approval-amount-required
|
||||||
(number/to-fixed (number/hex->whole
|
(number/to-fixed (number/hex->whole
|
||||||
approval-amount-required
|
approval-amount-required
|
||||||
|
@ -345,15 +331,12 @@
|
||||||
asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])
|
asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])
|
||||||
asset-to-receive (rf/sub [:wallet/swap-asset-to-receive])
|
asset-to-receive (rf/sub [:wallet/swap-asset-to-receive])
|
||||||
network (rf/sub [:wallet/swap-network])
|
network (rf/sub [:wallet/swap-network])
|
||||||
|
pay-token-balance-selected-chain (rf/sub [:wallet/swap-asset-to-pay-balance-for-chain
|
||||||
|
(:chain-id network)])
|
||||||
start-point (rf/sub [:wallet/swap-start-point])
|
start-point (rf/sub [:wallet/swap-start-point])
|
||||||
|
current-account-address (rf/sub [:wallet/current-viewing-account-address])
|
||||||
pay-input-amount (controlled-input/input-value pay-input-state)
|
pay-input-amount (controlled-input/input-value pay-input-state)
|
||||||
pay-token-decimals (:decimals asset-to-pay)
|
pay-token-decimals (:decimals asset-to-pay)
|
||||||
pay-token-balance-selected-chain (number/convert-to-whole-number
|
|
||||||
(get-in asset-to-pay
|
|
||||||
[:balances-per-chain
|
|
||||||
(:chain-id network) :raw-balance]
|
|
||||||
0)
|
|
||||||
pay-token-decimals)
|
|
||||||
pay-input-error? (and (not (string/blank? pay-input-amount))
|
pay-input-error? (and (not (string/blank? pay-input-amount))
|
||||||
(money/greater-than
|
(money/greater-than
|
||||||
(money/bignumber pay-input-amount)
|
(money/bignumber pay-input-amount)
|
||||||
|
@ -392,7 +375,8 @@
|
||||||
(set-pay-input-state
|
(set-pay-input-state
|
||||||
controlled-input/delete-last)
|
controlled-input/delete-last)
|
||||||
(rf/dispatch [:wallet/clean-swap-proposal
|
(rf/dispatch [:wallet/clean-swap-proposal
|
||||||
{:clean-approval-transaction?
|
{:clean-amounts? true
|
||||||
|
:clean-approval-transaction?
|
||||||
true}])))
|
true}])))
|
||||||
on-max-press (rn/use-callback
|
on-max-press (rn/use-callback
|
||||||
(fn [max-value]
|
(fn [max-value]
|
||||||
|
@ -443,8 +427,19 @@
|
||||||
on-refresh-swap-proposal
|
on-refresh-swap-proposal
|
||||||
constants/swap-proposal-refresh-interval-ms))))
|
constants/swap-proposal-refresh-interval-ms))))
|
||||||
[swap-proposal error-response])
|
[swap-proposal error-response])
|
||||||
|
(rn/use-effect (fn []
|
||||||
|
(rf/dispatch [:wallet/clean-swap-proposal
|
||||||
|
{:clean-amounts? false
|
||||||
|
:clean-approval-transaction? true}])
|
||||||
|
(when @refetch-interval
|
||||||
|
(js/clearInterval @refetch-interval)
|
||||||
|
(reset! refetch-interval nil))
|
||||||
|
(refetch-swap-proposal))
|
||||||
|
[current-account-address])
|
||||||
(rn/use-unmount (fn []
|
(rn/use-unmount (fn []
|
||||||
(rf/dispatch [:wallet/clean-swap-proposal {:clean-approval-transaction? true}])
|
(rf/dispatch [:wallet/clean-swap-proposal
|
||||||
|
{:clean-amounts? true
|
||||||
|
:clean-approval-transaction? true}])
|
||||||
(when @refetch-interval
|
(when @refetch-interval
|
||||||
(js/clearInterval @refetch-interval)
|
(js/clearInterval @refetch-interval)
|
||||||
(reset! refetch-interval nil))))
|
(reset! refetch-interval nil))))
|
||||||
|
|
|
@ -17,6 +17,16 @@
|
||||||
:<- [:wallet/swap]
|
:<- [:wallet/swap]
|
||||||
:-> :asset-to-pay)
|
:-> :asset-to-pay)
|
||||||
|
|
||||||
|
(rf/reg-sub
|
||||||
|
:wallet/swap-asset-to-pay-decimals
|
||||||
|
:<- [:wallet/swap-asset-to-pay]
|
||||||
|
:-> :decimals)
|
||||||
|
|
||||||
|
(rf/reg-sub
|
||||||
|
:wallet/swap-asset-to-pay-symbol
|
||||||
|
:<- [:wallet/swap-asset-to-pay]
|
||||||
|
:-> :symbol)
|
||||||
|
|
||||||
(rf/reg-sub
|
(rf/reg-sub
|
||||||
:wallet/swap-asset-to-receive
|
:wallet/swap-asset-to-receive
|
||||||
:<- [:wallet/swap]
|
:<- [:wallet/swap]
|
||||||
|
@ -253,3 +263,42 @@
|
||||||
currency-symbol
|
currency-symbol
|
||||||
fee-in-fiat)]
|
fee-in-fiat)]
|
||||||
fee-formatted))))
|
fee-formatted))))
|
||||||
|
|
||||||
|
(rf/reg-sub
|
||||||
|
:wallet/swap-asset-to-pay-balance-for-chain-data
|
||||||
|
:<- [:wallet/swap-asset-to-pay]
|
||||||
|
(fn [asset-to-pay]
|
||||||
|
(let [token-symbol (or (:symbol asset-to-pay) constants/token-for-fees-symbol)]
|
||||||
|
@(rf/subscribe [:wallet/token-by-symbol token-symbol]))))
|
||||||
|
|
||||||
|
(rf/reg-sub
|
||||||
|
:wallet/swap-asset-to-pay-balance-for-chain
|
||||||
|
:<- [:wallet/swap-asset-to-pay-balance-for-chain-data]
|
||||||
|
(fn [asset-to-pay-with-current-account-balance [_ chain-id]]
|
||||||
|
(let [pay-token-decimals (:decimals asset-to-pay-with-current-account-balance)
|
||||||
|
pay-token-balance-selected-chain (-> (get-in asset-to-pay-with-current-account-balance
|
||||||
|
[:balances-per-chain chain-id :raw-balance]
|
||||||
|
0)
|
||||||
|
(number/convert-to-whole-number pay-token-decimals))]
|
||||||
|
pay-token-balance-selected-chain)))
|
||||||
|
|
||||||
|
(rf/reg-sub
|
||||||
|
:wallet/swap-asset-to-pay-balance-for-chain-ui
|
||||||
|
:<- [:wallet/swap-asset-to-pay-balance-for-chain-data]
|
||||||
|
(fn [asset-to-pay-with-current-account-balance [_ chain-id]]
|
||||||
|
(utils/token-balance-display-for-network
|
||||||
|
asset-to-pay-with-current-account-balance
|
||||||
|
chain-id
|
||||||
|
constants/min-token-decimals-to-display)))
|
||||||
|
|
||||||
|
(rf/reg-sub
|
||||||
|
:wallet/swap-asset-to-pay-amount-in-fiat
|
||||||
|
:<- [:wallet/swap-asset-to-pay-balance-for-chain-data]
|
||||||
|
:<- [:profile/currency]
|
||||||
|
:<- [:profile/currency-symbol]
|
||||||
|
(fn [[asset-to-pay-with-current-account-balance currency currency-symbol] [_ amount]]
|
||||||
|
(utils/formatted-token-fiat-value
|
||||||
|
{:currency currency
|
||||||
|
:currency-symbol currency-symbol
|
||||||
|
:balance (or amount 0)
|
||||||
|
:token asset-to-pay-with-current-account-balance})))
|
||||||
|
|
Loading…
Reference in New Issue