feat: elevate and highlight wallet CTAs on wallet home screen (#21985)

Signed-off-by: Brian Sztamfater <brian@status.im>
This commit is contained in:
Brian Sztamfater 2025-02-04 14:50:42 -03:00 committed by GitHub
parent ba4a8b7d81
commit e38cb0dee6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 165 additions and 76 deletions

View File

@ -11,56 +11,61 @@
(defn- header (defn- header
[] []
[:<> (let [{:keys [status]} (rf/sub [:get-screen-params])]
[rn/view {:style style/header-row} [:<>
[quo/button [rn/view {:style style/header-row}
{:icon-only? true [quo/button
:type :grey {:icon-only? true
:background :blur :type :grey
:size 32 :background :blur
:accessibility-label :close-shell-share-tab :size 32
:container-style style/header-button :accessibility-label :close-shell-share-tab
:on-press #(rf/dispatch [:navigate-back])} :container-style style/header-button
:i/close] :on-press #(rf/dispatch [:navigate-back])}
[quo/button :i/close]
{:icon-only? true [quo/button
:type :grey {:icon-only? true
:background :blur :type :grey
:size 32 :background :blur
:accessibility-label :shell-scan-button :size 32
:on-press (fn [] :accessibility-label :shell-scan-button
(rf/dispatch [:navigate-back]) :on-press (fn []
(rf/dispatch [:open-modal :shell-qr-reader]))} (rf/dispatch [:navigate-back])
:i/scan]] (rf/dispatch [:open-modal :shell-qr-reader]))}
[quo/text :i/scan]]
{:size :heading-1 [quo/text
:weight :semi-bold {:size :heading-1
:style style/header-heading} :weight :semi-bold
(i18n/label :t/share)]]) :style style/header-heading}
(if (= :receive status)
(i18n/label :t/receive)
(i18n/label :t/share))]]))
(defn- tab-content (defn- tab-content
[initial-tab] []
(let [[selected-tab set-selected-tab] (rn/use-state initial-tab)] (let [{:keys [initial-tab hide-tab-selector?]
:or {initial-tab :profile
hide-tab-selector? false}} (rf/sub [:get-screen-params])
[selected-tab set-selected-tab] (rn/use-state initial-tab)]
[rn/view {:style {:padding-top (safe-area/get-top)}} [rn/view {:style {:padding-top (safe-area/get-top)}}
[header] [header]
[rn/view {:style style/tabs-container} (when-not hide-tab-selector?
[quo/segmented-control [rn/view {:style style/tabs-container}
{:size 28 [quo/segmented-control
:blur? true {:size 28
:on-change set-selected-tab :blur? true
:default-active selected-tab :on-change set-selected-tab
:data [{:id :profile :default-active selected-tab
:label (i18n/label :t/profile)} :data [{:id :profile
{:id :wallet :label (i18n/label :t/profile)}
:label (i18n/label :t/wallet)}]}]] {:id :wallet
:label (i18n/label :t/wallet)}]}]])
(if (= selected-tab :wallet) (if (= selected-tab :wallet)
[wallet-view/wallet-tab] [wallet-view/wallet-tab]
[profile-view/profile-tab])])) [profile-view/profile-tab])]))
(defn view (defn view
[] []
(let [{:keys [initial-tab] :or {initial-tab :profile}} (rf/sub [:get-screen-params])] [quo/overlay {:type :shell}
[quo/overlay {:type :shell} [rn/view {:key :share}
[rn/view [tab-content]]])
{:key :share}
[tab-content initial-tab]]]))

View File

@ -433,13 +433,17 @@
[:wallet/bridge-select-token [:wallet/bridge-select-token
(assoc params :network network)]))}])}]]])}))) (assoc params :network network)]))}])}]]])})))
(rf/reg-event-fx :wallet/start-bridge (rf/reg-event-fx
:wallet/start-bridge
(fn [{:keys [db]}] (fn [{:keys [db]}]
{:db (assoc-in db [:wallet :ui :send :tx-type] :tx/bridge) (let [view-id (:view-id db)]
:fx [[:dispatch (cond-> {:db (assoc-in db [:wallet :ui :send :tx-type] :tx/bridge)}
[:wallet/wizard-navigate-forward (= view-id :screen/wallet.accounts)
{:start-flow? true (assoc :fx
:flow-id :wallet-bridge-flow}]]]})) [[:dispatch
[:wallet/wizard-navigate-forward
{:start-flow? true
:flow-id :wallet-bridge-flow}]]])))))
(rf/reg-event-fx :wallet/select-bridge-network (rf/reg-event-fx :wallet/select-bridge-network
(fn [{:keys [db]} [{:keys [network-chain-id stack-id]}]] (fn [{:keys [db]} [{:keys [network-chain-id stack-id]}]]

View File

@ -31,3 +31,8 @@
(defn header-container (defn header-container
[theme] [theme]
{:background-color (colors/theme-colors colors/white colors/neutral-95 theme)}) {:background-color (colors/theme-colors colors/white colors/neutral-95 theme)})
(def cta-buttons
{:padding-horizontal 20
:padding-bottom 13
:flex-direction :row})

View File

@ -8,6 +8,7 @@
[status-im.common.refreshable-flat-list.view :as refreshable-flat-list] [status-im.common.refreshable-flat-list.view :as refreshable-flat-list]
[status-im.contexts.wallet.home.style :as style] [status-im.contexts.wallet.home.style :as style]
[status-im.contexts.wallet.home.tabs.view :as tabs] [status-im.contexts.wallet.home.tabs.view :as tabs]
[status-im.contexts.wallet.sheets.buy-token.view :as buy-token]
[status-im.contexts.wallet.sheets.network-filter.view :as network-filter] [status-im.contexts.wallet.sheets.network-filter.view :as network-filter]
[status-im.feature-flags :as ff] [status-im.feature-flags :as ff]
[utils.i18n :as i18n] [utils.i18n :as i18n]
@ -64,6 +65,62 @@
:data data :data data
:on-change on-change}]) :on-change on-change}])
(defn- call-to-actions
[]
(let [account-cards-data (rf/sub [:wallet/account-cards-data])
testnet-mode? (rf/sub [:profile/test-networks-enabled?])
multiple-accounts? (> (count account-cards-data) 1)
first-account-address (:address (first account-cards-data))
on-send-press (rn/use-callback
(fn []
(rf/dispatch [:wallet/clean-send-data])
(when-not multiple-accounts?
(rf/dispatch [:wallet/switch-current-viewing-account
first-account-address]))
(if multiple-accounts?
(rf/dispatch [:open-modal :screen/wallet.select-from])
(rf/dispatch [:wallet/wizard-navigate-forward
{:start-flow? true
:flow-id :wallet-send-flow}])))
[multiple-accounts? first-account-address])
on-receive-press (rn/use-callback #(rf/dispatch [:open-modal :screen/share-shell
{:initial-tab :wallet
:status :receive
:hide-tab-selector? true}]))
on-buy-press (rn/use-callback #(rf/dispatch [:show-bottom-sheet
{:content buy-token/view}]))
on-bridge-press (rn/use-callback
(fn []
(rf/dispatch [:wallet/clean-send-data])
(when-not multiple-accounts?
(rf/dispatch [:wallet/switch-current-viewing-account
first-account-address]))
(rf/dispatch [:wallet/start-bridge])
(when multiple-accounts?
(rf/dispatch [:open-modal :screen/wallet.select-from])))
[multiple-accounts? first-account-address])
on-swap-press (rn/use-callback
(fn []
(rf/dispatch [:wallet.tokens/get-token-list])
(when-not multiple-accounts?
(rf/dispatch [:wallet/switch-current-viewing-account
first-account-address]))
(if multiple-accounts?
(rf/dispatch [:open-modal
:screen/wallet.swap-select-account])
(rf/dispatch [:open-modal
:screen/wallet.swap-select-asset-to-pay])))
[multiple-accounts? first-account-address])]
[quo/wallet-ctas
{:container-style style/cta-buttons
:send-action on-send-press
:receive-action on-receive-press
:buy-action on-buy-press
:bridge-action on-bridge-press
:swap-action on-swap-press
:bridge-disabled? testnet-mode?
:swap-disabled? testnet-mode?}]))
(defn view (defn view
[] []
(let [selected-tab (rf/sub [:wallet/home-tab]) (let [selected-tab (rf/sub [:wallet/home-tab])
@ -106,6 +163,7 @@
(when (ff/enabled? ::ff/wallet.graph) (when (ff/enabled? ::ff/wallet.graph)
[quo/wallet-graph {:time-frame :empty}]) [quo/wallet-graph {:time-frame :empty}])
[render-cards cards account-list-ref] [render-cards cards account-list-ref]
[call-to-actions]
[render-tabs tabs-data change-tab selected-tab]] [render-tabs tabs-data change-tab selected-tab]]
:content-container-style style/list-container :content-container-style style/list-container
:sticky-header-indices [0] :sticky-header-indices [0]

View File

@ -293,8 +293,9 @@
[:wallet :ui :send] [:wallet :ui :send]
dissoc dissoc
:token :token
:token-symbol :token-display-name :token-symbol
:tx-type :network)})) :token-display-name
:network)}))
(rf/reg-event-fx :wallet/clean-selected-collectible (rf/reg-event-fx :wallet/clean-selected-collectible
(fn [{:keys [db]} [{:keys [ignore-entry-point?]}]] (fn [{:keys [db]} [{:keys [ignore-entry-point?]}]]
@ -740,6 +741,7 @@
token-symbol token-symbol
address)] address)]
(utils/token-with-balance token network-details))) (utils/token-with-balance token network-details)))
asset-selected? (or collectible-tx? (some? token))
bridge-tx? (= tx-type :tx/bridge) bridge-tx? (= tx-type :tx/bridge)
flow-id (if bridge-tx? flow-id (if bridge-tx?
:wallet-bridge-flow :wallet-bridge-flow
@ -762,7 +764,7 @@
network (assoc-in [:wallet :ui :send :network] network) network (assoc-in [:wallet :ui :send :network] network)
token-symbol (assoc-in [:wallet :ui :send :token] token) token-symbol (assoc-in [:wallet :ui :send :token] token)
bridge-tx? (assoc-in [:wallet :ui :send :to-address] address)) bridge-tx? (assoc-in [:wallet :ui :send :to-address] address))
:fx (if (or no-tx-type? (some? network) collectible-tx?) :fx (if (or no-tx-type? (some? network) collectible-tx? (not asset-selected?))
[[:dispatch [:wallet/switch-current-viewing-account address]] [[:dispatch [:wallet/switch-current-viewing-account address]]
[:dispatch [:dispatch
[:wallet/wizard-navigate-forward [:wallet/wizard-navigate-forward

View File

@ -215,8 +215,7 @@
(reset! rf-db/app-db (reset! rf-db/app-db
{:wallet {:ui {:send {:other-props :value {:wallet {:ui {:send {:other-props :value
:token "ETH" :token "ETH"
:token-display-name "ETH" :token-display-name "ETH"}}}})
:tx-type :tx/collectible-erc-721}}}})
(is (match-strict? expected-db (:db (dispatch [event-id])))))) (is (match-strict? expected-db (:db (dispatch [event-id]))))))
(h/deftest-event :wallet/clean-selected-collectible (h/deftest-event :wallet/clean-selected-collectible
@ -497,12 +496,16 @@
address "0x01" address "0x01"
network {:chain-id 1}] network {:chain-id 1}]
(testing "when tx-type is :tx/bridge and token-symbol is nil" (testing "when tx-type is :tx/bridge and token-symbol is nil"
(let [tx-type :tx/bridge (let [flow-id :wallet-bridge-flow
tx-type :tx/bridge
expected-result {:db {:wallet {:ui {:send {:to-address address expected-result {:db {:wallet {:ui {:send {:to-address address
:tx-type tx-type}}}} :tx-type tx-type}}}}
:fx [[:dispatch [:dismiss-modal :screen/wallet.select-from]] :fx [[:dispatch [:wallet/switch-current-viewing-account address]]
[:dispatch [:wallet/switch-current-viewing-account address]] [:dispatch
[:dispatch [:show-bottom-sheet {:content (m/pred fn?)}]]]}] [:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id flow-id}]]]}]
(reset! rf-db/app-db {:wallet {:ui {:send {:tx-type tx-type}}}}) (reset! rf-db/app-db {:wallet {:ui {:send {:tx-type tx-type}}}})
(is (match? expected-result (is (match? expected-result
(dispatch [event-id (dispatch [event-id

View File

@ -14,14 +14,14 @@
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
(defn- on-account-press (defn- on-account-press
[address general-flow? collectible-tx?] [address general-flow? collectible-tx? token-selected?]
(when general-flow? (when general-flow?
(rf/dispatch [:wallet/clean-selected-token]) (rf/dispatch [:wallet/clean-selected-token])
(rf/dispatch [:wallet/clean-selected-collectible])) (rf/dispatch [:wallet/clean-selected-collectible]))
(rf/dispatch [:wallet/select-from-account (rf/dispatch [:wallet/select-from-account
{:address address {:address address
:stack-id :screen/wallet.select-from :stack-id :screen/wallet.select-from
:start-flow? (not (or general-flow? collectible-tx?))}])) :start-flow? (not (or general-flow? collectible-tx? token-selected?))}]))
(defn- on-close (defn- on-close
[] []
@ -29,7 +29,7 @@
(rf/dispatch [:wallet/clean-current-viewing-account])) (rf/dispatch [:wallet/clean-current-viewing-account]))
(defn- render-fn (defn- render-fn
[item _ _ {:keys [general-flow? collectible-tx? collectible]}] [item _ _ {:keys [general-flow? collectible-tx? collectible token]}]
(let [account-address (:address item) (let [account-address (:address item)
balance (cond balance (cond
general-flow? 0 general-flow? 0
@ -41,8 +41,13 @@
asset-value (if collectible-tx? (str balance) (:asset-pay-balance item))] asset-value (if collectible-tx? (str balance) (:asset-pay-balance item))]
[quo/account-item [quo/account-item
{:type (if has-balance? :tag :default) {:type (if has-balance? :tag :default)
:on-press #(on-account-press account-address general-flow? collectible-tx?) :on-press #(on-account-press account-address
:state (if (or has-balance? general-flow?) :default :disabled) general-flow?
collectible-tx?
(and (nil? collectible) (nil? token)))
:state (if (or has-balance? general-flow? (and (nil? collectible) (nil? token)))
:default
:disabled)
:token-props (when-not general-flow? :token-props (when-not general-flow?
{:symbol asset-symbol {:symbol asset-symbol
:value asset-value}) :value asset-value})
@ -77,6 +82,7 @@
:data accounts :data accounts
:render-data {:general-flow? general-flow? :render-data {:general-flow? general-flow?
:collectible-tx? collectible-tx? :collectible-tx? collectible-tx?
:collectible collectible} :collectible collectible
:token token}
:render-fn render-fn :render-fn render-fn
:shows-horizontal-scroll-indicator false}]])) :shows-horizontal-scroll-indicator false}]]))

View File

@ -571,10 +571,15 @@
(fn [{:keys [db]} [account]] (fn [{:keys [db]} [account]]
(let [asset-to-pay (get-in db [:wallet :ui :swap :asset-to-pay]) (let [asset-to-pay (get-in db [:wallet :ui :swap :asset-to-pay])
asset-to-receive (get-in db [:wallet :ui :swap :asset-to-receive])] asset-to-receive (get-in db [:wallet :ui :swap :asset-to-receive])]
{:fx [[:dispatch [:dismiss-modal :screen/wallet.swap-select-account]] {:fx (if asset-to-pay
[:dispatch [[:dispatch [:dismiss-modal :screen/wallet.swap-select-account]]
[:wallet.swap/start [:dispatch
{:asset-to-pay asset-to-pay [:wallet.swap/start
:asset-to-receive asset-to-receive {:asset-to-pay asset-to-pay
:open-new-screen? true :asset-to-receive asset-to-receive
:from-account account}]]]}))) :open-new-screen? true
:from-account account}]]]
[[:dispatch [:wallet/switch-current-viewing-account (:address account)]]
[:dispatch
[:navigate-to-within-stack
[:screen/wallet.swap-select-asset-to-pay :screen/wallet.swap-select-account]]]])})))

View File

@ -11,16 +11,16 @@
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
(defn- on-account-press (defn- on-account-press
[account] [account asset-to-pay]
(rf/dispatch [:wallet.swap/start-from-account account])) (rf/dispatch [:wallet.swap/start-from-account account asset-to-pay]))
(defn- render-fn (defn- render-fn
[item _ _] [item _ _ {:keys [asset-to-pay]}]
(let [has-balance (money/above-zero? (:asset-pay-balance item))] (let [has-balance (money/above-zero? (:asset-pay-balance item))]
[quo/account-item [quo/account-item
{:type (if has-balance :tag :default) {:type (if has-balance :tag :default)
:on-press #(on-account-press item) :on-press #(on-account-press item asset-to-pay)
:state (if has-balance :default :disabled) :state (if (or has-balance (nil? asset-to-pay)) :default :disabled)
:token-props {:symbol (:asset-pay-symbol item) :token-props {:symbol (:asset-pay-symbol item)
:value (:asset-pay-balance item)} :value (:asset-pay-balance item)}
:account-props (assoc item :account-props (assoc item
@ -49,5 +49,6 @@
{:style style/accounts-list {:style style/accounts-list
:content-container-style style/accounts-list-container :content-container-style style/accounts-list-container
:data accounts :data accounts
:render-data {:asset-to-pay asset-to-pay}
:render-fn render-fn :render-fn render-fn
:shows-horizontal-scroll-indicator false}]])) :shows-horizontal-scroll-indicator false}]]))