Wallet: receive screen (#18167)

* wallet: receive screen
This commit is contained in:
Omar Basem 2023-12-21 22:07:27 +04:00 committed by GitHub
parent ad8d537b9c
commit 4f9544d20c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 282 additions and 159 deletions

View File

@ -12,9 +12,10 @@
(defn description
[theme blur?]
{:color (if blur?
colors/white-opa-40
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))})
{:color (if blur?
colors/white-opa-40
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))
:margin-top 2})
(def left-container
{:margin-right 8

View File

@ -160,7 +160,8 @@
[rn/view {:style style/title-container}
[text/text
{:size :heading-2
:weight :semi-bold}
:weight :semi-bold
:style {:color (when blur? colors/white)}}
title]
(when title-icon
[icons/icon title-icon

View File

@ -3,23 +3,22 @@
[quo.components.markdown.text :as text]
[quo.components.settings.category.style :as style]
[quo.components.settings.settings-item.view :as settings-item]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]))
(defn- category-internal
[{:keys [label data blur? container-style theme]}]
[rn/view {:style (merge (style/container label) container-style)}
[{:keys [label data] :as props}]
[rn/view {:style (style/container label)}
(when label
[text/text
{:weight :medium
:size :paragraph-2
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}}
:style (style/label props)}
label])
[rn/flat-list
{:data data
:style (style/settings-items theme blur?)
:render-fn (fn [item] [settings-item/view item])
:separator [rn/view {:style (style/settings-separator theme blur?)}]}]])
:style (style/settings-items props)
:render-fn settings-item/view
:separator [rn/view {:style (style/settings-separator props)}]}]])
(def settings-category (quo.theme/with-theme category-internal))

View File

