diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 1d3308d36..55416d15f 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -27,6 +27,7 @@ class PreferencesController { useBlockie: false, featureFlags: {}, currentLocale: opts.initLangCode, + identities: {}, }, opts.initState) this.store = new ObservableStore(initState) } @@ -155,6 +156,21 @@ class PreferencesController { return this.store.getState().tokens } + /** + * Sets a custom label for an account + * @param {string} account the account to set a label for + * @param {string} label the custom label for the account + * @return {Promise} + */ + setAccountLabel (account, label) { + const address = normalizeAddress(account) + const {identities} = this.store.getState() + identities[address] = identities[address] || {} + identities[address].name = label + this.store.updateState({ identities }) + return Promise.resolve(label) + } + /** * Gets an updated rpc list from this.addToFrequentRpcList() and sets the `frequentRpcList` to this update list. * @@ -189,8 +205,8 @@ class PreferencesController { * The returned list will have a max length of 2. If the _url currently exists it the list, it will be moved to the * end of the list. The current list is modified and returned as a promise. * - * @param {string} _url The rpc url to add to the frequentRpcList. - * @returns {Promise} The updated frequentRpcList. + * @param {string} _url The rpc url to add to the frequentRpcList. + * @returns {Promise} The updated frequentRpcList. * */ addToFrequentRpcList (_url) { diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index a6b5d3453..4dcec8ef7 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -363,6 +363,7 @@ module.exports = class MetamaskController extends EventEmitter { addToken: nodeify(preferencesController.addToken, preferencesController), removeToken: nodeify(preferencesController.removeToken, preferencesController), setCurrentAccountTab: nodeify(preferencesController.setCurrentAccountTab, preferencesController), + setAccountLabel: nodeify(preferencesController.setAccountLabel, preferencesController), setFeatureFlag: nodeify(preferencesController.setFeatureFlag, preferencesController), // AddressController @@ -373,7 +374,6 @@ module.exports = class MetamaskController extends EventEmitter { createNewVaultAndKeychain: nodeify(this.createNewVaultAndKeychain, this), createNewVaultAndRestore: nodeify(this.createNewVaultAndRestore, this), addNewKeyring: nodeify(keyringController.addNewKeyring, keyringController), - saveAccountLabel: nodeify(keyringController.saveAccountLabel, keyringController), exportAccount: nodeify(keyringController.exportAccount, keyringController), // txController diff --git a/old-ui/app/account-detail.js b/old-ui/app/account-detail.js index 692d50491..c67f0cf71 100644 --- a/old-ui/app/account-detail.js +++ b/old-ui/app/account-detail.js @@ -91,7 +91,7 @@ AccountDetailScreen.prototype.render = function () { isEditingLabel: false, }, saveText: (text) => { - props.dispatch(actions.saveAccountLabel(selected, text)) + props.dispatch(actions.setAccountLabel(selected, text)) }, }, [ diff --git a/test/unit/actions/save_account_label_test.js b/test/unit/actions/save_account_label_test.js deleted file mode 100644 index c5ffd6cbf..000000000 --- a/test/unit/actions/save_account_label_test.js +++ /dev/null @@ -1,35 +0,0 @@ -// var jsdom = require('mocha-jsdom') -var assert = require('assert') -var freeze = require('deep-freeze-strict') -var path = require('path') - -var actions = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'actions.js')) -var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'reducers.js')) - -describe('SAVE_ACCOUNT_LABEL', function () { - it('updates the state.metamask.identities[:i].name property of the state to the action.value.label', function () { - var initialState = { - metamask: { - identities: { - foo: { - name: 'bar', - }, - }, - }, - } - freeze(initialState) - - const action = { - type: actions.SAVE_ACCOUNT_LABEL, - value: { - account: 'foo', - label: 'baz', - }, - } - freeze(action) - - var resultingState = reducers(initialState, action) - assert.equal(resultingState.metamask.identities.foo.name, action.value.label) - }) -}) - diff --git a/test/unit/actions/set_account_label_test.js b/test/unit/actions/set_account_label_test.js new file mode 100644 index 000000000..53ea1d130 --- /dev/null +++ b/test/unit/actions/set_account_label_test.js @@ -0,0 +1,34 @@ +const assert = require('assert') +const freeze = require('deep-freeze-strict') +const path = require('path') + +const actions = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'actions.js')) +const reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'reducers.js')) + +describe('SET_ACCOUNT_LABEL', function () { + it('updates the state.metamask.identities[:i].name property of the state to the action.value.label', function () { + const initialState = { + metamask: { + identities: { + foo: { + name: 'bar', + }, + }, + }, + } + freeze(initialState) + + const action = { + type: actions.SET_ACCOUNT_LABEL, + value: { + account: 'foo', + label: 'baz', + }, + } + freeze(action) + + const resultingState = reducers(initialState, action) + assert.equal(resultingState.metamask.identities.foo.name, action.value.label) + }) +}) + diff --git a/test/unit/preferences-controller-test.js b/test/unit/preferences-controller-test.js index 9fb5e4251..8ff1c8648 100644 --- a/test/unit/preferences-controller-test.js +++ b/test/unit/preferences-controller-test.js @@ -4,16 +4,26 @@ const PreferencesController = require('../../app/scripts/controllers/preferences describe('preferences controller', function () { let preferencesController - before(() => { + beforeEach(() => { preferencesController = new PreferencesController() }) + describe('getTokens', function () { + it('should return an empty list initially', async function () { + await preferencesController.setSelectedAddress('0x7e57e2') + + const tokens = preferencesController.getTokens() + assert.equal(tokens.length, 0, 'empty list of tokens') + }) + }) + describe('addToken', function () { it('should add that token to its state', async function () { const address = '0xabcdef1234567' const symbol = 'ABBR' const decimals = 5 + await preferencesController.setSelectedAddress('0x7e57e2') await preferencesController.addToken(address, symbol, decimals) const tokens = preferencesController.getTokens() @@ -30,6 +40,7 @@ describe('preferences controller', function () { const symbol = 'ABBR' const decimals = 5 + await preferencesController.setSelectedAddress('0x7e57e2') await preferencesController.addToken(address, symbol, decimals) const newDecimals = 6 @@ -43,6 +54,44 @@ describe('preferences controller', function () { assert.equal(added.symbol, symbol, 'set symbol correctly') assert.equal(added.decimals, newDecimals, 'updated decimals correctly') }) + + it('should allow adding tokens to two separate addresses', async function () { + const address = '0xabcdef1234567' + const symbol = 'ABBR' + const decimals = 5 + + await preferencesController.setSelectedAddress('0x7e57e2') + await preferencesController.addToken(address, symbol, decimals) + assert.equal(preferencesController.getTokens().length, 1, 'one token added for 1st address') + + await preferencesController.setSelectedAddress('0xda22le') + await preferencesController.addToken(address, symbol, decimals) + assert.equal(preferencesController.getTokens().length, 1, 'one token added for 2nd address') + }) + }) + + describe('removeToken', function () { + it('should remove the only token from its state', async function () { + await preferencesController.setSelectedAddress('0x7e57e2') + await preferencesController.addToken('0xa', 'A', 5) + await preferencesController.removeToken('0xa') + + const tokens = preferencesController.getTokens() + assert.equal(tokens.length, 0, 'one token removed') + }) + + it('should remove a token from its state', async function () { + await preferencesController.setSelectedAddress('0x7e57e2') + await preferencesController.addToken('0xa', 'A', 4) + await preferencesController.addToken('0xb', 'B', 5) + await preferencesController.removeToken('0xa') + + const tokens = preferencesController.getTokens() + assert.equal(tokens.length, 1, 'one token removed') + + const [token1] = tokens + assert.deepEqual(token1, {address: '0xb', symbol: 'B', decimals: 5}) + }) }) }) diff --git a/ui/app/accounts/new-account/index.js b/ui/app/accounts/new-account/index.js index 207cf7760..795bd7ce6 100644 --- a/ui/app/accounts/new-account/index.js +++ b/ui/app/accounts/new-account/index.js @@ -24,7 +24,7 @@ function mapDispatchToProps (dispatch) { dispatch(actions.showModal({ name: 'EXPORT_PRIVATE_KEY' })) }, hideModal: () => dispatch(actions.hideModal()), - saveAccountLabel: (address, label) => dispatch(actions.saveAccountLabel(address, label)), + setAccountLabel: (address, label) => dispatch(actions.setAccountLabel(address, label)), } } diff --git a/ui/app/actions.js b/ui/app/actions.js index 2d238b2f8..57bffb046 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -124,8 +124,8 @@ var actions = { SHOW_PRIVATE_KEY: 'SHOW_PRIVATE_KEY', showPrivateKey: showPrivateKey, exportAccountComplete, - SAVE_ACCOUNT_LABEL: 'SAVE_ACCOUNT_LABEL', - saveAccountLabel: saveAccountLabel, + SET_ACCOUNT_LABEL: 'SET_ACCOUNT_LABEL', + setAccountLabel, // tx conf screen COMPLETED_TX: 'COMPLETED_TX', TRANSACTION_ERROR: 'TRANSACTION_ERROR', @@ -1598,13 +1598,13 @@ function showPrivateKey (key) { } } -function saveAccountLabel (account, label) { +function setAccountLabel (account, label) { return (dispatch) => { dispatch(actions.showLoadingIndication()) - log.debug(`background.saveAccountLabel`) + log.debug(`background.setAccountLabel`) return new Promise((resolve, reject) => { - background.saveAccountLabel(account, label, (err) => { + background.setAccountLabel(account, label, (err) => { dispatch(actions.hideLoadingIndication()) if (err) { @@ -1613,7 +1613,7 @@ function saveAccountLabel (account, label) { } dispatch({ - type: actions.SAVE_ACCOUNT_LABEL, + type: actions.SET_ACCOUNT_LABEL, value: { account, label }, }) diff --git a/ui/app/components/modals/account-details-modal.js b/ui/app/components/modals/account-details-modal.js index d9885daf5..5607cf051 100644 --- a/ui/app/components/modals/account-details-modal.js +++ b/ui/app/components/modals/account-details-modal.js @@ -25,7 +25,7 @@ function mapDispatchToProps (dispatch) { dispatch(actions.showModal({ name: 'EXPORT_PRIVATE_KEY' })) }, hideModal: () => dispatch(actions.hideModal()), - saveAccountLabel: (address, label) => dispatch(actions.saveAccountLabel(address, label)), + setAccountLabel: (address, label) => dispatch(actions.setAccountLabel(address, label)), } } @@ -49,7 +49,7 @@ AccountDetailsModal.prototype.render = function () { selectedIdentity, network, showExportPrivateKeyModal, - saveAccountLabel, + setAccountLabel, } = this.props const { name, address } = selectedIdentity @@ -57,7 +57,7 @@ AccountDetailsModal.prototype.render = function () { h(EditableLabel, { className: 'account-modal__name', defaultValue: name, - onSubmit: label => saveAccountLabel(address, label), + onSubmit: label => setAccountLabel(address, label), }), h(QrView, { diff --git a/ui/app/components/modals/edit-account-name-modal.js b/ui/app/components/modals/edit-account-name-modal.js index c79645dbf..5681a3cad 100644 --- a/ui/app/components/modals/edit-account-name-modal.js +++ b/ui/app/components/modals/edit-account-name-modal.js @@ -18,8 +18,8 @@ function mapDispatchToProps (dispatch) { hideModal: () => { dispatch(actions.hideModal()) }, - saveAccountLabel: (account, label) => { - dispatch(actions.saveAccountLabel(account, label)) + setAccountLabel: (account, label) => { + dispatch(actions.setAccountLabel(account, label)) }, } } @@ -41,7 +41,7 @@ module.exports = connect(mapStateToProps, mapDispatchToProps)(EditAccountNameMod EditAccountNameModal.prototype.render = function () { - const { hideModal, saveAccountLabel, identity } = this.props + const { hideModal, setAccountLabel, identity } = this.props return h('div', {}, [ h('div.flex-column.edit-account-name-modal-content', { @@ -69,7 +69,7 @@ EditAccountNameModal.prototype.render = function () { h('button.btn-clear.edit-account-name-modal-save-button.allcaps', { onClick: () => { if (this.state.inputText.length !== 0) { - saveAccountLabel(identity.address, this.state.inputText) + setAccountLabel(identity.address, this.state.inputText) hideModal() } }, diff --git a/ui/app/components/modals/new-account-modal.js b/ui/app/components/modals/new-account-modal.js index 0635b3f72..a66a3ed4a 100644 --- a/ui/app/components/modals/new-account-modal.js +++ b/ui/app/components/modals/new-account-modal.js @@ -95,7 +95,7 @@ const mapDispatchToProps = dispatch => { dispatch(actions.addNewAccount()) .then((newAccountAddress) => { if (newAccountName) { - dispatch(actions.saveAccountLabel(newAccountAddress, newAccountName)) + dispatch(actions.setAccountLabel(newAccountAddress, newAccountName)) } dispatch(actions.hideModal()) }) diff --git a/ui/app/components/pages/create-account/index.js b/ui/app/components/pages/create-account/index.js index 0962477d8..475261253 100644 --- a/ui/app/components/pages/create-account/index.js +++ b/ui/app/components/pages/create-account/index.js @@ -75,7 +75,7 @@ const mapDispatchToProps = dispatch => ({ dispatch(actions.showModal({ name: 'EXPORT_PRIVATE_KEY' })) }, hideModal: () => dispatch(actions.hideModal()), - saveAccountLabel: (address, label) => dispatch(actions.saveAccountLabel(address, label)), + setAccountLabel: (address, label) => dispatch(actions.setAccountLabel(address, label)), }) module.exports = connect(mapStateToProps, mapDispatchToProps)(CreateAccountPage) diff --git a/ui/app/components/pages/create-account/new-account.js b/ui/app/components/pages/create-account/new-account.js index 40fa584be..03a5ee72d 100644 --- a/ui/app/components/pages/create-account/new-account.js +++ b/ui/app/components/pages/create-account/new-account.js @@ -87,7 +87,7 @@ const mapDispatchToProps = dispatch => { return dispatch(actions.addNewAccount()) .then(newAccountAddress => { if (newAccountName) { - dispatch(actions.saveAccountLabel(newAccountAddress, newAccountName)) + dispatch(actions.setAccountLabel(newAccountAddress, newAccountName)) } }) }, diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js index bb35cf990..9afaf6a50 100644 --- a/ui/app/reducers/metamask.js +++ b/ui/app/reducers/metamask.js @@ -163,7 +163,7 @@ function reduceMetamask (state, action) { selectedTokenAddress: action.value, }) - case actions.SAVE_ACCOUNT_LABEL: + case actions.SET_ACCOUNT_LABEL: const account = action.value.account const name = action.value.label const id = {}