migrating to react state. step 2 (#18905)

This commit is contained in:
flexsurfer 2024-02-28 11:49:49 +01:00 committed by GitHub
parent 047e45d2a3
commit b7bffb3bd3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 507 additions and 526 deletions

View File

@ -8,36 +8,36 @@
[quo.components.calendar.calendar.years-list.view :as years-list] [quo.components.calendar.calendar.years-list.view :as years-list]
[quo.theme :as theme] [quo.theme :as theme]
[react-native.core :as rn] [react-native.core :as rn]
[reagent.core :as reagent]
[utils.number :as utils.number])) [utils.number :as utils.number]))
(defn- view-internal (defn view
[] [{:keys [on-change start-date end-date]}]
(let [selected-year (reagent/atom (utils/current-year)) (let [theme (theme/use-theme-value)
selected-month (reagent/atom (utils/current-month)) [selected-year set-selected-year] (rn/use-state (utils/current-year))
on-change-year #(reset! selected-year %) [selected-month set-selected-month] (rn/use-state (utils/current-month))
on-change-month (fn [new-date] on-change-year (rn/use-callback #(set-selected-year %))
(reset! selected-year (utils.number/parse-int (:year new-date))) on-change-month (rn/use-callback
(reset! selected-month (utils.number/parse-int (:month new-date))))] (fn [new-date]
(fn [{:keys [on-change start-date end-date theme]}] (set-selected-year
(utils.number/parse-int (:year new-date)))
(set-selected-month
(utils.number/parse-int (:month new-date)))))]
[rn/view [rn/view
{:style (style/container theme)} {:style (style/container theme)}
[years-list/view [years-list/view
{:on-change-year on-change-year {:on-change-year on-change-year
:year @selected-year}] :year selected-year}]
[rn/view [rn/view
{:style style/container-main} {:style style/container-main}
[month-picker/view [month-picker/view
{:year @selected-year {:year selected-year
:month @selected-month :month selected-month
:on-change on-change-month}] :on-change on-change-month}]
[weekdays-header/view] [weekdays-header/view]
[days-grid/view [days-grid/view
{:year @selected-year {:year selected-year
:month @selected-month :month selected-month
:start-date start-date :start-date start-date
:end-date end-date :end-date end-date
:on-change on-change :on-change on-change
:customization-color :blue}]]]))) :customization-color :blue}]]]))
(def view (theme/with-theme view-internal))

View File

@ -3,13 +3,7 @@
[quo.components.colors.color.constants :as constants] [quo.components.colors.color.constants :as constants]
[quo.components.colors.color.view :as color] [quo.components.colors.color.view :as color]
[quo.foundations.colors :as colors] [quo.foundations.colors :as colors]
[react-native.core :as rn] [react-native.core :as rn]))
[reagent.core :as reagent]))
(defn- on-change-handler
[selected color-name on-change]
(reset! selected color-name)
(when on-change (on-change color-name)))
(defn get-item-layout (defn get-item-layout
[_ index] [_ index]
@ -18,15 +12,34 @@
:offset (* (+ constants/color-size 8) index) :offset (* (+ constants/color-size 8) index)
:index index}) :index index})
(defn- view-internal (defn view
"Options "Options
- `default-selected` Default selected color name. - `default-selected` Default selected color name.
- `on-change` Callback called when a color is selected `(fn [color-name])`. - `on-change` Callback called when a color is selected `(fn [color-name])`.
- `blur?` Boolean to enable blur background support.}" - `blur?` Boolean to enable blur background support.}"
[{:keys [default-selected blur? on-change feng-shui? container-style]}] [{:keys [default-selected blur? on-change feng-shui? container-style]}]
(let [selected (reagent/atom default-selected) (let [[selected set-selected] (rn/use-state default-selected)
{window-width :width} (rn/get-window) {window-width :width} (rn/get-window)
ref (atom nil)] ref (rn/use-ref-atom nil)
on-ref (rn/use-callback #(reset! ref %))
render-fn (rn/use-callback
(fn [color idx]
[color/view
{:selected? (= color selected)
:on-press (fn [color-name]
(.scrollToIndex ^js @ref
#js
{:animated true
:index idx
:viewPosition 0.5})
(set-selected color-name)
(when on-change (on-change color-name)))
:blur? blur?
:key color
:color color
:idx idx
:window-width window-width}])
[selected blur? on-change @ref])]
(rn/use-mount (rn/use-mount
(fn [] (fn []
(js/setTimeout (js/setTimeout
@ -40,32 +53,14 @@
:viewPosition 0.5}))))) :viewPosition 0.5})))))
50))) 50)))
[rn/flat-list [rn/flat-list
{:ref #(reset! ref %) {:ref on-ref
;; TODO: using :feng-shui? temporarily while b & w is being developed. ;; TODO: using :feng-shui? temporarily while b & w is being developed.
;; https://github.com/status-im/status-mobile/discussions/16676 ;; https://github.com/status-im/status-mobile/discussions/16676
:data (if feng-shui? :data (if feng-shui?
(conj colors/account-colors :feng-shui) (conj colors/account-colors :feng-shui)
colors/account-colors) colors/account-colors)
:render-fn (fn [color idx] :render-fn render-fn
[color/view
{:selected? (= color @selected)
:on-press (fn [e]
(.scrollToIndex ^js @ref
#js
{:animated true
:index idx
:viewPosition 0.5})
(on-change-handler selected e on-change))
:blur? blur?
:key color
:color color
:idx idx
:window-width window-width}])
:get-item-layout get-item-layout :get-item-layout get-item-layout
:horizontal true :horizontal true
:shows-horizontal-scroll-indicator false :shows-horizontal-scroll-indicator false
:content-container-style container-style}])) :content-container-style container-style}]))
(defn view
[props]
[:f> view-internal props])

