Compare commits

...
14 changed files with 501 additions and 523 deletions
+3 -3
View File
@@ -1115,7 +1115,7 @@ PODS:
- React-perflogger (= 0.73.5) - React-perflogger (= 0.73.5)
- ReactNativeAudioToolkit (2.0.3): - ReactNativeAudioToolkit (2.0.3):
- React - React
- ReactNativeCameraKit (14.0.0-beta13): - ReactNativeCameraKit (14.0.0-beta15):
- React-Core - React-Core
- ReactNativeNavigation (7.39.0): - ReactNativeNavigation (7.39.0):
- HMSegmentedControl - HMSegmentedControl
@@ -1485,7 +1485,7 @@ SPEC CHECKSUMS:
FBLazyVector: 56e0e498dbb513b96c40bac6284729ba4e62672d FBLazyVector: 56e0e498dbb513b96c40bac6284729ba4e62672d
FBReactNativeSpec: 146c741a3f40361f6bc13a4ba284678cbedb5881 FBReactNativeSpec: 146c741a3f40361f6bc13a4ba284678cbedb5881
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
glog: 530710e7949eb12c82670bd09e946becf50a3c2a glog: 36c15c01d70ed395c6e4f3619f98a23ee415b07a
hermes-engine: 1d1835b2cc54c381909d94d1b3c8e0a2f1a94a0e hermes-engine: 1d1835b2cc54c381909d94d1b3c8e0a2f1a94a0e
HMSegmentedControl: 34c1f54d822d8308e7b24f5d901ec674dfa31352 HMSegmentedControl: 34c1f54d822d8308e7b24f5d901ec674dfa31352
Keycard: ac6df4d91525c3c82635ac24d4ddd9a80aca5fc8 Keycard: ac6df4d91525c3c82635ac24d4ddd9a80aca5fc8
@@ -1551,7 +1551,7 @@ SPEC CHECKSUMS:
React-utils: 987a4526a2fc0acdfaf87888adfe0bf9d0452066 React-utils: 987a4526a2fc0acdfaf87888adfe0bf9d0452066
ReactCommon: 2947b0bffd82ea0e58ca7928881152d4c6dae9af ReactCommon: 2947b0bffd82ea0e58ca7928881152d4c6dae9af
ReactNativeAudioToolkit: de9610f323e855ac6574be8c99621f3d57c5df06 ReactNativeAudioToolkit: de9610f323e855ac6574be8c99621f3d57c5df06
ReactNativeCameraKit: d95d3e19c514526a234d9f93c6db7e7f10eef9ea ReactNativeCameraKit: 71343efc1256720184ce980f164c7eedb78d5c16
ReactNativeNavigation: 679d8c8bf3b8ae98d285bce810964d10da85dbdb ReactNativeNavigation: 679d8c8bf3b8ae98d285bce810964d10da85dbdb
RNCAsyncStorage: c913ede1fa163a71cea118ed4670bbaaa4b511bb RNCAsyncStorage: c913ede1fa163a71cea118ed4670bbaaa4b511bb
RNCClipboard: 60fed4b71560d7bfe40e9d35dea9762b024da86d RNCClipboard: 60fed4b71560d7bfe40e9d35dea9762b024da86d
@@ -6,16 +6,20 @@
(h/describe "Wallet: Token Input" (h/describe "Wallet: Token Input"
(h/test "Token label renders" (h/test "Token label renders"
(h/render-with-theme-provider [token-input/view (h/render-with-theme-provider [token-input/view
{:token :snt {:token-symbol :snt
:currency :eur :currency-symbol :snt
:currency-symbol "€" :on-swap nil
:conversion 1}]) :error? false
:value "1"
:converted-value "10"}])
(h/is-truthy (h/get-by-text "SNT"))) (h/is-truthy (h/get-by-text "SNT")))
(h/test "Amount renders" (h/test "Amount renders"
(h/render-with-theme-provider [token-input/view (h/render-with-theme-provider [token-input/view
{:token :snt {:token-symbol :snt
:currency :eur :currency-symbol :snt
:currency-symbol "€" :on-swap nil
:conversion 1}]) :error? false
:value "1"
:converted-value "€0.00"}])
(h/is-truthy (h/get-by-text "€0.00")))) (h/is-truthy (h/get-by-text "€0.00"))))
@@ -4,15 +4,14 @@
[:=> [:=>
[:catn [:catn
[:props [:props
[:map [:map {:closed true}
[:token {:optional true} [:maybe [:or :string :keyword]]] [:token-symbol [:maybe [:or :string :keyword]]]
[:currency {:optional true} [:maybe [:or :string :keyword]]] [:currency-symbol [:maybe [:or :string :keyword]]]
[:error? {:optional true} [:maybe :boolean]] [:hint-component {:optional true} [:maybe :schema.common/hiccup]]
[:title {:optional true} [:maybe :string]] [:on-token-press {:optional true} [:maybe fn?]]
[:conversion {:optional true} [:maybe :double]] [:on-swap [:maybe fn?]]
[:show-keyboard? {:optional true} [:maybe :boolean]] [:container-style {:optional true} [:maybe :map]]
[:networks {:optional true} [:error? [:maybe :boolean]]
[:maybe [:sequential [:map [:source [:maybe :schema.common/image-source]]]]]] [:value [:maybe :string]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]] [:converted-value [:maybe :string]]]]]
[:value {:optional true} [:maybe :string]]]]]
:any]) :any])
@@ -25,9 +25,11 @@
(defn input-container (defn input-container
[window-width] [window-width]
{:width (- window-width 120) {:width (- window-width 120)
:margin-left 8 :margin-left 8
:margin-right 8}) :margin-right 8
:flex-direction :row
:align-items :flex-end})
(def text-input-dimensions (def text-input-dimensions
(-> typography/heading-1 (-> typography/heading-1
@@ -65,6 +67,6 @@
:justify-content :space-between :justify-content :space-between
:align-items :center}) :align-items :center})
(defn fiat-amount (defn converted-amount
[theme] [theme]
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}) {:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)})
+53 -123
View File
@@ -1,57 +1,15 @@
(ns quo.components.wallet.token-input.view (ns quo.components.wallet.token-input.view
(:require (:require
[clojure.string :as string] [clojure.string :as string]
[oops.core :as oops]
[quo.components.buttons.button.view :as button] [quo.components.buttons.button.view :as button]
[quo.components.dividers.divider-line.view :as divider-line] [quo.components.dividers.divider-line.view :as divider-line]
[quo.components.markdown.text :as text] [quo.components.markdown.text :as text]
[quo.components.tags.network-tags.view :as network-tag]
[quo.components.utilities.token.view :as token] [quo.components.utilities.token.view :as token]
[quo.components.wallet.token-input.schema :as component-schema] [quo.components.wallet.token-input.schema :as component-schema]
[quo.components.wallet.token-input.style :as style] [quo.components.wallet.token-input.style :as style]
[quo.theme :as quo.theme] [quo.theme :as quo.theme]
[react-native.core :as rn] [react-native.core :as rn]
[schema.core :as schema] [schema.core :as schema]))
[utils.number :as number]))
(defn fiat-format
[currency-symbol num-value conversion]
(str currency-symbol (.toFixed (* num-value conversion) 2)))
(defn crypto-format
[num-value conversion crypto-decimals token]
(str (number/remove-trailing-zeroes
(.toFixed (/ num-value conversion) (or crypto-decimals 2)))
" "
(string/upper-case (or (clj->js token) ""))))
(defn calc-value
[{:keys [crypto? currency-symbol token value conversion crypto-decimals]}]
(let [num-value (if (string? value)
(or (parse-double value) 0)
value)]
(if crypto?
(fiat-format currency-symbol num-value conversion)
(crypto-format num-value conversion crypto-decimals token))))
(defn- data-info
[{:keys [theme token crypto-decimals conversion networks title crypto? currency-symbol amount
error?]}]
[rn/view {:style style/data-container}
[network-tag/view
{:networks networks
:title title
:status (when error? :error)}]
[text/text
{:size :paragraph-2
:weight :medium
:style (style/fiat-amount theme)}
(calc-value {:crypto? crypto?
:currency-symbol currency-symbol
:token token
:value amount
:conversion conversion
:crypto-decimals crypto-decimals})]])
(defn- token-name-text (defn- token-name-text
[theme text] [theme text]
@@ -62,91 +20,63 @@
(string/upper-case (or (clj->js text) ""))]) (string/upper-case (or (clj->js text) ""))])
(defn input-section (defn input-section
[{:keys [on-change-text value value-internal set-value-internal on-selection-change [{:keys [token-symbol on-token-press value error? on-swap currency-symbol]}]
on-token-press]}] (let [theme (quo.theme/use-theme)
(let [input-ref (atom nil) window-width (:width (rn/get-window))]
set-ref #(reset! input-ref %) [rn/pressable
focus-input #(when-let [ref ^js @input-ref] {:style {:width "100%"
(.focus ref)) :flex-direction :row}
controlled-input? (some? value) :on-press on-token-press}
handle-on-change-text (fn [v] [token/view
(when-not controlled-input? {:token token-symbol
(set-value-internal v)) :size :size-32}]
(when on-change-text [rn/view {:style (style/input-container window-width)}
(on-change-text v))) [rn/text-input
handle-selection-change (fn [^js e] {:style (style/text-input theme error?)
(when on-selection-change :placeholder-text-color (style/placeholder-text theme)
(-> e :placeholder "0"
(oops/oget "nativeEvent.selection") :keyboard-type :numeric
(js->clj :keywordize-keys true) :editable false
(on-selection-change))))] :value value}]
(fn [{:keys [token customization-color show-keyboard? crypto? currency value error? selection [token-name-text theme currency-symbol]]
handle-on-swap allow-selection?] [button/button
:or {show-keyboard? true {:icon true
allow-selection? true}}] :icon-only? true
(let [theme (quo.theme/use-theme) :size 32
window-width (:width (rn/get-window))] :on-press #(when on-swap (on-swap))
[rn/pressable :type :outline
{:style {:width "100%" :accessibility-label :reorder}
:flex-direction :row} :i/reorder]]))
:on-press on-token-press}
[token/view
{:token token
:size :size-32}]
[rn/view {:style (style/input-container window-width)}
[rn/pressable
{:style style/text-input-container
:on-press focus-input}
[rn/text-input
(cond-> {:style (style/text-input theme error?)
:placeholder-text-color (style/placeholder-text theme)
:auto-focus true
:ref set-ref
:placeholder "0"
:keyboard-type :numeric
:on-change-text handle-on-change-text
:selection-color customization-color
:show-soft-input-on-focus show-keyboard?
:on-selection-change handle-selection-change
:selection (clj->js selection)
:editable allow-selection?}
controlled-input? (assoc :value value)
(not controlled-input?) (assoc :default-value value-internal))]
[token-name-text theme (if crypto? token currency)]]]
[button/button
{:icon true
:icon-only? true
:size 32
:on-press handle-on-swap
:type :outline
:accessibility-label :reorder}
:i/reorder]]))))
(defn- view-internal (defn- view-internal
[{:keys [container-style value on-swap] :as props}] [{:keys [token-symbol
(let [theme (quo.theme/use-theme) value
width (:width (rn/get-window)) on-token-press
[value-internal set-value-internal] (rn/use-state nil) error?
[crypto? set-crypto] (rn/use-state true) container-style
handle-on-swap (rn/use-callback on-swap
(fn [] converted-value
(set-crypto (not crypto?)) hint-component
(when on-swap (on-swap (not crypto?)))) currency-symbol]}]
[crypto? on-swap])] (let [theme (quo.theme/use-theme)
width (:width (rn/get-window))]
[rn/view {:style (merge (style/main-container width) container-style)} [rn/view {:style (merge (style/main-container width) container-style)}
[rn/view {:style style/amount-container} [rn/view {:style style/amount-container}
[input-section [input-section
(assoc props {:theme theme
:value-internal value-internal :token-symbol token-symbol
:theme theme :on-token-press on-token-press
:set-value-internal set-value-internal :value value
:handle-on-swap handle-on-swap :error? error?
:crypto? crypto?)]] :on-swap on-swap
:currency-symbol currency-symbol}]]
[divider-line/view {:container-style (style/divider theme)}] [divider-line/view {:container-style (style/divider theme)}]
[data-info [rn/view {:style style/data-container}
(assoc props hint-component
:theme theme [text/text
:crypto? crypto? {:size :paragraph-2
:amount (or value value-internal))]])) :weight :medium
:style (style/converted-amount theme)}
converted-value]]]))
(def view (schema/instrument #'view-internal component-schema/?schema)) (def view (schema/instrument #'view-internal component-schema/?schema))
@@ -1,6 +1,8 @@
(ns status-im.common.controlled-input.utils (ns status-im.common.controlled-input.utils
(:require (:require
[clojure.string :as string])) [clojure.string :as string]
[status-im.contexts.wallet.common.utils :as wallet-utils]
[utils.money :as money]))
(def init-state (def init-state
{:value "" {:value ""
@@ -14,7 +16,7 @@
(defn numeric-value (defn numeric-value
[state] [state]
(or (parse-double (input-value state)) 0)) (money/bignumber (input-value state)))
(defn input-error (defn input-error
[state] [state]
@@ -26,23 +28,29 @@
(defn upper-limit (defn upper-limit
[state] [state]
(:upper-limit state)) (money/bignumber (:upper-limit state)))
(defn lower-limit (defn lower-limit
[state] [state]
(:lower-limit state)) (money/bignumber (:lower-limit state)))
(defn upper-limit-exceeded? (defn upper-limit-exceeded?
[state] [state]
(and (let [num-value (numeric-value state)]
(upper-limit state) (and
(> (numeric-value state) (upper-limit state)))) (upper-limit state)
(if (money/bignumber? num-value)
(money/greater-than (numeric-value state) (upper-limit state))
false))))
(defn lower-limit-exceeded? (defn- lower-limit-exceeded?
[state] [state]
(and (let [num-value (numeric-value state)]
(lower-limit state) (and
(< (numeric-value state) (lower-limit state)))) (lower-limit state)
(if (money/bignumber? num-value)
(money/less-than (numeric-value state) (lower-limit state))
false))))
(defn- recheck-errorness (defn- recheck-errorness
[state] [state]
@@ -75,13 +83,11 @@
(defn increase (defn increase
[state] [state]
(let [new-val (inc (numeric-value state))] (set-input-value state (str (money/add (numeric-value state) 1))))
(set-input-value state (str new-val))))
(defn decrease (defn decrease
[state] [state]
(let [new-val (dec (numeric-value state))] (set-input-value state (str (money/add (numeric-value state) -1))))
(set-input-value state (str new-val))))
(def ^:private not-digits-or-dot-pattern (def ^:private not-digits-or-dot-pattern
#"[^0-9+\.]") #"[^0-9+\.]")
@@ -130,3 +136,29 @@
(defn empty-value? (defn empty-value?
[state] [state]
(string/blank? (:value state))) (string/blank? (:value state)))
(defn- fiat->crypto
[value conversion-rate]
(-> value
(money/fiat->crypto conversion-rate)
(wallet-utils/cut-crypto-decimals-to-fit-usd-cents conversion-rate)))
(defn- crypto->fiat
[value conversion-rate]
(-> value
(money/crypto->fiat conversion-rate)
(wallet-utils/cut-fiat-balance-to-two-decimals)))
(defn ->crypto
[state conversion-rate]
(-> state
(set-input-value (fiat->crypto (input-value state) conversion-rate))
(set-upper-limit (fiat->crypto (upper-limit state) conversion-rate))
(set-lower-limit (fiat->crypto (lower-limit state) conversion-rate))))
(defn ->fiat
[state conversion-rate]
(-> state
(set-input-value (crypto->fiat (input-value state) conversion-rate))
(set-upper-limit (crypto->fiat (upper-limit state) conversion-rate))
(set-lower-limit (crypto->fiat (lower-limit state) conversion-rate))))
@@ -2,9 +2,13 @@
(:require (:require
[quo.core :as quo] [quo.core :as quo]
[quo.foundations.resources :as resources] [quo.foundations.resources :as resources]
[react-native.core :as rn]
[react-native.safe-area :as safe-area] [react-native.safe-area :as safe-area]
[reagent.core :as reagent] [reagent.core :as reagent]
[status-im.contexts.preview.quo.preview :as preview])) [status-im.common.controlled-input.utils :as controlled-input]
[status-im.contexts.preview.quo.preview :as preview]
[status-im.contexts.wallet.common.utils :as utils]
[utils.money :as money]))
(def networks (def networks
[{:source (resources/get-network :arbitrum)} [{:source (resources/get-network :arbitrum)}
@@ -12,47 +16,75 @@
{:source (resources/get-network :ethereum)}]) {:source (resources/get-network :ethereum)}])
(def title "Max: 200 SNT") (def title "Max: 200 SNT")
(def conversion-rate 3450.28)
(def descriptor (def descriptor
[{:key :token [{:key :token-symbol
:type :select :type :select
:options [{:key :eth} :options [{:key :eth}
{:key :snt}]} {:key :snt}]}
{:key :currency {:key :currency
:type :select :type :select
:options [{:key :usd} :options [{:key "$"}
{:key :eur}]} {:key "€"}]}
{:key :error? {:key :error?
:type :boolean}
{:key :allow-selection?
:type :boolean}]) :type :boolean}])
(defn view (defn view
[] []
(let [state (reagent/atom {:token :eth (let [state (reagent/atom {:token-symbol :eth
:currency :usd :currency "$"
:conversion 0.02 :crypto? true
:networks networks :error? false})]
:title title
:customization-color :blue
:show-keyboard? false
:allow-selection? true})
value (reagent/atom "")
set-value (fn [v]
(swap! value str v))
delete (fn [_]
(swap! value #(subs % 0 (dec (count %)))))]
(fn [] (fn []
[preview/preview-container (let [{:keys [currency token-symbol crypto? error?]} @state
{:state state [input-state set-input-state] (rn/use-state controlled-input/init-state)
:descriptor descriptor input-amount (controlled-input/input-value input-state)
:full-screen? true swap-between-fiat-and-crypto (fn []
:component-container-style {:flex 1 (if crypto?
:justify-content :space-between}} (set-input-state #(controlled-input/->fiat
[quo/token-input (assoc @state :value @value)] %
[quo/numbered-keyboard conversion-rate))
{:container-style {:padding-bottom (safe-area/get-top)} (set-input-state
:left-action :dot #(controlled-input/->crypto
:delete-key? true %
:on-press set-value conversion-rate))))
:on-delete delete}]]))) converted-value (if crypto?
(utils/prettify-balance currency
(money/crypto->fiat
input-amount
conversion-rate))
(utils/prettify-crypto-balance
(or (clj->js token-symbol) "")
(money/fiat->crypto input-amount
conversion-rate)
conversion-rate))]
[preview/preview-container
{:state state
:descriptor descriptor
:full-screen? true
:component-container-style {:flex 1
:justify-content :space-between}}
[quo/token-input
{:token-symbol token-symbol
:currency-symbol (if crypto? token-symbol currency)
:error? error?
:value input-amount
:converted-value converted-value
:on-swap (fn []
(swap! state assoc :crypto? (not crypto?))
(swap-between-fiat-and-crypto))
:hint-component [quo/network-tags
{:networks networks
:title title
:status (when (:error? @state) :error)}]}]
[quo/numbered-keyboard
{:container-style {:padding-bottom (safe-area/get-top)}
:left-action :dot
:delete-key? true
:on-press (fn [c]
(set-input-state #(controlled-input/add-character % c)))
:on-delete (fn []
(set-input-state controlled-input/delete-last))}]]))))
+58 -36
View File
@@ -12,14 +12,17 @@
[full-name] [full-name]
(first (string/split full-name #" "))) (first (string/split full-name #" ")))
(defn prettify-balance (defn cut-fiat-balance-to-two-decimals
[currency-symbol balance] [balance]
(let [valid-balance? (and balance (let [valid-balance? (and balance
(or (number? balance) (.-toFixed balance)))] (or (number? balance) (.-toFixed balance)))]
(as-> balance $ (as-> balance $
(if valid-balance? $ 0) (if valid-balance? $ 0)
(.toFixed $ 2) (.toFixed $ 2))))
(str currency-symbol $))))
(defn prettify-balance
[currency-symbol balance]
(str currency-symbol (cut-fiat-balance-to-two-decimals balance)))
(defn get-derivation-path (defn get-derivation-path
[number-of-accounts] [number-of-accounts]
@@ -47,8 +50,8 @@
nil)) nil))
(defn calc-max-crypto-decimals (defn calc-max-crypto-decimals
[value] [one-cent-value]
(let [str-representation (str value) (let [str-representation (str one-cent-value)
decimal-part (second (clojure.string/split str-representation #"\.")) decimal-part (second (clojure.string/split str-representation #"\."))
exponent (extract-exponent str-representation) exponent (extract-exponent str-representation)
zeroes-count (count (take-while #(= \0 %) decimal-part)) zeroes-count (count (take-while #(= \0 %) decimal-part))
@@ -58,29 +61,60 @@
(inc max-decimals) (inc max-decimals)
max-decimals))) max-decimals)))
(defn get-crypto-decimals-count (defn token-usd-price
[{:keys [market-values-per-currency]}] [token]
(let [price (get-in market-values-per-currency [:usd :price]) (get-in token [:market-values-per-currency :usd :price]))
one-cent-value (if (pos? price) (/ 0.01 price) 0)]
(calc-max-crypto-decimals one-cent-value))) (defn one-cent-value
[token-price-in-usd]
(if (pos? token-price-in-usd)
(/ 0.01 token-price-in-usd)
0))
(defn analyze-token-amount-for-price
"For full details: https://github.com/status-im/status-mobile/issues/18225"
[token-price-in-usd token-units]
(if (or (nil? token-units)
(not (money/bignumber? token-units))
(money/equal-to token-units 0))
{:zero-value? true}
(let [cent-value (one-cent-value token-price-in-usd)]
{:usd-cent-value cent-value
:standardized-decimals-count (if (nil? token-price-in-usd)
missing-price-decimals
(calc-max-crypto-decimals cent-value))})))
(defn cut-crypto-decimals-to-fit-usd-cents
[token-units token-price-in-usd]
(let [{:keys [zero-value? usd-cent-value standardized-decimals-count]}
(analyze-token-amount-for-price token-price-in-usd token-units)]
(cond
zero-value? "0"
(< token-units usd-cent-value) "0"
:else (number/remove-trailing-zeroes
(.toFixed token-units standardized-decimals-count)))))
(defn prettify-crypto-balance
[token-symbol crypto-balance conversion-rate]
(str (cut-crypto-decimals-to-fit-usd-cents crypto-balance conversion-rate)
" "
(string/upper-case token-symbol)))
(defn get-standard-crypto-format (defn get-standard-crypto-format
"For full details: https://github.com/status-im/status-mobile/issues/18225" "For full details: https://github.com/status-im/status-mobile/issues/18225"
[{:keys [market-values-per-currency]} token-units] [token token-units]
(cond (or (nil? token-units) (let [token-price-in-usd (token-usd-price token)
(money/equal-to token-units 0)) {:keys [zero-value? usd-cent-value standardized-decimals-count]}
"0" (analyze-token-amount-for-price token-price-in-usd token-units)]
(cond
zero-value?
"0"
(nil? (-> market-values-per-currency :usd :price)) (< token-units usd-cent-value)
(number/remove-trailing-zeroes (.toFixed token-units missing-price-decimals)) (str "<" (number/remove-trailing-zeroes (.toFixed usd-cent-value standardized-decimals-count)))
:else :else
(let [price (-> market-values-per-currency :usd :price) (number/remove-trailing-zeroes (.toFixed token-units standardized-decimals-count)))))
one-cent-value (if (pos? price) (/ 0.01 price) 0)
decimals-count (calc-max-crypto-decimals one-cent-value)]
(if (< token-units one-cent-value)
(str "<" (number/remove-trailing-zeroes (.toFixed one-cent-value decimals-count)))
(number/remove-trailing-zeroes (.toFixed token-units decimals-count))))))
(defn get-market-value (defn get-market-value
[currency {:keys [market-values-per-currency]}] [currency {:keys [market-values-per-currency]}]
@@ -286,18 +320,6 @@
(let [counter (count address)] (let [counter (count address)]
(str (subs address 0 6) "\u2026" (subs address (- counter 3) counter))))) (str (subs address 0 6) "\u2026" (subs address (- counter 3) counter)))))
(defn make-limit-label-crypto
[amount currency]
(str amount
" "
(some-> currency
name
string/upper-case)))
(defn make-limit-label-fiat
[amount currency-symbol]
(str currency-symbol amount))
(defn get-account-name-error (defn get-account-name-error
[s existing-account-names] [s existing-account-names]
(cond (cond
@@ -73,7 +73,12 @@
token-units (money/bignumber 0.01)] token-units (money/bignumber 0.01)]
(is (= (utils/get-standard-crypto-format {:market-values-per-currency market-values-per-currency} (is (= (utils/get-standard-crypto-format {:market-values-per-currency market-values-per-currency}
token-units) token-units)
"<2"))))) "<2")))
(let [market-values-per-currency {:usd {:price 0.005}}
token-units "0.01"]
(is (= (utils/get-standard-crypto-format {:market-values-per-currency market-values-per-currency}
token-units)
"0")))))
(deftest calculate-total-token-balance-test (deftest calculate-total-token-balance-test
(testing "calculate-total-token-balance function" (testing "calculate-total-token-balance function"
@@ -163,5 +168,3 @@
expected-order ["DAI" "ETH" "SNT"]] expected-order ["DAI" "ETH" "SNT"]]
(is (= expected-order sorted-tokens))))) (is (= expected-order sorted-tokens)))))
@@ -110,46 +110,23 @@
(h/setup-subs sub-mocks) (h/setup-subs sub-mocks)
(h/render-with-theme-provider [input-amount/view (h/render-with-theme-provider [input-amount/view
{:crypto-decimals 2 {:crypto-decimals 2
:limit-crypto 250 :limit-crypto (money/bignumber 250)
:initial-crypto-currency? false}]) :initial-crypto-currency? false}])
(h/is-truthy (h/get-by-text "0")) (h/is-truthy (h/get-by-text "0"))
(h/is-truthy (h/get-by-text "ETH")) (h/is-truthy (h/get-by-text "USD"))
(h/is-truthy (h/get-by-text "$0.00")) (h/is-truthy (h/get-by-text "0 ETH"))
(h/is-truthy (h/get-by-label-text :container)) (h/is-truthy (h/get-by-label-text :container))
(h/is-disabled (h/get-by-label-text :button-one))) (h/is-disabled (h/get-by-label-text :button-one)))
(h/test "Fill token input and confirm"
(h/setup-subs sub-mocks)
(let [on-confirm (h/mock-fn)]
(h/render-with-theme-provider [input-amount/view
{:on-confirm on-confirm
:crypto-decimals 10
:limit-crypto 1000
:initial-crypto-currency? false}])
(h/fire-event :press (h/query-by-label-text :keyboard-key-1))
(h/fire-event :press (h/query-by-label-text :keyboard-key-2))
(h/fire-event :press (h/query-by-label-text :keyboard-key-3))
(h/fire-event :press (h/query-by-label-text :keyboard-key-.))
(h/fire-event :press (h/query-by-label-text :keyboard-key-4))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(-> (h/wait-for #(h/get-by-text "$1234.50"))
(.then (fn []
(h/is-truthy (h/get-by-label-text :button-one))
(h/is-truthy (h/get-by-label-text :container))
(h/fire-event :press (h/get-by-label-text :button-one))
(h/was-called on-confirm))))))
(h/test "Fill token input and confirm" (h/test "Fill token input and confirm"
(h/setup-subs sub-mocks) (h/setup-subs sub-mocks)
(let [on-confirm (h/mock-fn)] (let [on-confirm (h/mock-fn)]
(h/render-with-theme-provider [input-amount/view (h/render-with-theme-provider [input-amount/view
{:crypto-decimals 10 {:crypto-decimals 10
:limit-crypto 1000 :limit-crypto (money/bignumber 1000)
:on-confirm on-confirm :on-confirm on-confirm
:initial-crypto-currency? false}]) :initial-crypto-currency? true}])
(h/fire-event :press (h/query-by-label-text :keyboard-key-1)) (h/fire-event :press (h/query-by-label-text :keyboard-key-1))
(h/fire-event :press (h/query-by-label-text :keyboard-key-2)) (h/fire-event :press (h/query-by-label-text :keyboard-key-2))
@@ -158,18 +135,19 @@
(h/fire-event :press (h/query-by-label-text :keyboard-key-4)) (h/fire-event :press (h/query-by-label-text :keyboard-key-4))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5)) (h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(-> (h/wait-for #(h/get-by-text "$1234.50")) (-> (h/wait-for #(h/get-by-text "$1234.50"))
(.then (fn [] (.then (fn []
(h/is-truthy (h/get-by-label-text :button-one)) (let [btn (h/get-by-label-text :button-one)]
(h/is-truthy (h/get-by-label-text :container)) (h/is-truthy btn)
(h/fire-event :press (h/get-by-label-text :button-one)) (h/fire-event :press btn)
(h/was-called on-confirm)))))) (h/was-called on-confirm)))))))
(h/test "Try to fill more than limit" (h/test "Try to fill more than limit"
(h/setup-subs sub-mocks) (h/setup-subs sub-mocks)
(h/render-with-theme-provider [input-amount/view (h/render-with-theme-provider [input-amount/view
{:crypto-decimals 1 {:crypto-decimals 1
:limit-crypto 1}]) :limit-crypto (money/bignumber 1)}])
(h/fire-event :press (h/query-by-label-text :keyboard-key-2)) (h/fire-event :press (h/query-by-label-text :keyboard-key-2))
(h/fire-event :press (h/query-by-label-text :keyboard-key-9)) (h/fire-event :press (h/query-by-label-text :keyboard-key-9))
@@ -181,13 +159,12 @@
(h/setup-subs sub-mocks) (h/setup-subs sub-mocks)
(h/render-with-theme-provider [input-amount/view (h/render-with-theme-provider [input-amount/view
{:crypto-decimals 1 {:crypto-decimals 1
:limit-crypto 1 :limit-crypto (money/bignumber 10)
:on-confirm #()}]) :on-confirm #()}])
(h/fire-event :press (h/query-by-label-text :keyboard-key-9))
(h/fire-event :press (h/query-by-label-text :keyboard-key-9)) (h/fire-event :press (h/query-by-label-text :keyboard-key-9))
(h/is-truthy (h/get-by-label-text :container-error)) (h/is-truthy (h/get-by-label-text :container-error))
(h/fire-event :press (h/query-by-label-text :reorder)) (h/fire-event :press (h/query-by-label-text :reorder))
(h/is-truthy #(h/get-by-text "Max: $100.00"))))
(-> (h/wait-for #(h/get-by-text "Max: $1000.00"))
(.then (fn []
(h/wait-for #(h/is-truthy (h/get-by-label-text :container))))))))
@@ -153,9 +153,8 @@
bottom (safe-area/get-bottom) bottom (safe-area/get-bottom)
[crypto-currency? set-crypto-currency] (rn/use-state initial-crypto-currency?) [crypto-currency? set-crypto-currency] (rn/use-state initial-crypto-currency?)
on-navigate-back on-navigate-back on-navigate-back on-navigate-back
[input-state set-input-state] (rn/use-state controlled-input/init-state)
[just-toggled-mode? set-just-toggled-mode?] (rn/use-state false) [just-toggled-mode? set-just-toggled-mode?] (rn/use-state false)
clear-input! #(set-input-state controlled-input/delete-all)
handle-on-confirm (fn [amount] handle-on-confirm (fn [amount]
(rf/dispatch [:wallet/set-token-amount-to-send (rf/dispatch [:wallet/set-token-amount-to-send
{:amount amount {:amount amount
@@ -167,59 +166,39 @@
:as :as
token} (rf/sub [:wallet/wallet-send-token]) token} (rf/sub [:wallet/wallet-send-token])
send-from-locked-amounts (rf/sub [:wallet/wallet-send-from-locked-amounts]) send-from-locked-amounts (rf/sub [:wallet/wallet-send-from-locked-amounts])
{token-balance :total-balance {token-balance :total-balance
available-balance :available-balance :as token-by-symbol} (rf/sub [:wallet/token-by-symbol
:as token-by-symbol} (rf/sub [:wallet/token-by-symbol
(str token-symbol) (str token-symbol)
enabled-from-chain-ids]) enabled-from-chain-ids])
currency-symbol (rf/sub [:profile/currency-symbol])
currency (rf/sub [:profile/currency]) currency (rf/sub [:profile/currency])
conversion-rate (-> token conversion-rate (-> token
:market-values-per-currency :market-values-per-currency
currency currency
:price) :price)
[input-state set-input-state] (rn/use-state
(-> controlled-input/init-state
(controlled-input/set-upper-limit
(utils/cut-crypto-decimals-to-fit-usd-cents
(or default-limit-crypto token-balance)
conversion-rate))))
clear-input! #(set-input-state controlled-input/delete-all)
currency-symbol (rf/sub [:profile/currency-symbol])
loading-routes? (rf/sub loading-routes? (rf/sub
[:wallet/wallet-send-loading-suggested-routes?]) [:wallet/wallet-send-loading-suggested-routes?])
route (rf/sub [:wallet/wallet-send-route]) route (rf/sub [:wallet/wallet-send-route])
on-confirm (or default-on-confirm handle-on-confirm) on-confirm (or default-on-confirm handle-on-confirm)
crypto-decimals (or token-decimals default-crypto-decimals) crypto-decimals (or token-decimals default-crypto-decimals)
current-crypto-limit (or default-limit-crypto input-value (controlled-input/input-value input-state)
(utils/get-standard-crypto-format valid-input? (not (or (controlled-input/empty-value? input-state)
token (controlled-input/input-error input-state)))
token-balance))
available-crypto-limit (or default-limit-crypto
(utils/get-standard-crypto-format
token
available-balance))
current-fiat-limit (.toFixed (* token-balance conversion-rate) 2)
available-fiat-limit (.toFixed (* available-balance conversion-rate) 2)
current-limit (if crypto-currency?
current-crypto-limit
current-fiat-limit)
available-limit (if crypto-currency?
available-crypto-limit
available-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)
available-limit)))
input-num-value (controlled-input/numeric-value input-state)
input-amount (controlled-input/input-value input-state)
confirm-disabled? (or (nil? route) confirm-disabled? (or (nil? route)
(empty? route) (empty? route)
(string/blank? (controlled-input/input-value (not valid-input?))
input-state))
(<= input-num-value 0)
(> input-num-value current-limit))
amount-in-crypto (if crypto-currency? amount-in-crypto (if crypto-currency?
input-amount input-value
(number/remove-trailing-zeroes (number/remove-trailing-zeroes
(.toFixed (/ input-amount conversion-rate) (.toFixed (/ input-value conversion-rate)
crypto-decimals))) crypto-decimals)))
total-amount-receiver (rf/sub [:wallet/total-amount true]) total-amount-receiver (rf/sub [:wallet/total-amount true])
amount-text (str (number/remove-trailing-zeroes amount-text (str (number/remove-trailing-zeroes
@@ -267,8 +246,7 @@
receiver-selected-network)) receiver-selected-network))
receiver-networks)) receiver-networks))
input-error (controlled-input/input-error input-state) input-error (controlled-input/input-error input-state)
limit-insufficient? (> (controlled-input/numeric-value input-state) limit-insufficient? (controlled-input/upper-limit-exceeded? input-state)
current-limit)
should-try-again? (and (not limit-insufficient?) no-routes-found?) should-try-again? (and (not limit-insufficient?) no-routes-found?)
current-address (rf/sub [:wallet/current-viewing-account-address]) current-address (rf/sub [:wallet/current-viewing-account-address])
owned-eth-token (rf/sub [:wallet/token-by-symbol owned-eth-token (rf/sub [:wallet/token-by-symbol
@@ -281,7 +259,9 @@
(if (= token-symbol (if (= token-symbol
(string/upper-case (string/upper-case
constants/mainnet-short-name)) constants/mainnet-short-name))
(= current-limit input-amount) (money/equal-to
(controlled-input/numeric-value input-state)
(controlled-input/upper-limit input-state))
(money/equal-to (:total-balance (money/equal-to (:total-balance
owned-eth-token) owned-eth-token)
0))) 0)))
@@ -295,34 +275,20 @@
:valid-input? valid-input? :valid-input? valid-input?
:bounce-duration-ms bounce-duration-ms :bounce-duration-ms bounce-duration-ms
:token token})) :token token}))
swap-between-fiat-and-crypto (fn [swap-to-crypto-currency?] swap-between-fiat-and-crypto (fn []
(set-just-toggled-mode? true) (set-just-toggled-mode? true)
(set-crypto-currency swap-to-crypto-currency?) (if crypto-currency?
(set-input-state (set-input-state
(fn [input-state] #(controlled-input/->fiat % conversion-rate))
(controlled-input/set-input-value (set-input-state
input-state #(controlled-input/->crypto % conversion-rate)))
(let [value (controlled-input/input-value (set-crypto-currency (not crypto-currency?)))]
input-state)
new-value (if swap-to-crypto-currency?
(.toFixed (/ value
conversion-rate)
crypto-decimals)
(.toFixed (* value
conversion-rate)
12))]
(number/remove-trailing-zeroes
new-value))))))]
(rn/use-mount (rn/use-mount
(fn [] (fn []
(let [dismiss-keyboard-fn #(when (= % "active") (rn/dismiss-keyboard!)) (let [dismiss-keyboard-fn #(when (= % "active") (rn/dismiss-keyboard!))
app-keyboard-listener (.addEventListener rn/app-state "change" dismiss-keyboard-fn)] app-keyboard-listener (.addEventListener rn/app-state "change" dismiss-keyboard-fn)]
#(.remove app-keyboard-listener)))) #(.remove app-keyboard-listener))))
(hot-reload/use-safe-unmount on-navigate-back) (hot-reload/use-safe-unmount on-navigate-back)
(rn/use-effect
(fn []
(set-input-state #(controlled-input/set-upper-limit % current-limit)))
[current-limit])
(rn/use-effect (rn/use-effect
(fn [] (fn []
(when input-error (debounce/clear-all)) (when input-error (debounce/clear-all))
@@ -346,24 +312,36 @@
:on-press #(rf/dispatch [:navigate-back]) :on-press #(rf/dispatch [:navigate-back])
:switcher-type :select-account}] :switcher-type :select-account}]
[quo/token-input [quo/token-input
{:container-style style/input-container {:container-style style/input-container
:token token-symbol :token-symbol token-symbol
:currency fiat-currency :value input-value
:currency-symbol currency-symbol :on-swap swap-between-fiat-and-crypto
:crypto-decimals (min token-decimals 6) :on-token-press show-select-asset-sheet
:error? (controlled-input/input-error input-state) :error? (controlled-input/input-error input-state)
:networks (seq from-enabled-networks) :currency-symbol (if crypto-currency? token-symbol fiat-currency)
:title (i18n/label :converted-value (if crypto-currency?
:t/send-limit (utils/prettify-balance
{:limit (if crypto-currency? currency-symbol
(utils/make-limit-label-crypto current-limit token-symbol) (money/crypto->fiat input-value
(utils/make-limit-label-fiat current-limit currency-symbol))}) conversion-rate))
:conversion conversion-rate (utils/prettify-crypto-balance
:show-keyboard? false (or (clj->js token-symbol) "")
:value input-amount (money/fiat->crypto input-value
:on-swap swap-between-fiat-and-crypto conversion-rate)
:on-token-press show-select-asset-sheet conversion-rate))
:allow-selection? false}] :hint-component [quo/network-tags
{:networks (seq from-enabled-networks)
:title (i18n/label
:t/send-limit
{:limit (if crypto-currency?
(utils/prettify-crypto-balance
(or (clj->js token-symbol) "")
(controlled-input/upper-limit input-state)
conversion-rate)
(utils/prettify-balance currency-symbol
(controlled-input/upper-limit
input-state)))})
:status (when (controlled-input/input-error input-state) :error)}]}]
[routes/view [routes/view
{:token token-by-symbol {:token token-by-symbol
:send-amount-in-crypto amount-in-crypto :send-amount-in-crypto amount-in-crypto
@@ -408,7 +386,7 @@
:left-action :dot :left-action :dot
:delete-key? true :delete-key? true
:on-press (fn [c] :on-press (fn [c]
(let [new-text (str input-amount c) (let [new-text (str input-value c)
max-decimals (if crypto-currency? crypto-decimals 2) max-decimals (if crypto-currency? crypto-decimals 2)
regex-pattern (str "^\\d*\\.?\\d{0," max-decimals "}$") regex-pattern (str "^\\d*\\.?\\d{0," max-decimals "}$")
regex (re-pattern regex-pattern)] regex (re-pattern regex-pattern)]
@@ -1,19 +1,12 @@
(ns status-im.contexts.wallet.send.routes.view (ns status-im.contexts.wallet.send.routes.view
(:require (:require
[clojure.string :as string]
[quo.core :as quo] [quo.core :as quo]
[react-native.core :as rn] [react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.common.controlled-input.utils :as controlled-input]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.common.utils.networks :as network-utils] [status-im.contexts.wallet.common.utils.networks :as network-utils]
[status-im.contexts.wallet.send.routes.style :as style] [status-im.contexts.wallet.send.routes.style :as style]
[status-im.contexts.wallet.send.utils :as send-utils] [status-im.contexts.wallet.send.utils :as send-utils]
[status-im.contexts.wallet.sheets.network-preferences.view :as network-preferences] [status-im.contexts.wallet.sheets.network-preferences.view :as network-preferences]
[status-im.feature-flags :as ff]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.money :as money]
[utils.number :as number]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
@@ -87,160 +80,164 @@
(rf/dispatch [:wallet/update-receiver-networks (rf/dispatch [:wallet/update-receiver-networks
chain-ids]))}]))}])) chain-ids]))}]))}]))
(defn- edit-amount #_(defn- edit-amount
[{:keys [chain-id token-symbol send-amount-in-crypto init-amount]}] [{:keys [chain-id token-symbol send-amount-in-crypto init-amount]}]
(rf/dispatch (rf/dispatch
[:show-bottom-sheet [:show-bottom-sheet
{:content {:content
(fn [] (fn []
(let [{:keys [network-name] :as network-details} (rf/sub [:wallet/network-details-by-chain-id (let [{:keys [network-name] :as network-details} (rf/sub [:wallet/network-details-by-chain-id
chain-id]) chain-id])
{fiat-currency :currency} (rf/sub [:profile/profile]) {fiat-currency :currency} (rf/sub [:profile/profile])
{token-decimals :decimals {token-decimals :decimals
:as :as
token} (rf/sub [:wallet/wallet-send-token]) token} (rf/sub [:wallet/wallet-send-token])
currency (rf/sub [:profile/currency]) currency (rf/sub [:profile/currency])
currency-symbol (rf/sub [:profile/currency-symbol]) currency-symbol (rf/sub [:profile/currency-symbol])
send-from-locked-amounts (rf/sub send-from-locked-amounts (rf/sub
[:wallet/wallet-send-from-locked-amounts]) [:wallet/wallet-send-from-locked-amounts])
{account-color :color} (rf/sub [:wallet/current-viewing-account]) {account-color :color} (rf/sub [:wallet/current-viewing-account])
locked-amount (get send-from-locked-amounts chain-id) locked-amount (get send-from-locked-amounts chain-id)
network-name-str (string/capitalize (name network-name)) network-name-str (string/capitalize (name network-name))
[input-state set-input-state] (rn/use-state (cond-> controlled-input/init-state [input-state set-input-state] (rn/use-state
init-amount (cond-> controlled-input/init-state
(controlled-input/set-input-value init-amount
(money/to-string init-amount))
locked-amount
(controlled-input/set-input-value
locked-amount)))
[crypto-currency? set-crypto-currency] (rn/use-state true)
conversion-rate (-> token
:market-values-per-currency
currency
:price)
{token-balance :total-balance} (rf/sub [:wallet/token-by-symbol
(str token-symbol)
[chain-id]])
current-crypto-limit (utils/get-standard-crypto-format
token
token-balance)
current-fiat-limit (.toFixed (* token-balance conversion-rate) 2)
current-limit (if crypto-currency?
current-crypto-limit
current-fiat-limit)
crypto-decimals token-decimals
input-amount (controlled-input/input-value input-state)
[is-amount-locked? set-is-amount-locked] (rn/use-state (some? locked-amount))
bottom (safe-area/get-bottom)
amount-in-crypto (if crypto-currency?
input-amount
(number/remove-trailing-zeroes
(.toFixed (/ input-amount
conversion-rate)
crypto-decimals)))
locked-greater-then-send-amount? (let [amount (money/bignumber
amount-in-crypto)
send-amount (money/bignumber
send-amount-in-crypto)]
(and (money/bignumber? amount)
(money/bignumber? send-amount)
(money/greater-than amount send-amount)))
swap-between-fiat-and-crypto (fn [swap-to-crypto-currency?]
(set-crypto-currency swap-to-crypto-currency?)
(set-input-state
(fn [input-state]
(controlled-input/set-input-value (controlled-input/set-input-value
input-state (money/to-string init-amount))
(let [value (controlled-input/input-value locked-amount
input-state) (controlled-input/set-input-value
new-value (if locked-amount)))
swap-to-crypto-currency? [crypto-currency? set-crypto-currency] (rn/use-state true)
(.toFixed conversion-rate (-> token
(/ value :market-values-per-currency
conversion-rate) currency
crypto-decimals) :price)
(.toFixed {token-balance :total-balance} (rf/sub [:wallet/token-by-symbol
(* value (str token-symbol)
conversion-rate) [chain-id]])
12))] current-crypto-limit (utils/get-standard-crypto-format
(number/remove-trailing-zeroes token
new-value)))))) token-balance)
lock-or-unlock-amount (fn [] current-fiat-limit (.toFixed (* token-balance conversion-rate) 2)
(if is-amount-locked? current-limit (if crypto-currency?
(rf/dispatch [:wallet/lock-from-amount current-crypto-limit
chain-id current-fiat-limit)
amount-in-crypto]) crypto-decimals token-decimals
(rf/dispatch [:wallet/unlock-from-amount input-amount (controlled-input/input-value input-state)
chain-id])) [is-amount-locked? set-is-amount-locked] (rn/use-state (some? locked-amount))
(rf/dispatch [:hide-bottom-sheet]))] bottom (safe-area/get-bottom)
(rn/use-effect amount-in-crypto (if crypto-currency?
(fn [] input-amount
(set-input-state #(controlled-input/set-upper-limit % current-limit))) (number/remove-trailing-zeroes
[current-limit]) (.toFixed (/ input-amount
[:<> conversion-rate)
[quo/drawer-top crypto-decimals)))
{:title (i18n/label :t/send-from-network {:network network-name-str}) locked-greater-then-send-amount? (let [amount (money/bignumber
:description (i18n/label :t/define-amount-sent-from-network {:network network-name-str})}] amount-in-crypto)
[quo/token-input send-amount (money/bignumber
{:container-style style/input-container send-amount-in-crypto)]
:token token-symbol (and (money/bignumber? amount)
:currency fiat-currency (money/bignumber? send-amount)
:currency-symbol currency-symbol (money/greater-than amount
:crypto-decimals (min token-decimals 6) send-amount)))
:error? (controlled-input/input-error input-state) swap-between-fiat-and-crypto (fn [swap-to-crypto-currency?]
:networks [network-details] (set-crypto-currency
:title (i18n/label swap-to-crypto-currency?)
:t/send-limit (set-input-state
{:limit (if crypto-currency? (fn [input-state]
(utils/make-limit-label-crypto current-limit token-symbol) (controlled-input/set-input-value
(utils/make-limit-label-fiat current-limit currency-symbol))}) input-state
:conversion conversion-rate (let [value (controlled-input/input-value
:show-keyboard? false input-state)
:value (controlled-input/input-value input-state) new-value
:on-swap swap-between-fiat-and-crypto (if
:allow-selection? false}] swap-to-crypto-currency?
(when locked-greater-then-send-amount? (.toFixed
[quo/information-box (/ value
{:type :error conversion-rate)
:icon :i/info crypto-decimals)
:style style/error-box} (.toFixed
(i18n/label :t/value-higher-than-send-amount)]) (* value
[quo/disclaimer conversion-rate)
{:on-change (fn [checked?] 12))]
(set-is-amount-locked checked?)) (number/remove-trailing-zeroes
:checked? is-amount-locked? new-value))))))
:container-style style/disclaimer lock-or-unlock-amount (fn []
:icon (if is-amount-locked? (if is-amount-locked?
:i/locked (rf/dispatch [:wallet/lock-from-amount
:i/unlocked) chain-id
:customization-color account-color} amount-in-crypto])
(i18n/label :t/dont-auto-recalculate-network {:network network-name-str})] (rf/dispatch [:wallet/unlock-from-amount
[quo/bottom-actions chain-id]))
{:actions :one-action (rf/dispatch [:hide-bottom-sheet]))]
:button-one-label (i18n/label :t/update) (rn/use-effect
:button-one-props {:on-press lock-or-unlock-amount (fn []
:customization-color account-color (set-input-state #(controlled-input/set-upper-limit % current-limit)))
:disabled? (or (controlled-input/empty-value? input-state) [current-limit])
(controlled-input/input-error input-state) [:<>
locked-greater-then-send-amount?)}}] [quo/drawer-top
[quo/numbered-keyboard {:title (i18n/label :t/send-from-network {:network network-name-str})
{:container-style (style/keyboard-container bottom) :description (i18n/label :t/define-amount-sent-from-network {:network network-name-str})}]
:left-action :dot [quo/token-input
:delete-key? true {:container-style style/input-container
:on-press (fn [c] :token token-symbol
(let [new-text (str input-amount c) :currency fiat-currency
max-decimals (if crypto-currency? crypto-decimals 2) :currency-symbol currency-symbol
regex-pattern (str "^\\d*\\.?\\d{0," max-decimals "}$") :crypto-decimals (min token-decimals 6)
regex (re-pattern regex-pattern)] :error? (controlled-input/input-error input-state)
(when (re-matches regex new-text) :networks [network-details]
(set-is-amount-locked true) :title (i18n/label
(set-input-state #(controlled-input/add-character % c))))) :t/send-limit
:on-delete (fn [] {:limit (if crypto-currency?
(set-is-amount-locked true) (utils/make-limit-label-crypto current-limit token-symbol)
(set-input-state controlled-input/delete-last)) (utils/make-limit-label-fiat current-limit currency-symbol))})
:on-long-press-delete (fn [] :conversion conversion-rate
(set-is-amount-locked true) :show-keyboard? false
(set-input-state controlled-input/delete-all))}]]))}])) :value (controlled-input/input-value input-state)
:on-swap swap-between-fiat-and-crypto
:allow-selection? false}]
(when locked-greater-then-send-amount?
[quo/information-box
{:type :error
:icon :i/info
:style style/error-box}
(i18n/label :t/value-higher-than-send-amount)])
[quo/disclaimer
{:on-change (fn [checked?]
(set-is-amount-locked checked?))
:checked? is-amount-locked?
:container-style style/disclaimer
:icon (if is-amount-locked?
:i/locked
:i/unlocked)
:customization-color account-color}
(i18n/label :t/dont-auto-recalculate-network {:network network-name-str})]
[quo/bottom-actions
{:actions :one-action
:button-one-label (i18n/label :t/update)
:button-one-props {:on-press lock-or-unlock-amount
:customization-color account-color
:disabled? (or (controlled-input/empty-value? input-state)
(controlled-input/input-error input-state)
locked-greater-then-send-amount?)}}]
[quo/numbered-keyboard
{:container-style (style/keyboard-container bottom)
:left-action :dot
:delete-key? true
:on-press (fn [c]
(let [new-text (str input-amount c)
max-decimals (if crypto-currency? crypto-decimals 2)
regex-pattern (str "^\\d*\\.?\\d{0," max-decimals "}$")
regex (re-pattern regex-pattern)]
(when (re-matches regex new-text)
(set-is-amount-locked true)
(set-input-state #(controlled-input/add-character % c)))))
:on-delete (fn []
(set-is-amount-locked true)
(set-input-state controlled-input/delete-last))
:on-long-press-delete (fn []
(set-is-amount-locked true)
(set-input-state controlled-input/delete-all))}]]))}]))
(defn render-network-values (defn render-network-values
[{:keys [network-values token-symbol on-press on-long-press receiver? loading-routes? [{:keys [network-values token-symbol on-press on-long-press receiver? loading-routes?
@@ -388,13 +385,6 @@
chain-id-to-disable chain-id-to-disable
disabled-from-chain-ids disabled-from-chain-ids
token-available-networks-for-suggested-routes)) token-available-networks-for-suggested-routes))
(when (ff/enabled? ::ff/wallet.custom-network-amounts)
:on-long-press) (fn [chain-id amount-calculated-for-chain]
(edit-amount
{:chain-id chain-id
:token-symbol token-symbol
:send-amount-in-crypto send-amount-in-crypto
:init-amount amount-calculated-for-chain}))
:receiver? false :receiver? false
:theme theme :theme theme
:loading-routes? loading-routes? :loading-routes? loading-routes?
+2
View File
@@ -6,6 +6,8 @@
prefer to use it for more general purpose concepts, such as the re-frame event prefer to use it for more general purpose concepts, such as the re-frame event
layer." layer."
(:require-macros test-helpers.unit) (:require-macros test-helpers.unit)
;; We must require test-helpers.matchers namespace to register the custom cljs.test directive
;; `match-strict?`
(:require (:require
[re-frame.core :as rf] [re-frame.core :as rf]
[re-frame.db :as rf-db] [re-frame.db :as rf-db]
+9 -2
View File
@@ -55,8 +55,9 @@
(.lessThan ^js bn1 bn2)) (.lessThan ^js bn1 bn2))
(defn equal-to (defn equal-to
[bn1 bn2] [n1 n2]
(.eq ^js bn1 bn2)) (when-let [^js bn1 (bignumber n1)]
(.eq ^js bn1 n2)))
(extend-type BigNumber (extend-type BigNumber
IEquiv IEquiv
@@ -262,6 +263,12 @@
[bn1 bn2] [bn1 bn2]
(.round (.dividedBy ^js bn1 bn2) 0)) (.round (.dividedBy ^js bn1 bn2) 0))
(defn fiat->crypto
[crypto fiat-price]
(when-let [crypto-bn (bignumber crypto)]
(div crypto-bn
(bignumber fiat-price))))
(defn absolute-value (defn absolute-value
[bn] [bn]
(when bn (when bn