@ -11,7 +11,7 @@
:padding-bottom 8})
(defn settings-items
[theme blur?]
[{:keys [blur? theme]}]
{:margin-top 12
:border-radius 16
:background-color (if blur?
@ -22,11 +22,17 @@
colors/white-opa-5
(colors/theme-colors colors/neutral-10 colors/neutral-80 theme))})
(defn label
[{:keys [blur? theme]}]
{:color (if blur?
colors/white-opa-40
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))})
(def reorder-items
{:margin-top 12})
(defn settings-separator
[theme blur?]
[{:keys [blur? theme]}]
{:height 1
:background-color (if blur?
colors/white-opa-5

View File

@ -10,6 +10,9 @@
:padding-horizontal (when (= size :default) 12)
:border-radius 16
:border-width (when (and card? (not= size :small)) 1)
:background-color (if blur?
colors/white-opa-5
(colors/theme-colors colors/white colors/neutral-95 theme))
:border-color (if blur?
colors/white-opa-10
(colors/theme-colors colors/neutral-10
@ -42,10 +45,12 @@
:justify-content :center})
(defn title
[theme]
{:color (colors/theme-colors colors/neutral-50
colors/neutral-40
theme)
[blur? theme]
{:color (if blur?
colors/white-opa-40
(colors/theme-colors colors/neutral-50
colors/neutral-40
theme))
:margin-right 4})
(def title-container

View File

@ -44,18 +44,18 @@
subtitle]])
(defn- left-title
[{:keys [title label size theme]}]
[{:keys [title label size blur? theme]}]
[rn/view {:style style/title-container}
[text/text
{:weight :regular
:size :paragraph-2
:style (style/title theme)}
:style (style/title blur? theme)}
title]
(when (and (= :graph label) (not= :small size))
[text/text
{:weight :regular
:size :label
:style (style/title theme)}
:style (style/title blur? theme)}
(i18n/label :t/days)])])
(defn- left-side
@ -68,6 +68,7 @@
{:title title
:label label
:size size
:blur? blur?
:theme theme}]
(if (= status :loading)
[left-loading

View File

@ -10,6 +10,7 @@
[quo.components.settings.settings-item.style :as style]
[quo.components.tags.context-tag.view :as context-tag]
[quo.components.tags.status-tags :as status-tags]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[utils.i18n :as i18n]))
@ -101,7 +102,7 @@
nil)])
(defn- internal-view
[{:keys [title on-press action-props accessibility-label container-style] :as props}]
[{:keys [title on-press action-props accessibility-label blur? container-style] :as props}]
[rn/pressable
{:style (merge style/container container-style)
:on-press on-press
@ -109,7 +110,9 @@
[rn/view {:style (style/left-sub-container props)}
[image-component props]
[rn/view {:style style/left-container}
[text/text {:weight :medium} title]
[text/text
{:weight :medium
:style {:color (when blur? colors/white)}} title]
[description-component props]
[tag-component props]]]
[rn/view {:style (style/sub-container (:alignment action-props))}

View File

@ -73,10 +73,6 @@
:accessibility-label :link-to-profile
:event-name :press
:callback-prop-key :on-share-press}
{:test-name "Info icon pressed"
:accessibility-label :share-qr-code-info-icon
:event-name :press
:callback-prop-key :on-info-press}
{:test-name "Legacy tab pressed"
:accessibility-label :share-qr-code-legacy-tab
:event-name :press
@ -101,10 +97,6 @@
:accessibility-label :link-to-profile
:event-name :press
:callback-prop-key :on-share-press}
{:test-name "Info icon pressed"
:accessibility-label :share-qr-code-info-icon
:event-name :press
:callback-prop-key :on-info-press}
{:test-name "Legacy tab pressed"
:accessibility-label :share-qr-code-legacy-tab
:event-name :press

View File

@ -25,17 +25,20 @@
(def header-tab-inactive {:background-color colors/white-opa-5})
(def space-between-tabs {:width 8})
(def info-icon
{:margin-left :auto
:align-self :center})
(def info-icon-color colors/white-opa-40)
;;; QR code
(defn qr-code-size
[total-width]
(- total-width (* 2 padding-horizontal)))
(def share-qr-container
{:flex-direction :row
:justify-content :space-between
:margin-bottom 20})
(def share-qr-inner-container
{:flex-direction :row
:align-items :center})
;;; Bottom part
(def bottom-container
{:margin-top 8
@ -57,29 +60,12 @@
{:width (- total-width (* 2 padding-horizontal) share-button-size share-button-gap)})
;;; Wallet variants
(def wallet-data-and-share-container
{:margin-top 2
:flex-direction :row
:justify-content :space-between})
(def wallet-legacy-container {:flex 1})
(def wallet-multichain-container {:flex 1 :margin-top 4})
(def wallet-multichain-networks
(def wallet-multichain-container
{:flex-direction :row
:justify-content :space-between
:margin-bottom 8})
(def wallet-multichain-data-container {:margin-top 4})
:flex 1})
;;; Dashed line
(def divider-container
{:height 8
:margin-horizontal 4
:justify-content :center
:overflow :hidden})
(def ^:private padding-for-divider (+ padding-horizontal 4))
(def ^:private dashed-line-width 2)
(def ^:private dashed-line-space 4)

View File

@ -2,14 +2,12 @@
(:require [clojure.set]
[clojure.string :as string]
[oops.core :as oops]
[quo.components.avatars.account-avatar.view :as account-avatar]
[quo.components.buttons.button.view :as button]
[quo.components.icon :as icon]
[quo.components.list-items.preview-list.view :as preview-list]
[quo.components.markdown.text :as text]
[quo.components.share.qr-code.view :as qr-code]
[quo.components.share.share-qr-code.style :as style]
[quo.components.tabs.tab.view :as tab]
[quo.foundations.resources :as quo.resources]
[quo.theme]
[react-native.blur :as blur]
[react-native.core :as rn]
@ -17,17 +15,8 @@
[reagent.core :as reagent]
[utils.i18n :as i18n]))
(defn- line [] [rn/view {:style style/line}])
(defn- space [] [rn/view {:style style/line-space}])
(defn- dashed-line
[width]
(into [rn/view {:style style/dashed-line}]
(take (style/number-lines-and-spaces-to-fill width))
(cycle [[line] [space]])))
(defn- header
[{:keys [share-qr-type on-info-press on-legacy-press on-multichain-press]}]
[{:keys [share-qr-type on-legacy-press on-multichain-press]}]
[rn/view {:style style/header-container}
[tab/view
{:accessibility-label :share-qr-code-legacy-tab
@ -47,22 +36,13 @@
:size 24
:active (= :wallet-multichain share-qr-type)
:on-press on-multichain-press}
(i18n/label :t/multichain)]
[rn/pressable
{:accessibility-label :share-qr-code-info-icon
:style style/info-icon
:on-press on-info-press
:hit-slop 6}
[icon/icon :i/info
{:size 20
:color style/info-icon-color}]]])
(i18n/label :t/multichain)]])
(defn- info-label
[share-qr-code-type]
[text/text {:size :paragraph-2 :weight :medium :style style/title}
(if (= share-qr-code-type :profile)
(i18n/label :t/link-to-profile)
(i18n/label :t/wallet-address))])
(when (= share-qr-code-type :profile)
(i18n/label :t/link-to-profile))])
(defn- info-text
[{:keys [width on-press on-long-press ellipsize?]} qr-data-text]
@ -106,74 +86,60 @@
(conj $ address))))
(defn- profile-bottom
[{:keys [component-width qr-data on-text-press on-text-long-press on-share-press share-qr-type]}]
[:<>
[rn/view
[info-label share-qr-type]
[info-text
{:width component-width
:ellipsize? true
:on-press on-text-press
:on-long-press on-text-long-press}
qr-data]]
[share-button
{:alignment :center
:on-press on-share-press}]])
[{:keys [component-width qr-data on-text-press on-text-long-press share-qr-type]}]
[rn/view
[info-label share-qr-type]
[info-text
{:width component-width
:ellipsize? true
:on-press on-text-press
:on-long-press on-text-long-press}
qr-data]])
(defn- wallet-legacy-bottom
[{:keys [share-qr-type component-width qr-data on-text-press on-text-long-press on-share-press]}]
[rn/view {:style style/wallet-legacy-container}
[info-label share-qr-type]
[rn/view {:style style/wallet-data-and-share-container}
[info-text
{:width component-width
:on-press on-text-press
:on-long-press on-text-long-press}
qr-data]
[share-button
{:alignment :top
:on-press on-share-press}]]])
(def ^:private known-networks #{:ethereum :optimism :arbitrum})
(defn- get-network-image-source
[network]
{:source (quo.resources/get-network (get known-networks network :unknown))})
[{:keys [component-width qr-data on-text-press on-text-long-press]}]
[info-text
{:width component-width
:on-press on-text-press
:on-long-press on-text-long-press}
qr-data])
(defn wallet-multichain-bottom
[{:keys [share-qr-type component-width qr-data on-text-press on-text-long-press
on-share-press networks on-settings-press]}]
[rn/view {:style style/wallet-multichain-container}
[rn/view {:style style/wallet-multichain-networks}
[preview-list/view {:type :network :size :size-32}
(map get-network-image-source networks)]
[button/button
{:icon-only? true
:type :grey
:background :blur
:size 32
:accessibility-label :share-qr-code-settings
:on-press on-settings-press}
:i/advanced]]
[rn/view {:style style/divider-container}
[dashed-line component-width]]
[rn/view {:style style/wallet-multichain-data-container}
[info-label share-qr-type]
[rn/view {:style style/wallet-data-and-share-container}
[info-text
{:width component-width
:on-press on-text-press
:on-long-press on-text-long-press}
[wallet-multichain-colored-address qr-data]]
[share-button
{:alignment :top
:on-press on-share-press}]]]])
[{:keys [component-width qr-data on-text-press on-text-long-press on-settings-press]}]
[rn/view
{:style style/wallet-multichain-container}
[info-text
{:width component-width
:on-press on-text-press
:on-long-press on-text-long-press}
[wallet-multichain-colored-address qr-data]]
[button/button
{:icon-only? true
:type :grey
:background :blur
:size 32
:accessibility-label :share-qr-code-settings
:on-press on-settings-press}
:i/advanced]])
(defn- share-qr-code
[{:keys [share-qr-type qr-image-uri component-width customization-color full-name
profile-picture emoji]
profile-picture emoji on-share-press]
:as props}]
[rn/view {:style style/content-container}
[rn/view
{:style style/share-qr-container}
[rn/view
{:style style/share-qr-inner-container}
[account-avatar/view
{:customization-color customization-color
:emoji emoji
:size 32}]
[text/text
{:size :heading-2
:weight :semi-bold
:style {:margin-left 8}} full-name]]
[share-button {:on-press on-share-press}]]
(when (#{:wallet-legacy :wallet-multichain} share-qr-type)
[header props])
[quo.theme/provider {:theme :light}
@ -211,14 +177,12 @@
- profile-picture: map ({:source image-source}) or any image source.
`:wallet-legacy`
- emoji: Emoji in a string to show in the QR code.
- on-info-press: Callback for the info icon.
- on-legacy-press: Callback for the legacy tab.
- on-multichain-press: Callback for the multichain tab.
`:wallet-multichain`
- networks: A vector of network names as keywords (`[:ethereum, :my-net, ...]`).
- on-settings-press: Callback for the settings button.
- emoji: Emoji in a string to show in the QR code.
- on-info-press: Callback for the info icon.
- on-legacy-press: Callback for the legacy tab.
- on-multichain-press: Callback for the multichain tab.

View File

@ -3,7 +3,9 @@
(defn address-text
[format blur? theme]
(when (= format :short)
{:color (if blur?
colors/white-opa-40
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))}))
(if (and (= format :long) blur?)
{:color colors/white}
(when (= format :short)
{:color (if blur?
colors/white-opa-40
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))})))

