aboutsummaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorChi Kei Chan <chikeichan@gmail.com>2017-09-06 18:17:49 +0800
committerChi Kei Chan <chikeichan@gmail.com>2017-09-06 18:17:49 +0800
commitf1fb9e10a06d1811d97f61b6369684979b7ecf70 (patch)
tree674886d99e6be7ea30b83f8f3811643beb792f07 /ui
parent690ddf5ed75967537aa5513986146c262012832a (diff)
downloadtangerine-wallet-browser-f1fb9e10a06d1811d97f61b6369684979b7ecf70.tar
tangerine-wallet-browser-f1fb9e10a06d1811d97f61b6369684979b7ecf70.tar.gz
tangerine-wallet-browser-f1fb9e10a06d1811d97f61b6369684979b7ecf70.tar.bz2
tangerine-wallet-browser-f1fb9e10a06d1811d97f61b6369684979b7ecf70.tar.lz
tangerine-wallet-browser-f1fb9e10a06d1811d97f61b6369684979b7ecf70.tar.xz
tangerine-wallet-browser-f1fb9e10a06d1811d97f61b6369684979b7ecf70.tar.zst
tangerine-wallet-browser-f1fb9e10a06d1811d97f61b6369684979b7ecf70.zip
Adding Token transaction detail screen
Diffstat (limited to 'ui')
-rw-r--r--ui/app/actions.js9
-rw-r--r--ui/app/components/balance-component.js28
-rw-r--r--ui/app/components/token-balance.js104
-rw-r--r--ui/app/components/token-cell.js25
-rw-r--r--ui/app/components/token-list.js10
-rw-r--r--ui/app/components/tx-view.js75
-rw-r--r--ui/app/components/wallet-view.js48
-rw-r--r--ui/app/css/itcss/components/token-list.scss13
-rw-r--r--ui/app/css/itcss/components/wallet-balance.scss10
-rw-r--r--ui/app/reducers/metamask.js6
-rw-r--r--ui/app/selectors.js9
11 files changed, 256 insertions, 81 deletions
diff --git a/ui/app/actions.js b/ui/app/actions.js
index 312c1106d..fe6048aa2 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -83,6 +83,8 @@ var actions = {
hideWarning: hideWarning,
// accounts screen
SET_SELECTED_ACCOUNT: 'SET_SELECTED_ACCOUNT',
+ SET_SELECTED_TOKEN: 'SET_SELECTED_TOKEN',
+ setSelectedToken,
SHOW_ACCOUNT_DETAIL: 'SHOW_ACCOUNT_DETAIL',
SHOW_ACCOUNTS_PAGE: 'SHOW_ACCOUNTS_PAGE',
SHOW_CONF_TX_PAGE: 'SHOW_CONF_TX_PAGE',
@@ -585,6 +587,13 @@ function setCurrentAccountTab (newTabName) {
return callBackgroundThenUpdateNoSpinner(background.setCurrentAccountTab, newTabName)
}
+function setSelectedToken (tokenAddress) {
+ return {
+ type: actions.SET_SELECTED_TOKEN,
+ value: tokenAddress || null,
+ }
+}
+
function showAccountDetail (address) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
diff --git a/ui/app/components/balance-component.js b/ui/app/components/balance-component.js
index 48efc7b6a..6b997944f 100644
--- a/ui/app/components/balance-component.js
+++ b/ui/app/components/balance-component.js
@@ -2,13 +2,19 @@ const Component = require('react').Component
const connect = require('react-redux').connect
const h = require('react-hyperscript')
const inherits = require('util').inherits
+const TokenBalance = require('./token-balance')
const { formatBalance, generateBalanceObject } = require('../util')
module.exports = connect(mapStateToProps)(BalanceComponent)
function mapStateToProps (state) {
+ const accounts = state.metamask.accounts
+ const selectedAddress = state.metamask.selectedAddress || Object.keys(accounts)[0]
+ const account = accounts[selectedAddress]
+
return {
+ account,
conversionRate: state.metamask.conversionRate,
currentCurrency: state.metamask.currentCurrency,
}
@@ -21,9 +27,8 @@ function BalanceComponent () {
BalanceComponent.prototype.render = function () {
const props = this.props
- const { balanceValue } = props
- const needsParse = 'needsParse' in props ? props.needsParse : true
- const formattedBalance = balanceValue ? formatBalance(balanceValue, 6, needsParse) : '...'
+ // const { balanceValue } = props
+ const { token } = props
return h('div.balance-container', {}, [
@@ -33,13 +38,24 @@ BalanceComponent.prototype.render = function () {
style: {},
}),
- this.renderBalance(formattedBalance),
+ token ? this.renderTokenBalance() : this.renderBalance(),
])
}
-BalanceComponent.prototype.renderBalance = function (formattedBalance) {
+BalanceComponent.prototype.renderTokenBalance = function () {
+ const { token } = this.props
+
+ return h('div.flex-column.balance-display', [
+ h('div.token-amount', [ h(TokenBalance, { token }) ]),
+ ])
+}
+
+BalanceComponent.prototype.renderBalance = function () {
const props = this.props
- const { shorten } = props
+ const { shorten, account } = props
+ const balanceValue = account && account.balance
+ const needsParse = 'needsParse' in props ? props.needsParse : true
+ const formattedBalance = balanceValue ? formatBalance(balanceValue, 6, needsParse) : '...'
const showFiat = 'showFiat' in props ? props.showFiat : true
if (formattedBalance === 'None' || formattedBalance === '...') {
diff --git a/ui/app/components/token-balance.js b/ui/app/components/token-balance.js
new file mode 100644
index 000000000..0757cc65c
--- /dev/null
+++ b/ui/app/components/token-balance.js
@@ -0,0 +1,104 @@
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const inherits = require('util').inherits
+const TokenTracker = require('eth-token-tracker')
+const connect = require('react-redux').connect
+const selectors = require('../selectors')
+
+function mapStateToProps (state) {
+ return {
+ userAddress: selectors.getSelectedAddress(state),
+ }
+}
+
+module.exports = connect(mapStateToProps)(TokenBalance)
+
+
+inherits(TokenBalance, Component)
+function TokenBalance () {
+ this.state = {
+ balance: '',
+ isLoading: true,
+ error: null,
+ }
+ Component.call(this)
+}
+
+TokenBalance.prototype.render = function () {
+ const state = this.state
+ const { balance, isLoading } = state
+
+ return isLoading
+ ? h('span', '')
+ : h('span', balance)
+}
+
+TokenBalance.prototype.componentDidMount = function () {
+ this.createFreshTokenTracker()
+}
+
+TokenBalance.prototype.createFreshTokenTracker = function () {
+ if (this.tracker) {
+ // Clean up old trackers when refreshing:
+ this.tracker.stop()
+ this.tracker.removeListener('update', this.balanceUpdater)
+ this.tracker.removeListener('error', this.showError)
+ }
+
+ if (!global.ethereumProvider) return
+ const { userAddress, token } = this.props
+
+ this.tracker = new TokenTracker({
+ userAddress,
+ provider: global.ethereumProvider,
+ tokens: [token],
+ pollingInterval: 8000,
+ })
+
+
+ // Set up listener instances for cleaning up
+ this.balanceUpdater = this.updateBalance.bind(this)
+ this.showError = error => {
+ this.setState({ error, isLoading: false })
+ }
+ this.tracker.on('update', this.balanceUpdater)
+ this.tracker.on('error', this.showError)
+
+ this.tracker.updateBalances()
+ .then(() => {
+ this.updateBalance(this.tracker.serialize())
+ })
+ .catch((reason) => {
+ log.error(`Problem updating balances`, reason)
+ this.setState({ isLoading: false })
+ })
+}
+
+TokenBalance.prototype.componentDidUpdate = function (nextProps) {
+ const {
+ userAddress: oldAddress,
+ } = this.props
+ const {
+ userAddress: newAddress,
+ } = nextProps
+
+ if (!oldAddress || !newAddress) return
+ if (oldAddress === newAddress) return
+
+ this.setState({ isLoading: true })
+ this.createFreshTokenTracker()
+}
+
+TokenBalance.prototype.updateBalance = function (tokens = []) {
+ const [{ string }] = tokens
+ this.setState({
+ balance: string,
+ isLoading: false,
+ })
+}
+
+TokenBalance.prototype.componentWillUnmount = function () {
+ if (!this.tracker) return
+ this.tracker.stop()
+}
+
diff --git a/ui/app/components/token-cell.js b/ui/app/components/token-cell.js
index a24e4e1ac..7fae67de6 100644
--- a/ui/app/components/token-cell.js
+++ b/ui/app/components/token-cell.js
@@ -1,10 +1,27 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
+const connect = require('react-redux').connect
const Identicon = require('./identicon')
const prefixForNetwork = require('../../lib/etherscan-prefix-for-network')
+const selectors = require('../selectors')
+const actions = require('../actions')
-module.exports = TokenCell
+function mapStateToProps (state) {
+ return {
+ network: state.metamask.network,
+ selectedTokenAddress: state.metamask.selectedTokenAddress,
+ userAddress: selectors.getSelectedAddress(state),
+ }
+}
+
+function mapDispatchToProps (dispatch) {
+ return {
+ setSelectedToken: address => dispatch(actions.setSelectedToken(address)),
+ }
+}
+
+module.exports = connect(mapStateToProps, mapDispatchToProps)(TokenCell)
inherits(TokenCell, Component)
function TokenCell () {
@@ -18,13 +35,17 @@ TokenCell.prototype.render = function () {
symbol,
string,
network,
+ setSelectedToken,
+ selectedTokenAddress,
// userAddress,
} = props
return (
h('div.token-list-item', {
- style: { cursor: network === '1' ? 'pointer' : 'default' },
+ className: `token-list-item ${selectedTokenAddress ? 'token-list-item--active' : ''}`,
+ // style: { cursor: network === '1' ? 'pointer' : 'default' },
// onClick: this.view.bind(this, address, userAddress, network),
+ onClick: () => setSelectedToken(address),
}, [
h(Identicon, {
diff --git a/ui/app/components/token-list.js b/ui/app/components/token-list.js
index fea87a733..2d1dd0ea7 100644
--- a/ui/app/components/token-list.js
+++ b/ui/app/components/token-list.js
@@ -8,7 +8,6 @@ const connect = require('react-redux').connect
const selectors = require('../selectors')
function mapStateToProps (state) {
-
return {
network: state.metamask.network,
tokens: state.metamask.tokens,
@@ -42,7 +41,6 @@ function TokenList () {
TokenList.prototype.render = function () {
const state = this.state
const { tokens, isLoading, error } = state
- const { userAddress, network } = this.props
if (isLoading) {
return this.message('Loading Tokens...')
@@ -53,13 +51,7 @@ TokenList.prototype.render = function () {
return this.message('There was a problem loading your token balances.')
}
- const tokenViews = tokens.map((tokenData) => {
- tokenData.network = network
- tokenData.userAddress = userAddress
- return h(TokenCell, tokenData)
- })
-
- return h('div', tokenViews)
+ return h('div', tokens.map((tokenData) => h(TokenCell, tokenData)))
}
TokenList.prototype.message = function (body) {
diff --git a/ui/app/components/tx-view.js b/ui/app/components/tx-view.js
index 9f75f7b31..d7e4a5b4b 100644
--- a/ui/app/components/tx-view.js
+++ b/ui/app/components/tx-view.js
@@ -4,10 +4,12 @@ const h = require('react-hyperscript')
const ethUtil = require('ethereumjs-util')
const inherits = require('util').inherits
const actions = require('../actions')
+const selectors = require('../selectors')
const BalanceComponent = require('./balance-component')
const TxList = require('./tx-list')
const Identicon = require('./identicon')
+const TokenBalance = require('./token-balance')
module.exports = connect(mapStateToProps, mapDispatchToProps)(TxView)
@@ -16,6 +18,7 @@ function mapStateToProps (state) {
const identities = state.metamask.identities
const accounts = state.metamask.accounts
+ const selectedTokenAddress = state.metamask.selectedTokenAddress
const selectedAddress = state.metamask.selectedAddress || Object.keys(accounts)[0]
const checksumAddress = selectedAddress && ethUtil.toChecksumAddress(selectedAddress)
const identity = identities[selectedAddress]
@@ -25,6 +28,8 @@ function mapStateToProps (state) {
sidebarOpen,
selectedAddress,
checksumAddress,
+ selectedTokenAddress,
+ selectedToken: selectors.getSelectedToken(state),
identity,
account,
}
@@ -44,9 +49,41 @@ function TxView () {
Component.call(this)
}
+TxView.prototype.renderHeroBalance = function () {
+ const {account, selectedToken, showModal, showSendPage } = this.props
+
+ return h('div.hero-balance', {}, [
+
+ h(BalanceComponent, {
+ balanceValue: account && account.balance,
+ token: selectedToken,
+ }),
+
+ h('div.flex-row.flex-center.hero-balance-buttons', {}, [
+ h('button.btn-clear', {
+ style: {
+ textAlign: 'center',
+ },
+ onClick: () => showModal({
+ name: 'BUY',
+ }),
+ }, 'BUY'),
+
+ h('button.btn-clear', {
+ style: {
+ textAlign: 'center',
+ marginLeft: '0.8em',
+ },
+ onClick: showSendPage,
+ }, 'SEND'),
+
+ ]),
+ ])
+}
+
TxView.prototype.render = function () {
- const { selectedAddress, identity, account } = this.props
+ const { selectedAddress, identity } = this.props
return h('div.tx-view.flex-column', {
style: {},
@@ -87,41 +124,7 @@ TxView.prototype.render = function () {
]),
- h('div.hero-balance', {
- style: {},
- }, [
-
- h(BalanceComponent, {
- balanceValue: account && account.balance,
- style: {},
- }),
-
- h('div.flex-row.flex-center.hero-balance-buttons', {
- style: {},
- }, [
- h('button.btn-clear', {
- style: {
- textAlign: 'center',
- },
- onClick: () => {
- this.props.showModal({
- name: 'BUY',
- })
- },
- }, 'BUY'),
-
- h('button.btn-clear', {
- style: {
- textAlign: 'center',
- marginLeft: '0.8em',
- },
- onClick: () => {
- this.props.showSendPage()
- },
- }, 'SEND'),
-
- ]),
- ]),
+ this.renderHeroBalance(),
h(TxList, {}),
diff --git a/ui/app/components/wallet-view.js b/ui/app/components/wallet-view.js
index 304b5daba..f9710ea4c 100644
--- a/ui/app/components/wallet-view.js
+++ b/ui/app/components/wallet-view.js
@@ -22,6 +22,7 @@ function mapStateToProps (state) {
selectedAddress: selectors.getSelectedAddress(state),
selectedIdentity: selectors.getSelectedIdentity(state),
selectedAccount: selectors.getSelectedAccount(state),
+ selectedTokenAddress: state.metamask.selectedTokenAddress,
}
}
@@ -29,6 +30,7 @@ function mapDispatchToProps (dispatch) {
return {
showSendPage: () => { dispatch(actions.showSendPage()) },
hideSidebar: () => { dispatch(actions.hideSidebar()) },
+ unsetSelectedToken: () => dispatch(actions.setSelectedToken()),
}
}
@@ -37,15 +39,26 @@ function WalletView () {
Component.call(this)
}
-WalletView.prototype.renderTokenBalances = function () {
- // const { tokens = [] } = this.props
- // return tokens.map(({ address, decimals, symbol }) => (
- // h(BalanceComponent, {
- // balanceValue: 0,
- // style: {},
- // })
- // ))
- return h(TokenList)
+WalletView.prototype.renderWalletBalance = function () {
+ const { selectedTokenAddress, selectedAccount, unsetSelectedToken } = this.props
+ const selectedClass = selectedTokenAddress
+ ? ''
+ : 'wallet-balance-wrapper--active'
+ const className = `flex-column wallet-balance-wrapper ${selectedClass}`
+
+ return h('div', { className }, [
+ h('div.wallet-balance',
+ {
+ onClick: () => unsetSelectedToken(),
+ },
+ [
+ h(BalanceComponent, {
+ balanceValue: selectedAccount.balance,
+ style: {},
+ }),
+ ]
+ ),
+ ])
}
WalletView.prototype.render = function () {
@@ -139,22 +152,9 @@ WalletView.prototype.render = function () {
]),
]),
- // Wallet Balances
- h('div.flex-column.wallet-balance-wrapper.wallet-balance-wrapper-active', {}, [
-
- h('div.wallet-balance', {}, [
-
- h(BalanceComponent, {
- balanceValue: selectedAccount.balance,
- style: {},
- }),
-
- ]),
-
-
- ]),
+ this.renderWalletBalance(),
- this.renderTokenBalances(),
+ h(TokenList),
])
}
diff --git a/ui/app/css/itcss/components/token-list.scss b/ui/app/css/itcss/components/token-list.scss
index dd1d533c7..2195dc1b8 100644
--- a/ui/app/css/itcss/components/token-list.scss
+++ b/ui/app/css/itcss/components/token-list.scss
@@ -1,9 +1,22 @@
+$wallet-balance-breakpoint: 890px;
+$wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and (max-width: #{$wallet-balance-breakpoint})";
+
.token-list-item {
display: flex;
flex-flow: row nowrap;
align-items: center;
padding: 20px 24px;
cursor: pointer;
+ transition: linear 200ms;
+ background-color: rgba($wallet-balance-bg, 0);
+
+ @media #{$wallet-balance-breakpoint-range} {
+ padding: 10% 4%;
+ }
+
+ &--active {
+ background-color: rgba($wallet-balance-bg, 1);
+ }
&__identicon {
margin-right: 15px;
diff --git a/ui/app/css/itcss/components/wallet-balance.scss b/ui/app/css/itcss/components/wallet-balance.scss
index 113380769..cd44f89bb 100644
--- a/ui/app/css/itcss/components/wallet-balance.scss
+++ b/ui/app/css/itcss/components/wallet-balance.scss
@@ -4,6 +4,12 @@ $wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and (
.wallet-balance-wrapper {
flex: 0 0 auto;
+ transition: linear 200ms;
+ background: rgba($wallet-balance-bg, 0);
+
+ &--active {
+ background: rgba($wallet-balance-bg, 1);
+ }
}
.wallet-balance {
@@ -62,7 +68,3 @@ $wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and (
border: 1px solid $alto;
}
}
-
-.wallet-balance-wrapper-active {
- background: $wallet-balance-bg;
-}
diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js
index e0c416c2d..8d361195d 100644
--- a/ui/app/reducers/metamask.js
+++ b/ui/app/reducers/metamask.js
@@ -17,6 +17,7 @@ function reduceMetamask (state, action) {
lastUnreadNotice: undefined,
frequentRpcList: [],
addressBook: [],
+ selectedTokenAddress: null,
}, state.metamask)
switch (action.type) {
@@ -115,6 +116,11 @@ function reduceMetamask (state, action) {
delete newState.seedWords
return newState
+ case actions.SET_SELECTED_TOKEN:
+ return extend(metamaskState, {
+ selectedTokenAddress: action.value,
+ })
+
case actions.SAVE_ACCOUNT_LABEL:
const account = action.value.account
const name = action.value.label
diff --git a/ui/app/selectors.js b/ui/app/selectors.js
index 4ff3e33f2..400f5cd45 100644
--- a/ui/app/selectors.js
+++ b/ui/app/selectors.js
@@ -4,6 +4,7 @@ const selectors = {
getSelectedAddress,
getSelectedIdentity,
getSelectedAccount,
+ getSelectedToken,
conversionRateSelector,
transactionsSelector,
}
@@ -31,6 +32,14 @@ function getSelectedAccount (state) {
return accounts[selectedAddress]
}
+function getSelectedToken (state) {
+ const tokens = state.metamask.tokens || []
+ const selectedTokenAddress = state.metamask.selectedTokenAddress
+ const selectedToken = tokens.filter(({ address }) => address === selectedTokenAddress)[0]
+
+ return selectedToken || null
+}
+
function conversionRateSelector (state) {
return state.metamask.conversionRate
}