diff --git a/src/quo/components/calendar/calendar/view.cljs b/src/quo/components/calendar/calendar/view.cljs index 831bc57883..efc5d946ce 100644 --- a/src/quo/components/calendar/calendar/view.cljs +++ b/src/quo/components/calendar/calendar/view.cljs @@ -8,36 +8,36 @@ [quo.components.calendar.calendar.years-list.view :as years-list] [quo.theme :as theme] [react-native.core :as rn] - [reagent.core :as reagent] [utils.number :as utils.number])) -(defn- view-internal - [] - (let [selected-year (reagent/atom (utils/current-year)) - selected-month (reagent/atom (utils/current-month)) - on-change-year #(reset! selected-year %) - on-change-month (fn [new-date] - (reset! selected-year (utils.number/parse-int (:year new-date))) - (reset! selected-month (utils.number/parse-int (:month new-date))))] - (fn [{:keys [on-change start-date end-date theme]}] - [rn/view - {:style (style/container theme)} - [years-list/view - {:on-change-year on-change-year - :year @selected-year}] - [rn/view - {:style style/container-main} - [month-picker/view - {:year @selected-year - :month @selected-month - :on-change on-change-month}] - [weekdays-header/view] - [days-grid/view - {:year @selected-year - :month @selected-month - :start-date start-date - :end-date end-date - :on-change on-change - :customization-color :blue}]]]))) - -(def view (theme/with-theme view-internal)) +(defn view + [{:keys [on-change start-date end-date]}] + (let [theme (theme/use-theme-value) + [selected-year set-selected-year] (rn/use-state (utils/current-year)) + [selected-month set-selected-month] (rn/use-state (utils/current-month)) + on-change-year (rn/use-callback #(set-selected-year %)) + on-change-month (rn/use-callback + (fn [new-date] + (set-selected-year + (utils.number/parse-int (:year new-date))) + (set-selected-month + (utils.number/parse-int (:month new-date)))))] + [rn/view + {:style (style/container theme)} + [years-list/view + {:on-change-year on-change-year + :year selected-year}] + [rn/view + {:style style/container-main} + [month-picker/view + {:year selected-year + :month selected-month + :on-change on-change-month}] + [weekdays-header/view] + [days-grid/view + {:year selected-year + :month selected-month + :start-date start-date + :end-date end-date + :on-change on-change + :customization-color :blue}]]])) diff --git a/src/quo/components/colors/color_picker/view.cljs b/src/quo/components/colors/color_picker/view.cljs index 84c9445f03..11b22381b1 100644 --- a/src/quo/components/colors/color_picker/view.cljs +++ b/src/quo/components/colors/color_picker/view.cljs @@ -3,13 +3,7 @@ [quo.components.colors.color.constants :as constants] [quo.components.colors.color.view :as color] [quo.foundations.colors :as colors] - [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))) + [react-native.core :as rn])) (defn get-item-layout [_ index] @@ -18,15 +12,34 @@ :offset (* (+ constants/color-size 8) index) :index index}) -(defn- view-internal +(defn view "Options - `default-selected` Default selected color name. - `on-change` Callback called when a color is selected `(fn [color-name])`. - `blur?` Boolean to enable blur background support.}" [{:keys [default-selected blur? on-change feng-shui? container-style]}] - (let [selected (reagent/atom default-selected) - {window-width :width} (rn/get-window) - ref (atom nil)] + (let [[selected set-selected] (rn/use-state default-selected) + {window-width :width} (rn/get-window) + 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 (fn [] (js/setTimeout @@ -40,32 +53,14 @@ :viewPosition 0.5}))))) 50))) [rn/flat-list - {:ref #(reset! ref %) + {:ref on-ref ;; TODO: using :feng-shui? temporarily while b & w is being developed. ;; https://github.com/status-im/status-mobile/discussions/16676 :data (if feng-shui? (conj colors/account-colors :feng-shui) colors/account-colors) - :render-fn (fn [color idx] - [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}]) + :render-fn render-fn :get-item-layout get-item-layout :horizontal true :shows-horizontal-scroll-indicator false :content-container-style container-style}])) - -(defn view - [props] - [:f> view-internal props]) diff --git a/src/quo/components/common/no_flicker_image.cljs b/src/quo/components/common/no_flicker_image.cljs index c2a7e9a422..7949b593e5 100644 --- a/src/quo/components/common/no_flicker_image.cljs +++ b/src/quo/components/common/no_flicker_image.cljs @@ -35,12 +35,11 @@ (defn image "Same as rn/image but cache the image source in a js/Set, so the image won't flicker when re-render on android" - [] - (let [loaded-source (reagent/atom nil) - on-source-loaded #(reset! loaded-source %)] - (fn [props] - (if platform/ios? - [rn/image props] - [:<> - [rn/image (assoc props :source @loaded-source)] - [caching-image props on-source-loaded]])))) + [props] + (let [[loaded-source set-loaded-source] (rn/use-state nil) + on-source-loaded (rn/use-callback #(set-loaded-source %))] + (if platform/ios? + [rn/image props] + [:<> + [rn/image (assoc props :source loaded-source)] + [caching-image props on-source-loaded]]))) diff --git a/src/quo/components/dropdowns/network_dropdown/view.cljs b/src/quo/components/dropdowns/network_dropdown/view.cljs index 35c2b9fbaa..6bce73abdc 100644 --- a/src/quo/components/dropdowns/network_dropdown/view.cljs +++ b/src/quo/components/dropdowns/network_dropdown/view.cljs @@ -3,25 +3,23 @@ [quo.components.dropdowns.network-dropdown.style :as style] [quo.components.list-items.preview-list.view :as preview-list] [quo.theme :as quo.theme] - [react-native.core :as rn] - [reagent.core :as reagent])) + [react-native.core :as rn])) -(defn- internal-view - [_ _] - (let [pressed? (reagent/atom false)] - (fn - [{:keys [on-press state] :as props} networks] - [rn/pressable - {:style (style/dropdown-container (merge props {:pressed? @pressed?})) - :accessibility-label :network-dropdown - :disabled (= state :disabled) - :on-press on-press - :on-press-in (fn [] (reset! pressed? true)) - :on-press-out (fn [] (reset! pressed? false))} - [preview-list/view - {:type :network - :list-size (count networks) - :size :size-20} - networks]]))) - -(def view (quo.theme/with-theme internal-view)) +(defn view + [{: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 + {:style (style/dropdown-container (merge props {:pressed? pressed? :theme theme})) + :accessibility-label :network-dropdown + :disabled (= state :disabled) + :on-press on-press + :on-press-in on-press-in + :on-press-out on-press-out} + [preview-list/view + {:type :network + :list-size (count networks) + :size :size-20} + networks]])) diff --git a/src/quo/components/graph/interactive_graph/view.cljs b/src/quo/components/graph/interactive_graph/view.cljs index 342cfb718e..94c997bec7 100644 --- a/src/quo/components/graph/interactive_graph/view.cljs +++ b/src/quo/components/graph/interactive_graph/view.cljs @@ -14,7 +14,6 @@ (def initial-spacing 56) (def end-spacing 22) (def y-axis-label-width -33) -(def inspecting? (reagent/atom false)) (defn- pointer [customization-color] @@ -32,14 +31,9 @@ :pointer-color customization-color :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 - [state theme] - (if @inspecting? + [state theme inspecting?] + (if inspecting? (colors/theme-colors colors/neutral-80-opa-40 colors/white-opa-20 theme) @@ -51,11 +45,13 @@ colors/danger-60 theme)))) -(defn- view-internal - [{:keys [data state customization-color theme reference-value reference-prefix decimal-separator] +(defn view + [{:keys [data state customization-color reference-value reference-prefix decimal-separator] :or {reference-prefix "$" 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) data) highest-value (utils/find-highest-value data) @@ -64,7 +60,11 @@ max-value (- (utils/calculate-rounded-max highest-value) min-value) step-value (/ max-value 4) 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 colors/white-opa-5 theme) @@ -119,7 +119,7 @@ :show-strip-on-focus true :reference-line-1-config {:color rules-color} :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 lowest-value)) :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)) y-axis-label-text-color) :x-axis-label-texts x-axis-label-texts}]])) - -(def view (quo.theme/with-theme view-internal)) diff --git a/src/quo/components/inputs/address_input/component_spec.cljs b/src/quo/components/inputs/address_input/component_spec.cljs index 7f3d8a49c0..4c38e42977 100644 --- a/src/quo/components/inputs/address_input/component_spec.cljs +++ b/src/quo/components/inputs/address_input/component_spec.cljs @@ -20,7 +20,7 @@ (h/fire-event :on-focus (h/get-by-label-text :address-text-input)) (h/has-prop (h/get-by-label-text :address-text-input) :placeholder-text-color - colors/neutral-40))) + colors/neutral-30))) (h/test "on focus with blur? true" (with-redefs [clipboard/get-string #(% "")] @@ -30,27 +30,22 @@ (h/fire-event :on-focus (h/get-by-label-text :address-text-input)) (h/has-prop (h/get-by-label-text :address-text-input) :placeholder-text-color - colors/neutral-80-opa-40))) + colors/neutral-80-opa-20))) - (h/test "scanned value is properly set" - (let [on-change-text (h/mock-fn) - scanned-value "scanned-value"] + (h/test "default value is properly set" + (let [default-value "default-value"] (with-redefs [clipboard/get-string #(% "")] (h/render [address-input/address-input - {:scanned-value scanned-value - :on-change-text on-change-text - :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) - :default-value - scanned-value))))))) + {:default-value default-value + :ens-regex ens-regex}]) + (h/has-prop (h/get-by-label-text :address-text-input) + :value + default-value)))) (h/test "clear icon is shown when input has text" (with-redefs [clipboard/get-string #(% "")] (h/render [address-input/address-input - {:scanned-value "scanned value" + {:default-value "default value" :ens-regex ens-regex}]) (-> (h/wait-for #(h/get-by-label-text :clear-button-container)) (.then #(h/is-truthy (h/get-by-label-text :clear-button)))))) @@ -58,7 +53,7 @@ (h/test "on blur with text and blur? false" (with-redefs [clipboard/get-string #(% "")] (h/render [address-input/address-input - {:scanned-value "scanned value" + {:default-value "default value" :ens-regex ens-regex}]) (-> (h/wait-for #(h/get-by-label-text :clear-button)) (.then (fn [] @@ -71,7 +66,7 @@ (h/test "on blur with text blur? true" (with-redefs [clipboard/get-string #(% "")] (h/render [address-input/address-input - {:scanned-value "scanned value" + {:default-value "default value" :blur? true :ens-regex ens-regex}]) (-> (h/wait-for #(h/get-by-label-text :clear-button)) @@ -106,7 +101,7 @@ (let [on-clear (h/mock-fn)] (with-redefs [clipboard/get-string #(% "")] (h/render [address-input/address-input - {:scanned-value "scanned value" + {:default-value "default value" :on-clear on-clear :ens-regex ens-regex}]) (-> (h/wait-for #(h/get-by-label-text :clear-button)) @@ -148,7 +143,7 @@ (-> (h/wait-for #(h/get-by-label-text :clear-button)) (.then (fn [] (h/has-prop (h/get-by-label-text :address-text-input) - :default-value + :value clipboard))))))) (h/test "ENS loading state and call on-detect-ens" diff --git a/src/quo/components/inputs/address_input/view.cljs b/src/quo/components/inputs/address_input/view.cljs index 8efd13f346..c5246e6580 100644 --- a/src/quo/components/inputs/address_input/view.cljs +++ b/src/quo/components/inputs/address_input/view.cljs @@ -8,7 +8,6 @@ [react-native.clipboard :as clipboard] [react-native.core :as rn] [react-native.platform :as platform] - [reagent.core :as reagent] [utils.i18n :as i18n])) (defn- icon-color @@ -55,124 +54,135 @@ (and (not= status :default) (not blur?)) (colors/theme-colors colors/neutral-30 colors/neutral-60 theme))) -(defn- f-address-input-internal - [] - (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 - valid-ens-or-address? container-style]}] - (let [on-change (fn [text] - (when (not= @value text) - (let [address? (when address-regex - (boolean (re-matches address-regex text))) - ens? (when ens-regex - (boolean (re-matches ens-regex text)))] - (if (> (count text) 0) - (reset! status :typing) - (reset! status :active)) - (reset! value text) - (when on-change-text - (on-change-text text)) - (when (and on-detect-ens ens?) - (reset! status :loading) - (on-detect-ens text #(reset! status :typing))) - (when (and address? on-detect-address) - (reset! status :loading) - (on-detect-address text)) - (when (and (not address?) - (not ens?) - on-detect-unclassified) - (on-detect-unclassified text))))) - on-paste (fn [] - (clipboard/get-string - (fn [clipboard] - (when-not (empty? clipboard) - (on-change clipboard) - (reset! value clipboard))))) - on-clear (fn [] - (reset! value "") - (reset! status (if @focused? :active :default)) - (when on-change-text - (on-change-text "")) - (when on-clear - (on-clear))) - on-scan #(when on-scan - (on-scan)) - on-focus (fn [] - (when (= (count @value) 0) - (reset! status :active)) - (reset! focused? true) - (when on-focus (on-focus))) - on-blur (fn [] - (when (= @status :active) - (reset! status :default)) - (reset! focused? false) - (when on-blur (on-blur))) - placeholder-text-color (get-placeholder-text-color @status theme blur?)] - (rn/use-effect (fn [] - (when-not (empty? scanned-value) - (on-change scanned-value))) - [scanned-value]) - [rn/view {:style (style/container container-style)} - [rn/text-input - {:accessibility-label :address-text-input - :style (style/input-text theme) - :placeholder (i18n/label :t/name-ens-or-address) - :placeholder-text-color placeholder-text-color - :default-value @value - :auto-complete (when platform/ios? :off) - :auto-capitalize :none - :auto-correct false - :spell-check false - :keyboard-appearance (quo.theme/theme-value :light :dark theme) - :on-focus on-focus - :on-blur on-blur - :on-change-text on-change}] - (when (or (= @status :default) - (= @status :active)) - [rn/view - {:style style/buttons-container - :accessibility-label :paste-scan-buttons-container} - [button/button - {:accessibility-label :paste-button - :type :outline - :size 24 - :container-style {:margin-right 8} - :inner-style (style/accessory-button blur? theme) - :on-press on-paste} - (i18n/label :t/paste)] - [button/button - {:accessibility-label :scan-button - :icon-only? true - :type :outline - :size 24 - :inner-style (style/accessory-button blur? theme) - :on-press on-scan} - :main-icons/scan]]) - (when (= @status :typing) - [rn/view - {:style style/buttons-container - :accessibility-label :clear-button-container} - [clear-button - {:on-press on-clear - :blur? blur? - :theme theme}]]) - (when (and (= @status :loading) (not valid-ens-or-address?)) - [rn/view - {:style style/buttons-container - :accessibility-label :loading-button-container} - [loading-icon blur? theme]]) - (when (and (= @status :loading) valid-ens-or-address?) - [rn/view - {:style style/buttons-container - :accessibility-label :positive-button-container} - [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)) +(defn address-input + [{:keys [default-value 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 + valid-ens-or-address? container-style]}] + (let [theme (quo.theme/use-theme-value) + [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 + (boolean (re-matches address-regex text))) + ens? (when ens-regex + (boolean (re-matches ens-regex text)))] + (reset! value text) + (if (> (count text) 0) + (set-status :typing) + (set-status :active)) + (when on-change-text + (on-change-text text)) + (when (and on-detect-ens ens?) + (set-status :loading) + (on-detect-ens text #(set-status :typing))) + (when (and address? on-detect-address) + (set-status :loading) + (on-detect-address text)) + (when (and (not address?) + (not ens?) + on-detect-unclassified) + (on-detect-unclassified text))))) + 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 + (fn [clipboard] + (when-not (empty? clipboard) + (set-value clipboard)))))) + on-clear (rn/use-callback + (fn [] + (set-value "") + (set-status (if focused? :active :default)) + (when on-change-text + (on-change-text "")) + (when on-clear + (on-clear))) + [focused?]) + on-clear (rn/use-callback + (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) + (set-status :active)) + (set-focused true) + (when on-focus (on-focus)))) + on-blur (rn/use-callback + (fn [] + (when (= status :active) + (set-status :default)) + (set-focused false) + (when on-blur (on-blur))) + [status]) + placeholder-text-color (rn/use-memo #(get-placeholder-text-color status theme blur?) + [status theme blur?])] + (rn/use-mount #(on-change (or default-value ""))) + [rn/view {:style (style/container container-style)} + [rn/text-input + {:accessibility-label :address-text-input + :style (style/input-text theme) + :placeholder (i18n/label :t/name-ens-or-address) + :placeholder-text-color placeholder-text-color + :value @value + :auto-complete (when platform/ios? :off) + :auto-capitalize :none + :auto-correct false + :spell-check false + :keyboard-appearance (quo.theme/theme-value :light :dark theme) + :on-focus on-focus + :on-blur on-blur + :on-change-text on-change}] + (when (or (= status :default) + (= status :active)) + [rn/view + {:style style/buttons-container + :accessibility-label :paste-scan-buttons-container} + [button/button + {:accessibility-label :paste-button + :type :outline + :size 24 + :container-style {:margin-right 8} + :inner-style (style/accessory-button blur? theme) + :on-press on-paste} + (i18n/label :t/paste)] + (when on-scan + [button/button + {:accessibility-label :scan-button + :icon-only? true + :type :outline + :size 24 + :inner-style (style/accessory-button blur? theme) + :on-press on-scan} + :main-icons/scan])]) + (when (= status :typing) + [rn/view + {:style style/buttons-container + :accessibility-label :clear-button-container} + [clear-button + {:on-press on-clear + :blur? blur? + :theme theme}]]) + (when (and (= status :loading) (not valid-ens-or-address?)) + [rn/view + {:style style/buttons-container + :accessibility-label :loading-button-container} + [loading-icon blur? theme]]) + (when (and (= status :loading) valid-ens-or-address?) + [rn/view + {:style style/buttons-container + :accessibility-label :positive-button-container} + [positive-state-icon theme]])])) diff --git a/src/quo/components/inputs/input/view.cljs b/src/quo/components/inputs/input/view.cljs index 5d792bd079..a5e2b880e9 100644 --- a/src/quo/components/inputs/input/view.cljs +++ b/src/quo/components/inputs/input/view.cljs @@ -5,8 +5,7 @@ [quo.components.markdown.text :as text] [quo.theme :as quo.theme] [react-native.core :as rn] - [react-native.platform :as platform] - [reagent.core :as reagent])) + [react-native.platform :as platform])) (defn- label-&-counter [{:keys [label current-chars char-limit variant-colors]}] @@ -61,99 +60,104 @@ :container-style]) (defn- base-input - [{:keys [on-change-text on-char-limit-reach weight default-value]}] - (let [status (reagent/atom :default) - internal-on-focus #(reset! status :focus) - internal-on-blur #(reset! status :default) - multiple-lines? (reagent/atom false) - set-multiple-lines! #(let [height (oops/oget % "nativeEvent.contentSize.height") - ;; In Android height comes with padding - min-height (if platform/android? 40 22)] - (if (> height min-height) - (reset! multiple-lines? true) - (reset! multiple-lines? false))) - char-count (reagent/atom (count default-value)) - update-char-limit! (fn [new-text char-limit] - (when on-change-text (on-change-text new-text)) - (let [amount-chars (count new-text)] - (reset! char-count amount-chars) - (when (and (>= amount-chars char-limit) on-char-limit-reach) - (on-char-limit-reach amount-chars))))] - (fn [{:keys [blur? theme error? right-icon left-icon disabled? small? button - label char-limit multiline? clearable? on-focus on-blur container-style] - :as props}] - (let [status-kw (cond - disabled? :disabled - error? :error - :else @status) - colors-by-status (style/status-colors status-kw blur? theme) - variant-colors (style/variants-colors blur? theme) - clean-props (apply dissoc props custom-props)] - [rn/view {:style container-style} - (when (or label char-limit) - [label-&-counter - {:variant-colors variant-colors - :label label - :current-chars @char-count - :char-limit char-limit}]) - [rn/view {:style (style/input-container colors-by-status small? disabled?)} - (when-let [{:keys [icon-name]} left-icon] - [left-accessory - {:variant-colors variant-colors - :small? small? - :icon-name icon-name}]) - [rn/text-input - (cond-> {:style (style/input colors-by-status small? @multiple-lines? weight) - :accessibility-label :input - :placeholder-text-color (:placeholder colors-by-status) - :keyboard-appearance (quo.theme/theme-value :light :dark theme) - :cursor-color (:cursor variant-colors) - :editable (not disabled?) - :on-focus (fn [] - (when on-focus (on-focus)) - (internal-on-focus)) - :on-blur (fn [] - (when on-blur (on-blur)) - (internal-on-blur))} - :always (merge clean-props) - multiline? (assoc :multiline true - :on-content-size-change set-multiple-lines!) - char-limit (assoc :on-change-text #(update-char-limit! % char-limit)))] - (when-let [{:keys [on-press icon-name style-fn]} right-icon] - [right-accessory - {:variant-colors variant-colors - :small? small? - :disabled? disabled? - :icon-style-fn style-fn - :icon-name icon-name - :on-press (fn [] - (when clearable? (reset! char-count 0)) - (on-press))}]) - (when-let [{:keys [on-press text]} button] - [right-button - {:colors-by-status colors-by-status - :variant-colors variant-colors - :small? small? - :disabled? disabled? - :on-press on-press - :text text}])]])))) + [{:keys [blur? theme error? right-icon left-icon disabled? small? button + label char-limit multiline? clearable? on-focus on-blur container-style + on-change-text on-char-limit-reach weight default-value] + :as props}] + (let [[status set-status] (rn/use-state :default) + 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 + min-height (if platform/android? 40 22)] + (if (> height min-height) + (set-multiple-lines true) + (set-multiple-lines false))))) + [char-count + 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)) + (let [amount-chars (count new-text)] + (set-char-count amount-chars) + (when (and (>= amount-chars char-limit) on-char-limit-reach) + (on-char-limit-reach amount-chars))))) + status-kw (cond + disabled? :disabled + error? :error + :else status) + colors-by-status (style/status-colors status-kw blur? theme) + variant-colors (style/variants-colors blur? theme) + clean-props (apply dissoc props custom-props)] + [rn/view {:style container-style} + (when (or label char-limit) + [label-&-counter + {:variant-colors variant-colors + :label label + :current-chars char-count + :char-limit char-limit}]) + [rn/view {:style (style/input-container colors-by-status small? disabled?)} + (when-let [{:keys [icon-name]} left-icon] + [left-accessory + {:variant-colors variant-colors + :small? small? + :icon-name icon-name}]) + [rn/text-input + (cond-> {:style (style/input colors-by-status small? multiple-lines? weight) + :accessibility-label :input + :placeholder-text-color (:placeholder colors-by-status) + :keyboard-appearance (quo.theme/theme-value :light :dark theme) + :cursor-color (:cursor variant-colors) + :editable (not disabled?) + :on-focus (fn [] + (when on-focus (on-focus)) + (internal-on-focus)) + :on-blur (fn [] + (when on-blur (on-blur)) + (internal-on-blur))} + :always (merge clean-props) + multiline? (assoc :multiline true + :on-content-size-change on-content-size-change) + char-limit (assoc :on-change-text on-change-text))] + (when-let [{:keys [on-press icon-name style-fn]} right-icon] + [right-accessory + {:variant-colors variant-colors + :small? small? + :disabled? disabled? + :icon-style-fn style-fn + :icon-name icon-name + :on-press (fn [] + (when clearable? (set-char-count 0)) + (on-press))}]) + (when-let [{:keys [on-press text]} button] + [right-button + {:colors-by-status colors-by-status + :variant-colors variant-colors + :small? small? + :disabled? disabled? + :on-press on-press + :text text}])]])) (defn- password-input [{:keys [default-shown?] - :or {default-shown? false}}] - (let [password-shown? (reagent/atom default-shown?)] - (fn [props] - [base-input - (assoc props - :accessibility-label :password-input - :auto-capitalize :none - :auto-complete :password - :secure-text-entry (not @password-shown?) - :right-icon {:style-fn style/password-icon - :icon-name (if @password-shown? :i/hide-password :i/reveal) - :on-press #(swap! password-shown? not)})]))) + :or {default-shown? false} + :as props}] + (let [[password-shown? set-password-shown] (rn/use-state default-shown?)] + [base-input + (assoc props + :accessibility-label :password-input + :auto-capitalize :none + :auto-complete :password + :secure-text-entry (not password-shown?) + :right-icon {:style-fn style/password-icon + :icon-name (if password-shown? :i/hide-password :i/reveal) + :on-press #(set-password-shown (not password-shown?))})])) -(defn input-internal +(defn input "This input supports the following properties: - :type - Can be `:text`(default) or `:password`. - :blur? - Boolean to set the blur color variant. @@ -177,21 +181,15 @@ - :on-change-text ... " - [{:keys [type clearable? on-clear on-change-text icon-name] + [{:keys [type clearable? on-clear icon-name] :or {type :text} :as props}] (let [base-props (cond-> props - icon-name (assoc-in [:left-icon :icon-name] icon-name) - clearable? (assoc :right-icon - {:style-fn style/clear-icon - :icon-name :i/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))))] + icon-name (assoc-in [:left-icon :icon-name] icon-name) + clearable? (assoc :right-icon + {:style-fn style/clear-icon + :icon-name :i/clear + :on-press #(when on-clear (on-clear))}))] (if (= type :password) [password-input base-props] [base-input base-props]))) - -(def input (quo.theme/with-theme input-internal)) diff --git a/src/quo/components/inputs/recovery_phrase/view.cljs b/src/quo/components/inputs/recovery_phrase/view.cljs index 706e4d2a47..382a1daf73 100644 --- a/src/quo/components/inputs/recovery_phrase/view.cljs +++ b/src/quo/components/inputs/recovery_phrase/view.cljs @@ -3,8 +3,7 @@ [clojure.string :as string] [quo.components.inputs.recovery-phrase.style :as style] [quo.theme :as quo.theme] - [react-native.core :as rn] - [reagent.core :as reagent])) + [react-native.core :as rn])) (def ^:private custom-props [:customization-color :theme :blur? :cursor-color :multiline :on-focus :on-blur @@ -38,42 +37,42 @@ :idx 0}) :result))) -(defn recovery-phrase-input-internal - [_ _] - (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 - container-style] - :or {customization-color :blue - word-limit ##Inf - error-pred-current-word (constantly false) - error-pred-written-words (constantly false)} - :as props} - text] - (let [extra-props (apply dissoc props custom-props)] - [rn/view {:style (style/container container-style)} - [rn/text-input - (merge {:accessibility-label :recovery-phrase-input - :style (style/input) - :placeholder-text-color (style/placeholder-color @state theme blur?) - :cursor-color (style/cursor-color customization-color theme) - :keyboard-appearance (quo.theme/theme-value :light :dark theme) - :multiline true - :on-focus (fn [] - (set-focused) - (when on-focus (on-focus))) - :on-blur (fn [] - (set-default) - (when on-blur (on-blur)))} - extra-props) - (if mark-errors? - (mark-error-words {:pred-last-word error-pred-current-word - :pred-previous-words error-pred-written-words - :text text - :word-limit word-limit - :theme theme}) - text)]])))) - -(def recovery-phrase-input (quo.theme/with-theme recovery-phrase-input-internal)) +(defn recovery-phrase-input + [{:keys [customization-color blur? on-focus on-blur mark-errors? + error-pred-current-word error-pred-written-words word-limit + container-style] + :or {customization-color :blue + word-limit ##Inf + error-pred-current-word (constantly false) + error-pred-written-words (constantly false)} + :as props} + text] + (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/text-input + (merge {:accessibility-label :recovery-phrase-input + :style (style/input) + :placeholder-text-color (style/placeholder-color state theme blur?) + :cursor-color (style/cursor-color customization-color theme) + :keyboard-appearance (quo.theme/theme-value :light :dark theme) + :multiline true + :on-focus on-focus + :on-blur on-blur} + extra-props) + (if mark-errors? + (mark-error-words {:pred-last-word error-pred-current-word + :pred-previous-words error-pred-written-words + :text text + :word-limit word-limit + :theme theme}) + text)]])) diff --git a/src/quo/components/inputs/search_input/view.cljs b/src/quo/components/inputs/search_input/view.cljs index 53b16106ba..133dabc2ab 100644 --- a/src/quo/components/inputs/search_input/view.cljs +++ b/src/quo/components/inputs/search_input/view.cljs @@ -4,8 +4,7 @@ [quo.components.icon :as icon] [quo.components.inputs.search-input.style :as style] [quo.foundations.colors :as colors] - [react-native.core :as rn] - [reagent.core :as reagent])) + [react-native.core :as rn])) (def ^:private tag-separator [rn/view {:style style/tag-separator}]) @@ -41,54 +40,52 @@ text-input)) (defn search-input - [{:keys [value]}] - (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] - :or {customization-color :blue} - :as props} - & children] - (let [clean-props (apply dissoc props props-to-remove)] - [rn/view - {:accessibility-label :search-input - :style (style/container container-style)} - [rn/scroll-view - {:ref #(reset! scroll-view-ref %) - :style style/scroll-container - :content-container-style style/scroll-content - :horizontal true - :shows-horizontal-scroll-indicator false} - (when (seq tags) - [inner-tags tags]) - - (add-children - [rn/text-input - (cond-> {:style (style/input-text disabled?) - :cursor-color (style/cursor customization-color override-theme) - :placeholder-text-color (style/placeholder-color @state blur? override-theme) - :editable (not disabled?) - :on-key-press #(handle-backspace % @scroll-view-ref) - :keyboard-appearance (colors/theme-colors :light :dark override-theme) - :on-change-text (fn [new-text] - (when on-change-text - (on-change-text new-text)) - (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) - (seq clean-props) (merge clean-props))] - (when-not use-value? children))] - - (when (or (seq value) (seq children)) - [clear-button - {:on-press on-clear - :blur? blur? - :override-theme override-theme}])])))) + [{:keys [value tags disabled? blur? on-change-text customization-color + on-clear on-focus on-blur override-theme container-style] + :or {customization-color :blue} + :as props} + & children] + (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 + {:accessibility-label :search-input + :style (style/container container-style)} + [rn/scroll-view + {:ref on-scroll-view-ref + :style style/scroll-container + :content-container-style style/scroll-content + :horizontal true + :shows-horizontal-scroll-indicator false} + (when (seq tags) + [inner-tags tags]) + (add-children + [rn/text-input + (cond-> {:style (style/input-text disabled?) + :cursor-color (style/cursor customization-color override-theme) + :placeholder-text-color (style/placeholder-color state blur? override-theme) + :editable (not disabled?) + :on-key-press on-key-press + :keyboard-appearance (colors/theme-colors :light :dark override-theme) + :on-change-text on-change-text + :on-focus on-focus + :on-blur on-blur} + use-value? (assoc :value value) + (seq clean-props) (merge clean-props))] + (when-not use-value? children))] + (when (or (seq value) (seq children)) + [clear-button + {:on-press on-clear + :blur? blur? + :override-theme override-theme}])])) diff --git a/src/quo/components/inputs/title_input/view.cljs b/src/quo/components/inputs/title_input/view.cljs index 17c84ae30c..e2d3864fea 100644 --- a/src/quo/components/inputs/title_input/view.cljs +++ b/src/quo/components/inputs/title_input/view.cljs @@ -4,8 +4,7 @@ [quo.components.inputs.title-input.style :as style] [quo.components.markdown.text :as text] [quo.theme :as quo.theme] - [react-native.core :as rn] - [reagent.core :as reagent])) + [react-native.core :as rn])) (defn- pad-0 [value] @@ -13,81 +12,70 @@ (str 0 value) value)) -(defn- view-internal - [{:keys [blur? - on-change-text - auto-focus - placeholder - max-length - default-value - return-key-type - size - theme - on-focus - on-blur - container-style] +(defn view + [{:keys [blur? on-change-text auto-focus placeholder max-length default-value return-key-type + size on-focus on-blur container-style customization-color disabled?] :or {max-length 0 auto-focus false default-value ""}}] - (let [focused? (reagent/atom auto-focus) - value (reagent/atom default-value) - input-ref (atom nil) - on-change (fn [v] - (reset! value v) - (when on-change-text - (on-change-text v)))] - (fn [{:keys [customization-color disabled?]}] - [rn/view - {:style (merge (style/container disabled?) container-style)} - [rn/view {:style style/text-input-container} - [rn/text-input - {:style - (text/text-style - {:size (or size :heading-1) - :weight :semi-bold - :style (style/title-text theme)}) - :default-value default-value - :accessibility-label :profile-title-input - :keyboard-appearance (quo.theme/theme-value :light :dark theme) - :return-key-type return-key-type - :on-focus (fn [] - (when (fn? on-focus) - (on-focus)) - (reset! focused? true)) - :on-blur (fn [] - (when (fn? on-blur) - (on-blur)) - (reset! focused? false)) - :auto-focus auto-focus - :input-mode :text - :on-change-text on-change - :editable (not disabled?) - :max-length max-length - :placeholder placeholder - :ref #(reset! input-ref %) - :selection-color (style/get-selection-color customization-color blur? theme) - :placeholder-text-color (if @focused? - (style/get-focused-placeholder-color blur? theme) - (style/get-placeholder-color blur? theme))}]] - [rn/view - {:style (style/counter-container @focused?)} - (if @focused? - [text/text - [text/text - {:style (style/char-count blur? theme) - :size :paragraph-2} - (pad-0 - (str - (count @value)))] - [text/text - {:style (style/char-count blur? theme) - :size :paragraph-2} - (str "/" - (pad-0 - (str max-length)))]] - [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)) + (let [theme (quo.theme/use-theme-value) + [focused? set-focused] (rn/use-state auto-focus) + [value set-value] (rn/use-state default-value) + input-ref (rn/use-ref-atom nil) + on-inpur-ref (rn/use-callback #(reset! input-ref %)) + on-press (rn/use-callback + #(when-not disabled? (.focus ^js @input-ref)) + [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 + {:style (merge (style/container disabled?) container-style)} + [rn/view {:style style/text-input-container} + [rn/text-input + {:style (text/text-style + {:size (or size :heading-1) + :weight :semi-bold + :style (style/title-text theme)}) + :default-value default-value + :accessibility-label :profile-title-input + :keyboard-appearance (quo.theme/theme-value :light :dark theme) + :return-key-type return-key-type + :on-focus on-focus + :on-blur on-blur + :auto-focus auto-focus + :input-mode :text + :on-change-text on-change + :editable (not disabled?) + :max-length max-length + :placeholder placeholder + :ref on-inpur-ref + :selection-color (style/get-selection-color customization-color blur? theme) + :placeholder-text-color (if focused? + (style/get-focused-placeholder-color blur? theme) + (style/get-placeholder-color blur? theme))}]] + [rn/view + {:style (style/counter-container focused?)} + (if focused? + [text/text + [text/text + {:style (style/char-count blur? theme) + :size :paragraph-2} + (pad-0 + (str + (count value)))] + [text/text + {:style (style/char-count blur? theme) + :size :paragraph-2} + (str "/" (pad-0 (str max-length)))]] + [rn/pressable {:on-press on-press} + [icon/icon :i/edit {:color (style/get-char-count-color blur? theme)}]])]])) diff --git a/src/status_im/contexts/preview/quo/inputs/address_input.cljs b/src/status_im/contexts/preview/quo/inputs/address_input.cljs index 8423b8ccb9..bcc48390be 100644 --- a/src/status_im/contexts/preview/quo/inputs/address_input.cljs +++ b/src/status_im/contexts/preview/quo/inputs/address_input.cljs @@ -25,8 +25,8 @@ :blur? (:blur? @state) :show-blur-background? true} [quo/address-input - (merge @state - {:on-scan #(js/alert "Not implemented yet") + (merge (dissoc @state :scanned-value) + {:on-scan (fn [on-result] (on-result (:scanned-value @state))) :ens-regex constants/regx-ens :on-detect-ens (fn [_] (swap! state assoc :valid-ens-or-address? false) diff --git a/src/status_im/contexts/wallet/add_address_to_watch/component_spec.cljs b/src/status_im/contexts/wallet/add_address_to_watch/component_spec.cljs index 2ba18fd98a..c12b109dfe 100644 --- a/src/status_im/contexts/wallet/add_address_to_watch/component_spec.cljs +++ b/src/status_im/contexts/wallet/add_address_to_watch/component_spec.cljs @@ -21,10 +21,14 @@ (h/fire-event :change-text (h/get-by-label-text :add-address-to-watch) "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/render [add-address-to-watch/view]) (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/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)))))))