Connect Wallet Home Page to backend - Accounts card (#17447)

* add functionality

* fix lint issues

* change class component to functional component

* fix refactor-data function

* rename events and fix reagent-render structure

* change rf/defn to re-frame/reg-event-fx

* convert camelCase to kebab-case

* refactor on-error callback

* refactor on-success callback

* replace merge with assoc and conj

* refactor calculate-balance structure

* rename reagent-render

* move calculate-balance into a sub

* use created sub in the view

* fix lint issues

* move calculate-balance function to wallet sub

* change on-success callback

* revert podfile.lock

* remove extra line

* add template for test

* edit calculate-balance and calculate-raw-balance

* add tests

* revert podfile.lock and fix lint issues

* rename refactor-data

* add toFixed to get-balance

* fix lint issues

* finalize code

* replace re-frame/reg-event-fx with rf/defn

* resolve comments

* remove use-fixtures in the test

* break down calculate-balance

* fix lint issues

* fix placeholder color

* move accounts to view-internal

* remove accounts param in wallet-2/get-wallet-token

* Connect Wallet Account Page to backend (basic) (#17700)

* pass address to wallet-accounts screen

* move get-balance-by-address to common/utils

* add functionality

* fix lint issues

* move logic to sub

* convert filter+first to some

* change code structure

* rename wallet-2 to wallet

* add tests

* fix lint issues

* fix style

* remove unused code

* remove view-internal defn

* fix lint issues

* fix networks param issue

* fix .toFixed issue
This commit is contained in:
mmilad75 2023-10-31 21:48:21 +03:30 committed by GitHub
parent 88b3d7d2e6
commit ca822ff51d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 254 additions and 58 deletions

View File

@ -76,6 +76,7 @@
{:db (assoc db {:db (assoc db
:chats/loading? true :chats/loading? true
:networks/current-network current-network :networks/current-network current-network
:wallet/tokens-loading? true
:networks/networks (merge networks config/default-networks-by-id) :networks/networks (merge networks config/default-networks-by-id)
:profile/profile (merge profile settings))} :profile/profile (merge profile settings))}
(notifications/load-preferences) (notifications/load-preferences)

View File

@ -1,5 +1,8 @@
(ns status-im2.contexts.wallet.account.style) (ns status-im2.contexts.wallet.account.style)
(def container
{:flex 1})
(def tabs (def tabs
{:padding-left 20 {:padding-left 20
:padding-vertical 12}) :padding-vertical 12})

View File

@ -2,11 +2,11 @@
(:require (:require
[quo.core :as quo] [quo.core :as quo]
[react-native.core :as rn] [react-native.core :as rn]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent] [reagent.core :as reagent]
[status-im2.contexts.wallet.account.style :as style] [status-im2.contexts.wallet.account.style :as style]
[status-im2.contexts.wallet.account.tabs.view :as tabs] [status-im2.contexts.wallet.account.tabs.view :as tabs]
[status-im2.contexts.wallet.common.temp :as temp] [status-im2.contexts.wallet.common.temp :as temp]
[status-im2.contexts.wallet.common.utils :as utils]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
@ -57,14 +57,13 @@
{:id :about :label (i18n/label :t/about) :accessibility-label :about}]) {:id :about :label (i18n/label :t/about) :accessibility-label :about}])
(defn view (defn view
[] [account-address]
(let [top (safe-area/get-top) (let [selected-tab (reagent/atom (:id (first tabs-data)))]
selected-tab (reagent/atom (:id (first tabs-data)))]
(fn [] (fn []
(let [networks (rf/sub [:wallet/network-details])] (let [account-address (or account-address (rf/sub [:get-screen-params :wallet-accounts]))
[rn/view account (rf/sub [:wallet/account account-address])
{:style {:flex 1 networks (rf/sub [:wallet/network-details])]
:margin-top top}} [rn/view {:style style/container}
[quo/page-nav [quo/page-nav
{:type :wallet-networks {:type :wallet-networks
:background :blur :background :blur
@ -79,7 +78,11 @@
:gradient-cover? true :gradient-cover? true
:customization-color :purple}]) :customization-color :purple}])
:emoji "🍑"}}] :emoji "🍑"}}]
[quo/account-overview temp/account-overview-state] [quo/account-overview
{:current-value (utils/prettify-balance (:balance account))
:account-name (:name account)
:account :default
:customization-color :blue}]
[quo/wallet-graph {:time-frame :empty}] [quo/wallet-graph {:time-frame :empty}]
[quo/wallet-ctas [quo/wallet-ctas
{:send-action #(rf/dispatch [:open-modal :wallet-select-address]) {:send-action #(rf/dispatch [:open-modal :wallet-select-address])

View File

@ -6,6 +6,21 @@
[full-name] [full-name]
(first (string/split full-name #" "))) (first (string/split full-name #" ")))
(defn get-balance-by-address
[balances address]
(->> balances
(filter #(= (:address %) address))
first
:balance))
(defn get-account-by-address
[accounts address]
(some #(when (= (:address %) address) %) accounts))
(defn prettify-balance
[balance]
(str "$" (.toFixed (if (number? balance) balance 0) 2)))
(defn get-derivation-path (defn get-derivation-path
[number-of-accounts] [number-of-accounts]
(str constants/path-wallet-root "/" number-of-accounts)) (str constants/path-wallet-root "/" number-of-accounts))

View File

@ -6,6 +6,26 @@
[utils.re-frame :as rf] [utils.re-frame :as rf]
[utils.security.core :as security])) [utils.security.core :as security]))
(rf/defn get-wallet-token
{:events [:wallet/get-wallet-token]}
[{:keys [db]}]
(let [params (map :address (:profile/wallet-accounts db))]
{:json-rpc/call [{:method "wallet_getWalletToken"
:params [params]
:on-success #(rf/dispatch [:wallet/get-wallet-token-success %])
:on-error (fn [error]
(log/info "failed to get wallet token"
{:event :wallet/get-wallet-token
:error error
:params params}))}]}))
(rf/defn get-wallet-token-success
{:events [:wallet/get-wallet-token-success]}
[{:keys [db]} data]
{:db (assoc db
:wallet/tokens data
:wallet/tokens-loading? false)})
(rf/defn scan-address-success (rf/defn scan-address-success
{:events [:wallet/scan-address-success]} {:events [:wallet/scan-address-success]}
[{:keys [db]} address] [{:keys [db]} address]

View File

@ -8,7 +8,7 @@
[status-im2.contexts.wallet.common.activity-tab.view :as activity] [status-im2.contexts.wallet.common.activity-tab.view :as activity]
[status-im2.contexts.wallet.common.collectibles-tab.view :as collectibles] [status-im2.contexts.wallet.common.collectibles-tab.view :as collectibles]
[status-im2.contexts.wallet.common.temp :as temp] [status-im2.contexts.wallet.common.temp :as temp]
[status-im2.contexts.wallet.common.token-value.view :as token-value] [status-im2.contexts.wallet.common.utils :as utils]
[status-im2.contexts.wallet.home.style :as style] [status-im2.contexts.wallet.home.style :as style]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
@ -28,30 +28,43 @@
:on-press #(rf/dispatch [:navigate-to :wallet-address-watch]) :on-press #(rf/dispatch [:navigate-to :wallet-address-watch])
:add-divider? true}]]]) :add-divider? true}]]])
(def account-cards (defn- add-account-placeholder
[{:name "Account 1" [color]
:balance "€0.00" {:customization-color color
:percentage-value "€0.00" :on-press #(rf/dispatch [:show-bottom-sheet {:content new-account}])
:customization-color :blue :type :add-account})
:type :empty
:emoji "🍑"
:on-press #(rf/dispatch [:navigate-to :wallet-accounts])}
{:customization-color :blue
:on-press #(rf/dispatch
[:show-bottom-sheet {:content new-account}])
:type :add-account}])
(def tabs-data (def tabs-data
[{:id :assets :label (i18n/label :t/assets) :accessibility-label :assets-tab} [{:id :assets :label (i18n/label :t/assets) :accessibility-label :assets-tab}
{:id :collectibles :label (i18n/label :t/collectibles) :accessibility-label :collectibles-tab} {:id :collectibles :label (i18n/label :t/collectibles) :accessibility-label :collectibles-tab}
{:id :activity :label (i18n/label :t/activity) :accessibility-label :activity-tab}]) {:id :activity :label (i18n/label :t/activity) :accessibility-label :activity-tab}])
(defn account-cards
[{:keys [accounts loading? balances profile]}]
(let [accounts-with-balances
(mapv
(fn [account]
(assoc account
:type :empty
:customization-color (:customization-color profile)
:on-press #(rf/dispatch [:navigate-to :wallet-accounts (:address account)])
:loading? loading?
:balance (utils/prettify-balance
(utils/get-balance-by-address balances (:address account)))))
accounts)]
(conj accounts-with-balances (add-account-placeholder (:customization-color profile)))))
(defn view (defn view
[] []
(let [top (safe-area/get-top) (rf/dispatch [:wallet/get-wallet-token])
selected-tab (reagent/atom (:id (first tabs-data)))]
(fn [] (fn []
(let [networks (rf/sub [:wallet/network-details])] (let [accounts (rf/sub [:profile/wallet-accounts])
top (safe-area/get-top)
selected-tab (reagent/atom (:id (first tabs-data)))
loading? (rf/sub [:wallet/tokens-loading?])
balances (rf/sub [:wallet/balances])
profile (rf/sub [:profile/profile])
networks (rf/sub [:wallet/network-details])]
[rn/view [rn/view
{:style {:margin-top top {:style {:margin-top top
:flex 1}} :flex 1}}
@ -59,16 +72,17 @@
[rn/view {:style style/overview-container} [rn/view {:style style/overview-container}
[quo/wallet-overview (temp/wallet-overview-state networks)]] [quo/wallet-overview (temp/wallet-overview-state networks)]]
[rn/pressable [rn/pressable
{:on-long-press #(rf/dispatch [:show-bottom-sheet {:on-long-press #(rf/dispatch [:show-bottom-sheet {:content temp/wallet-temporary-navigation}])}
{:content temp/wallet-temporary-navigation}])}
[quo/wallet-graph {:time-frame :empty}]] [quo/wallet-graph {:time-frame :empty}]]
[rn/view {:style style/accounts-container}
[rn/flat-list [rn/flat-list
{:style style/accounts-list {:style style/accounts-list
:data account-cards :data (account-cards {:accounts accounts
:loading? loading?
:balances balances
:profile profile})
:horizontal true :horizontal true
:separator [rn/view {:style {:width 12}}] :separator [rn/view {:style {:width 12}}]
:render-fn quo/account-card}]] :render-fn quo/account-card}]
[quo/tabs [quo/tabs
{:style style/tabs {:style style/tabs
:size 32 :size 32
@ -77,9 +91,9 @@
:on-change #(reset! selected-tab %)}] :on-change #(reset! selected-tab %)}]
(case @selected-tab (case @selected-tab
:assets [rn/flat-list :assets [rn/flat-list
{:render-fn token-value/view {:render-fn quo/token-value
:data temp/tokens :data temp/tokens
:key :assets-list :key :assets-list
:content-container-style {:padding-horizontal 8}}] :content-container-style {:padding-horizontal 8}}]
:collectibles [collectibles/view] :collectibles [collectibles/view]
[activity/view])])))) [activity/view])])))

View File

@ -243,6 +243,7 @@
:component emoji-picker/view} :component emoji-picker/view}
{:name :wallet-accounts {:name :wallet-accounts
:options {:insets {:top? true}}
:component wallet-accounts/view} :component wallet-accounts/view}
{:name :wallet-edit-account {:name :wallet-edit-account

View File

@ -11,7 +11,8 @@
status-im2.subs.pairing status-im2.subs.pairing
status-im2.subs.profile status-im2.subs.profile
status-im2.subs.shell status-im2.subs.shell
status-im2.subs.wallet.networks)) status-im2.subs.wallet.networks
status-im2.subs.wallet.wallet))
(defn reg-root-key-sub (defn reg-root-key-sub
[sub-name db-key] [sub-name db-key]
@ -142,6 +143,10 @@
(reg-root-key-sub :communities/selected-tab :communities/selected-tab) (reg-root-key-sub :communities/selected-tab :communities/selected-tab)
(reg-root-key-sub :contract-communities :contract-communities) (reg-root-key-sub :contract-communities :contract-communities)
;;wallet
(reg-root-key-sub :wallet/tokens :wallet/tokens)
(reg-root-key-sub :wallet/tokens-loading? :wallet/tokens-loading?)
;;activity center ;;activity center
(reg-root-key-sub :activity-center :activity-center) (reg-root-key-sub :activity-center :activity-center)

View File

@ -0,0 +1,51 @@
(ns status-im2.subs.wallet.wallet
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im2.contexts.wallet.common.utils :as utils]
[utils.number]))
(defn- calculate-raw-balance
[raw-balance decimals]
(if-let [n (utils.number/parse-int raw-balance nil)]
(/ n (Math/pow 10 (utils.number/parse-int decimals)))
0))
(defn- total-per-token
[item]
(reduce (fn [ac balances]
(+ (calculate-raw-balance (:rawBalance balances)
(:decimals item))
ac))
0
(vals (:balancesPerChain item))))
(defn- calculate-balance
[address tokens]
(let [token (get tokens (keyword (string/lower-case address)))
result (reduce
(fn [acc item]
(let [total-values (* (total-per-token item)
(get-in item [:marketValuesPerCurrency :USD :price]))]
(+ acc total-values)))
0
token)]
result))
(re-frame/reg-sub
:wallet/balances
:<- [:profile/wallet-accounts]
:<- [:wallet/tokens]
(fn [[accounts tokens]]
(for [{:keys [address]} accounts]
{:address address
:balance (calculate-balance address tokens)})))
(re-frame/reg-sub
:wallet/account
:<- [:profile/wallet-accounts]
:<- [:wallet/balances]
(fn [[accounts balances] [_ account-address]]
(assoc
(utils/get-account-by-address accounts account-address)
:balance
(utils/get-balance-by-address balances account-address))))

