mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 18:00:18 +01:00
Merge branch 'master' into i#1203MainNetSwitch
This commit is contained in:
commit
57d1a1f186
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
- The default network on installation is now MainNet
|
- The default network on installation is now MainNet
|
||||||
- Allow sending to ENS names in send form on Ropsten.
|
- Allow sending to ENS names in send form on Ropsten.
|
||||||
|
- Added an address book functionality that remembers the last 15 unique addresses sent to.
|
||||||
- Can now change network to custom RPC URL from lock screen.
|
- Can now change network to custom RPC URL from lock screen.
|
||||||
|
|
||||||
## 3.4.0 2017-3-8
|
## 3.4.0 2017-3-8
|
||||||
|
79
app/scripts/controllers/address-book.js
Normal file
79
app/scripts/controllers/address-book.js
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
const ObservableStore = require('obs-store')
|
||||||
|
const extend = require('xtend')
|
||||||
|
|
||||||
|
class AddressBookController {
|
||||||
|
|
||||||
|
|
||||||
|
// Controller in charge of managing the address book functionality from the
|
||||||
|
// recipients field on the send screen. Manages a history of all saved
|
||||||
|
// addresses and all currently owned addresses.
|
||||||
|
constructor (opts = {}, keyringController) {
|
||||||
|
const initState = extend({
|
||||||
|
addressBook: [],
|
||||||
|
}, opts.initState)
|
||||||
|
this.store = new ObservableStore(initState)
|
||||||
|
this.keyringController = keyringController
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// PUBLIC METHODS
|
||||||
|
//
|
||||||
|
|
||||||
|
// Sets a new address book in store by accepting a new address and nickname.
|
||||||
|
setAddressBook (address, name) {
|
||||||
|
return this._addToAddressBook(address, name)
|
||||||
|
.then((addressBook) => {
|
||||||
|
this.store.updateState({
|
||||||
|
addressBook,
|
||||||
|
})
|
||||||
|
return Promise.resolve()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// PRIVATE METHODS
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
// Performs the logic to add the address and name into the address book. The
|
||||||
|
// pushed object is an object of two fields. Current behavior does not set an
|
||||||
|
// upper limit to the number of addresses.
|
||||||
|
_addToAddressBook (address, name) {
|
||||||
|
let addressBook = this._getAddressBook()
|
||||||
|
let identities = this._getIdentities()
|
||||||
|
|
||||||
|
let addressBookIndex = addressBook.findIndex((element) => { return element.address.toLowerCase() === address.toLowerCase() || element.name === name })
|
||||||
|
let identitiesIndex = Object.keys(identities).findIndex((element) => { return element.toLowerCase() === address.toLowerCase() })
|
||||||
|
// trigger this condition if we own this address--no need to overwrite.
|
||||||
|
if (identitiesIndex !== -1) {
|
||||||
|
return Promise.resolve(addressBook)
|
||||||
|
// trigger this condition if we've seen this address before--may need to update nickname.
|
||||||
|
} else if (addressBookIndex !== -1) {
|
||||||
|
addressBook.splice(addressBookIndex, 1)
|
||||||
|
} else if (addressBook.length > 15) {
|
||||||
|
addressBook.shift()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
addressBook.push({
|
||||||
|
address: address,
|
||||||
|
name,
|
||||||
|
})
|
||||||
|
return Promise.resolve(addressBook)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal method to get the address book. Current persistence behavior
|
||||||
|
// should not require that this method be called from the UI directly.
|
||||||
|
_getAddressBook () {
|
||||||
|
return this.store.getState().addressBook
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieves identities from the keyring controller in order to avoid
|
||||||
|
// duplication
|
||||||
|
_getIdentities () {
|
||||||
|
return this.keyringController.memStore.getState().identities
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = AddressBookController
|
@ -5,7 +5,9 @@ const extend = require('xtend')
|
|||||||
class PreferencesController {
|
class PreferencesController {
|
||||||
|
|
||||||
constructor (opts = {}) {
|
constructor (opts = {}) {
|
||||||
const initState = extend({ frequentRpcList: [] }, opts.initState)
|
const initState = extend({
|
||||||
|
frequentRpcList: [],
|
||||||
|
}, opts.initState)
|
||||||
this.store = new ObservableStore(initState)
|
this.store = new ObservableStore(initState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ const PreferencesController = require('./controllers/preferences')
|
|||||||
const CurrencyController = require('./controllers/currency')
|
const CurrencyController = require('./controllers/currency')
|
||||||
const NoticeController = require('./notice-controller')
|
const NoticeController = require('./notice-controller')
|
||||||
const ShapeShiftController = require('./controllers/shapeshift')
|
const ShapeShiftController = require('./controllers/shapeshift')
|
||||||
|
const AddressBookController = require('./controllers/address-book')
|
||||||
const MessageManager = require('./lib/message-manager')
|
const MessageManager = require('./lib/message-manager')
|
||||||
const PersonalMessageManager = require('./lib/personal-message-manager')
|
const PersonalMessageManager = require('./lib/personal-message-manager')
|
||||||
const TxManager = require('./transaction-manager')
|
const TxManager = require('./transaction-manager')
|
||||||
@ -80,6 +81,11 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
autoFaucet(address)
|
autoFaucet(address)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// address book controller
|
||||||
|
this.addressBookController = new AddressBookController({
|
||||||
|
initState: initState.AddressBookController,
|
||||||
|
}, this.keyringController)
|
||||||
|
|
||||||
// tx mgmt
|
// tx mgmt
|
||||||
this.txManager = new TxManager({
|
this.txManager = new TxManager({
|
||||||
initState: initState.TransactionManager,
|
initState: initState.TransactionManager,
|
||||||
@ -124,6 +130,9 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
this.preferencesController.store.subscribe((state) => {
|
this.preferencesController.store.subscribe((state) => {
|
||||||
this.store.updateState({ PreferencesController: state })
|
this.store.updateState({ PreferencesController: state })
|
||||||
})
|
})
|
||||||
|
this.addressBookController.store.subscribe((state) => {
|
||||||
|
this.store.updateState({ AddressBookController: state })
|
||||||
|
})
|
||||||
this.currencyController.store.subscribe((state) => {
|
this.currencyController.store.subscribe((state) => {
|
||||||
this.store.updateState({ CurrencyController: state })
|
this.store.updateState({ CurrencyController: state })
|
||||||
})
|
})
|
||||||
@ -142,6 +151,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
this.personalMessageManager.memStore.subscribe(this.sendUpdate.bind(this))
|
this.personalMessageManager.memStore.subscribe(this.sendUpdate.bind(this))
|
||||||
this.keyringController.memStore.subscribe(this.sendUpdate.bind(this))
|
this.keyringController.memStore.subscribe(this.sendUpdate.bind(this))
|
||||||
this.preferencesController.store.subscribe(this.sendUpdate.bind(this))
|
this.preferencesController.store.subscribe(this.sendUpdate.bind(this))
|
||||||
|
this.addressBookController.store.subscribe(this.sendUpdate.bind(this))
|
||||||
this.currencyController.store.subscribe(this.sendUpdate.bind(this))
|
this.currencyController.store.subscribe(this.sendUpdate.bind(this))
|
||||||
this.noticeController.memStore.subscribe(this.sendUpdate.bind(this))
|
this.noticeController.memStore.subscribe(this.sendUpdate.bind(this))
|
||||||
this.shapeshiftController.store.subscribe(this.sendUpdate.bind(this))
|
this.shapeshiftController.store.subscribe(this.sendUpdate.bind(this))
|
||||||
@ -219,6 +229,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
this.personalMessageManager.memStore.getState(),
|
this.personalMessageManager.memStore.getState(),
|
||||||
this.keyringController.memStore.getState(),
|
this.keyringController.memStore.getState(),
|
||||||
this.preferencesController.store.getState(),
|
this.preferencesController.store.getState(),
|
||||||
|
this.addressBookController.store.getState(),
|
||||||
this.currencyController.store.getState(),
|
this.currencyController.store.getState(),
|
||||||
this.noticeController.memStore.getState(),
|
this.noticeController.memStore.getState(),
|
||||||
// config manager
|
// config manager
|
||||||
@ -240,6 +251,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
const preferencesController = this.preferencesController
|
const preferencesController = this.preferencesController
|
||||||
const txManager = this.txManager
|
const txManager = this.txManager
|
||||||
const noticeController = this.noticeController
|
const noticeController = this.noticeController
|
||||||
|
const addressBookController = this.addressBookController
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// etc
|
// etc
|
||||||
@ -267,6 +279,9 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
setDefaultRpc: nodeify(this.setDefaultRpc).bind(this),
|
setDefaultRpc: nodeify(this.setDefaultRpc).bind(this),
|
||||||
setCustomRpc: nodeify(this.setCustomRpc).bind(this),
|
setCustomRpc: nodeify(this.setCustomRpc).bind(this),
|
||||||
|
|
||||||
|
// AddressController
|
||||||
|
setAddressBook: nodeify(addressBookController.setAddressBook).bind(addressBookController),
|
||||||
|
|
||||||
// KeyringController
|
// KeyringController
|
||||||
setLocked: nodeify(keyringController.setLocked).bind(keyringController),
|
setLocked: nodeify(keyringController.setLocked).bind(keyringController),
|
||||||
createNewVaultAndKeychain: nodeify(keyringController.createNewVaultAndKeychain).bind(keyringController),
|
createNewVaultAndKeychain: nodeify(keyringController.createNewVaultAndKeychain).bind(keyringController),
|
||||||
|
56
test/unit/address-book-controller.js
Normal file
56
test/unit/address-book-controller.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
const assert = require('assert')
|
||||||
|
const extend = require('xtend')
|
||||||
|
const AddressBookController = require('../../app/scripts/controllers/address-book')
|
||||||
|
|
||||||
|
const mockKeyringController = {
|
||||||
|
memStore: {
|
||||||
|
getState: function () {
|
||||||
|
return {
|
||||||
|
identities: {
|
||||||
|
'0x0aaa' : {
|
||||||
|
address: '0x0aaa',
|
||||||
|
name: 'owned',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
describe('address-book-controller', function() {
|
||||||
|
var addressBookController
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
addressBookController = new AddressBookController({}, mockKeyringController)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('addres book management', function () {
|
||||||
|
describe('#_getAddressBook', function () {
|
||||||
|
it('should be empty by default.', function () {
|
||||||
|
assert.equal(addressBookController._getAddressBook().length, 0)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('#setAddressBook', function () {
|
||||||
|
it('should properly set a new address.', function () {
|
||||||
|
addressBookController.setAddressBook('0x01234', 'test')
|
||||||
|
var addressBook = addressBookController._getAddressBook()
|
||||||
|
assert.equal(addressBook.length, 1, 'incorrect address book length.')
|
||||||
|
assert.equal(addressBook[0].address, '0x01234', 'incorrect addresss')
|
||||||
|
assert.equal(addressBook[0].name, 'test', 'incorrect nickname')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject duplicates.', function () {
|
||||||
|
addressBookController.setAddressBook('0x01234', 'test')
|
||||||
|
addressBookController.setAddressBook('0x01234', 'test')
|
||||||
|
var addressBook = addressBookController._getAddressBook()
|
||||||
|
assert.equal(addressBook.length, 1, 'incorrect address book length.')
|
||||||
|
})
|
||||||
|
it('should not add any identities that are under user control', function () {
|
||||||
|
addressBookController.setAddressBook('0x0aaa', ' ')
|
||||||
|
var addressBook = addressBookController._getAddressBook()
|
||||||
|
assert.equal(addressBook.length, 0, 'incorrect address book length.')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
@ -7,7 +7,7 @@ const rp = require('request-promise')
|
|||||||
const nock = require('nock')
|
const nock = require('nock')
|
||||||
const CurrencyController = require('../../app/scripts/controllers/currency')
|
const CurrencyController = require('../../app/scripts/controllers/currency')
|
||||||
|
|
||||||
describe('config-manager', function() {
|
describe('currency-controller', function() {
|
||||||
var currencyController
|
var currencyController
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
|
@ -75,6 +75,8 @@ var actions = {
|
|||||||
// account detail screen
|
// account detail screen
|
||||||
SHOW_SEND_PAGE: 'SHOW_SEND_PAGE',
|
SHOW_SEND_PAGE: 'SHOW_SEND_PAGE',
|
||||||
showSendPage: showSendPage,
|
showSendPage: showSendPage,
|
||||||
|
ADD_TO_ADDRESS_BOOK: 'ADD_TO_ADDRESS_BOOK',
|
||||||
|
addToAddressBook: addToAddressBook,
|
||||||
REQUEST_ACCOUNT_EXPORT: 'REQUEST_ACCOUNT_EXPORT',
|
REQUEST_ACCOUNT_EXPORT: 'REQUEST_ACCOUNT_EXPORT',
|
||||||
requestExportAccount: requestExportAccount,
|
requestExportAccount: requestExportAccount,
|
||||||
EXPORT_ACCOUNT: 'EXPORT_ACCOUNT',
|
EXPORT_ACCOUNT: 'EXPORT_ACCOUNT',
|
||||||
@ -696,6 +698,19 @@ function setRpcTarget (newRpc) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calls the addressBookController to add a new address.
|
||||||
|
function addToAddressBook (recipient, nickname) {
|
||||||
|
log.debug(`background.addToAddressBook`)
|
||||||
|
return (dispatch) => {
|
||||||
|
background.setAddressBook(recipient, nickname, (err, result) => {
|
||||||
|
if (err) {
|
||||||
|
log.error(err)
|
||||||
|
return dispatch(self.displayWarning('Address book failed to update'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function setProviderType (type) {
|
function setProviderType (type) {
|
||||||
log.debug(`background.setProviderType`)
|
log.debug(`background.setProviderType`)
|
||||||
background.setProviderType(type)
|
background.setProviderType(type)
|
||||||
|
@ -21,6 +21,7 @@ function EnsInput () {
|
|||||||
EnsInput.prototype.render = function () {
|
EnsInput.prototype.render = function () {
|
||||||
const props = this.props
|
const props = this.props
|
||||||
const opts = extend(props, {
|
const opts = extend(props, {
|
||||||
|
list: 'addresses',
|
||||||
onChange: () => {
|
onChange: () => {
|
||||||
const network = this.props.network
|
const network = this.props.network
|
||||||
let resolverAddress = networkResolvers[network]
|
let resolverAddress = networkResolvers[network]
|
||||||
@ -46,6 +47,25 @@ EnsInput.prototype.render = function () {
|
|||||||
style: { width: '100%' },
|
style: { width: '100%' },
|
||||||
}, [
|
}, [
|
||||||
h('input.large-input', opts),
|
h('input.large-input', opts),
|
||||||
|
// The address book functionality.
|
||||||
|
h('datalist#addresses',
|
||||||
|
[
|
||||||
|
// Corresponds to the addresses owned.
|
||||||
|
Object.keys(props.identities).map((key) => {
|
||||||
|
let identity = props.identities[key]
|
||||||
|
return h('option', {
|
||||||
|
value: identity.address,
|
||||||
|
label: identity.name,
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
// Corresponds to previously sent-to addresses.
|
||||||
|
props.addressBook.map((identity) => {
|
||||||
|
return h('option', {
|
||||||
|
value: identity.address,
|
||||||
|
label: identity.name,
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
]),
|
||||||
this.ensIcon(),
|
this.ensIcon(),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
@ -80,11 +100,13 @@ EnsInput.prototype.lookupEnsName = function () {
|
|||||||
this.setState({
|
this.setState({
|
||||||
loadingEns: false,
|
loadingEns: false,
|
||||||
ensResolution: address,
|
ensResolution: address,
|
||||||
|
nickname: recipient.trim(),
|
||||||
hoverText: address + '\nClick to Copy',
|
hoverText: address + '\nClick to Copy',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((reason) => {
|
.catch((reason) => {
|
||||||
|
log.error(reason)
|
||||||
return this.setState({
|
return this.setState({
|
||||||
loadingEns: false,
|
loadingEns: false,
|
||||||
ensFailure: true,
|
ensFailure: true,
|
||||||
@ -95,10 +117,13 @@ EnsInput.prototype.lookupEnsName = function () {
|
|||||||
|
|
||||||
EnsInput.prototype.componentDidUpdate = function (prevProps, prevState) {
|
EnsInput.prototype.componentDidUpdate = function (prevProps, prevState) {
|
||||||
const state = this.state || {}
|
const state = this.state || {}
|
||||||
const { ensResolution } = state
|
const ensResolution = state.ensResolution
|
||||||
|
// If an address is sent without a nickname, meaning not from ENS or from
|
||||||
|
// the user's own accounts, a default of a one-space string is used.
|
||||||
|
const nickname = state.nickname || ' '
|
||||||
if (ensResolution && this.props.onChange &&
|
if (ensResolution && this.props.onChange &&
|
||||||
ensResolution !== prevState.ensResolution) {
|
ensResolution !== prevState.ensResolution) {
|
||||||
this.props.onChange(ensResolution)
|
this.props.onChange(ensResolution, nickname)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ function reduceMetamask (state, action) {
|
|||||||
noActiveNotices: true,
|
noActiveNotices: true,
|
||||||
lastUnreadNotice: undefined,
|
lastUnreadNotice: undefined,
|
||||||
frequentRpcList: [],
|
frequentRpcList: [],
|
||||||
|
addressBook: [],
|
||||||
}, state.metamask)
|
}, state.metamask)
|
||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
@ -20,6 +20,7 @@ function mapStateToProps (state) {
|
|||||||
identities: state.metamask.identities,
|
identities: state.metamask.identities,
|
||||||
warning: state.appState.warning,
|
warning: state.appState.warning,
|
||||||
network: state.metamask.network,
|
network: state.metamask.network,
|
||||||
|
addressBook: state.metamask.addressBook,
|
||||||
}
|
}
|
||||||
|
|
||||||
result.error = result.warning && result.warning.split('.')[0]
|
result.error = result.warning && result.warning.split('.')[0]
|
||||||
@ -44,6 +45,8 @@ SendTransactionScreen.prototype.render = function () {
|
|||||||
var account = state.account
|
var account = state.account
|
||||||
var identity = state.identity
|
var identity = state.identity
|
||||||
var network = state.network
|
var network = state.network
|
||||||
|
var identities = state.identities
|
||||||
|
var addressBook = state.addressBook
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
@ -153,6 +156,8 @@ SendTransactionScreen.prototype.render = function () {
|
|||||||
placeholder: 'Recipient Address',
|
placeholder: 'Recipient Address',
|
||||||
onChange: this.recipientDidChange.bind(this),
|
onChange: this.recipientDidChange.bind(this),
|
||||||
network,
|
network,
|
||||||
|
identities,
|
||||||
|
addressBook,
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
@ -222,13 +227,17 @@ SendTransactionScreen.prototype.back = function () {
|
|||||||
this.props.dispatch(actions.backToAccountDetail(address))
|
this.props.dispatch(actions.backToAccountDetail(address))
|
||||||
}
|
}
|
||||||
|
|
||||||
SendTransactionScreen.prototype.recipientDidChange = function (recipient) {
|
SendTransactionScreen.prototype.recipientDidChange = function (recipient, nickname) {
|
||||||
this.setState({ recipient })
|
this.setState({
|
||||||
|
recipient: recipient,
|
||||||
|
nickname: nickname,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
SendTransactionScreen.prototype.onSubmit = function () {
|
SendTransactionScreen.prototype.onSubmit = function () {
|
||||||
const state = this.state || {}
|
const state = this.state || {}
|
||||||
const recipient = state.recipient || document.querySelector('input[name="address"]').value
|
const recipient = state.recipient || document.querySelector('input[name="address"]').value
|
||||||
|
const nickname = state.nickname || ' '
|
||||||
const input = document.querySelector('input[name="amount"]').value
|
const input = document.querySelector('input[name="amount"]').value
|
||||||
const value = util.normalizeEthStringToWei(input)
|
const value = util.normalizeEthStringToWei(input)
|
||||||
const txData = document.querySelector('input[name="txData"]').value
|
const txData = document.querySelector('input[name="txData"]').value
|
||||||
@ -257,6 +266,8 @@ SendTransactionScreen.prototype.onSubmit = function () {
|
|||||||
|
|
||||||
this.props.dispatch(actions.hideWarning())
|
this.props.dispatch(actions.hideWarning())
|
||||||
|
|
||||||
|
this.props.dispatch(actions.addToAddressBook(recipient, nickname))
|
||||||
|
|
||||||
var txParams = {
|
var txParams = {
|
||||||
from: this.props.address,
|
from: this.props.address,
|
||||||
value: '0x' + value.toString(16),
|
value: '0x' + value.toString(16),
|
||||||
|
Loading…
Reference in New Issue
Block a user