View File

@ -35,12 +35,11 @@
(defn image (defn image
"Same as rn/image but cache the image source in a js/Set, so the image won't "Same as rn/image but cache the image source in a js/Set, so the image won't
flicker when re-render on android" flicker when re-render on android"
[] [props]
(let [loaded-source (reagent/atom nil) (let [[loaded-source set-loaded-source] (rn/use-state nil)
on-source-loaded #(reset! loaded-source %)] on-source-loaded (rn/use-callback #(set-loaded-source %))]
(fn [props]
(if platform/ios? (if platform/ios?
[rn/image props] [rn/image props]
[:<> [:<>
[rn/image (assoc props :source @loaded-source)] [rn/image (assoc props :source loaded-source)]
[caching-image props on-source-loaded]])))) [caching-image props on-source-loaded]])))

View File

@ -3,25 +3,23 @@
[quo.components.dropdowns.network-dropdown.style :as style] [quo.components.dropdowns.network-dropdown.style :as style]
[quo.components.list-items.preview-list.view :as preview-list] [quo.components.list-items.preview-list.view :as preview-list]
[quo.theme :as quo.theme] [quo.theme :as quo.theme]
[react-native.core :as rn] [react-native.core :as rn]))
[reagent.core :as reagent]))
(defn- internal-view (defn view
[_ _]
(let [pressed? (reagent/atom false)]
(fn
[{:keys [on-press state] :as props} networks] [{:keys [on-press state] :as props} networks]
(let [theme (quo.theme/use-theme-value)
[pressed? set-pressed] (rn/use-state false)
on-press-in (rn/use-callback #(set-pressed true))
on-press-out (rn/use-callback #(set-pressed false))]
[rn/pressable [rn/pressable
{:style (style/dropdown-container (merge props {:pressed? @pressed?})) {:style (style/dropdown-container (merge props {:pressed? pressed? :theme theme}))
:accessibility-label :network-dropdown :accessibility-label :network-dropdown
:disabled (= state :disabled) :disabled (= state :disabled)
:on-press on-press :on-press on-press
:on-press-in (fn [] (reset! pressed? true)) :on-press-in on-press-in
:on-press-out (fn [] (reset! pressed? false))} :on-press-out on-press-out}
[preview-list/view [preview-list/view
{:type :network {:type :network
:list-size (count networks) :list-size (count networks)
:size :size-20} :size :size-20}
networks]]))) networks]]))
(def view (quo.theme/with-theme internal-view))

View File

@ -14,7 +14,6 @@
(def initial-spacing 56) (def initial-spacing 56)
(def end-spacing 22) (def end-spacing 22)
(def y-axis-label-width -33) (def y-axis-label-width -33)
(def inspecting? (reagent/atom false))
(defn- pointer (defn- pointer
[customization-color] [customization-color]
@ -32,14 +31,9 @@
:pointer-color customization-color :pointer-color customization-color
:pointer-strip-enable-gradient true}) :pointer-strip-enable-gradient true})
(defn- get-pointer-props
[pointer-props]
(let [pointer-index (.-pointerIndex ^js pointer-props)]
(reset! inspecting? (not= pointer-index -1))))
(defn- get-line-color (defn- get-line-color
[state theme] [state theme inspecting?]
(if @inspecting? (if inspecting?
(colors/theme-colors colors/neutral-80-opa-40 (colors/theme-colors colors/neutral-80-opa-40
colors/white-opa-20 colors/white-opa-20
theme) theme)
@ -51,11 +45,13 @@
colors/danger-60 colors/danger-60
theme)))) theme))))
(defn- view-internal (defn view
[{:keys [data state customization-color theme reference-value reference-prefix decimal-separator] [{:keys [data state customization-color reference-value reference-prefix decimal-separator]
:or {reference-prefix "$" :or {reference-prefix "$"
decimal-separator :dot}}] decimal-separator :dot}}]
(let [data (if (> (count data) max-data-points) (let [theme (quo.theme/use-theme-value)
[inspecting? set-inspecting] (rn/use-state false)
data (if (> (count data) max-data-points)
(utils/downsample-data data max-data-points) (utils/downsample-data data max-data-points)
data) data)
highest-value (utils/find-highest-value data) highest-value (utils/find-highest-value data)
@ -64,7 +60,11 @@
max-value (- (utils/calculate-rounded-max highest-value) min-value) max-value (- (utils/calculate-rounded-max highest-value) min-value)
step-value (/ max-value 4) step-value (/ max-value 4)
width (:width (rn/get-window)) width (:width (rn/get-window))
line-color (get-line-color state theme) line-color (get-line-color state theme inspecting?)
get-pointer-props (rn/use-callback
(fn [pointer-props]
(let [pointer-index (.-pointerIndex ^js pointer-props)]
(set-inspecting (not= pointer-index -1)))))
rules-color (colors/theme-colors colors/neutral-80-opa-10 rules-color (colors/theme-colors colors/neutral-80-opa-10
colors/white-opa-5 colors/white-opa-5
theme) theme)
@ -119,7 +119,7 @@
:show-strip-on-focus true :show-strip-on-focus true
:reference-line-1-config {:color rules-color} :reference-line-1-config {:color rules-color}
:reference-line-1-position 0 :reference-line-1-position 0
:show-reference-line-2 (and (not @inspecting?) :show-reference-line-2 (and (not inspecting?)
(<= reference-value highest-value) (<= reference-value highest-value)
(>= reference-value lowest-value)) (>= reference-value lowest-value))
:reference-line-2-config {:color y-axis-label-text-color :reference-line-2-config {:color y-axis-label-text-color
@ -138,5 +138,3 @@
:x-axis-label-text-style (style/x-axis-label-text (/ width (count x-axis-label-texts)) :x-axis-label-text-style (style/x-axis-label-text (/ width (count x-axis-label-texts))
y-axis-label-text-color) y-axis-label-text-color)
:x-axis-label-texts x-axis-label-texts}]])) :x-axis-label-texts x-axis-label-texts}]]))
(def view (quo.theme/with-theme view-internal))

