aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrankie <email": null>2016-06-06 22:20:05 +0800
committerFrankie <email": null>2016-06-06 22:20:05 +0800
commit2c2fcd60bfc28c7e500c3cb5e7243cafb8f388e5 (patch)
treecef049dd3e409d1beb23cc316c7f43b47fac3d93
parentf59ca7a6bf42b0cae70aab838a08a7d15e192d10 (diff)
parent5d9ced3c058d44524f37600bea076ca09da1d9bd (diff)
downloadtangerine-wallet-browser-2c2fcd60bfc28c7e500c3cb5e7243cafb8f388e5.tar
tangerine-wallet-browser-2c2fcd60bfc28c7e500c3cb5e7243cafb8f388e5.tar.gz
tangerine-wallet-browser-2c2fcd60bfc28c7e500c3cb5e7243cafb8f388e5.tar.bz2
tangerine-wallet-browser-2c2fcd60bfc28c7e500c3cb5e7243cafb8f388e5.tar.lz
tangerine-wallet-browser-2c2fcd60bfc28c7e500c3cb5e7243cafb8f388e5.tar.xz
tangerine-wallet-browser-2c2fcd60bfc28c7e500c3cb5e7243cafb8f388e5.tar.zst
tangerine-wallet-browser-2c2fcd60bfc28c7e500c3cb5e7243cafb8f388e5.zip
Merge branch 'master' into uiFixes
-rw-r--r--.gitignore1
-rw-r--r--CHANGELOG.md7
-rw-r--r--app/images/ethereum-network.jpgbin0 -> 10807 bytes
-rw-r--r--app/images/morden-test-network.jpgbin0 -> 10517 bytes
-rw-r--r--app/images/no-connection.jpgbin0 -> 6946 bytes
-rw-r--r--app/images/unknown-private-network.jpgbin0 -> 3962 bytes
-rw-r--r--app/manifest.json2
-rw-r--r--app/scripts/background.js21
-rw-r--r--app/scripts/lib/idStore.js27
-rw-r--r--app/scripts/lib/notifications.js43
-rw-r--r--package.json2
-rw-r--r--svg-notifications.md20
-rw-r--r--test/unit/util_test.js7
-rw-r--r--ui/app/actions.js35
-rw-r--r--ui/app/app.js89
-rw-r--r--ui/app/components/network.js65
-rw-r--r--ui/app/config.js30
-rw-r--r--ui/app/css/index.css4
-rw-r--r--ui/app/css/lib.css19
-rw-r--r--ui/app/first-time/create-vault-complete.js2
-rw-r--r--ui/app/recover-seed/confirmation.js149
-rw-r--r--ui/app/reducers/app.js20
-rw-r--r--ui/app/util.js2
23 files changed, 477 insertions, 68 deletions
diff --git a/.gitignore b/.gitignore
index 476b197db..2ad6b035f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,4 @@ package
.DS_Store
builds/
+notes.txt
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2bf056b31..64200fe6d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,9 +2,16 @@
## Current Master
+- Show network status in title bar
+- Added seed word recovery to config screen.
+- Clicking network status indicator now reveals a provider menu.
+
+## 2.2.0 2016-06-02
+
- Redesigned init, vault create, vault restore and seed confirmation screens.
- Added pending transactions to transaction list on account screen.
- Clicking a pending transaction takes you back to the transaction approval screen.
+- Update provider-engine to fix intermittent out of gas errors.
## 2.1.0 2016-05-26
diff --git a/app/images/ethereum-network.jpg b/app/images/ethereum-network.jpg
new file mode 100644
index 000000000..61cb000ed
--- /dev/null
+++ b/app/images/ethereum-network.jpg
Binary files differ
diff --git a/app/images/morden-test-network.jpg b/app/images/morden-test-network.jpg
new file mode 100644
index 000000000..458708c78
--- /dev/null
+++ b/app/images/morden-test-network.jpg
Binary files differ
diff --git a/app/images/no-connection.jpg b/app/images/no-connection.jpg
new file mode 100644
index 000000000..a5d21242b
--- /dev/null
+++ b/app/images/no-connection.jpg
Binary files differ
diff --git a/app/images/unknown-private-network.jpg b/app/images/unknown-private-network.jpg
new file mode 100644
index 000000000..b8a5a9bbf
--- /dev/null
+++ b/app/images/unknown-private-network.jpg
Binary files differ
diff --git a/app/manifest.json b/app/manifest.json
index 5b1be504d..ce157bdf3 100644
--- a/app/manifest.json
+++ b/app/manifest.json
@@ -1,7 +1,7 @@
{
"name": "__MSG_appName__",
"short_name": "Metamask",
- "version": "2.1.0",
+ "version": "2.2.0",
"manifest_version": 2,
"description": "__MSG_appDescription__",
"icons": {
diff --git a/app/scripts/background.js b/app/scripts/background.js
index bfd1fc92b..f64209ecc 100644
--- a/app/scripts/background.js
+++ b/app/scripts/background.js
@@ -76,13 +76,20 @@ var providerOpts = {
var provider = MetaMaskProvider(providerOpts)
var web3 = new Web3(provider)
idStore.web3 = web3
-idStore.getNetwork(3)
+idStore.getNetwork()
// log new blocks
provider.on('block', function(block){
console.log('BLOCK CHANGED:', '#'+block.number.toString('hex'), '0x'+block.hash.toString('hex'))
+
+ // Check network when restoring connectivity:
+ if (idStore._currentState.network === 'loading') {
+ idStore.getNetwork()
+ }
})
+provider.on('error', idStore.getNetwork.bind(idStore))
+
var ethStore = new EthStore(provider)
idStore.setStore(ethStore)
@@ -145,7 +152,7 @@ function setupPublicConfig(stream){
}
function setupProviderConnection(stream, originDomain){
-
+
stream.on('data', function onRpcRequest(payload){
// Append origin to rpc payload
payload.origin = originDomain
@@ -195,6 +202,8 @@ function setupControllerConnection(stream){
exportAccount: idStore.exportAccount.bind(idStore),
revealAccount: idStore.revealAccount.bind(idStore),
saveAccountLabel: idStore.saveAccountLabel.bind(idStore),
+ tryPassword: idStore.tryPassword.bind(idStore),
+ recoverSeed: idStore.recoverSeed.bind(idStore),
})
stream.pipe(dnode).pipe(stream)
dnode.on('remote', function(remote){
@@ -246,7 +255,7 @@ function newUnsignedTransaction(txParams, cb){
})
var txId = idStore.addUnconfirmedTransaction(txParams, cb)
} else {
- addUnconfirmedTx(txParams, cb)
+ addUnconfirmedTx(txParams, cb)
}
}
@@ -258,7 +267,7 @@ function newUnsignedMessage(msgParams, cb){
})
var msgId = idStore.addUnconfirmedMessage(msgParams, cb)
} else {
- addUnconfirmedMsg(msgParams, cb)
+ addUnconfirmedMsg(msgParams, cb)
}
}
@@ -290,13 +299,13 @@ function addUnconfirmedMsg(msgParams, cb){
function setRpcTarget(rpcTarget){
configManager.setRpcTarget(rpcTarget)
chrome.runtime.reload()
- idStore.getNetwork(3) // 3 retry attempts
+ idStore.getNetwork()
}
function setProviderType(type) {
configManager.setProviderType(type)
chrome.runtime.reload()
- idStore.getNetwork(3)
+ idStore.getNetwork()
}
function useEtherscanProvider() {
diff --git a/app/scripts/lib/idStore.js b/app/scripts/lib/idStore.js
index 4ce4fd6f2..33d842d54 100644
--- a/app/scripts/lib/idStore.js
+++ b/app/scripts/lib/idStore.js
@@ -59,6 +59,13 @@ IdentityStore.prototype.createNewVault = function(password, entropy, cb){
})
}
+IdentityStore.prototype.recoverSeed = function(cb){
+ configManager.setShowSeedWords(true)
+ if (!this._idmgmt) return cb(new Error('Unauthenticated. Please sign in.'))
+ var seedWords = this._idmgmt.getSeed()
+ cb(null, seedWords)
+}
+
IdentityStore.prototype.recoverFromSeed = function(password, seed, cb){
this._createIdmgmt(password, seed, null, (err) => {
if (err) return cb(err)
@@ -130,16 +137,22 @@ IdentityStore.prototype.revealAccount = function(cb) {
cb(null)
}
-IdentityStore.prototype.getNetwork = function(tries) {
- if (tries === 0) {
- this._currentState.network = 'error'
- return
+IdentityStore.prototype.getNetwork = function(err) {
+
+ if (err) {
+ this._currentState.network = 'loading'
+ this._didUpdate()
}
+
this.web3.version.getNetwork((err, network) => {
if (err) {
- return this.getNetwork(tries - 1, cb)
+ this._currentState.network = 'loading'
+ return this._didUpdate()
}
+
+ console.log('web3.getNetwork returned ' + network)
this._currentState.network = network
+ this._didUpdate()
})
}
@@ -150,7 +163,7 @@ IdentityStore.prototype.setLocked = function(cb){
}
IdentityStore.prototype.submitPassword = function(password, cb){
- this._tryPassword(password, (err) => {
+ this.tryPassword(password, (err) => {
if (err) return cb(err)
// load identities before returning...
this._loadIdentities()
@@ -366,7 +379,7 @@ IdentityStore.prototype._mayBeFauceting = function(i) {
// keyStore managment - unlocking + deserialization
//
-IdentityStore.prototype._tryPassword = function(password, cb){
+IdentityStore.prototype.tryPassword = function(password, cb){
this._createIdmgmt(password, null, null, cb)
}
diff --git a/app/scripts/lib/notifications.js b/app/scripts/lib/notifications.js
index d011d778b..90edaea12 100644
--- a/app/scripts/lib/notifications.js
+++ b/app/scripts/lib/notifications.js
@@ -8,24 +8,35 @@ module.exports = {
createMsgNotification: createMsgNotification,
}
-// notification button press
-chrome.notifications.onButtonClicked.addListener(function(notificationId, buttonIndex){
- var handlers = notificationHandlers[notificationId]
- if (buttonIndex === 0) {
- handlers.confirm()
- } else {
- handlers.cancel()
- }
- chrome.notifications.clear(notificationId)
-})
+setupListeners()
+
+function setupListeners(){
+
+ // guard for chrome bug https://github.com/MetaMask/metamask-plugin/issues/236
+ if (!chrome.notifications) return console.error('Chrome notifications API missing...')
+
+ // notification button press
+ chrome.notifications.onButtonClicked.addListener(function(notificationId, buttonIndex){
+ var handlers = notificationHandlers[notificationId]
+ if (buttonIndex === 0) {
+ handlers.confirm()
+ } else {
+ handlers.cancel()
+ }
+ chrome.notifications.clear(notificationId)
+ })
+
+ // notification teardown
+ chrome.notifications.onClosed.addListener(function(notificationId){
+ delete notificationHandlers[notificationId]
+ })
-// notification teardown
-chrome.notifications.onClosed.addListener(function(notificationId){
- delete notificationHandlers[notificationId]
-})
+}
// creation helper
function createUnlockRequestNotification(opts){
+ // guard for chrome bug https://github.com/MetaMask/metamask-plugin/issues/236
+ if (!chrome.notifications) return console.error('Chrome notifications API missing...')
var message = 'An Ethereum app has requested a signature. Please unlock your account.'
var id = createId()
@@ -39,6 +50,8 @@ function createUnlockRequestNotification(opts){
}
function createTxNotification(opts){
+ // guard for chrome bug https://github.com/MetaMask/metamask-plugin/issues/236
+ if (!chrome.notifications) return console.error('Chrome notifications API missing...')
var message = [
'Submitted by '+opts.txParams.origin,
'to: '+uiUtils.addressSummary(opts.txParams.to),
@@ -67,6 +80,8 @@ function createTxNotification(opts){
}
function createMsgNotification(opts){
+ // guard for chrome bug https://github.com/MetaMask/metamask-plugin/issues/236
+ if (!chrome.notifications) return console.error('Chrome notifications API missing...')
var message = [
'Submitted by '+opts.msgParams.origin,
'to be signed by: '+uiUtils.addressSummary(opts.msgParams.from),
diff --git a/package.json b/package.json
index e9faea36c..af032a79a 100644
--- a/package.json
+++ b/package.json
@@ -62,7 +62,7 @@
"through2": "^2.0.1",
"vreme": "^3.0.2",
"web3": "ethereum/web3.js#0.16.0",
- "web3-provider-engine": "^7.7.0",
+ "web3-provider-engine": "^7.8.1",
"web3-stream-provider": "^2.0.1",
"xtend": "^4.0.1"
},
diff --git a/svg-notifications.md b/svg-notifications.md
index fd3b63f7a..577e7d07c 100644
--- a/svg-notifications.md
+++ b/svg-notifications.md
@@ -2,19 +2,9 @@ Chrome notifications allow you to show an SVG image via a data-uri
Taking advantage of this might allow us to show nicely formatted notifications
-Heres some utilities for preparing the data uri:
- http://dopiaza.org/tools/datauri/index.php
- provide text
- no base64
- specify mime type: image/svg+xml
- result should look like:
- data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%0D%0A%20%20width%3D%271000px%27%20height%3D%27500px%27%20viewBox%3D%270%200%20200%20100%27%3E%0D%0A%20%20%3Crect%20x%3D%270%27%20y%3D%270%27%20width%3D%27100%25%27%20height%3D%27100%25%27%20fill%3D%27white%27%20%2F%3E%0D%0A%20%20%3Ctext%20x%3D%270%27%20y%3D%2720%27%20font-family%3D%27monospace%27%20font-size%3D%276%27%20fill%3D%27black%27%3E%0D%0A%20%20%20%20%3Ctspan%20x%3D%270%27%20dy%3D%271.2em%27%3EDomain%3A%20https%3A%2F%2Fboardroom.to%3C%2Ftspan%3E%0D%0A%20%20%20%20%3Ctspan%20x%3D%270%27%20dy%3D%271.2em%27%3EFrom%3A%20%200xabcdef%3C%2Ftspan%3E%0D%0A%20%20%20%20%3Ctspan%20x%3D%270%27%20dy%3D%271.2em%27%3ETo%3A%20%20%20%200xfedcba%3C%2Ftspan%3E%0D%0A%20%20%20%20%3Ctspan%20x%3D%270%27%20dy%3D%271.2em%27%3EValue%3A%201.025%20Ether%3C%2Ftspan%3E%0D%0A%20%20%20%20%3Ctspan%20x%3D%270%27%20dy%3D%271.2em%27%3EGas%3A%200.025%20Ether%3C%2Ftspan%3E%0D%0A%20%20%3C%2Ftext%3E%0D%0A%3C%2Fsvg%3E
-
build a template using pure svg:
-generate uri
-'data:image/svg+xml;charset=utf-8,'+encodeURIComponent(svgSrc)
-
+```svg
<svg xmlns='http://www.w3.org/2000/svg'
width='1000px' height='500px' viewBox='0 0 200 100'>
<rect x='0' y='0' width='100%' height='100%' fill='white' />
@@ -26,9 +16,14 @@ generate uri
<tspan x='0' dy='1.2em'>Gas: 0.025 Ether</tspan>
</text>
</svg>
+```
+
+generate uri
+`'data:image/svg+xml;charset=utf-8,'+encodeURIComponent(svgSrc)`
or svg-embedded html:
+```svg
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="500">
<rect x='0' y='0' width='100%' height='100%' fill='white' />
<foreignObject class="node" x="46" y="22" width="200" height="300">
@@ -39,4 +34,5 @@ or svg-embedded html:
</div>
</body>
</foreignObject>
-</svg> \ No newline at end of file
+</svg>
+```
diff --git a/test/unit/util_test.js b/test/unit/util_test.js
index f003395b3..12a16999e 100644
--- a/test/unit/util_test.js
+++ b/test/unit/util_test.js
@@ -77,6 +77,13 @@ describe('util', function() {
assert.ok(!result)
})
+ it('should recognize this sample hashed address', function() {
+ const address = '0x5Fda30Bb72B8Dfe20e48A00dFc108d0915BE9Bb0'
+ const result = util.isValidAddress(address)
+ const hashed = ethUtil.toChecksumAddress(address.toLowerCase())
+ assert.equal(hashed, address, 'example is hashed correctly')
+ assert.ok(result, 'is valid by our check')
+ })
})
describe('numericBalance', function() {
diff --git a/ui/app/actions.js b/ui/app/actions.js
index ae6125b20..dd38c5f0a 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -6,6 +6,8 @@ var actions = {
toggleMenu: toggleMenu,
SET_MENU_STATE: 'SET_MENU_STATE',
closeMenu: closeMenu,
+ getNetworkStatus: 'getNetworkStatus',
+
// remote state
UPDATE_METAMASK_STATE: 'UPDATE_METAMASK_STATE',
updateMetamaskState: updateMetamaskState,
@@ -29,6 +31,10 @@ var actions = {
createNewVaultInProgress: createNewVaultInProgress,
showNewVaultSeed: showNewVaultSeed,
showInfoPage: showInfoPage,
+ // seed recovery actions
+ REVEAL_SEED_CONFIRMATION: 'REVEAL_SEED_CONFIRMATION',
+ revealSeedConfirmation: revealSeedConfirmation,
+ requestRevealSeed: requestRevealSeed,
// unlock screen
UNLOCK_IN_PROGRESS: 'UNLOCK_IN_PROGRESS',
UNLOCK_FAILED: 'UNLOCK_FAILED',
@@ -131,6 +137,12 @@ function closeMenu() {
}
}
+function getNetworkStatus(){
+ return {
+ type: actions.getNetworkStatus,
+ }
+}
+
// async actions
function tryUnlockMetamask(password) {
@@ -155,6 +167,26 @@ function createNewVault(password, entropy) {
}
}
+function revealSeedConfirmation() {
+ return {
+ type: this.REVEAL_SEED_CONFIRMATION,
+ }
+}
+
+function requestRevealSeed(password) {
+ return (dispatch) => {
+ dispatch(actions.showLoadingIndication())
+ _accountManager.tryPassword(password, (err, seed) => {
+ dispatch(actions.hideLoadingIndication())
+ if (err) return dispatch(actions.displayWarning(err.message))
+ _accountManager.recoverSeed((err, seed) => {
+ if (err) return dispatch(actions.displayWarning(err.message))
+ dispatch(actions.showNewVaultSeed(seed))
+ })
+ })
+ }
+}
+
function recoverFromSeed(password, seed) {
return (dispatch) => {
// dispatch(actions.createNewVaultInProgress())
@@ -402,9 +434,10 @@ function previousTx() {
}
}
-function showConfigPage() {
+function showConfigPage(transitionForward = true) {
return {
type: actions.SHOW_CONFIG_PAGE,
+ value: transitionForward,
}
}
diff --git a/ui/app/app.js b/ui/app/app.js
index 7e7ca24ad..446c743a9 100644
--- a/ui/app/app.js
+++ b/ui/app/app.js
@@ -21,12 +21,14 @@ const SendTransactionScreen = require('./send')
const ConfirmTxScreen = require('./conf-tx')
// other views
const ConfigScreen = require('./config')
+const RevealSeedConfirmation = require('./recover-seed/confirmation')
const InfoScreen = require('./info')
const LoadingIndicator = require('./loading')
const txHelper = require('../lib/tx-helper')
const SandwichExpando = require('sandwich-expando')
const MenuDroppo = require('menu-droppo')
const DropMenuItem = require('./components/drop-menu-item')
+const NetworkIndicator = require('./components/network')
module.exports = connect(mapStateToProps)(App)
@@ -46,14 +48,14 @@ function mapStateToProps(state) {
unconfTxs: state.metamask.unconfTxs,
unconfMsgs: state.metamask.unconfMsgs,
menuOpen: state.appState.menuOpen,
+ network: state.metamask.network,
}
}
App.prototype.render = function() {
- // const { selectedReddit, posts, isFetching, lastUpdated } = this.props
- var state = this.props
- var view = state.currentView.name
- var transForward = state.transForward
+ var props = this.props
+ var view = props.currentView.name
+ var transForward = props.transForward
return (
@@ -68,6 +70,7 @@ App.prototype.render = function() {
// app bar
this.renderAppBar(),
+ this.renderNetworkDropdown(),
this.renderDropdown(),
// panel content
@@ -91,7 +94,9 @@ App.prototype.render = function() {
}
App.prototype.renderAppBar = function(){
- var state = this.props
+ const props = this.props
+ const state = this.state || {}
+ const isNetworkMenuOpen = state.isNetworkMenuOpen || false
return (
@@ -100,30 +105,31 @@ App.prototype.renderAppBar = function(){
h('.app-header.flex-row.flex-space-between', {
style: {
alignItems: 'center',
- visibility: state.isUnlocked ? 'visible' : 'none',
- background: state.isUnlocked ? 'white' : 'none',
+ visibility: props.isUnlocked ? 'visible' : 'none',
+ background: props.isUnlocked ? 'white' : 'none',
height: '36px',
position: 'relative',
zIndex: 1,
},
- }, state.isUnlocked && [
+ }, props.isUnlocked && [
- // mini logo
- h('img', {
- height: 24,
- width: 24,
- src: '/images/icon-128.png',
+ h(NetworkIndicator, {
+ network: this.props.network,
+ onClick:(event) => {
+ event.preventDefault()
+ event.stopPropagation()
+ this.setState({ isNetworkMenuOpen: !isNetworkMenuOpen })
+ }
}),
// metamask name
h('h1', 'MetaMask'),
-
// hamburger
h(SandwichExpando, {
width: 16,
barHeight: 2,
padding: 0,
- isOpen: state.menuOpen,
+ isOpen: props.menuOpen,
color: 'rgb(247,146,30)',
onClick: (event) => {
event.preventDefault()
@@ -136,6 +142,56 @@ App.prototype.renderAppBar = function(){
)
}
+App.prototype.renderNetworkDropdown = function() {
+ const props = this.props
+ const state = this.state || {}
+ const isOpen = state.isNetworkMenuOpen
+
+ const checked = h('i.fa.fa-check.fa-lg', { ariaHidden: true })
+
+ return h(MenuDroppo, {
+ isOpen,
+ onClickOutside:(event) => {
+ this.setState({ isNetworkMenuOpen: !isOpen })
+ },
+ style: {
+ position: 'fixed',
+ left: 0,
+ zIndex: 0,
+ },
+ innerStyle: {
+ background: 'white',
+ boxShadow: '1px 1px 2px rgba(0,0,0,0.1)',
+ },
+ }, [ // DROP MENU ITEMS
+ h('style', `
+ .drop-menu-item:hover { background:rgb(235, 235, 235); }
+ .drop-menu-item i { margin: 11px; }
+ `),
+
+ h(DropMenuItem, {
+ label: 'Main Ethereum Network',
+ closeMenu:() => this.setState({ isNetworkMenuOpen: false }),
+ action:() => props.dispatch(actions.setProviderType('mainnet')),
+ icon: h('.menu-icon.ether-icon'),
+ }),
+
+ h(DropMenuItem, {
+ label: 'Morden Test Network',
+ closeMenu:() => this.setState({ isNetworkMenuOpen: false }),
+ action:() => props.dispatch(actions.setProviderType('testnet')),
+ icon: h('.menu-icon.morden-icon'),
+ }),
+
+ h(DropMenuItem, {
+ label: 'Localhost 8545',
+ closeMenu:() => this.setState({ isNetworkMenuOpen: false }),
+ action:() => props.dispatch(actions.setRpcTarget('http://localhost:8545')),
+ icon: h('i.fa.fa-question-circle.fa-lg', { ariaHidden: true }),
+ }),
+ ])
+}
+
App.prototype.renderDropdown = function() {
const props = this.props
return h(MenuDroppo, {
@@ -232,6 +288,9 @@ App.prototype.renderPrimary = function(){
case 'config':
return h(ConfigScreen, {key: 'config'})
+ case 'reveal-seed-conf':
+ return h(RevealSeedConfirmation, {key: 'reveal-seed-conf'})
+
case 'info':
return h(InfoScreen, {key: 'info'})
diff --git a/ui/app/components/network.js b/ui/app/components/network.js
new file mode 100644
index 000000000..5ad5f9351
--- /dev/null
+++ b/ui/app/components/network.js
@@ -0,0 +1,65 @@
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const inherits = require('util').inherits
+
+module.exports = Network
+
+inherits(Network, Component)
+
+function Network() {
+ Component.call(this)
+}
+
+Network.prototype.render = function() {
+ const state = this.props
+ const networkNumber = state.network
+ let iconName, hoverText
+ const imagePath = "/images/"
+
+ if (networkNumber == 'loading') {
+ return h('img', {
+ title: 'Attempting to connect to blockchain.',
+ style: {
+ width: '27px',
+ marginRight: '-27px'
+ },
+ src: 'images/loading.svg',
+ })
+ } else if (parseInt(networkNumber) == 1) {
+ hoverText = 'Main Ethereum Network'
+ iconName = 'ethereum-network'
+ }else if (parseInt(networkNumber) == 2) {
+ hoverText = "Morden Test Network"
+ iconName = 'morden-test-network'
+ }else {
+ hoverText = "Unknown Private Network"
+ iconName = 'unknown-private-network'
+ }
+ return (
+ h('#network_component.flex-center.pointer', {
+ style: {
+ marginRight: '-27px',
+ marginLeft: '-3px',
+ },
+ title: hoverText,
+ onClick:(event) => this.props.onClick(event),
+ },[
+ function() {
+ switch (iconName) {
+ case 'ethereum-network':
+ return h('.menu-icon.ether-icon')
+ case 'morden-test-network':
+ return h('.menu-icon.morden-icon')
+ default:
+ return h('i.fa.fa-question-circle.fa-lg', {
+ ariaHidden: true,
+ style: {
+ margin: '10px',
+ color: 'rgb(125, 128, 130)',
+ },
+ })
+ }
+ }()
+ ])
+ )
+}
diff --git a/ui/app/config.js b/ui/app/config.js
index ddf158325..c4d473b10 100644
--- a/ui/app/config.js
+++ b/ui/app/config.js
@@ -78,7 +78,7 @@ ConfigScreen.prototype.render = function() {
]),
h('div', [
- h('button', {
+ h('button.spaced', {
style: {
alignSelf: 'center',
},
@@ -86,11 +86,11 @@ ConfigScreen.prototype.render = function() {
event.preventDefault()
state.dispatch(actions.setProviderType('mainnet'))
}
- }, 'Use Main Network')
+ }, 'Use Main Network'),
]),
h('div', [
- h('button', {
+ h('button.spaced', {
style: {
alignSelf: 'center',
},
@@ -98,11 +98,11 @@ ConfigScreen.prototype.render = function() {
event.preventDefault()
state.dispatch(actions.setProviderType('testnet'))
}
- }, 'Use Morden Test Network')
+ }, 'Use Morden Test Network'),
]),
h('div', [
- h('button', {
+ h('button.spaced', {
style: {
alignSelf: 'center',
},
@@ -110,7 +110,25 @@ ConfigScreen.prototype.render = function() {
event.preventDefault()
state.dispatch(actions.setRpcTarget('http://localhost:8545/'))
}
- }, 'Use http://localhost:8545')
+ }, 'Use http://localhost:8545'),
+ ]),
+
+ h('hr.horizontal-line'),
+
+ h('div', {
+ style: {
+ marginTop: '20px',
+ }
+ }, [
+ h('button', {
+ style: {
+ alignSelf: 'center',
+ },
+ onClick(event) {
+ event.preventDefault()
+ state.dispatch(actions.revealSeedConfirmation())
+ }
+ }, 'Reveal Seed Words')
]),
]),
diff --git a/ui/app/css/index.css b/ui/app/css/index.css
index fb2a1f552..2ee7c4094 100644
--- a/ui/app/css/index.css
+++ b/ui/app/css/index.css
@@ -45,6 +45,10 @@ button {
transition: transform 50ms ease-in;
}
+button.spaced {
+ margin: 2px;
+}
+
button:hover {
transform: scale(1.1);
}
diff --git a/ui/app/css/lib.css b/ui/app/css/lib.css
index 865d8060c..f17882b9b 100644
--- a/ui/app/css/lib.css
+++ b/ui/app/css/lib.css
@@ -199,3 +199,22 @@ hr.horizontal-line {
display: flex;
align-items: center;
}
+
+.menu-icon {
+ display: inline-block;
+ width: 14px;
+ height: 14px;
+ margin: 13px;
+}
+.ether-icon {
+ background: rgb(0, 163, 68);
+ border-radius: 20px;
+}
+.morden-icon {
+ background: #2465E1;
+}
+
+.drop-menu-item {
+ display: flex;
+ align-items: center;
+}
diff --git a/ui/app/first-time/create-vault-complete.js b/ui/app/first-time/create-vault-complete.js
index 7d2db8003..9eceb4421 100644
--- a/ui/app/first-time/create-vault-complete.js
+++ b/ui/app/first-time/create-vault-complete.js
@@ -14,7 +14,7 @@ function CreateVaultCompleteScreen() {
function mapStateToProps(state) {
return {
- seed: state.appState.currentView.context,
+ seed: state.appState.currentView.seedWords,
cachedSeed: state.metamask.seedWords,
}
}
diff --git a/ui/app/recover-seed/confirmation.js b/ui/app/recover-seed/confirmation.js
new file mode 100644
index 000000000..0276d547d
--- /dev/null
+++ b/ui/app/recover-seed/confirmation.js
@@ -0,0 +1,149 @@
+const inherits = require('util').inherits
+
+const Component = require('react').Component
+const connect = require('react-redux').connect
+const h = require('react-hyperscript')
+const actions = require('../actions')
+
+module.exports = connect(mapStateToProps)(RevealSeedConfirmatoin)
+
+
+inherits(RevealSeedConfirmatoin, Component)
+function RevealSeedConfirmatoin() {
+ Component.call(this)
+}
+
+function mapStateToProps(state) {
+ return {
+ warning: state.appState.warning,
+ }
+}
+
+RevealSeedConfirmatoin.prototype.confirmationPhrase = 'I understand'
+
+RevealSeedConfirmatoin.prototype.render = function() {
+ const props = this.props
+ const state = this.state
+
+ return (
+
+ h('.initialize-screen.flex-column.flex-center.flex-grow', [
+
+ h('h3.flex-center.text-transform-uppercase', {
+ style: {
+ background: '#EBEBEB',
+ color: '#AEAEAE',
+ marginBottom: 24,
+ width: '100%',
+ fontSize: '20px',
+ padding: 6,
+ },
+ }, [
+ 'Reveal Seed Words',
+ ]),
+
+ h('.div', {
+ style: {
+ display: 'flex',
+ flexDirection: 'column',
+ padding: '20px',
+ justifyContent: 'center',
+ }
+ }, [
+
+ h('h4', 'Do not recover your seed words in a public place! These words can be used to steal all your accounts.'),
+
+ // confirmation
+ h('input.large-input.letter-spacey', {
+ type: 'password',
+ id: 'password-box',
+ placeholder: 'Enter your password to confirm',
+ onKeyPress: this.checkConfirmation.bind(this),
+ style: {
+ width: 260,
+ marginTop: '12px',
+ },
+ }),
+
+ h(`h4${state && state.confirmationWrong ? '.error' : ''}`, {
+ style: {
+ marginTop: '12px',
+ }
+ }, `Enter the phrase "I understand" to proceed.`),
+
+ // confirm confirmation
+ h('input.large-input.letter-spacey', {
+ type: 'text',
+ id: 'confirm-box',
+ placeholder: this.confirmationPhrase,
+ onKeyPress: this.checkConfirmation.bind(this),
+ style: {
+ width: 260,
+ marginTop: 16,
+ },
+ }),
+
+ h('.flex-row.flex-space-between', {
+ style: {
+ marginTop: 30,
+ width: '50%',
+ },
+ }, [
+// cancel
+ h('button.primary', {
+ onClick: this.goHome.bind(this),
+ }, 'CANCEL'),
+
+ // submit
+ h('button.primary', {
+ onClick: this.revealSeedWords.bind(this),
+ }, 'OK'),
+
+ ]),
+
+ (props.warning) && (
+ h('span.error', {
+ style: {
+ margin: '20px',
+ }
+ }, props.warning.split('-'))
+ ),
+
+ props.inProgress && (
+ h('span.in-progress-notification', 'Generating Seed...')
+ ),
+ ]),
+ ])
+ )
+}
+
+RevealSeedConfirmatoin.prototype.componentDidMount = function(){
+ document.getElementById('password-box').focus()
+}
+
+RevealSeedConfirmatoin.prototype.goHome = function() {
+ this.props.dispatch(actions.showConfigPage(false))
+}
+
+// create vault
+
+RevealSeedConfirmatoin.prototype.checkConfirmation = function(event) {
+ if (event.key === 'Enter') {
+ event.preventDefault()
+ this.revealSeedWords()
+ }
+}
+
+RevealSeedConfirmatoin.prototype.revealSeedWords = function(){
+ this.setState({ confirmationWrong: false })
+
+ const confirmBox = document.getElementById('confirm-box')
+ const confirmation = confirmBox.value
+ if (confirmation !== this.confirmationPhrase) {
+ confirmBox.value = ''
+ return this.setState({ confirmationWrong: true })
+ }
+
+ var password = document.getElementById('password-box').value
+ this.props.dispatch(actions.requestRevealSeed(password))
+}
diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js
index 08c2268c1..3ee9a61fe 100644
--- a/ui/app/reducers/app.js
+++ b/ui/app/reducers/app.js
@@ -25,10 +25,11 @@ function reduceApp(state, action) {
}
// confirm seed words
+ var seedWords = state.metamask.seedWords
var seedConfView = {
name: 'createVaultComplete',
+ seedWords,
}
- var seedWords = state.metamask.seedWords
var appState = extend({
menuOpen: false,
@@ -85,7 +86,7 @@ function reduceApp(state, action) {
name: 'config',
context: appState.currentView.context,
},
- transForward: true,
+ transForward: action.value,
})
case actions.SHOW_INFO_PAGE:
@@ -111,7 +112,7 @@ function reduceApp(state, action) {
return extend(appState, {
currentView: {
name: 'createVaultComplete',
- context: action.value,
+ seedWords: action.value,
},
transForward: true,
isLoading: false,
@@ -144,6 +145,18 @@ function reduceApp(state, action) {
warning: null,
})
+ // reveal seed words
+
+ case actions.REVEAL_SEED_CONFIRMATION:
+ return extend(appState, {
+ currentView: {
+ name: 'reveal-seed-conf',
+ },
+ transForward: true,
+ warning: null,
+ })
+
+
// accounts
case actions.SET_SELECTED_ACCOUNT:
@@ -198,6 +211,7 @@ function reduceApp(state, action) {
return extend(appState, {
currentView: {
name: seedWords ? 'createVaultComplete' : 'accounts',
+ seedWords,
},
transForward: true,
isLoading: false,
diff --git a/ui/app/util.js b/ui/app/util.js
index 91f85e43f..6ece28a9e 100644
--- a/ui/app/util.js
+++ b/ui/app/util.js
@@ -52,7 +52,7 @@ function addressSummary(address) {
function isValidAddress(address) {
var prefixed = ethUtil.addHexPrefix(address)
- return isAllOneCase(prefixed) && ethUtil.isValidAddress(prefixed) || ethUtil.isValidChecksumAddress(prefixed)
+ return (isAllOneCase(prefixed) && ethUtil.isValidAddress(prefixed)) || ethUtil.isValidChecksumAddress(prefixed)
}
function isAllOneCase(address) {