Skip to content

Commit

Permalink
Add simple cursor-based input handling to token-input
Browse files Browse the repository at this point in the history
  • Loading branch information
ulisesmac committed Jan 25, 2024
1 parent ad7a95e commit 0b8a7af
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 42 deletions.
36 changes: 23 additions & 13 deletions src/quo/components/wallet/token_input/view.cljs
Original file line number Diff line number Diff line change
@@ -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]
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
103 changes: 74 additions & 29 deletions src/status_im/contexts/wallet/send/input_amount/view.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -30,35 +30,66 @@
(defn valid-input?
[current v]
(let [max-length 12
length-owerflow? (>= (count current) max-length)
length-overflow? (>= (count current) max-length)
extra-dot? (and (= v dot) (string/includes? current dot))
extra-leading-zero? (and (= current "0") (= "0" (str v)))
non-numeric? (re-find not-digits-or-dot-pattern (str v))]
(not (or non-numeric? extra-dot? extra-leading-zero? length-owerflow?))))
(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
[new-value prev-value input-error]
(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 and limit-crypto args are needed for component tests only
[{:keys [crypto-decimals limit-crypto]}]
Expand All @@ -76,6 +107,7 @@
input-error (reagent/atom false)
current-limit (reagent/atom {:amount limit-crypto
:currency token-symbol})
input-selection (reagent/atom {:start 0 :end 0})
handle-swap (fn [crypto?]
(let [num-value (parse-double @input-value)]
(reset! current-limit (if crypto?
Expand All @@ -88,7 +120,9 @@
input-error)))
handle-keyboard-press (fn [v]
(let [current-value @input-value
new-value (make-new-input current-value v)
new-value (make-new-input current-value
v
input-selection)
num-value (or (parse-double new-value) 0)
current-limit-amount (:amount @current-limit)]
(when (not loading-suggested-routes?)
Expand All @@ -97,17 +131,27 @@
(reagent/flush))))
handle-delete (fn [_]
(when-not loading-suggested-routes?
(let [current-limit-amount (:amount @current-limit)]
(swap! input-value #(subs % 0 (dec (count %))))
(let [{:keys [start end]} @input-selection
current-limit-amount (:amount @current-limit)]
(reset-input-error @input-value current-limit-amount input-error)
(reagent/flush))))
(when (= start end)
(swap! input-value delete-from-string start)
(move-input-cursor input-selection (dec start))))
(reagent/flush)))
handle-on-change (fn [v]
(when (valid-input? @input-value v)
(let [num-value (or (parse-double v) 0)
current-limit-amount (:amount @current-limit)]
(reset! input-value v)
(reset-input-error num-value current-limit-amount input-error)
(reagent/flush))))]
(reagent/flush))))
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 [{:keys [on-confirm]
:or {on-confirm #(rf/dispatch [:wallet/send-select-amount
{:amount @input-value
Expand Down Expand Up @@ -146,19 +190,20 @@
:on-press #(rf/dispatch [:navigate-back-within-stack :wallet-send-input-amount])
:switcher-type :select-account}]
[quo/token-input
{:container-style style/input-container
:token token-symbol
:currency currency
:crypto-decimals crypto-decimals
{:container-style style/input-container
:token token-symbol
:currency currency
:crypto-decimals crypto-decimals
:error? @input-error
:networks (:networks token)
:title (i18n/label :t/send-limit {:limit limit-label})
:conversion conversion-rate
:show-keyboard? false
:value @input-value
:on-swap handle-swap
:on-change-text (fn [text]
(handle-on-change text))}]
:networks (:networks token)
:title (i18n/label :t/send-limit {:limit limit-label})
:conversion conversion-rate
:show-keyboard? false
:value @input-value
:selection @input-selection
:on-swap handle-swap
:on-change-text handle-on-change
:on-selection-change selection-change}]
[routes/view
{:amount amount
:routes suggested-routes
Expand Down

0 comments on commit 0b8a7af

Please sign in to comment.