View File

@ -20,7 +20,7 @@
(h/fire-event :on-focus (h/get-by-label-text :address-text-input)) (h/fire-event :on-focus (h/get-by-label-text :address-text-input))
(h/has-prop (h/get-by-label-text :address-text-input) (h/has-prop (h/get-by-label-text :address-text-input)
:placeholder-text-color :placeholder-text-color
colors/neutral-40))) colors/neutral-30)))
(h/test "on focus with blur? true" (h/test "on focus with blur? true"
(with-redefs [clipboard/get-string #(% "")] (with-redefs [clipboard/get-string #(% "")]
@ -30,27 +30,22 @@
(h/fire-event :on-focus (h/get-by-label-text :address-text-input)) (h/fire-event :on-focus (h/get-by-label-text :address-text-input))
(h/has-prop (h/get-by-label-text :address-text-input) (h/has-prop (h/get-by-label-text :address-text-input)
:placeholder-text-color :placeholder-text-color
colors/neutral-80-opa-40))) colors/neutral-80-opa-20)))
(h/test "scanned value is properly set" (h/test "default value is properly set"
(let [on-change-text (h/mock-fn) (let [default-value "default-value"]
scanned-value "scanned-value"]
(with-redefs [clipboard/get-string #(% "")] (with-redefs [clipboard/get-string #(% "")]
(h/render [address-input/address-input (h/render [address-input/address-input
{:scanned-value scanned-value {:default-value default-value
:on-change-text on-change-text
:ens-regex ens-regex}]) :ens-regex ens-regex}])
(-> (h/wait-for #(h/get-by-label-text :clear-button))
(.then (fn []
(h/was-called-with on-change-text scanned-value)
(h/has-prop (h/get-by-label-text :address-text-input) (h/has-prop (h/get-by-label-text :address-text-input)
:default-value :value
scanned-value))))))) default-value))))
(h/test "clear icon is shown when input has text" (h/test "clear icon is shown when input has text"
(with-redefs [clipboard/get-string #(% "")] (with-redefs [clipboard/get-string #(% "")]
(h/render [address-input/address-input (h/render [address-input/address-input
{:scanned-value "scanned value" {:default-value "default value"
:ens-regex ens-regex}]) :ens-regex ens-regex}])
(-> (h/wait-for #(h/get-by-label-text :clear-button-container)) (-> (h/wait-for #(h/get-by-label-text :clear-button-container))
(.then #(h/is-truthy (h/get-by-label-text :clear-button)))))) (.then #(h/is-truthy (h/get-by-label-text :clear-button))))))
@ -58,7 +53,7 @@
(h/test "on blur with text and blur? false" (h/test "on blur with text and blur? false"
(with-redefs [clipboard/get-string #(% "")] (with-redefs [clipboard/get-string #(% "")]
(h/render [address-input/address-input (h/render [address-input/address-input
{:scanned-value "scanned value" {:default-value "default value"
:ens-regex ens-regex}]) :ens-regex ens-regex}])
(-> (h/wait-for #(h/get-by-label-text :clear-button)) (-> (h/wait-for #(h/get-by-label-text :clear-button))
(.then (fn [] (.then (fn []
@ -71,7 +66,7 @@
(h/test "on blur with text blur? true" (h/test "on blur with text blur? true"
(with-redefs [clipboard/get-string #(% "")] (with-redefs [clipboard/get-string #(% "")]
(h/render [address-input/address-input (h/render [address-input/address-input
{:scanned-value "scanned value" {:default-value "default value"
:blur? true :blur? true
:ens-regex ens-regex}]) :ens-regex ens-regex}])
(-> (h/wait-for #(h/get-by-label-text :clear-button)) (-> (h/wait-for #(h/get-by-label-text :clear-button))
@ -106,7 +101,7 @@
(let [on-clear (h/mock-fn)] (let [on-clear (h/mock-fn)]
(with-redefs [clipboard/get-string #(% "")] (with-redefs [clipboard/get-string #(% "")]
(h/render [address-input/address-input (h/render [address-input/address-input
{:scanned-value "scanned value" {:default-value "default value"
:on-clear on-clear :on-clear on-clear
:ens-regex ens-regex}]) :ens-regex ens-regex}])
(-> (h/wait-for #(h/get-by-label-text :clear-button)) (-> (h/wait-for #(h/get-by-label-text :clear-button))
@ -148,7 +143,7 @@
(-> (h/wait-for #(h/get-by-label-text :clear-button)) (-> (h/wait-for #(h/get-by-label-text :clear-button))
(.then (fn [] (.then (fn []
(h/has-prop (h/get-by-label-text :address-text-input) (h/has-prop (h/get-by-label-text :address-text-input)
:default-value :value
clipboard))))))) clipboard)))))))
(h/test "ENS loading state and call on-detect-ens" (h/test "ENS loading state and call on-detect-ens"

View File

@ -8,7 +8,6 @@
[react-native.clipboard :as clipboard] [react-native.clipboard :as clipboard]
[react-native.core :as rn] [react-native.core :as rn]
[react-native.platform :as platform] [react-native.platform :as platform]
[reagent.core :as reagent]
[utils.i18n :as i18n])) [utils.i18n :as i18n]))
(defn- icon-color (defn- icon-color
@ -55,73 +54,90 @@
(and (not= status :default) (not blur?)) (and (not= status :default) (not blur?))
(colors/theme-colors colors/neutral-30 colors/neutral-60 theme))) (colors/theme-colors colors/neutral-30 colors/neutral-60 theme)))
(defn- f-address-input-internal (defn address-input
[] [{:keys [default-value blur? on-change-text on-blur on-focus on-clear on-scan
(let [status (reagent/atom :default)
value (reagent/atom "")
focused? (atom false)]
(fn [{:keys [scanned-value theme blur? on-change-text on-blur on-focus on-clear on-scan
on-detect-ens on-detect-address on-detect-unclassified address-regex ens-regex on-detect-ens on-detect-address on-detect-unclassified address-regex ens-regex
valid-ens-or-address? container-style]}] valid-ens-or-address? container-style]}]
(let [on-change (fn [text] (let [theme (quo.theme/use-theme-value)
(when (not= @value text) [status set-status] (rn/use-state :default)
value (rn/use-ref-atom nil)
[_ trigger-render-value] (rn/use-state @value)
[focused? set-focused] (rn/use-state false)
on-change (rn/use-callback
(fn [text]
(let [address? (when address-regex (let [address? (when address-regex
(boolean (re-matches address-regex text))) (boolean (re-matches address-regex text)))
ens? (when ens-regex ens? (when ens-regex
(boolean (re-matches ens-regex text)))] (boolean (re-matches ens-regex text)))]
(if (> (count text) 0)
(reset! status :typing)
(reset! status :active))
(reset! value text) (reset! value text)
(if (> (count text) 0)
(set-status :typing)
(set-status :active))
(when on-change-text (when on-change-text
(on-change-text text)) (on-change-text text))
(when (and on-detect-ens ens?) (when (and on-detect-ens ens?)
(reset! status :loading) (set-status :loading)
(on-detect-ens text #(reset! status :typing))) (on-detect-ens text #(set-status :typing)))
(when (and address? on-detect-address) (when (and address? on-detect-address)
(reset! status :loading) (set-status :loading)
(on-detect-address text)) (on-detect-address text))
(when (and (not address?) (when (and (not address?)
(not ens?) (not ens?)
on-detect-unclassified) on-detect-unclassified)
(on-detect-unclassified text))))) (on-detect-unclassified text)))))
on-paste (fn [] set-value (rn/use-callback
(fn [new-value]
(reset! value new-value)
(on-change new-value)
(trigger-render-value new-value)))
on-paste (rn/use-callback
(fn []
(clipboard/get-string (clipboard/get-string
(fn [clipboard] (fn [clipboard]
(when-not (empty? clipboard) (when-not (empty? clipboard)
(on-change clipboard) (set-value clipboard))))))
(reset! value clipboard))))) on-clear (rn/use-callback
on-clear (fn [] (fn []
(reset! value "") (set-value "")
(reset! status (if @focused? :active :default)) (set-status (if focused? :active :default))
(when on-change-text (when on-change-text
(on-change-text "")) (on-change-text ""))
(when on-clear (when on-clear
(on-clear))) (on-clear)))
on-scan #(when on-scan [focused?])
(on-scan)) on-clear (rn/use-callback
on-focus (fn [] (fn []
(set-value "")
(set-status (if focused? :active :default))
(when on-change-text
(on-change-text ""))
(when on-clear
(on-clear)))
[focused?])
on-scan (when on-scan (rn/use-callback #(on-scan set-value)))
on-focus (rn/use-callback
(fn []
(when (= (count @value) 0) (when (= (count @value) 0)
(reset! status :active)) (set-status :active))
(reset! focused? true) (set-focused true)
(when on-focus (on-focus))) (when on-focus (on-focus))))
on-blur (fn [] on-blur (rn/use-callback
(when (= @status :active) (fn []
(reset! status :default)) (when (= status :active)
(reset! focused? false) (set-status :default))
(set-focused false)
(when on-blur (on-blur))) (when on-blur (on-blur)))
placeholder-text-color (get-placeholder-text-color @status theme blur?)] [status])
(rn/use-effect (fn [] placeholder-text-color (rn/use-memo #(get-placeholder-text-color status theme blur?)
(when-not (empty? scanned-value) [status theme blur?])]
(on-change scanned-value))) (rn/use-mount #(on-change (or default-value "")))
[scanned-value])
[rn/view {:style (style/container container-style)} [rn/view {:style (style/container container-style)}
[rn/text-input [rn/text-input
{:accessibility-label :address-text-input {:accessibility-label :address-text-input
:style (style/input-text theme) :style (style/input-text theme)
:placeholder (i18n/label :t/name-ens-or-address) :placeholder (i18n/label :t/name-ens-or-address)
:placeholder-text-color placeholder-text-color :placeholder-text-color placeholder-text-color
:default-value @value :value @value
:auto-complete (when platform/ios? :off) :auto-complete (when platform/ios? :off)
:auto-capitalize :none :auto-capitalize :none
:auto-correct false :auto-correct false
@ -130,8 +146,8 @@
:on-focus on-focus :on-focus on-focus
:on-blur on-blur :on-blur on-blur
:on-change-text on-change}] :on-change-text on-change}]
(when (or (= @status :default) (when (or (= status :default)
(= @status :active)) (= status :active))
[rn/view [rn/view
{:style style/buttons-container {:style style/buttons-container
:accessibility-label :paste-scan-buttons-container} :accessibility-label :paste-scan-buttons-container}
@ -143,6 +159,7 @@
:inner-style (style/accessory-button blur? theme) :inner-style (style/accessory-button blur? theme)
:on-press on-paste} :on-press on-paste}
(i18n/label :t/paste)] (i18n/label :t/paste)]
(when on-scan
[button/button [button/button
{:accessibility-label :scan-button {:accessibility-label :scan-button
:icon-only? true :icon-only? true
@ -150,8 +167,8 @@
:size 24 :size 24
:inner-style (style/accessory-button blur? theme) :inner-style (style/accessory-button blur? theme)
:on-press on-scan} :on-press on-scan}
:main-icons/scan]]) :main-icons/scan])])
(when (= @status :typing) (when (= status :typing)
[rn/view [rn/view
{:style style/buttons-container {:style style/buttons-container
:accessibility-label :clear-button-container} :accessibility-label :clear-button-container}
@ -159,20 +176,13 @@
{:on-press on-clear {:on-press on-clear
:blur? blur? :blur? blur?
:theme theme}]]) :theme theme}]])
(when (and (= @status :loading) (not valid-ens-or-address?)) (when (and (= status :loading) (not valid-ens-or-address?))
[rn/view [rn/view
{:style style/buttons-container {:style style/buttons-container
:accessibility-label :loading-button-container} :accessibility-label :loading-button-container}
[loading-icon blur? theme]]) [loading-icon blur? theme]])
(when (and (= @status :loading) valid-ens-or-address?) (when (and (= status :loading) valid-ens-or-address?)
[rn/view [rn/view
{:style style/buttons-container {:style style/buttons-container
:accessibility-label :positive-button-container} :accessibility-label :positive-button-container}
[positive-state-icon theme]])])))) [positive-state-icon theme]])]))
(defn address-input-internal
[props]
[:f> f-address-input-internal props])
(def address-input
(quo.theme/with-theme address-input-internal))

View File

@ -5,8 +5,7 @@
[quo.components.markdown.text :as text] [quo.components.markdown.text :as text]
[quo.theme :as quo.theme] [quo.theme :as quo.theme]
[react-native.core :as rn] [react-native.core :as rn]
[react-native.platform :as platform] [react-native.platform :as platform]))
[reagent.core :as reagent]))
(defn- label-&-counter (defn- label-&-counter
[{:keys [label current-chars char-limit variant-colors]}] [{:keys [label current-chars char-limit variant-colors]}]
@ -61,31 +60,36 @@
:container-style]) :container-style])
(defn- base-input (defn- base-input
[{:keys [on-change-text on-char-limit-reach weight default-value]}] [{:keys [blur? theme error? right-icon left-icon disabled? small? button
(let [status (reagent/atom :default) label char-limit multiline? clearable? on-focus on-blur container-style
internal-on-focus #(reset! status :focus) on-change-text on-char-limit-reach weight default-value]
internal-on-blur #(reset! status :default) :as props}]
multiple-lines? (reagent/atom false) (let [[status set-status] (rn/use-state :default)
set-multiple-lines! #(let [height (oops/oget % "nativeEvent.contentSize.height") internal-on-focus (rn/use-callback #(set-status :focus))
internal-on-blur (rn/use-callback #(set-status :default))
[multiple-lines?
set-multiple-lines] (rn/use-state false)
on-content-size-change (rn/use-callback
(fn [event]
(let [height (oops/oget event "nativeEvent.contentSize.height")
;; In Android height comes with padding ;; In Android height comes with padding
min-height (if platform/android? 40 22)] min-height (if platform/android? 40 22)]
(if (> height min-height) (if (> height min-height)
(reset! multiple-lines? true) (set-multiple-lines true)
(reset! multiple-lines? false))) (set-multiple-lines false)))))
char-count (reagent/atom (count default-value)) [char-count
update-char-limit! (fn [new-text char-limit] set-char-count] (rn/use-state (count default-value))
on-change-text (rn/use-callback
(fn [new-text]
(when on-change-text (on-change-text new-text)) (when on-change-text (on-change-text new-text))
(let [amount-chars (count new-text)] (let [amount-chars (count new-text)]
(reset! char-count amount-chars) (set-char-count amount-chars)
(when (and (>= amount-chars char-limit) on-char-limit-reach) (when (and (>= amount-chars char-limit) on-char-limit-reach)
(on-char-limit-reach amount-chars))))] (on-char-limit-reach amount-chars)))))
(fn [{:keys [blur? theme error? right-icon left-icon disabled? small? button status-kw (cond
label char-limit multiline? clearable? on-focus on-blur container-style]
:as props}]
(let [status-kw (cond
disabled? :disabled disabled? :disabled
error? :error error? :error
:else @status) :else status)
colors-by-status (style/status-colors status-kw blur? theme) colors-by-status (style/status-colors status-kw blur? theme)
variant-colors (style/variants-colors blur? theme) variant-colors (style/variants-colors blur? theme)
clean-props (apply dissoc props custom-props)] clean-props (apply dissoc props custom-props)]
@ -94,7 +98,7 @@
[label-&-counter [label-&-counter
{:variant-colors variant-colors {:variant-colors variant-colors
:label label :label label
:current-chars @char-count :current-chars char-count
:char-limit char-limit}]) :char-limit char-limit}])
[rn/view {:style (style/input-container colors-by-status small? disabled?)} [rn/view {:style (style/input-container colors-by-status small? disabled?)}
(when-let [{:keys [icon-name]} left-icon] (when-let [{:keys [icon-name]} left-icon]
@ -103,7 +107,7 @@
:small? small? :small? small?
:icon-name icon-name}]) :icon-name icon-name}])
[rn/text-input [rn/text-input
(cond-> {:style (style/input colors-by-status small? @multiple-lines? weight) (cond-> {:style (style/input colors-by-status small? multiple-lines? weight)
:accessibility-label :input :accessibility-label :input
:placeholder-text-color (:placeholder colors-by-status) :placeholder-text-color (:placeholder colors-by-status)
:keyboard-appearance (quo.theme/theme-value :light :dark theme) :keyboard-appearance (quo.theme/theme-value :light :dark theme)
@ -117,8 +121,8 @@
(internal-on-blur))} (internal-on-blur))}
:always (merge clean-props) :always (merge clean-props)
multiline? (assoc :multiline true multiline? (assoc :multiline true
:on-content-size-change set-multiple-lines!) :on-content-size-change on-content-size-change)
char-limit (assoc :on-change-text #(update-char-limit! % char-limit)))] char-limit (assoc :on-change-text on-change-text))]
(when-let [{:keys [on-press icon-name style-fn]} right-icon] (when-let [{:keys [on-press icon-name style-fn]} right-icon]
[right-accessory [right-accessory
{:variant-colors variant-colors {:variant-colors variant-colors
@ -127,7 +131,7 @@
:icon-style-fn style-fn :icon-style-fn style-fn
:icon-name icon-name :icon-name icon-name
:on-press (fn [] :on-press (fn []
(when clearable? (reset! char-count 0)) (when clearable? (set-char-count 0))
(on-press))}]) (on-press))}])
(when-let [{:keys [on-press text]} button] (when-let [{:keys [on-press text]} button]
[right-button [right-button
@ -136,24 +140,24 @@
:small? small? :small? small?
:disabled? disabled? :disabled? disabled?
:on-press on-press :on-press on-press
:text text}])]])))) :text text}])]]))
(defn- password-input (defn- password-input
[{:keys [default-shown?] [{:keys [default-shown?]
:or {default-shown? false}}] :or {default-shown? false}
(let [password-shown? (reagent/atom default-shown?)] :as props}]
(fn [props] (let [[password-shown? set-password-shown] (rn/use-state default-shown?)]
[base-input [base-input
(assoc props (assoc props
:accessibility-label :password-input :accessibility-label :password-input
:auto-capitalize :none :auto-capitalize :none
:auto-complete :password :auto-complete :password
:secure-text-entry (not @password-shown?) :secure-text-entry (not password-shown?)
:right-icon {:style-fn style/password-icon :right-icon {:style-fn style/password-icon
:icon-name (if @password-shown? :i/hide-password :i/reveal) :icon-name (if password-shown? :i/hide-password :i/reveal)
:on-press #(swap! password-shown? not)})]))) :on-press #(set-password-shown (not password-shown?))})]))
(defn input-internal (defn input
"This input supports the following properties: "This input supports the following properties:
- :type - Can be `:text`(default) or `:password`. - :type - Can be `:text`(default) or `:password`.
- :blur? - Boolean to set the blur color variant. - :blur? - Boolean to set the blur color variant.
@ -177,7 +181,7 @@
- :on-change-text - :on-change-text
... ...
" "
[{:keys [type clearable? on-clear on-change-text icon-name] [{:keys [type clearable? on-clear icon-name]
:or {type :text} :or {type :text}
:as props}] :as props}]
(let [base-props (cond-> props (let [base-props (cond-> props
@ -185,13 +189,7 @@
clearable? (assoc :right-icon clearable? (assoc :right-icon
{:style-fn style/clear-icon {:style-fn style/clear-icon
:icon-name :i/clear :icon-name :i/clear
:on-press #(when on-clear (on-clear))}) :on-press #(when on-clear (on-clear))}))]
on-change-text (assoc :on-change-text
(fn [new-text]
(on-change-text new-text)
(reagent/flush))))]
(if (= type :password) (if (= type :password)
[password-input base-props] [password-input base-props]
[base-input base-props]))) [base-input base-props])))
(def input (quo.theme/with-theme input-internal))

View File

@ -3,8 +3,7 @@
[clojure.string :as string] [clojure.string :as string]
[quo.components.inputs.recovery-phrase.style :as style] [quo.components.inputs.recovery-phrase.style :as style]
[quo.theme :as quo.theme] [quo.theme :as quo.theme]
[react-native.core :as rn] [react-native.core :as rn]))
[reagent.core :as reagent]))
(def ^:private custom-props (def ^:private custom-props
[:customization-color :theme :blur? :cursor-color :multiline :on-focus :on-blur [:customization-color :theme :blur? :cursor-color :multiline :on-focus :on-blur
@ -38,12 +37,8 @@
:idx 0}) :idx 0})
:result))) :result)))
(defn recovery-phrase-input-internal (defn recovery-phrase-input
[_ _] [{:keys [customization-color blur? on-focus on-blur mark-errors?
(let [state (reagent/atom :default)
set-focused #(reset! state :focused)
set-default #(reset! state :default)]
(fn [{:keys [customization-color theme blur? on-focus on-blur mark-errors?
error-pred-current-word error-pred-written-words word-limit error-pred-current-word error-pred-written-words word-limit
container-style] container-style]
:or {customization-color :blue :or {customization-color :blue
@ -52,21 +47,27 @@
error-pred-written-words (constantly false)} error-pred-written-words (constantly false)}
:as props} :as props}
text] text]
(let [extra-props (apply dissoc props custom-props)] (let [theme (quo.theme/use-theme-value)
[state set-state] (rn/use-state :default)
on-focus (rn/use-callback
(fn []
(set-state :focused)
(when on-focus (on-focus))))
on-blur (rn/use-callback
(fn []
(set-state :default)
(when on-blur (on-blur))))
extra-props (apply dissoc props custom-props)]
[rn/view {:style (style/container container-style)} [rn/view {:style (style/container container-style)}
[rn/text-input [rn/text-input
(merge {:accessibility-label :recovery-phrase-input (merge {:accessibility-label :recovery-phrase-input
:style (style/input) :style (style/input)
:placeholder-text-color (style/placeholder-color @state theme blur?) :placeholder-text-color (style/placeholder-color state theme blur?)
:cursor-color (style/cursor-color customization-color theme) :cursor-color (style/cursor-color customization-color theme)
:keyboard-appearance (quo.theme/theme-value :light :dark theme) :keyboard-appearance (quo.theme/theme-value :light :dark theme)
:multiline true :multiline true
:on-focus (fn [] :on-focus on-focus
(set-focused) :on-blur on-blur}
(when on-focus (on-focus)))
:on-blur (fn []
(set-default)
(when on-blur (on-blur)))}
extra-props) extra-props)
(if mark-errors? (if mark-errors?
(mark-error-words {:pred-last-word error-pred-current-word (mark-error-words {:pred-last-word error-pred-current-word
@ -74,6 +75,4 @@
:text text :text text
:word-limit word-limit :word-limit word-limit
:theme theme}) :theme theme})
text)]])))) text)]]))
(def recovery-phrase-input (quo.theme/with-theme recovery-phrase-input-internal))

View File

@ -4,8 +4,7 @@
[quo.components.icon :as icon] [quo.components.icon :as icon]
[quo.components.inputs.search-input.style :as style] [quo.components.inputs.search-input.style :as style]
[quo.foundations.colors :as colors] [quo.foundations.colors :as colors]
[react-native.core :as rn] [react-native.core :as rn]))
[reagent.core :as reagent]))
(def ^:private tag-separator [rn/view {:style style/tag-separator}]) (def ^:private tag-separator [rn/view {:style style/tag-separator}])
@ -41,54 +40,52 @@
text-input)) text-input))
(defn search-input (defn search-input
[{:keys [value]}] [{:keys [value tags disabled? blur? on-change-text customization-color
(let [state (reagent/atom :default)
set-active #(reset! state :active)
set-default #(reset! state :default)
scroll-view-ref (atom nil)
use-value? (boolean value)]
(fn [{:keys [value tags disabled? blur? on-change-text customization-color
on-clear on-focus on-blur override-theme container-style] on-clear on-focus on-blur override-theme container-style]
:or {customization-color :blue} :or {customization-color :blue}
:as props} :as props}
& children] & children]
(let [clean-props (apply dissoc props props-to-remove)] (let [[state set-state] (rn/use-state :default)
on-focus (rn/use-callback
(fn []
(set-state :active)
(when on-focus (on-focus))))
on-blur (rn/use-callback
(fn []
(set-state :default)
(when on-blur (on-blur))))
scroll-view-ref (rn/use-ref-atom nil)
on-scroll-view-ref (rn/use-callback #(reset! scroll-view-ref %))
on-key-press (rn/use-callback #(handle-backspace % @scroll-view-ref))
use-value? (boolean value)
clean-props (apply dissoc props props-to-remove)]
[rn/view [rn/view
{:accessibility-label :search-input {:accessibility-label :search-input
:style (style/container container-style)} :style (style/container container-style)}
[rn/scroll-view [rn/scroll-view
{:ref #(reset! scroll-view-ref %) {:ref on-scroll-view-ref
:style style/scroll-container :style style/scroll-container
:content-container-style style/scroll-content :content-container-style style/scroll-content
:horizontal true :horizontal true
:shows-horizontal-scroll-indicator false} :shows-horizontal-scroll-indicator false}
(when (seq tags) (when (seq tags)
[inner-tags tags]) [inner-tags tags])
(add-children (add-children
[rn/text-input [rn/text-input
(cond-> {:style (style/input-text disabled?) (cond-> {:style (style/input-text disabled?)
:cursor-color (style/cursor customization-color override-theme) :cursor-color (style/cursor customization-color override-theme)
:placeholder-text-color (style/placeholder-color @state blur? override-theme) :placeholder-text-color (style/placeholder-color state blur? override-theme)
:editable (not disabled?) :editable (not disabled?)
:on-key-press #(handle-backspace % @scroll-view-ref) :on-key-press on-key-press
:keyboard-appearance (colors/theme-colors :light :dark override-theme) :keyboard-appearance (colors/theme-colors :light :dark override-theme)
:on-change-text (fn [new-text] :on-change-text on-change-text
(when on-change-text :on-focus on-focus
(on-change-text new-text)) :on-blur on-blur}
(reagent/flush))
:on-focus (fn []
(set-active)
(when on-focus (on-focus)))
:on-blur (fn []
(set-default)
(when on-blur (on-blur)))}
use-value? (assoc :value value) use-value? (assoc :value value)
(seq clean-props) (merge clean-props))] (seq clean-props) (merge clean-props))]
(when-not use-value? children))] (when-not use-value? children))]
(when (or (seq value) (seq children)) (when (or (seq value) (seq children))
[clear-button [clear-button
{:on-press on-clear {:on-press on-clear
:blur? blur? :blur? blur?
:override-theme override-theme}])])))) :override-theme override-theme}])]))

View File

@ -4,8 +4,7 @@
[quo.components.inputs.title-input.style :as style] [quo.components.inputs.title-input.style :as style]
[quo.components.markdown.text :as text] [quo.components.markdown.text :as text]
[quo.theme :as quo.theme] [quo.theme :as quo.theme]
[react-native.core :as rn] [react-native.core :as rn]))
[reagent.core :as reagent]))
(defn- pad-0 (defn- pad-0
[value] [value]
@ -13,36 +12,37 @@
(str 0 value) (str 0 value)
value)) value))
(defn- view-internal (defn view
[{:keys [blur? [{:keys [blur? on-change-text auto-focus placeholder max-length default-value return-key-type
on-change-text size on-focus on-blur container-style customization-color disabled?]
auto-focus
placeholder
max-length
default-value
return-key-type
size
theme
on-focus
on-blur
container-style]
:or {max-length 0 :or {max-length 0
auto-focus false auto-focus false
default-value ""}}] default-value ""}}]
(let [focused? (reagent/atom auto-focus) (let [theme (quo.theme/use-theme-value)
value (reagent/atom default-value) [focused? set-focused] (rn/use-state auto-focus)
input-ref (atom nil) [value set-value] (rn/use-state default-value)
on-change (fn [v] input-ref (rn/use-ref-atom nil)
(reset! value v) on-inpur-ref (rn/use-callback #(reset! input-ref %))
(when on-change-text on-press (rn/use-callback
(on-change-text v)))] #(when-not disabled? (.focus ^js @input-ref))
(fn [{:keys [customization-color disabled?]}] [disabled?])
on-change (rn/use-callback
(fn [v]
(set-value v)
(when on-change-text (on-change-text v))))
on-focus (rn/use-callback
(fn []
(when (fn? on-focus) (on-focus))
(set-focused true)))
on-blur (rn/use-callback
(fn []
(when (fn? on-blur) (on-blur))
(set-focused false)))]
[rn/view [rn/view
{:style (merge (style/container disabled?) container-style)} {:style (merge (style/container disabled?) container-style)}
[rn/view {:style style/text-input-container} [rn/view {:style style/text-input-container}
[rn/text-input [rn/text-input
{:style {:style (text/text-style
(text/text-style
{:size (or size :heading-1) {:size (or size :heading-1)
:weight :semi-bold :weight :semi-bold
:style (style/title-text theme)}) :style (style/title-text theme)})
@ -50,44 +50,32 @@
:accessibility-label :profile-title-input :accessibility-label :profile-title-input
:keyboard-appearance (quo.theme/theme-value :light :dark theme) :keyboard-appearance (quo.theme/theme-value :light :dark theme)
:return-key-type return-key-type :return-key-type return-key-type
:on-focus (fn [] :on-focus on-focus
(when (fn? on-focus) :on-blur on-blur
(on-focus))
(reset! focused? true))
:on-blur (fn []
(when (fn? on-blur)
(on-blur))
(reset! focused? false))
:auto-focus auto-focus :auto-focus auto-focus
:input-mode :text :input-mode :text
:on-change-text on-change :on-change-text on-change
:editable (not disabled?) :editable (not disabled?)
:max-length max-length :max-length max-length
:placeholder placeholder :placeholder placeholder
:ref #(reset! input-ref %) :ref on-inpur-ref
:selection-color (style/get-selection-color customization-color blur? theme) :selection-color (style/get-selection-color customization-color blur? theme)
:placeholder-text-color (if @focused? :placeholder-text-color (if focused?
(style/get-focused-placeholder-color blur? theme) (style/get-focused-placeholder-color blur? theme)
(style/get-placeholder-color blur? theme))}]] (style/get-placeholder-color blur? theme))}]]
[rn/view [rn/view
{:style (style/counter-container @focused?)} {:style (style/counter-container focused?)}
(if @focused? (if focused?
[text/text [text/text
[text/text [text/text
{:style (style/char-count blur? theme) {:style (style/char-count blur? theme)
:size :paragraph-2} :size :paragraph-2}
(pad-0 (pad-0
(str (str
(count @value)))] (count value)))]
[text/text [text/text
{:style (style/char-count blur? theme) {:style (style/char-count blur? theme)
:size :paragraph-2} :size :paragraph-2}
(str "/" (str "/" (pad-0 (str max-length)))]]
(pad-0 [rn/pressable {:on-press on-press}
(str max-length)))]] [icon/icon :i/edit {:color (style/get-char-count-color blur? theme)}]])]]))
[rn/pressable
{:on-press #(when-not disabled?
(.focus ^js @input-ref))}
[icon/icon :i/edit {:color (style/get-char-count-color blur? theme)}]])]])))
(def view (quo.theme/with-theme view-internal))

View File

@ -25,8 +25,8 @@
:blur? (:blur? @state) :blur? (:blur? @state)
:show-blur-background? true} :show-blur-background? true}
[quo/address-input [quo/address-input
(merge @state (merge (dissoc @state :scanned-value)
{:on-scan #(js/alert "Not implemented yet") {:on-scan (fn [on-result] (on-result (:scanned-value @state)))
:ens-regex constants/regx-ens :ens-regex constants/regx-ens
:on-detect-ens (fn [_] :on-detect-ens (fn [_]
(swap! state assoc :valid-ens-or-address? false) (swap! state assoc :valid-ens-or-address? false)

View File

@ -21,10 +21,14 @@
(h/fire-event :change-text (h/fire-event :change-text
(h/get-by-label-text :add-address-to-watch) (h/get-by-label-text :add-address-to-watch)
"0x12E838Ae1f769147b12956485dc56e57138f3AC8") "0x12E838Ae1f769147b12956485dc56e57138f3AC8")
(h/is-truthy (h/get-by-translation-text :t/address-already-in-use))) (-> (h/wait-for #(h/get-by-translation-text :t/address-already-in-use))
(.then (fn []
(h/is-truthy (h/get-by-translation-text :t/address-already-in-use))))))
(h/test "validation messages show for invalid address" (h/test "validation messages show for invalid address"
(h/render [add-address-to-watch/view]) (h/render [add-address-to-watch/view])
(h/is-falsy (h/query-by-label-text :error-message)) (h/is-falsy (h/query-by-label-text :error-message))
(h/fire-event :change-text (h/get-by-label-text :add-address-to-watch) "0x12E838Ae1f769147b") (h/fire-event :change-text (h/get-by-label-text :add-address-to-watch) "0x12E838Ae1f769147b")
(h/is-truthy (h/get-by-translation-text :t/invalid-address)))) (-> (h/wait-for #(h/get-by-translation-text :t/invalid-address))
(.then (fn []
(h/is-truthy (h/get-by-translation-text :t/invalid-address)))))))