Merge branch 'develop' of github.com:MetaMask/metamask-extension into network-remove-provider-engine

feature/default_network_editable
kumavis 7 years ago
commit 2198276bd5
  1. 12
      CHANGELOG.md
  2. 43
      app/_locales/ja/messages.json
  3. 2
      app/manifest.json
  4. 1
      app/scripts/controllers/preferences.js
  5. 6
      app/scripts/controllers/transactions/index.js
  6. 24
      app/scripts/controllers/transactions/lib/recipient-blacklist-checker.js
  7. 14
      app/scripts/controllers/transactions/lib/recipient-blacklist-config.json
  8. 6
      development/states/add-token.json
  9. 6
      development/states/confirm-sig-requests.json
  10. 6
      development/states/currency-localization.json
  11. 6
      development/states/send-edit.json
  12. 6
      development/states/send-new-ui.json
  13. 6
      development/states/tx-list-items.json
  14. 5
      old-ui/app/components/account-dropdowns.js
  15. 3
      test/unit/app/controllers/metamask-controller-test.js
  16. 77
      test/unit/app/controllers/transactions/recipient-blacklist-checker-test.js
  17. 17
      test/unit/app/controllers/transactions/tx-controller-test.js
  18. 10
      ui/app/actions.js
  19. 7
      ui/app/app.js
  20. 7
      ui/app/components/account-menu/index.js
  21. 6
      ui/app/components/pages/add-token/add-token.component.js
  22. 24
      ui/app/components/pages/create-account/import-account/json.js
  23. 25
      ui/app/components/pages/create-account/import-account/private-key.js
  24. 15
      ui/app/components/pages/create-account/index.js
  25. 9
      ui/app/components/pages/settings/index.js
  26. 2
      ui/app/components/pages/unlock-page/unlock-page.component.js
  27. 7
      ui/app/reducers.js
  28. 15
      ui/app/reducers/identities.js
  29. 4
      ui/app/send-v2.js
  30. 14
      ui/app/welcome-screen.js

@ -2,7 +2,17 @@
## Current Master ## Current Master
- Fixes issue where old nicknames were kept around causing errors. ## 4.7.4 Tue Jun 05 2018
- Add diagnostic reporting for users with multiple HD keyrings
- Throw explicit error when selected account is unset
## 4.7.3 Mon Jun 04 2018
- Hide token now uses new modal
- Indicate the current selected account on the popup account view
- Reduce height of notice container in onboarding
- Fixes issue where old nicknames were kept around causing errors
## 4.7.2 Sun Jun 03 2018 ## 4.7.2 Sun Jun 03 2018

