aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--app/scripts/controllers/preferences.js29
-rw-r--r--app/scripts/metamask-controller.js1
-rw-r--r--ui/app/account-detail.js4
-rw-r--r--ui/app/actions.js14
-rw-r--r--ui/app/add-token.js10
-rw-r--r--ui/app/components/token-list.js40
7 files changed, 78 insertions, 21 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 37f334d23..567479862 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
## Current Master
- Add list of popular tokens held to the account detail view.
+- Add ability to add Tokens to token list.
- Add a warning to JSON file import.
## 3.7.8 2017-6-12
diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js
index aa8e05fcc..e45224593 100644
--- a/app/scripts/controllers/preferences.js
+++ b/app/scripts/controllers/preferences.js
@@ -8,13 +8,11 @@ class PreferencesController {
const initState = extend({
frequentRpcList: [],
currentAccountTab: 'history',
+ tokens: [],
}, opts.initState)
this.store = new ObservableStore(initState)
}
-
- //
- // PUBLIC METHODS
- //
+// PUBLIC METHODS
setSelectedAddress (_address) {
return new Promise((resolve, reject) => {
@@ -28,6 +26,29 @@ class PreferencesController {
return this.store.getState().selectedAddress
}
+ addToken (rawAddress, symbol, decimals) {
+ const address = normalizeAddress(rawAddress)
+ const newEntry = { address, symbol, decimals }
+
+ const tokens = this.store.getState().tokens
+ const previousIndex = tokens.find((token, index) => {
+ return token.address === address
+ })
+
+ if (previousIndex) {
+ tokens[previousIndex] = newEntry
+ } else {
+ tokens.push(newEntry)
+ }
+
+ this.store.updateState({ tokens })
+ return Promise.resolve()
+ }
+
+ getTokens () {
+ return this.store.getState().tokens
+ }
+
updateFrequentRpcList (_url) {
return this.addToFrequentRpcList(_url)
.then((rpcList) => {
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index 410693df4..e4267381d 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -275,6 +275,7 @@ module.exports = class MetamaskController extends EventEmitter {
// PreferencesController
setSelectedAddress: nodeify(preferencesController.setSelectedAddress).bind(preferencesController),
+ addToken: nodeify(preferencesController.addToken).bind(preferencesController),
setCurrentAccountTab: nodeify(preferencesController.setCurrentAccountTab).bind(preferencesController),
setDefaultRpc: nodeify(this.setDefaultRpc).bind(this),
setCustomRpc: nodeify(this.setCustomRpc).bind(this),
diff --git a/ui/app/account-detail.js b/ui/app/account-detail.js
index 19f2cba59..bed05a7fb 100644
--- a/ui/app/account-detail.js
+++ b/ui/app/account-detail.js
@@ -35,6 +35,7 @@ function mapStateToProps (state) {
conversionRate: state.metamask.conversionRate,
currentCurrency: state.metamask.currentCurrency,
currentAccountTab: state.metamask.currentAccountTab,
+ tokens: state.metamask.tokens,
}
}
@@ -273,13 +274,14 @@ AccountDetailScreen.prototype.tabSections = function () {
AccountDetailScreen.prototype.tabSwitchView = function () {
const props = this.props
const { address, network } = props
- const { currentAccountTab } = this.props
+ const { currentAccountTab, tokens } = this.props
switch (currentAccountTab) {
case 'tokens':
return h(TokenList, {
userAddress: address,
network,
+ tokens,
addToken: () => this.props.dispatch(actions.showAddTokenPage()),
})
default:
diff --git a/ui/app/actions.js b/ui/app/actions.js
index d17d4610e..6ff28f32f 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -124,6 +124,7 @@ var actions = {
showConfigPage,
SHOW_ADD_TOKEN_PAGE: 'SHOW_ADD_TOKEN_PAGE',
showAddTokenPage,
+ addToken,
setRpcTarget: setRpcTarget,
setDefaultRpcTarget: setDefaultRpcTarget,
setProviderType: setProviderType,
@@ -636,6 +637,19 @@ function showAddTokenPage (transitionForward = true) {
}
}
+function addToken (address, symbol, decimals) {
+ return (dispatch) => {
+ dispatch(actions.showLoadingIndication())
+ background.addToken(address, symbol, decimals, (err) => {
+ dispatch(actions.hideLoadingIndication())
+ if (err) {
+ return dispatch(actions.displayWarning(err.message))
+ }
+ dispatch(actions.goHome())
+ })
+ }
+}
+
function goBackToInitView () {
return {
type: actions.BACK_TO_INIT_MENU,
diff --git a/ui/app/add-token.js b/ui/app/add-token.js
index fdbb5fe53..025cfacb5 100644
--- a/ui/app/add-token.js
+++ b/ui/app/add-token.js
@@ -30,10 +30,8 @@ function AddTokenScreen () {
}
AddTokenScreen.prototype.render = function () {
- const props = this.props
-
const state = this.state
- const { warning, address, symbol, decimals } = state
+ const { warning, symbol, decimals } = state
return (
h('.flex-column.flex-grow', [
@@ -138,12 +136,12 @@ AddTokenScreen.prototype.render = function () {
style: {
alignSelf: 'center',
},
- onClick (event) {
+ onClick: (event) => {
const valid = this.validateInputs()
if (!valid) return
- const { address, symbol, decimals } = state
- this.props.dispatch(addToken(address.trim(), symbol.trim(), decimals))
+ const { address, symbol, decimals } = this.state
+ this.props.dispatch(actions.addToken(address.trim(), symbol.trim(), decimals))
},
}, 'Add'),
]),
diff --git a/ui/app/components/token-list.js b/ui/app/components/token-list.js
index d230ce74a..100e596ed 100644
--- a/ui/app/components/token-list.js
+++ b/ui/app/components/token-list.js
@@ -4,13 +4,14 @@ const inherits = require('util').inherits
const TokenTracker = require('eth-token-tracker')
const TokenCell = require('./token-cell.js')
const contracts = require('eth-contract-metadata')
+const normalizeAddress = require('eth-sig-util').normalize
-const tokens = []
+const defaultTokens = []
for (const address in contracts) {
const contract = contracts[address]
if (contract.erc20) {
contract.address = address
- tokens.push(contract)
+ defaultTokens.push(contract)
}
}
@@ -18,22 +19,23 @@ module.exports = TokenList
inherits(TokenList, Component)
function TokenList () {
- this.state = { tokens, isLoading: true, network: null }
+ this.state = {
+ tokens: null,
+ isLoading: true,
+ network: null,
+ }
Component.call(this)
}
TokenList.prototype.render = function () {
const state = this.state
- const { tokens, isLoading } = state
-
- const { userAddress } = this.props
+ const { isLoading, tokens } = state
+ const { userAddress, network } = this.props
if (isLoading) {
return this.message('Loading')
}
- const network = this.props.network
-
const tokenViews = tokens.map((tokenData) => {
tokenData.network = network
tokenData.userAddress = userAddress
@@ -120,7 +122,7 @@ TokenList.prototype.createFreshTokenTracker = function () {
this.tracker = new TokenTracker({
userAddress,
provider: global.ethereumProvider,
- tokens: tokens,
+ tokens: uniqueMergeTokens(defaultTokens, this.props.tokens),
pollingInterval: 8000,
})
@@ -149,7 +151,12 @@ TokenList.prototype.componentWillUpdate = function (nextProps) {
}
TokenList.prototype.updateBalances = function (tokenData) {
- const heldTokens = tokenData.filter(token => token.balance !== '0' && token.string !== '0.000')
+ const desired = this.props.tokens.map(token => token.address)
+ const heldTokens = tokenData.filter(token => {
+ const held = token.balance !== '0' && token.string !== '0.000'
+ const preferred = desired.includes(normalizeAddress(token.address))
+ return held || preferred
+ })
this.setState({ tokens: heldTokens, isLoading: false })
}
@@ -158,3 +165,16 @@ TokenList.prototype.componentWillUnmount = function () {
this.tracker.stop()
}
+function uniqueMergeTokens (tokensA, tokensB) {
+ const uniqueAddresses = []
+ const result = []
+ tokensA.concat(tokensB).forEach((token) => {
+ const normal = normalizeAddress(token.address)
+ if (!uniqueAddresses.includes(normal)) {
+ uniqueAddresses.push(normal)
+ result.push(token)
+ }
+ })
+ return result
+}
+