View File

@ -37,7 +37,7 @@
:error-level :highest})]
[quo/qr-code (assoc props :qr-image-uri qr-media-server-uri)]))
(defn- get-network-short-name-url
(defn get-network-short-name-url
[network]
(case network
:ethereum "eth:"

View File

@ -47,10 +47,11 @@
[quo/wallet-graph {:time-frame :empty}]
(when (not watch-only?)
[quo/wallet-ctas
{:send-action #(rf/dispatch [:open-modal :wallet-select-address])
:buy-action #(rf/dispatch [:show-bottom-sheet
{:content buy-drawer}])
:bridge-action #(rf/dispatch [:open-modal :wallet-bridge])}])
{:send-action #(rf/dispatch [:open-modal :wallet-select-address])
:receive-action #(rf/dispatch [:open-modal :wallet-receive])
:buy-action #(rf/dispatch [:show-bottom-sheet
{:content buy-drawer}])
:bridge-action #(rf/dispatch [:open-modal :wallet-bridge])}])
[quo/tabs
{:style style/tabs
:size 32

View File

@ -1,4 +1,13 @@
(ns status-im.contexts.wallet.common.sheets.network-preferences.style)
(ns status-im.contexts.wallet.common.sheets.network-preferences.style
(:require [quo.foundations.colors :as colors]))
(def blur
{:position :absolute
:top 0
:left 0
:right 0
:bottom 0
:overlay-color colors/neutral-100-opa-70-blur})
(def data-item
{:margin-horizontal 20

View File

@ -4,6 +4,7 @@
[quo.foundations.colors :as colors]
[quo.foundations.resources :as resources]
[quo.theme :as quo.theme]
[react-native.blur :as blur]
[reagent.core :as reagent]
[status-im.contexts.wallet.common.sheets.network-preferences.style :as style]
[utils.i18n :as i18n]
@ -12,8 +13,9 @@
(defn- make-network-item
[{:keys [network-name] :as _network}
{:keys [title color on-change network-preferences state] :as _options}]
{:keys [title color on-change network-preferences state blur?] :as _options}]
{:title (or title (string/capitalize (name network-name)))
:blur? blur?
:image :icon-avatar
:image-props {:icon (resources/get-network network-name)
:size :size-20}
@ -26,11 +28,11 @@
:on-change on-change}})
(defn- view-internal
[]
[{:keys [selected-networks]}]
(let [state (reagent/atom :default)
{:keys [color address
network-preferences-names]} (rf/sub [:wallet/current-viewing-account])
initial-network-preferences-names network-preferences-names
initial-network-preferences-names (or selected-networks network-preferences-names)
network-preferences-names-state (reagent/atom #{})
toggle-network (fn [network-name]
(reset! state :changed)
@ -44,7 +46,7 @@
(if (= @state :default)
initial-network-preferences-names
@network-preferences-names-state))]
(fn [{:keys [on-save theme]}]
(fn [{:keys [on-save blur? theme]}]
(let [network-details (rf/sub [:wallet/network-details])
mainnet (first network-details)
layer-2-networks (rest network-details)
@ -53,21 +55,29 @@
(:network-name network)))
network-details)]
[:<>
;; quo/overlay isn't compatible with sheets
(when blur?
[blur/view
{:style style/blur
:blur-amount 20
:blur-radius 25}])
[quo/drawer-top
{:title (i18n/label :t/network-preferences)
:description (i18n/label :t/network-preferences-desc)}]
:description (i18n/label :t/network-preferences-desc)
:blur? blur?}]
[quo/data-item
{:status :default
:size :default
:description :default
:label :none
:blur? false
:blur? blur?
:card? true
:title (i18n/label :t/address)
:custom-subtitle (fn []
[quo/address-text
{:networks current-networks
:address address
:blur? blur?
:format :long}])
:container-style (merge style/data-item
{:background-color (colors/theme-colors colors/neutral-2_5
@ -75,26 +85,30 @@
theme)})}]
[quo/category
{:list-type :settings
:blur? blur?
:data [(make-network-item mainnet
{:state @state
:title (i18n/label :t/mainnet)
:color color
:blur? blur?
:network-preferences (get-current-preferences-names)
:on-change #(toggle-network (:network-name
mainnet))})]}]
[quo/category
{:list-type :settings
:blur? blur?
:label (i18n/label :t/layer-2)
:data (mapv (fn [network]
(make-network-item network
{:state @state
:color color
:blur? blur?
:network-preferences (get-current-preferences-names)
:on-change #(toggle-network (:network-name
network))}))
layer-2-networks)}]
[quo/bottom-actions
{:button-one-label (i18n/label :t/confirm)
{:button-one-label (i18n/label :t/update)
:button-one-props {:disabled? (= @state :default)
:on-press (fn []
(let [chain-ids (map :chain-id current-networks)]

View File

@ -1,5 +1,6 @@
(ns status-im.contexts.wallet.common.utils
(:require [clojure.string :as string]
[status-im.common.qr-codes.view :as qr-codes]
[status-im.constants :as constants]
[utils.money :as money]
[utils.number]))
@ -98,3 +99,17 @@
(defn calculate-fiat-change
[fiat-value change-pct-24hour]
(money/bignumber (* fiat-value (/ change-pct-24hour (+ 100 change-pct-24hour)))))
(defn get-wallet-qr
[{:keys [wallet-type selected-networks address]}]
(if (= wallet-type :wallet-multichain)
(as-> selected-networks $
(map qr-codes/get-network-short-name-url $)
(apply str $)
(str $ address))
address))
(def id-to-network
{constants/mainnet-chain-id :ethereum
constants/optimism-chain-id :optimism
constants/arbitrum-chain-id :arbitrum})

View File

@ -0,0 +1,18 @@
(ns status-im.contexts.wallet.common.utils-test
(:require [cljs.test :refer [deftest is testing]]
[status-im.contexts.wallet.common.utils :as utils]))
(deftest test-get-wallet-qr
(testing "Test get-wallet-qr function"
(let [wallet-multichain {:wallet-type :wallet-multichain
:selected-networks [:ethereum :optimism]
:address "x000"}
wallet-singlechain {:wallet-type :wallet-singlechain
:selected-networks [:ethereum :optimism]
:address "x000"}]
(is (= (utils/get-wallet-qr wallet-multichain)
"eth:opt:x000"))
(is (= (utils/get-wallet-qr wallet-singlechain)
"x000")))))

View File

@ -0,0 +1,5 @@
(ns status-im.contexts.wallet.receive.style)
(def header-container
{:padding-horizontal 20
:padding-vertical 12})

View File

@ -0,0 +1,95 @@
(ns status-im.contexts.wallet.receive.view
(:require
[quo.core :as quo]
[react-native.core :as rn]
[react-native.platform :as platform]
[react-native.safe-area :as safe-area]
[react-native.share :as share]
[reagent.core :as reagent]
[status-im.contexts.wallet.common.sheets.network-preferences.view :as network-preferences]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.receive.style :as style]
[utils.i18n :as i18n]
[utils.image-server :as image-server]
[utils.re-frame :as rf]))
(def qr-size 500)
(defn- share-action
[address share-title]
(share/open
(if platform/ios?
{:activity-item-sources [{:placeholder-item {:type "text"
:content address}
:item {:default {:type "text"
:content
address}}
:link-metadata {:title share-title}}]}
{:title share-title
:subject share-title
:message address})))
(defn- open-preferences
[selected-networks]
(rf/dispatch [:show-bottom-sheet
{:theme :dark
:shell? true
:content
(fn []
[network-preferences/view
{:blur? true
:selected-networks (set @selected-networks)
:on-save (fn [chain-ids]
(rf/dispatch [:hide-bottom-sheet])
(reset! selected-networks (map #(get utils/id-to-network %)
chain-ids)))}])}]))
(defn view
[]
(let [padding-top (:top (safe-area/get-insets))
wallet-type (reagent/atom :wallet-legacy)
;; Design team is yet to confirm the default selected networks here.
;; Should be the current selected for the account or all the networks always
selected-networks (reagent/atom [:ethereum :optimism :arbitrum])]
(fn []
(let [{:keys [address color emoji] :as account} (rf/sub [:wallet/current-viewing-account])
share-title (str (:name account) " " (i18n/label :t/address))
qr-url (utils/get-wallet-qr {:wallet-type @wallet-type
:selected-networks
@selected-networks
:address address})
qr-media-server-uri (image-server/get-qr-image-uri-for-any-url
{:url qr-url
:port (rf/sub [:mediaserver/port])
:qr-size qr-size
:error-level :highest})]
[quo/overlay {:type :shell}
[rn/view
{:flex 1
:padding-top padding-top}
[quo/page-nav
{:icon-name :i/close
:on-press #(rf/dispatch [:navigate-back])
:background :blur
:right-side [{:icon-name :i/scan
:on-press #(js/alert "To be implemented")}]
:accessibility-label :top-bar}]
[quo/text-combinations
{:container-style style/header-container
:title (i18n/label :t/receive)}]
[rn/view {:style {:padding-horizontal 20}}
[quo/share-qr-code
{:type @wallet-type
:qr-image-uri qr-media-server-uri
:qr-data qr-url
:networks @selected-networks
:on-share-press #(share-action qr-url share-title)
:profile-picture nil
:unblur-on-android? true
:full-name (:name account)
:customization-color color
:emoji emoji
:on-legacy-press #(reset! wallet-type :wallet-legacy)
:on-multichain-press #(reset! wallet-type :wallet-multichain)
:on-settings-press #(open-preferences selected-networks)}]]]]))))

View File

@ -5,5 +5,6 @@
[status-im.contexts.communities.actions.community-options.component-spec]
[status-im.contexts.wallet.add-address-to-watch.component-spec]
[status-im.contexts.wallet.add-address-to-watch.confirm-address.component-spec]
[status-im.contexts.wallet.create-account.edit-derivation-path.component-spec]
[status-im.contexts.wallet.send.input-amount.component-spec]))
;; [status-im.contexts.wallet.create-account.edit-derivation-path.component-spec]

View File

@ -55,6 +55,7 @@
[status-im.contexts.wallet.create-account.select-keypair.view :as wallet-select-keypair]
[status-im.contexts.wallet.create-account.view :as wallet-create-account]
[status-im.contexts.wallet.edit-account.view :as wallet-edit-account]
[status-im.contexts.wallet.receive.view :as wallet-receive]
[status-im.contexts.wallet.saved-addresses.view :as wallet-saved-addresses]
[status-im.contexts.wallet.scan-account.view :as scan-address]
[status-im.contexts.wallet.send.input-amount.view :as wallet-send-input-amount]
@ -315,6 +316,10 @@
:options {:insets {:top? true :bottom? true}}
:component wallet-backup-recovery-phrase/view}
{:name :wallet-receive
:options options/transparent-screen-options
:component wallet-receive/view}
{:name :wallet-saved-addresses
:component wallet-saved-addresses/view}