@ -62,6 +62,9 @@
"message": " $1以上 $2以下にして下さい。", "message": " $1以上 $2以下にして下さい。",
"description": "helper for inputting hex as decimal input" "description": "helper for inputting hex as decimal input"
}, },
"blockiesIdenticon": {
"message": "Blockies Identicon を使用"
},
"borrowDharma": { "borrowDharma": {
"message": "Dharmaで借りる(ベータ版)" "message": "Dharmaで借りる(ベータ版)"
}, },
@ -95,6 +98,9 @@
"confirmTransaction": { "confirmTransaction": {
"message": "トランザクションの確認" "message": "トランザクションの確認"
}, },
"continue": {
"message": "続行"
},
"continueToCoinbase": { "continueToCoinbase": {
"message": "Coinbaseを開く" "message": "Coinbaseを開く"
}, },
@ -359,6 +365,9 @@
"likeToAddTokens": { "likeToAddTokens": {
"message": "トークンを追加しますか?" "message": "トークンを追加しますか?"
}, },
"links": {
"message": "リンク"
},
"limit": { "limit": {
"message": "リミット" "message": "リミット"
}, },
@ -371,12 +380,18 @@
"localhost": { "localhost": {
"message": "Localhost 8545" "message": "Localhost 8545"
}, },
"login": {
"message": "ログイン"
},
"logout": { "logout": {
"message": "ログアウト" "message": "ログアウト"
}, },
"loose": { "loose": {
"message": "外部秘密鍵" "message": "外部秘密鍵"
}, },
"max": {
"message": "最大"
},
"mainnet": { "mainnet": {
"message": "Ethereumメインネットワーク" "message": "Ethereumメインネットワーク"
}, },
@ -417,7 +432,7 @@
"message": "新規コントラクト" "message": "新規コントラクト"
}, },
"newPassword": { "newPassword": {
"message": "新規パスワード(最低文字)" "message": "新規パスワード(最低8文字)"
}, },
"newRecipient": { "newRecipient": {
"message": "新規受取人" "message": "新規受取人"
@ -453,6 +468,9 @@
"message": "または", "message": "または",
"description": "choice between creating or importing a new account" "description": "choice between creating or importing a new account"
}, },
"password": {
"message": "パスワード"
},
"passwordMismatch": { "passwordMismatch": {
"message": "パスワードが一致しません。", "message": "パスワードが一致しません。",
"description": "in password creation process, the two new password fields did not match" "description": "in password creation process, the two new password fields did not match"
@ -474,6 +492,9 @@
"popularTokens": { "popularTokens": {
"message": "人気のトークン" "message": "人気のトークン"
}, },
"privacyMsg": {
"message": "プライバシーポリシー"
},
"privateKey": { "privateKey": {
"message": "秘密鍵", "message": "秘密鍵",
"description": "select this type of file to use to import an account" "description": "select this type of file to use to import an account"
@ -546,6 +567,12 @@
"message": "ファイルとして保存", "message": "ファイルとして保存",
"description": "Account export process" "description": "Account export process"
}, },
"search": {
"message": "検索"
},
"searchResults": {
"message": "検索結果"
},
"selectService": { "selectService": {
"message": "サービスを選択" "message": "サービスを選択"
}, },
@ -575,7 +602,7 @@
}, },
"info": { "info": {
"message": "情報" "message": "情報"
}, },
"shapeshiftBuy": { "shapeshiftBuy": {
"message": "Shapeshiftで交換" "message": "Shapeshiftで交換"
}, },
@ -609,6 +636,9 @@
"takesTooLong": { "takesTooLong": {
"message": "送信に時間がかかりますか?" "message": "送信に時間がかかりますか?"
}, },
"terms": {
"message": "利用規約"
},
"testFaucet": { "testFaucet": {
"message": "Faucetをテスト" "message": "Faucetをテスト"
}, },
@ -619,6 +649,9 @@
"message": "ShapeShiftで $1をETHにする", "message": "ShapeShiftで $1をETHにする",
"description": "system will fill in deposit type in start of message" "description": "system will fill in deposit type in start of message"
}, },
"token": {
"message": "トークン"
},
"tokenAddress": { "tokenAddress": {
"message": "トークンアドレス" "message": "トークンアドレス"
}, },
@ -690,6 +723,12 @@
"warning": { "warning": {
"message": "警告" "message": "警告"
}, },
"welcomeBack": {
"message": "おかえりなさい!"
},
"welcomeBeta": {
"message": "MetaMask ベータ版へようこそ!"
},
"whatsThis": { "whatsThis": {
"message": "この機能について" "message": "この機能について"
}, },

