[#18459] - Add cursor-based input to token-input (#18594)

This commit is contained in:
Ulises Manuel 2024-02-01 20:02:00 -06:00 committed by GitHub
parent b6ddd8b5ff
commit c7bf9f00b5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 102 additions and 48 deletions

View File

@ -1,6 +1,7 @@
(ns quo.components.wallet.token-input.view
(:require
[clojure.string :as string]
[oops.core :as oops]
[quo.components.buttons.button.view :as button]
[quo.components.dividers.divider-line.view :as divider-line]
[quo.components.markdown.text :as text]
@ -72,18 +73,25 @@
[token-name-text theme text]])
(defn input-section
[{:keys [on-change-text value value-atom]}]
(let [input-ref (atom nil)
set-ref #(reset! input-ref %)
focus-input #(when-let [ref ^js @input-ref]
(.focus ref))
controlled-input? (some? value)
handle-on-change-text (fn [v]
(when-not controlled-input?
(reset! value-atom v))
(when on-change-text
(on-change-text v)))]
(fn [{:keys [theme token customization-color show-keyboard? crypto? currency value error?]
[{:keys [on-change-text value value-atom on-selection-change]}]
(let [input-ref (atom nil)
set-ref #(reset! input-ref %)
focus-input #(when-let [ref ^js @input-ref]
(.focus ref))
controlled-input? (some? value)
handle-on-change-text (fn [v]
(when-not controlled-input?
(reset! value-atom v))
(when on-change-text
(on-change-text v)))
handle-selection-change (fn [^js e]
(when on-selection-change
(-> e
(oops/oget "nativeEvent.selection")
(js->clj :keywordize-keys true)
(on-selection-change))))]
(fn [{:keys [theme token customization-color show-keyboard? crypto? currency value error?
selection]
:or {show-keyboard? true}}]
[rn/pressable
{:on-press focus-input
@ -102,7 +110,9 @@
:max-length 12
:on-change-text handle-on-change-text
:selection-color customization-color
:show-soft-input-on-focus show-keyboard?}
:show-soft-input-on-focus show-keyboard?
:on-selection-change handle-selection-change
:selection (clj->js selection)}
controlled-input? (assoc :value value)
(not controlled-input?) (assoc :default-value @value-atom))]]
[token-label

View File

@ -36,22 +36,48 @@
non-numeric? (re-find not-digits-or-dot-pattern (str v))]
(not (or non-numeric? extra-dot? extra-leading-zero? length-overflow?))))
(defn- add-char-to-string
[s c idx]
(let [size (count s)]
(if (= size idx)
(str s c)
(str (subs s 0 idx)
c
(subs s idx size)))))
(defn- move-input-cursor
([input-selection-atom new-idx]
(move-input-cursor input-selection-atom new-idx new-idx))
([input-selection-atom new-start-idx new-end-idx]
(let [start-idx (if (< new-start-idx 0) 0 new-start-idx)
end-idx (if (< new-end-idx 0) 0 new-start-idx)]
(swap! input-selection-atom assoc :start start-idx :end end-idx))))
(defn- normalize-input
[current v]
(cond
(and (string/blank? current) (= v dot))
(str "0" v)
[current v input-selection-atom]
(let [{:keys [start end]} @input-selection-atom]
(if (= start end)
(cond
(and (string/blank? current) (= v dot))
(do
(move-input-cursor input-selection-atom 2)
(str "0" v))
(and (= current "0") (not= v dot))
(str v)
(and (= current "0") (not= v dot))
(do
(move-input-cursor input-selection-atom 1)
(str v))
:else
(str current v)))
:else
(do
(move-input-cursor input-selection-atom (inc start))
(add-char-to-string current v start)))
current)))
(defn- make-new-input
[current v]
[current v input-selection-atom]
(if (valid-input? current v)
(normalize-input current v)
(normalize-input current v input-selection-atom)
current))
(defn- reset-input-error
@ -59,6 +85,11 @@
(reset! input-error
(> new-value prev-value)))
(defn delete-from-string
[s idx]
(let [size (count s)]
(str (subs s 0 (dec idx)) (subs s idx size))))
(defn- f-view-internal
;; crypto-decimals, limit-crypto and initial-crypto-currency? args are needed
;; for component tests only
@ -72,24 +103,28 @@
input-value (reagent/atom "")
input-error (reagent/atom false)
crypto-currency? (reagent/atom initial-crypto-currency?)
input-selection (reagent/atom {:start 0 :end 0})
handle-swap (fn [{:keys [crypto? limit-fiat limit-crypto]}]
(let [num-value (parse-double @input-value)
current-limit (if crypto? limit-crypto limit-fiat)]
(reset! crypto-currency? crypto?)
(reset-input-error num-value current-limit input-error)))
handle-keyboard-press (fn [v loading-routes? current-limit-amount]
(let [current-value @input-value
new-value (make-new-input current-value v)
num-value (or (parse-double new-value) 0)]
(when (not loading-routes?)
(when-not loading-routes?
(let [current-value @input-value
new-value (make-new-input current-value v input-selection)
num-value (or (parse-double new-value) 0)]
(reset! input-value new-value)
(reset-input-error num-value current-limit-amount input-error)
(reagent/flush))))
handle-delete (fn [loading-routes? current-limit-amount]
(when-not loading-routes?
(swap! input-value #(subs % 0 (dec (count %))))
(reset-input-error @input-value current-limit-amount input-error)
(reagent/flush)))
(let [{:keys [start end]} @input-selection]
(reset-input-error @input-value current-limit-amount input-error)
(when (= start end)
(swap! input-value delete-from-string start)
(move-input-cursor input-selection (dec start)))
(reagent/flush))))
handle-on-change (fn [v current-limit-amount]
(when (valid-input? @input-value v)
(let [num-value (or (parse-double v) 0)]
@ -110,7 +145,14 @@
handle-on-confirm (fn []
(rf/dispatch [:wallet/send-select-amount
{:amount @input-value
:stack-id :wallet-send-input-amount}]))]
:stack-id :wallet-send-input-amount}]))
selection-change (fn [selection]
;; `reagent/flush` is needed to properly propagate the
;; input cursor state. Since this is a controlled
;; component the cursor will become static if
;; `reagent/flush` is removed.
(reset! input-selection selection)
(reagent/flush))]
(fn []
(let [{fiat-currency :currency} (rf/sub [:profile/profile])
{:keys [color]} (rf/sub [:wallet/current-viewing-account])
@ -154,23 +196,25 @@
:on-press on-navigate-back
:switcher-type :select-account}]
[quo/token-input
{:container-style style/input-container
:token token-symbol
:currency current-currency
:crypto-decimals crypto-decimals
:error? @input-error
:networks token-networks
:title (i18n/label :t/send-limit {:limit limit-label})
:conversion conversion-rate
:show-keyboard? false
:value @input-value
:on-change-text #(handle-on-change % current-limit)
:on-swap #(handle-swap
{:crypto? %
:currency current-currency
:token-symbol token-symbol
:limit-fiat fiat-limit
:limit-crypto crypto-limit})}]
{:container-style style/input-container
:token token-symbol
:currency current-currency
:crypto-decimals crypto-decimals
:error? @input-error
:networks token-networks
:title (i18n/label :t/send-limit {:limit limit-label})
:conversion conversion-rate
:show-keyboard? false
:value @input-value
:selection @input-selection
:on-change-text #(handle-on-change % current-limit)
:on-selection-change selection-change
:on-swap #(handle-swap
{:crypto? %
:currency current-currency
:token-symbol token-symbol
:limit-fiat fiat-limit
:limit-crypto crypto-limit})}]
[routes/view
{:amount amount-text
:routes suggested-routes