wallet: main screen live data binding

Initial utility for live data binding in the new wallet.

- Add prices namespace to get fiat prices from Cryptocompare
- Events to init wallet balance and load prices
- Listen to these events in wallet main view
- Show accurate ETH balance, USD value and %change from yesterday
- Enable wallet tab in Jenkins
This commit is contained in:
Oskar Thorén 2017-08-18 19:03:36 +02:00 committed by Roman Volosovskyi
parent 55fd200c71
commit 8ac1535dab
8 changed files with 175 additions and 18 deletions

View File

@ -1 +1,2 @@
TESTFAIRY_ENABLED=1
WALLET_TAB_ENABLED=1

View File

@ -36,6 +36,8 @@
:discover-search-tags '()
:tags []
:sync-state :done
:wallet {}
:prices {}
:network "testnet"})
;;;;GLOBAL
@ -172,4 +174,6 @@
:discoveries/tags
:discoveries/current-tag
:discoveries/request-discoveries-timer
:discoveries/new-discover]))
:discoveries/new-discover
:wallet/wallet
:prices/prices]))

View File

@ -15,7 +15,7 @@
status-im.ui.screens.navigation
status-im.ui.screens.profile.events
status-im.ui.screens.qr-scanner.events
status-im.ui.screens.wallet.events
[re-frame.core :refer [dispatch reg-fx]]
[status-im.components.status :as status]
[status-im.components.permissions :as permissions]
@ -163,7 +163,8 @@
[:send-account-update-if-needed]
[:start-requesting-discoveries]
[:remove-old-discoveries!]
[:set :accounts/creating-account? false]]}))
[:set :accounts/creating-account? false]
[:init-wallet]]}))
(register-handler-fx
:check-console-chat

View File

@ -0,0 +1,7 @@
(ns status-im.ui.screens.wallet.db)
;; Placeholder namespace for wallet specs, which are a WIP depending on data
;; model we decide on for balances, prices, etc.
;; TODO(oskarth): spec for balance as BigNumber
;; TODO(oskarth): Spec for prices as as: {:from ETH, :to USD, :price 290.11, :last-day 304.17}

View File

@ -0,0 +1,75 @@
(ns status-im.ui.screens.wallet.events
(:require [re-frame.core :as re-frame :refer [dispatch reg-fx]]
[status-im.utils.handlers :as handlers]
[status-im.utils.prices :as prices]))
(defn get-balance [{:keys [web3 account-id on-success on-error]}]
(if (and web3 account-id)
(.getBalance
(.-eth web3)
account-id
(fn [err resp]
(if-not err
(on-success resp)
(on-error err))))
(on-error "web3 or account-id not available")))
;; FX
(reg-fx
:get-balance
(fn [{:keys [web3 account-id success-event error-event]}]
(get-balance
{:web3 web3
:account-id account-id
:on-success #(dispatch [success-event %])
:on-error #(dispatch [error-event %])})))
(reg-fx
:get-prices
(fn [{:keys [from to success-event error-event]}]
(prices/get-prices
from
to
#(dispatch [success-event %])
#(dispatch [error-event %]))))
;; Handlers
;; TODO(oskarth): At some point we want to get list of relevant assets to get prices for
(handlers/register-handler-fx
:load-prices
(fn [{{:keys [wallet] :as db} :db} [_ a]]
{:get-prices {:from "ETH"
:to "USD"
:success-event :update-prices
:error-event :update-prices-fail}}))
(handlers/register-handler-fx
:init-wallet
(fn [{{:keys [web3 accounts/current-account-id] :as db} :db} [_ a]]
{:get-balance {:web3 web3
:account-id current-account-id
:success-event :update-balance
:error-event :update-balance-fail}
:dispatch [:load-prices]}))
(handlers/register-handler-db
:update-balance
(fn [db [_ balance]]
(assoc db :wallet {:balance balance})))
(handlers/register-handler-db
:update-prices
(fn [db [_ prices]]
(assoc db :prices prices)))
(handlers/register-handler-fx
:update-balance-fail
(fn [_ [_ err]]
(.log js/console "Unable to get balance: " err)))
(handlers/register-handler-fx
:update-prices-fail
(fn [_ [_ err]]
(.log js/console "Unable to get prices: " err)))

View File

@ -1,5 +1,5 @@
(ns status-im.ui.screens.wallet.main.views
(:require-macros [status-im.utils.views :refer [defview]])
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [clojure.string :as string]
[re-frame.core :as rf]
[status-im.components.common.common :as common]
@ -11,7 +11,8 @@
[status-im.i18n :as i18n]
[status-im.utils.listview :as lw]
[status-im.utils.platform :as platform]
[status-im.ui.screens.wallet.main.styles :as st]))
[status-im.ui.screens.wallet.main.styles :as st]
[status-im.utils.money :as money]))
(defn toolbar-title []
[rn/view {:style st/toolbar-title-container}
@ -31,16 +32,17 @@
:custom-content [toolbar-title]
:custom-action [toolbar-buttons]}])
(defn main-section []
;; TODO(oskarth): Whatever triggers the "in progress" animation should also trigger wallet-init or load-prices event.
(defn main-section [usd-value change]
[rn/view {:style st/main-section}
[rn/view {:style st/total-balance-container}
[rn/view {:style st/total-balance}
[rn/text {:style st/total-balance-value} "12.43"]
[rn/text {:style st/total-balance-value} usd-value]
[rn/text {:style st/total-balance-currency} "USD"]]
[rn/view {:style st/value-variation}
[rn/text {:style st/value-variation-title} "Total value"]
[rn/view {:style st/today-variation-container}
[rn/text {:style st/today-variation} "+5.43%"]]]
[rn/text {:style st/today-variation} change]]]
[btn/buttons st/buttons
[{:text "Send"
:on-press #(rf/dispatch [:navigate-to :wallet-send-transaction])}
@ -72,20 +74,46 @@
[rn/view
[asset-list-item row]]]))
(defn asset-section []
(let [assets {"eth" {:currency :eth :amount 0.445}
"snt" {:currency :snt :amount 1}
"gno" {:currency :gno :amount 0.024794}}]
(def assets-example-map
{"eth" {:currency :eth :amount 0.445}
"snt" {:currency :snt :amount 1}
"gno" {:currency :gno :amount 0.024794}})
;; NOTE(oskarth): In development, replace assets with assets-example-map
;; to check multiple assets being rendered
(defn asset-section [eth]
(let [assets {"eth" {:currency :eth :amount eth}}]
[rn/view {:style st/asset-section}
[rn/text {:style st/asset-section-title} "Assets"]
[rn/list-view {:dataSource (lw/to-datasource assets)
:renderSeparator (when platform/ios? (render-separator-fn (count assets)))
:renderRow render-row-fn}]]))
(defn eth-balance [{:keys [balance]}]
(when balance
(money/wei->ether balance)))
(defn portfolio-value [{:keys [balance]} {:keys [price]}]
(when (and balance price)
(-> (money/wei->ether balance)
(money/eth->usd price)
(money/with-precision 2)
str)))
(defn portfolio-change [{:keys [price last-day]}]
(when (and price last-day)
(-> (money/percent-change price last-day)
(money/with-precision 2)
(str "%"))))
(defview wallet []
[]
[rn/view {:style st/wallet-container}
[toolbar-view]
[rn/scroll-view
[main-section]
[asset-section]]])
(letsubs [wallet [:get :wallet]
prices [:get :prices]]
(let [eth (or (eth-balance wallet) "...")
usd (or (portfolio-value wallet prices) "...")
change (or (portfolio-change prices) "-%")]
[rn/view {:style st/wallet-container}
[toolbar-view]
[rn/scroll-view
[main-section usd change]
[asset-section eth]]])))

View File

@ -29,3 +29,14 @@
(defn fee-value [gas gas-price]
(.times (bignumber gas) (bignumber gas-price)))
(defn eth->usd [eth usd-price]
(.times (bignumber eth) (bignumber usd-price)))
(defn percent-change [from to]
(-> (.dividedBy (bignumber from) (bignumber to))
(.minus 1)
(.times 100)))
(defn with-precision [n decimals]
(.round (bignumber n) decimals))

View File

@ -0,0 +1,30 @@
(ns status-im.utils.prices
(:require [status-im.utils.utils :as utils]
[status-im.utils.types :as types]))
;; Responsible for interacting with Cryptocompare API to get current prices for
;; currencies and tokens.
;;
;; No tests since fetch API (via http-get) relies on `window` being available.
;;
;; Example usage:
;; (get-prices "ETH" "USD" println print)
(def api-url "https://min-api.cryptocompare.com/data")
(defn- gen-price-url [fsyms tsyms]
(str api-url "/pricemultifull?fsyms=" fsyms "&tsyms=" tsyms))
(defn- format-price-resp [from to resp]
(let [raw (:RAW (types/json->clj resp))
entry (get-in raw [(keyword from) (keyword to)])]
{:from from
:to to
:price (:PRICE entry)
:last-day (:OPEN24HOUR entry)}))
(defn get-prices [from to on-success on-error]
(utils/http-get
(gen-price-url from to)
(fn [resp] (on-success (format-price-resp from to resp)))
on-error))