From aff4eb89cd6de2e7c275706b50cb4a6177349d1c Mon Sep 17 00:00:00 2001 From: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com> Date: Thu, 13 Jun 2024 19:28:41 +0530 Subject: [PATCH] feat(wallet): implement search in saved addresses list (#20443) This commit adds a feature to search through the saved addresses list by name, address, ENS and chain names. Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com> --- .../components/inputs/search_input/style.cljs | 26 ++-- .../components/inputs/search_input/view.cljs | 30 +++-- .../text_combinations/page_top/style.cljs | 3 +- .../wallet/saved_addresses/style.cljs | 11 -- .../settings/wallet/saved_addresses/view.cljs | 120 +++++++++++++----- .../subs/wallet/saved_addresses.cljs | 22 ++++ 6 files changed, 140 insertions(+), 72 deletions(-) diff --git a/src/quo/components/inputs/search_input/style.cljs b/src/quo/components/inputs/search_input/style.cljs index fa1f641840..5d5495ec92 100644 --- a/src/quo/components/inputs/search_input/style.cljs +++ b/src/quo/components/inputs/search_input/style.cljs @@ -4,19 +4,19 @@ [quo.foundations.colors :as colors])) (defn placeholder-color - [state blur? override-theme] + [state blur? theme] (cond (and blur? (= state :active)) - (colors/theme-colors colors/neutral-80-opa-20 colors/white-opa-20 override-theme) + (colors/theme-colors colors/neutral-80-opa-20 colors/white-opa-20 theme) blur? ; state is default - (colors/theme-colors colors/neutral-80-opa-40 colors/white-opa-30 override-theme) + (colors/theme-colors colors/neutral-80-opa-40 colors/white-opa-30 theme) (= state :active) - (colors/theme-colors colors/neutral-30 colors/neutral-60 override-theme) + (colors/theme-colors colors/neutral-30 colors/neutral-60 theme) :else ; Not blur and state is default - (colors/theme-colors colors/neutral-40 colors/neutral-50 override-theme))) + (colors/theme-colors colors/neutral-40 colors/neutral-50 theme))) (def clear-icon-container {:justify-content :center @@ -26,16 +26,14 @@ :width 20}) (defn clear-icon - [blur? override-theme] + [blur? theme] (if blur? - (colors/theme-colors colors/neutral-80-opa-30 colors/white-opa-10 override-theme) - (colors/theme-colors colors/neutral-40 colors/neutral-60 override-theme))) + (colors/theme-colors colors/neutral-80-opa-30 colors/white-opa-10 theme) + (colors/theme-colors colors/neutral-40 colors/neutral-60 theme))) (defn cursor - [customization-color override-theme] - (colors/theme-colors (colors/custom-color customization-color 50) - (colors/custom-color customization-color 60) - override-theme)) + [customization-color theme] + (colors/resolve-color customization-color theme)) (def tag-separator {:width 8}) @@ -61,10 +59,10 @@ :justify-content :flex-start}) (defn input-text - [disabled?] + [disabled? theme] (assoc (text/text-style {:size :paragraph-1 :weight :regular} - nil) + theme) :flex 1 :padding-vertical 5 :min-width 120 diff --git a/src/quo/components/inputs/search_input/view.cljs b/src/quo/components/inputs/search_input/view.cljs index 133dabc2ab..0aedadcd35 100644 --- a/src/quo/components/inputs/search_input/view.cljs +++ b/src/quo/components/inputs/search_input/view.cljs @@ -4,6 +4,7 @@ [quo.components.icon :as icon] [quo.components.inputs.search-input.style :as style] [quo.foundations.colors :as colors] + [quo.theme] [react-native.core :as rn])) (def ^:private tag-separator [rn/view {:style style/tag-separator}]) @@ -15,12 +16,12 @@ tags-coll)) (defn- clear-button - [{:keys [on-press blur? override-theme]}] + [{:keys [on-press blur? theme]}] [rn/touchable-opacity {:style style/clear-icon-container :on-press on-press} [icon/icon :i/clear - {:color (style/clear-icon blur? override-theme) + {:color (style/clear-icon blur? theme) :size 20}]]) (defn- handle-backspace @@ -31,7 +32,7 @@ (def ^:private props-to-remove [:cursor-color :placeholder-text-color :editable :on-change-text :on-focus - :on-blur :on-clear :value :tags :disabled? :blur? :customization-color :override-theme]) + :on-blur :on-clear :value :tags :disabled? :blur? :customization-color]) (defn- add-children [text-input children] @@ -40,12 +41,13 @@ text-input)) (defn search-input - [{:keys [value tags disabled? blur? on-change-text customization-color - on-clear on-focus on-blur override-theme container-style] + [{:keys [value tags disabled? blur? on-change-text customization-color show-clear-button? + on-clear on-focus on-blur container-style] :or {customization-color :blue} :as props} & children] - (let [[state set-state] (rn/use-state :default) + (let [theme (quo.theme/use-theme) + [state set-state] (rn/use-state :default) on-focus (rn/use-callback (fn [] (set-state :active) @@ -72,20 +74,20 @@ [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) + (cond-> {:style (style/input-text disabled? theme) + :cursor-color (style/cursor customization-color theme) + :placeholder-text-color (style/placeholder-color state blur? theme) :editable (not disabled?) :on-key-press on-key-press - :keyboard-appearance (colors/theme-colors :light :dark override-theme) + :keyboard-appearance (colors/theme-colors :light :dark 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)) + (when (or (seq value) (seq children) show-clear-button?) [clear-button - {:on-press on-clear - :blur? blur? - :override-theme override-theme}])])) + {:on-press on-clear + :blur? blur? + :theme theme}])])) diff --git a/src/quo/components/text_combinations/page_top/style.cljs b/src/quo/components/text_combinations/page_top/style.cljs index 77c288c478..483a325fdd 100644 --- a/src/quo/components/text_combinations/page_top/style.cljs +++ b/src/quo/components/text_combinations/page_top/style.cljs @@ -70,7 +70,8 @@ (colors/theme-colors colors/neutral-10 colors/neutral-80 theme))})) (def search-input-container - {:margin-top 8 + {:flex 0 + :margin-top 8 :margin-bottom 12 :margin-horizontal 20}) diff --git a/src/status_im/contexts/settings/wallet/saved_addresses/style.cljs b/src/status_im/contexts/settings/wallet/saved_addresses/style.cljs index f3d7015ad3..0943e597df 100644 --- a/src/status_im/contexts/settings/wallet/saved_addresses/style.cljs +++ b/src/status_im/contexts/settings/wallet/saved_addresses/style.cljs @@ -1,16 +1,5 @@ (ns status-im.contexts.settings.wallet.saved-addresses.style) -(def title-container - {:flex 0 - :padding-horizontal 20 - :margin-top 12 - :margin-bottom 16}) - -(defn page-wrapper - [inset-top] - {:padding-top inset-top - :flex 1}) - (def empty-container-style {:justify-content :center :flex 1}) diff --git a/src/status_im/contexts/settings/wallet/saved_addresses/view.cljs b/src/status_im/contexts/settings/wallet/saved_addresses/view.cljs index 2c6d66237a..0d5fe39219 100644 --- a/src/status_im/contexts/settings/wallet/saved_addresses/view.cljs +++ b/src/status_im/contexts/settings/wallet/saved_addresses/view.cljs @@ -1,17 +1,20 @@ (ns status-im.contexts.settings.wallet.saved-addresses.view (:require + [clojure.string :as string] + [oops.core :as oops] [quo.core :as quo] [quo.foundations.colors :as colors] [quo.theme :as quo.theme] [react-native.core :as rn] - [react-native.safe-area :as safe-area] + [react-native.platform :as platform] [status-im.common.resources :as resources] [status-im.contexts.settings.wallet.saved-addresses.sheets.address-options.view :as address-options] [status-im.contexts.settings.wallet.saved-addresses.style :as style] + [utils.debounce :as debounce] [utils.i18n :as i18n] [utils.re-frame :as rf])) -(defn- empty-state +(defn- empty-list [] (let [theme (quo.theme/use-theme)] [quo/empty-state @@ -20,6 +23,15 @@ :image (resources/get-themed-image :sweating-man theme) :container-style style/empty-container-style}])) +(defn- empty-result + [] + (let [theme (quo.theme/use-theme)] + [quo/empty-state + {:title (i18n/label :t/nothing-found) + :description (i18n/label :t/try-to-search-something-else) + :image (resources/get-themed-image :no-contacts theme) + :container-style style/empty-container-style}])) + (defn- saved-address [{:keys [name address chain-short-names customization-color ens? ens network-preferences-names]}] (let [full-address (str chain-short-names address) @@ -50,15 +62,36 @@ :on-press on-press-saved-address}])) (defn- header - [{:keys [title]}] + [{:keys [title index]}] [quo/divider-label - {:tight? true - :blur? true} + {:tight? true + :blur? true + :container-style (when (pos? index) {:margin-top 8})} title]) -(defn- footer - [] - [rn/view {:height 8}]) +(defn- filtered-list + [{:keys [search-text]}] + [rn/flat-list + {:key-fn :address + :data (rf/sub [:wallet/filtered-saved-addresses search-text]) + :render-fn saved-address + :shows-vertical-scroll-indicator false + :keyboard-should-persist-taps :always + :content-container-style {:flex-grow 1} + :empty-component [empty-result]}]) + +(defn- unfiltered-list + [{:keys [grouped-saved-addresses]}] + [rn/section-list + {:key-fn :title + :shows-vertical-scroll-indicator false + :sticky-section-headers-enabled false + :keyboard-should-persist-taps :always + :render-section-header-fn header + :sections grouped-saved-addresses + :render-fn saved-address + :content-container-style {:flex-grow 1} + :empty-component [empty-list]}]) (defn- navigate-back [] @@ -70,33 +103,56 @@ (defn view [] - (let [inset-top (safe-area/get-top) - customization-color (rf/sub [:profile/customization-color]) - saved-addresses (rf/sub [:wallet/grouped-saved-addresses])] + (let [alert-banners-top-margin (rf/sub [:alert-banners/top-margin]) + customization-color (rf/sub [:profile/customization-color]) + has-saved-addresses? (rf/sub [:wallet/has-saved-addresses?]) + grouped-saved-addresses (rf/sub [:wallet/grouped-saved-addresses]) + input-ref (rn/use-ref-atom nil) + [search-text set-search-text] (rn/use-state "") + set-input-ref (rn/use-callback #(reset! input-ref %)) + on-clear-input (rn/use-callback + (fn [] + (some-> @input-ref + (oops/ocall "clear")) + (set-search-text ""))) + on-change-text (rn/use-callback + (debounce/debounce + #(set-search-text %) + 500)) + search-active? (not (string/blank? search-text)) + page-top-props (rn/use-memo + #(cond-> {:title (i18n/label :t/saved-addresses) + :accessibility-label :saved-addresses-header + :title-right :action + :title-right-props {:icon :i/add + :customization-color + customization-color + :on-press add-address-to-save} + :blur? true} + + has-saved-addresses? + (assoc + :input :search + :input-props {:placeholder (i18n/label + :t/name-ens-or-address) + :ref set-input-ref + :on-change-text on-change-text + :show-clear-button? search-active? + :on-clear on-clear-input + :customization-color customization-color})) + [has-saved-addresses? customization-color search-text])] [quo/overlay - {:type :shell - :container-style (style/page-wrapper inset-top)} + {:type :shell + :top-inset? true} [quo/page-nav {:key :header :background :blur :icon-name :i/arrow-left :on-press navigate-back}] - [quo/standard-title - {:title (i18n/label :t/saved-addresses) - :accessibility-label :saved-addresses-header - :right :action - :on-press add-address-to-save - :customization-color customization-color - :icon :i/add - :container-style style/title-container}] - [rn/section-list - {:key-fn :title - :shows-vertical-scroll-indicator false - :sticky-section-headers-enabled false - :render-section-header-fn header - :render-section-footer-fn footer - :sections saved-addresses - :render-fn saved-address - :separator [rn/view {:style {:height 4}}] - :content-container-style {:flex-grow 1} - :empty-component [empty-state]}]])) + [quo/page-top page-top-props] + [rn/keyboard-avoiding-view + {:style {:flex 1} + :keyboard-vertical-offset (if platform/ios? alert-banners-top-margin 0)} + (if search-active? + [filtered-list {:search-text search-text}] + [unfiltered-list {:grouped-saved-addresses grouped-saved-addresses}])]])) diff --git a/src/status_im/subs/wallet/saved_addresses.cljs b/src/status_im/subs/wallet/saved_addresses.cljs index 33e18a3374..b4e977557a 100644 --- a/src/status_im/subs/wallet/saved_addresses.cljs +++ b/src/status_im/subs/wallet/saved_addresses.cljs @@ -26,6 +26,12 @@ (fn [saved-addresses [_ address]] (contains? saved-addresses address))) +(rf/reg-sub + :wallet/has-saved-addresses? + :<- [:wallet/saved-addresses-by-network-mode] + (fn [saved-addresses] + (-> saved-addresses vals boolean))) + (rf/reg-sub :wallet/grouped-saved-addresses :<- [:wallet/saved-addresses-by-network-mode] @@ -49,3 +55,19 @@ :<- [:wallet/saved-addresses-by-network-mode] (fn [saved-addresses [_ address]] (get saved-addresses address))) + +(rf/reg-sub + :wallet/filtered-saved-addresses + :<- [:wallet/saved-addresses-by-network-mode] + (fn [saved-addresses [_ query]] + (->> saved-addresses + vals + (sort-by :name) + (filter + (fn [{:keys [name address ens chain-short-names]}] + (let [lowercase-query (string/lower-case query)] + (or + (string/includes? (string/lower-case name) lowercase-query) + (string/includes? address lowercase-query) + (string/includes? ens lowercase-query) + (string/includes? chain-short-names lowercase-query))))))))