@ -1,7 +1,7 @@
{ {
"name": "__MSG_appName__", "name": "__MSG_appName__",
"short_name": "__MSG_appName__", "short_name": "__MSG_appName__",
"version": "4.7.2", "version": "4.7.4",
"manifest_version": 2, "manifest_version": 2,
"author": "https://metamask.io", "author": "https://metamask.io",
"description": "__MSG_appDescription__", "description": "__MSG_appDescription__",

@ -247,6 +247,7 @@ class PreferencesController {
* @return {Promise<string>} * @return {Promise<string>}
*/ */
setAccountLabel (account, label) { setAccountLabel (account, label) {
if (!account) throw new Error('setAccountLabel requires a valid address, got ' + String(account))
const address = normalizeAddress(account) const address = normalizeAddress(account)
const {identities} = this.store.getState() const {identities} = this.store.getState()
identities[address] = identities[address] || {} identities[address] = identities[address] || {}

@ -10,6 +10,7 @@ const NonceTracker = require('./nonce-tracker')
const txUtils = require('./lib/util') const txUtils = require('./lib/util')
const cleanErrorStack = require('../../lib/cleanErrorStack') const cleanErrorStack = require('../../lib/cleanErrorStack')
const log = require('loglevel') const log = require('loglevel')
const recipientBlacklistChecker = require('./lib/recipient-blacklist-checker')
/** /**
Transaction Controller is an aggregate of sub-controllers and trackers Transaction Controller is an aggregate of sub-controllers and trackers
@ -162,8 +163,11 @@ class TransactionController extends EventEmitter {
let txMeta = this.txStateManager.generateTxMeta({ txParams: normalizedTxParams }) let txMeta = this.txStateManager.generateTxMeta({ txParams: normalizedTxParams })
this.addTx(txMeta) this.addTx(txMeta)
this.emit('newUnapprovedTx', txMeta) this.emit('newUnapprovedTx', txMeta)
// add default tx params
try { try {
// check whether recipient account is blacklisted
recipientBlacklistChecker.checkAccount(txMeta.metamaskNetworkId, normalizedTxParams.to)
// add default tx params
txMeta = await this.addTxGasDefaults(txMeta) txMeta = await this.addTxGasDefaults(txMeta)
} catch (error) { } catch (error) {
console.log(error) console.log(error)

@ -0,0 +1,24 @@
const Config = require('./recipient-blacklist-config.json')
/** @module*/
module.exports = {
checkAccount,
}
/**
* Checks if a specified account on a specified network is blacklisted.
@param networkId {number}
@param account {string}
*/
function checkAccount (networkId, account) {
const mainnetId = 1
if (networkId !== mainnetId) {
return
}
const accountToCheck = account.toLowerCase()
if (Config.blacklist.includes(accountToCheck)) {
throw new Error('Recipient is a public account')
}
}

@ -0,0 +1,14 @@
{
"blacklist": [
"0x627306090abab3a6e1400e9345bc60c78a8bef57",
"0xf17f52151ebef6c7334fad080c5704d77216b732",
"0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef",
"0x821aea9a577a9b44299b9c15c88cf3087f3b5544",
"0x0d1d4e623d10f9fba5db95830f7d3839406c6af2",
"0x2932b7a2355d6fecc4b5c0b6bd44cc31df247a2e",
"0x2191ef87e392377ec08e7c08eb105ef5448eced5",
"0x0f4f2ac550a1b4e2280d04c21cea7ebd822934b5",
"0x6330a553fc93768f612722bb8c2ec78ac90b3bbc",
"0x5aeda56215b167893e80b4fe645ba6d5bab767de"
]
}

@ -75,9 +75,9 @@
{ {
"type": "HD Key Tree", "type": "HD Key Tree",
"accounts": [ "accounts": [
"fdea65c8e26263f6d9a1b5de9555d2931a33b825", "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb", "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"2f8d4a878cfa04a6e60d46362f5644deab66572d" "0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
] ]
}, },
{ {

@ -115,9 +115,9 @@
{ {
"type": "HD Key Tree", "type": "HD Key Tree",
"accounts": [ "accounts": [
"fdea65c8e26263f6d9a1b5de9555d2931a33b825", "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb", "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"2f8d4a878cfa04a6e60d46362f5644deab66572d" "0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
] ]
}, },
{ {

@ -76,9 +76,9 @@
{ {
"type": "HD Key Tree", "type": "HD Key Tree",
"accounts": [ "accounts": [
"fdea65c8e26263f6d9a1b5de9555d2931a33b825", "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb", "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"2f8d4a878cfa04a6e60d46362f5644deab66572d" "0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
] ]
}, },
{ {

@ -94,9 +94,9 @@
{ {
"type": "HD Key Tree", "type": "HD Key Tree",
"accounts": [ "accounts": [
"fdea65c8e26263f6d9a1b5de9555d2931a33b825", "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb", "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"2f8d4a878cfa04a6e60d46362f5644deab66572d" "0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
] ]
}, },
{ {

@ -76,9 +76,9 @@
{ {
"type": "HD Key Tree", "type": "HD Key Tree",
"accounts": [ "accounts": [
"fdea65c8e26263f6d9a1b5de9555d2931a33b825", "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb", "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"2f8d4a878cfa04a6e60d46362f5644deab66572d" "0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
] ]
}, },
{ {

@ -83,9 +83,9 @@
{ {
"type": "HD Key Tree", "type": "HD Key Tree",
"accounts": [ "accounts": [
"fdea65c8e26263f6d9a1b5de9555d2931a33b825", "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb", "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"2f8d4a878cfa04a6e60d46362f5644deab66572d" "0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
] ]
}, },
{ {

@ -23,9 +23,10 @@ class AccountDropdowns extends Component {
renderAccounts () { renderAccounts () {
const { identities, selected, keyrings } = this.props const { identities, selected, keyrings } = this.props
const accountOrder = keyrings.reduce((list, keyring) => list.concat(keyring.accounts), [])
return Object.keys(identities).map((key, index) => { return accountOrder.map((address, index) => {
const identity = identities[key] const identity = identities[address]
const isSelected = identity.address === selected const isSelected = identity.address === selected
const simpleAddress = identity.address.substring(2).toLowerCase() const simpleAddress = identity.address.substring(2).toLowerCase()

@ -55,6 +55,9 @@ describe('MetaMaskController', function () {
}, },
initState: clone(firstTimeState), initState: clone(firstTimeState),
}) })
// disable diagnostics
metamaskController.diagnostics = null
// add sinon method spies
sandbox.spy(metamaskController.keyringController, 'createNewVaultAndKeychain') sandbox.spy(metamaskController.keyringController, 'createNewVaultAndKeychain')
sandbox.spy(metamaskController.keyringController, 'createNewVaultAndRestore') sandbox.spy(metamaskController.keyringController, 'createNewVaultAndRestore')
}) })

@ -0,0 +1,77 @@
const assert = require('assert')
const recipientBlackListChecker = require('../../../../../app/scripts/controllers/transactions/lib/recipient-blacklist-checker')
const {
ROPSTEN_CODE,
RINKEYBY_CODE,
KOVAN_CODE,
} = require('../../../../../app/scripts/controllers/network/enums')
const KeyringController = require('eth-keyring-controller')
describe('Recipient Blacklist Checker', function () {
let publicAccounts
before(async function () {
const damnedMnemonic = 'candy maple cake sugar pudding cream honey rich smooth crumble sweet treat'
const keyringController = new KeyringController({})
const Keyring = keyringController.getKeyringClassForType('HD Key Tree')
const opts = {
mnemonic: damnedMnemonic,
numberOfAccounts: 10,
}
const keyring = new Keyring(opts)
publicAccounts = await keyring.getAccounts()
})
describe('#checkAccount', function () {
it('does not fail on test networks', function () {
let callCount = 0
const networks = [ROPSTEN_CODE, RINKEYBY_CODE, KOVAN_CODE]
for (let networkId in networks) {
publicAccounts.forEach((account) => {
recipientBlackListChecker.checkAccount(networkId, account)
callCount++
})
}
assert.equal(callCount, 30)
})
it('fails on mainnet', function () {
const mainnetId = 1
let callCount = 0
publicAccounts.forEach((account) => {
try {
recipientBlackListChecker.checkAccount(mainnetId, account)
assert.fail('function should have thrown an error')
} catch (err) {
assert.equal(err.message, 'Recipient is a public account')
}
callCount++
})
assert.equal(callCount, 10)
})
it('fails for public account - uppercase', function () {
const mainnetId = 1
const publicAccount = '0X0D1D4E623D10F9FBA5DB95830F7D3839406C6AF2'
try {
recipientBlackListChecker.checkAccount(mainnetId, publicAccount)
assert.fail('function should have thrown an error')
} catch (err) {
assert.equal(err.message, 'Recipient is a public account')
}
})
it('fails for public account - lowercase', async function () {
const mainnetId = 1
const publicAccount = '0x0d1d4e623d10f9fba5db95830f7d3839406c6af2'
try {
await recipientBlackListChecker.checkAccount(mainnetId, publicAccount)
assert.fail('function should have thrown an error')
} catch (err) {
assert.equal(err.message, 'Recipient is a public account')
}
})
})
})

@ -188,6 +188,23 @@ describe('Transaction Controller', function () {
.catch(done) .catch(done)
}) })
it('should fail if recipient is public', function (done) {
txController.networkStore = new ObservableStore(1)
txController.addUnapprovedTransaction({ from: '0x1678a085c290ebd122dc42cba69373b5953b831d', to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' })
.catch((err) => {
if (err.message === 'Recipient is a public account') done()
else done(err)
})
})
it('should not fail if recipient is public but not on mainnet', function (done) {
txController.once('newUnapprovedTx', (txMetaFromEmit) => {
assert(txMetaFromEmit, 'txMeta is falsey')
done()
})
txController.addUnapprovedTransaction({ from: '0x1678a085c290ebd122dc42cba69373b5953b831d', to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' })
.catch(done)
})
}) })
describe('#addTxGasDefaults', function () { describe('#addTxGasDefaults', function () {

@ -550,10 +550,12 @@ function importNewAccount (strategy, args) {
} }
dispatch(actions.hideLoadingIndication()) dispatch(actions.hideLoadingIndication())
dispatch(actions.updateMetamaskState(newState)) dispatch(actions.updateMetamaskState(newState))
dispatch({ if (newState.selectedAddress) {
type: actions.SHOW_ACCOUNT_DETAIL, dispatch({
value: newState.selectedAddress, type: actions.SHOW_ACCOUNT_DETAIL,
}) value: newState.selectedAddress,
})
}
return newState return newState
} }
} }

@ -99,7 +99,7 @@ class App extends Component {
} = this.props } = this.props
const isLoadingNetwork = network === 'loading' && currentView.name !== 'config' const isLoadingNetwork = network === 'loading' && currentView.name !== 'config'
const loadMessage = loadingMessage || isLoadingNetwork ? const loadMessage = loadingMessage || isLoadingNetwork ?
this.getConnectingLabel() : null this.getConnectingLabel(loadingMessage) : null
log.debug('Main ui render function') log.debug('Main ui render function')
return ( return (
@ -210,7 +210,10 @@ class App extends Component {
} }
} }
getConnectingLabel = function () { getConnectingLabel = function (loadingMessage) {
if (loadingMessage) {
return loadingMessage
}
const { provider } = this.props const { provider } = this.props
const providerName = provider.type const providerName = provider.type

@ -135,11 +135,12 @@ AccountMenu.prototype.renderAccounts = function () {
showAccountDetail, showAccountDetail,
} = this.props } = this.props
return Object.keys(identities).map((key, index) => { const accountOrder = keyrings.reduce((list, keyring) => list.concat(keyring.accounts), [])
const identity = identities[key] return accountOrder.map((address) => {
const identity = identities[address]
const isSelected = identity.address === selectedAddress const isSelected = identity.address === selectedAddress
const balanceValue = accounts[key] ? accounts[key].balance : '' const balanceValue = accounts[address] ? accounts[address].balance : ''
const formattedBalance = balanceValue ? formatBalance(balanceValue, 6) : '...' const formattedBalance = balanceValue ? formatBalance(balanceValue, 6) : '...'
const simpleAddress = identity.address.substring(2).toLowerCase() const simpleAddress = identity.address.substring(2).toLowerCase()

@ -231,7 +231,7 @@ class AddToken extends Component {
<div className="add-token__custom-token-form"> <div className="add-token__custom-token-form">
<TextField <TextField
id="custom-address" id="custom-address"
label="Token Address" label={this.context.t('tokenAddress')}
type="text" type="text"
value={customAddress} value={customAddress}
onChange={e => this.handleCustomAddressChange(e.target.value)} onChange={e => this.handleCustomAddressChange(e.target.value)}
@ -241,7 +241,7 @@ class AddToken extends Component {
/> />
<TextField <TextField
id="custom-symbol" id="custom-symbol"
label="Token Symbol" label={this.context.t('tokenSymbol')}
type="text" type="text"
value={customSymbol} value={customSymbol}
onChange={e => this.handleCustomSymbolChange(e.target.value)} onChange={e => this.handleCustomSymbolChange(e.target.value)}
@ -252,7 +252,7 @@ class AddToken extends Component {
/> />
<TextField <TextField
id="custom-decimals" id="custom-decimals"
label="Decimals of Precision" label={this.context.t('decimal')}
type="number" type="number"
value={customDecimals} value={customDecimals}
onChange={e => this.handleCustomDecimalsChange(e.target.value)} onChange={e => this.handleCustomDecimalsChange(e.target.value)}

@ -82,18 +82,19 @@ class JsonImportSubview extends Component {
} }
createNewKeychain () { createNewKeychain () {
const { firstAddress, displayWarning, importNewJsonAccount, setSelectedAddress } = this.props
const state = this.state const state = this.state
if (!state) { if (!state) {
const message = this.context.t('validFileImport') const message = this.context.t('validFileImport')
return this.props.displayWarning(message) return displayWarning(message)
} }
const { fileContents } = state const { fileContents } = state
if (!fileContents) { if (!fileContents) {
const message = this.context.t('needImportFile') const message = this.context.t('needImportFile')
return this.props.displayWarning(message) return displayWarning(message)
} }
const passwordInput = document.getElementById('json-password-box') const passwordInput = document.getElementById('json-password-box')
@ -101,12 +102,19 @@ class JsonImportSubview extends Component {
if (!password) { if (!password) {
const message = this.context.t('needImportPassword') const message = this.context.t('needImportPassword')
return this.props.displayWarning(message) return displayWarning(message)
} }
this.props.importNewJsonAccount([ fileContents, password ]) importNewJsonAccount([ fileContents, password ])
// JS runtime requires caught rejections but failures are handled by Redux .then(({ selectedAddress }) => {
.catch() if (selectedAddress) {
history.push(DEFAULT_ROUTE)
} else {
displayWarning('Error importing account.')
setSelectedAddress(firstAddress)
}
})
.catch(err => displayWarning(err))
} }
} }
@ -114,14 +122,17 @@ JsonImportSubview.propTypes = {
error: PropTypes.string, error: PropTypes.string,
goHome: PropTypes.func, goHome: PropTypes.func,
displayWarning: PropTypes.func, displayWarning: PropTypes.func,
firstAddress: PropTypes.string,
importNewJsonAccount: PropTypes.func, importNewJsonAccount: PropTypes.func,
history: PropTypes.object, history: PropTypes.object,
setSelectedAddress: PropTypes.func,
t: PropTypes.func, t: PropTypes.func,
} }
const mapStateToProps = state => { const mapStateToProps = state => {
return { return {
error: state.appState.warning, error: state.appState.warning,
firstAddress: Object.keys(state.metamask.accounts)[0],
} }
} }
@ -130,6 +141,7 @@ const mapDispatchToProps = dispatch => {
goHome: () => dispatch(actions.goHome()), goHome: () => dispatch(actions.goHome()),
displayWarning: warning => dispatch(actions.displayWarning(warning)), displayWarning: warning => dispatch(actions.displayWarning(warning)),
importNewJsonAccount: options => dispatch(actions.importNewAccount('JSON File', options)), importNewJsonAccount: options => dispatch(actions.importNewAccount('JSON File', options)),
setSelectedAddress: (address) => dispatch(actions.setSelectedAddress(address)),
} }
} }

@ -21,6 +21,7 @@ module.exports = compose(
function mapStateToProps (state) { function mapStateToProps (state) {
return { return {
error: state.appState.warning, error: state.appState.warning,
firstAddress: Object.keys(state.metamask.accounts)[0],
} }
} }
@ -29,7 +30,8 @@ function mapDispatchToProps (dispatch) {
importNewAccount: (strategy, [ privateKey ]) => { importNewAccount: (strategy, [ privateKey ]) => {
return dispatch(actions.importNewAccount(strategy, [ privateKey ])) return dispatch(actions.importNewAccount(strategy, [ privateKey ]))
}, },
displayWarning: () => dispatch(actions.displayWarning(null)), displayWarning: (message) => dispatch(actions.displayWarning(message || null)),
setSelectedAddress: (address) => dispatch(actions.setSelectedAddress(address)),
} }
} }
@ -40,7 +42,7 @@ function PrivateKeyImportView () {
} }
PrivateKeyImportView.prototype.render = function () { PrivateKeyImportView.prototype.render = function () {
const { error } = this.props const { error, displayWarning } = this.props
return ( return (
h('div.new-account-import-form__private-key', [ h('div.new-account-import-form__private-key', [
@ -60,7 +62,10 @@ PrivateKeyImportView.prototype.render = function () {
h('div.new-account-import-form__buttons', {}, [ h('div.new-account-import-form__buttons', {}, [
h('button.btn-default.btn--large.new-account-create-form__button', { h('button.btn-default.btn--large.new-account-create-form__button', {
onClick: () => this.props.history.push(DEFAULT_ROUTE), onClick: () => {
displayWarning(null)
this.props.history.push(DEFAULT_ROUTE)
},
}, [ }, [
this.context.t('cancel'), this.context.t('cancel'),
]), ]),
@ -88,10 +93,16 @@ PrivateKeyImportView.prototype.createKeyringOnEnter = function (event) {
PrivateKeyImportView.prototype.createNewKeychain = function () { PrivateKeyImportView.prototype.createNewKeychain = function () {
const input = document.getElementById('private-key-box') const input = document.getElementById('private-key-box')
const privateKey = input.value const privateKey = input.value
const { importNewAccount, history } = this.props const { importNewAccount, history, displayWarning, setSelectedAddress, firstAddress } = this.props
importNewAccount('Private Key', [ privateKey ]) importNewAccount('Private Key', [ privateKey ])
// JS runtime requires caught rejections but failures are handled by Redux .then(({ selectedAddress }) => {
.catch() if (selectedAddress) {
.then(() => history.push(DEFAULT_ROUTE)) history.push(DEFAULT_ROUTE)
} else {
displayWarning('Error importing account.')
setSelectedAddress(firstAddress)
}
})
.catch(err => displayWarning(err))
} }

@ -22,7 +22,9 @@ class CreateAccountPage extends Component {
}), }),
}), }),
onClick: () => history.push(NEW_ACCOUNT_ROUTE), onClick: () => history.push(NEW_ACCOUNT_ROUTE),
}, 'Create'), }, [
this.context.t('create'),
]),
h('div.new-account__tabs__tab', { h('div.new-account__tabs__tab', {
className: classnames('new-account__tabs__tab', { className: classnames('new-account__tabs__tab', {
@ -31,14 +33,16 @@ class CreateAccountPage extends Component {
}), }),
}), }),
onClick: () => history.push(IMPORT_ACCOUNT_ROUTE), onClick: () => history.push(IMPORT_ACCOUNT_ROUTE),
}, 'Import'), }, [
this.context.t('import'),
]),
]) ])
} }
render () { render () {
return h('div.new-account', {}, [ return h('div.new-account', {}, [
h('div.new-account__header', [ h('div.new-account__header', [
h('div.new-account__title', 'New Account'), h('div.new-account__title', this.context.t('newAccount') ),
this.renderTabs(), this.renderTabs(),
]), ]),
h('div.new-account__form', [ h('div.new-account__form', [
@ -62,6 +66,11 @@ class CreateAccountPage extends Component {
CreateAccountPage.propTypes = { CreateAccountPage.propTypes = {
location: PropTypes.object, location: PropTypes.object,
history: PropTypes.object, history: PropTypes.object,
t: PropTypes.func,
}
CreateAccountPage.contextTypes = {
t: PropTypes.func,
} }
const mapStateToProps = state => ({ const mapStateToProps = state => ({

@ -14,8 +14,8 @@ class Config extends Component {
return h('div.settings__tabs', [ return h('div.settings__tabs', [
h(TabBar, { h(TabBar, {
tabs: [ tabs: [
{ content: 'Settings', key: SETTINGS_ROUTE }, { content: this.context.t('settings'), key: SETTINGS_ROUTE },
{ content: 'Info', key: INFO_ROUTE }, { content: this.context.t('info'), key: INFO_ROUTE },
], ],
isActive: key => matchPath(location.pathname, { path: key, exact: true }), isActive: key => matchPath(location.pathname, { path: key, exact: true }),
onSelect: key => history.push(key), onSelect: key => history.push(key),
@ -54,6 +54,11 @@ class Config extends Component {
Config.propTypes = { Config.propTypes = {
location: PropTypes.object, location: PropTypes.object,
history: PropTypes.object, history: PropTypes.object,
t: PropTypes.func,
}
Config.contextTypes = {
t: PropTypes.func,
} }
module.exports = Config module.exports = Config

@ -120,7 +120,7 @@ class UnlockPage extends Component {
> >
<TextField <TextField
id="password" id="password"
label="Password" label={this.context.t('password')}
type="password" type="password"
value={this.state.password} value={this.state.password}
onChange={event => this.handleInputChange(event)} onChange={event => this.handleInputChange(event)}

@ -4,7 +4,6 @@ const copyToClipboard = require('copy-to-clipboard')
// //
// Sub-Reducers take in the complete state and return their sub-state // Sub-Reducers take in the complete state and return their sub-state
// //
const reduceIdentities = require('./reducers/identities')
const reduceMetamask = require('./reducers/metamask') const reduceMetamask = require('./reducers/metamask')
const reduceApp = require('./reducers/app') const reduceApp = require('./reducers/app')
const reduceLocale = require('./reducers/locale') const reduceLocale = require('./reducers/locale')
@ -21,12 +20,6 @@ function rootReducer (state, action) {
return action.value return action.value
} }
//
// Identities
//
state.identities = reduceIdentities(state, action)
// //
// MetaMask // MetaMask
// //

@ -1,15 +0,0 @@
const extend = require('xtend')
module.exports = reduceIdentities
function reduceIdentities (state, action) {
// clone + defaults
var idState = extend({
}, state.identities)
switch (action.type) {
default:
return idState
}
}

@ -224,7 +224,7 @@ SendTransactionScreen.prototype.renderFromRow = function () {
return h('div.send-v2__form-row', [ return h('div.send-v2__form-row', [
h('div.send-v2__form-label', 'From:'), h('div.send-v2__form-label', this.context.t('from')),
h('div.send-v2__form-field', [ h('div.send-v2__form-field', [
h(FromDropdown, { h(FromDropdown, {
@ -396,7 +396,7 @@ SendTransactionScreen.prototype.renderAmountRow = function () {
return h('div.send-v2__form-row', [ return h('div.send-v2__form-row', [
h('div.send-v2__form-label', [ h('div.send-v2__form-label', [
'Amount:', this.context.t('amount'),
this.renderErrorMessage('amount'), this.renderErrorMessage('amount'),
!errors.amount && gasTotal && h('div.send-v2__amount-max', { !errors.amount && gasTotal && h('div.send-v2__amount-max', {
onClick: (event) => { onClick: (event) => {

@ -14,6 +14,11 @@ class WelcomeScreen extends Component {
closeWelcomeScreen: PropTypes.func.isRequired, closeWelcomeScreen: PropTypes.func.isRequired,
welcomeScreenSeen: PropTypes.bool, welcomeScreenSeen: PropTypes.bool,
history: PropTypes.object, history: PropTypes.object,
t: PropTypes.func,
}
static contextTypes = {
t: PropTypes.func,
} }
constructor (props) { constructor (props) {
@ -45,16 +50,15 @@ class WelcomeScreen extends Component {
height: '225', height: '225',
}), }),
h('div.welcome-screen__info__header', 'Welcome to MetaMask Beta'), h('div.welcome-screen__info__header', this.context.t('welcomeBeta')),
h('div.welcome-screen__info__copy', 'MetaMask is a secure identity vault for Ethereum.'), h('div.welcome-screen__info__copy', this.context.t('metamaskDescription')),
h('div.welcome-screen__info__copy', `It allows you to hold ether & tokens, h('div.welcome-screen__info__copy', this.context.t('holdEther')),
and serves as your bridge to decentralized applications.`),
h('button.welcome-screen__button', { h('button.welcome-screen__button', {
onClick: this.initiateAccountCreation, onClick: this.initiateAccountCreation,
}, 'Continue'), }, this.context.t('continue')),
]), ]),

Loading…
Cancel
Save