View File

@ -0,0 +1,83 @@
(ns status-im2.subs.wallet.wallet-test
(:require [cljs.test :refer [is testing]]
[re-frame.db :as rf-db]
status-im2.subs.root
[test-helpers.unit :as h]
[utils.re-frame :as rf]))
(def tokens
{:0x1 [{:decimals 1
:symbol "ETH"
:name "Ether"
:balancesPerChain {:1 {:rawBalance "20"
:hasError false}
:2 {:rawBalance "10"
:hasError false}}
:marketValuesPerCurrency {:USD {:price 1000}}} ;; total should be 3000
{:decimals 2
:symbol "DAI"
:name "Dai Stablecoin"
:balancesPerChain {:1 {:rawBalance "100"
:hasError false}
:2 {:rawBalance "150"
:hasError false}}
:marketValuesPerCurrency {:USD {:price 100}}}] ;; total should be 250
:0x2 [{:decimals 3
:symbol "ETH"
:name "Ether"
:balancesPerChain {:1 {:rawBalance "2500"
:hasError false}
:2 {:rawBalance "3000"
:hasError false}
:3 {:rawBalance "<nil>"
:hasError false}}
:marketValuesPerCurrency {:USD {:price 200}}} ;; total should be 1100
{:decimals 10
:symbol "DAI"
:name "Dai Stablecoin"
:balancesPerChain {:1 {:rawBalance "10000000000"
:hasError false}
:2 {:rawBalance "0"
:hasError false}
:3 {:rawBalance "<nil>"
:hasError false}}
:marketValuesPerCurrency {:USD {:price 1000}}}]}) ;; total should be 1000
(def accounts
[{:address "0x1"
:name "Main account"
:hidden false
:removed false}
{:address "0x2"
:name "Secondary account"
:hidden false
:removed false}])
(h/deftest-sub :wallet/balances
[sub-name]
(testing "returns vector of maps containing :address and :balance"
(swap! rf-db/app-db assoc
:profile/wallet-accounts accounts
:wallet/tokens tokens)
(is (= [{:address "0x1"
:balance 3250}
{:address "0x2"
:balance 2100}]
(rf/sub [sub-name])))))
(h/deftest-sub :wallet/account
[sub-name]
(testing "returns current account with balance base on the account-address"
(swap! rf-db/app-db assoc
:profile/wallet-accounts accounts
:wallet/tokens tokens
:wallet/balances [{:address "0x1"
:balance 3250}
{:address "0x2"
:balance 2100}])
(is (= {:address "0x1"
:name "Main account"
:hidden false
:removed false
:balance 3250}
(rf/sub [sub-name "0x1"])))))