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>
This commit is contained in:
parent
60b3d01ceb
commit
aff4eb89cd
|
@ -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
|
||||
|
|
|
@ -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}])]))
|
||||
|
|
|
@ -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})
|
||||
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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}])]]))
|
||||
|
|
|
@ -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))))))))
|
||||
|
|
Loading…
Reference in New Issue