From d0f8a14acec274e97d35d9f7a63605581ad21511 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 1 Jun 2016 16:14:49 -0700 Subject: [PATCH 01/23] Add test case for sample hashed address --- test/unit/util_test.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/unit/util_test.js b/test/unit/util_test.js index f003395b3..6ad27ed81 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 = '0x5Fda30Bb72B8Dfe20e48A00dFc108d0915BE9BbA' + const result = util.isValidAddress(address) + const hashed = ethUtil.toChecksumAddress(address) + assert.equal(hashed, address, 'example is hashed correctly') + assert.ok(result) + }) }) describe('numericBalance', function() { From 924a65c956bc6bea4708a0f58b688e4edbb5d3f2 Mon Sep 17 00:00:00 2001 From: Zac Mitton Date: Wed, 1 Jun 2016 16:30:14 -0700 Subject: [PATCH 02/23] network status getting set upon start-up and showing in title bar but not auto-updating yet --- .gitignore | 1 + CHANGELOG.md | 1 + app/images/ethereum-network.jpg | Bin 0 -> 10807 bytes app/images/morden-test-network.jpg | Bin 0 -> 10517 bytes app/images/no-connection.jpg | Bin 0 -> 6946 bytes app/images/unknown-private-network.jpg | Bin 0 -> 3962 bytes ui/app/actions.js | 8 ++++++ ui/app/app.js | 15 ++++++----- ui/app/components/network.js | 34 +++++++++++++++++++++++++ 9 files changed, 53 insertions(+), 6 deletions(-) create mode 100644 app/images/ethereum-network.jpg create mode 100644 app/images/morden-test-network.jpg create mode 100644 app/images/no-connection.jpg create mode 100644 app/images/unknown-private-network.jpg create mode 100644 ui/app/components/network.js 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..97843cf58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Current Master +- show network conection in title bar - 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. diff --git a/app/images/ethereum-network.jpg b/app/images/ethereum-network.jpg new file mode 100644 index 0000000000000000000000000000000000000000..61cb000ed41ae14a21c7a2d51819273750224483 GIT binary patch literal 10807 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 S$8-sxlm#CvR>8@N`TqwurHZNm literal 0 HcmV?d00001 diff --git a/app/images/morden-test-network.jpg b/app/images/morden-test-network.jpg new file mode 100644 index 0000000000000000000000000000000000000000..458708c782511651d2a3352fa5c05a56f885af0f GIT binary patch literal 10517 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 aJnH3o0GTg99NQoye>f5P(XY^7&HfMDO^RIr literal 0 HcmV?d00001 diff --git a/app/images/unknown-private-network.jpg b/app/images/unknown-private-network.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b8a5a9bbfe9b9a0627f0dff41df80203318389d5 GIT binary patch literal 3962 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 UMQ_28#gX6Vo9WN { diff --git a/ui/app/components/network.js b/ui/app/components/network.js new file mode 100644 index 000000000..ac749fe5e --- /dev/null +++ b/ui/app/components/network.js @@ -0,0 +1,34 @@ +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() { + var state = this.props + var networkNumber = state.network + var networkName; + var imagePath = "/images/" + + if(networkNumber == undefined || networkNumber == "error"){ + networkName = "no-connection" + }else if(networkNumber == 1){ + networkName = "ethereum-network" + }else if(networkNumber == 2){ + networkName = "morden-test-network" + }else{ + networkName = "unknown-private-network" + } + return ( + h('#network_component.flex-center', { + style: {}, + title: networkName + },[ h('img',{src: imagePath + networkName + ".jpg", width: '25px'}) ]) + ) +} From f5d107cde10ff288bf93974c63d960c1ec845b57 Mon Sep 17 00:00:00 2001 From: Zac Mitton Date: Wed, 1 Jun 2016 16:37:36 -0700 Subject: [PATCH 03/23] undo style --- ui/app/app.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/ui/app/app.js b/ui/app/app.js index 34cfef7c0..7ddfed2e6 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -111,11 +111,11 @@ App.prototype.renderAppBar = function(){ }, state.isUnlocked && [ // mini logo - // h('img', { - // height: 24, - // width: 24, - // src: '/images/icon-128.png', - // }), + h('img', { + height: 24, + width: 24, + src: '/images/icon-128.png', + }), h(NetworkIndicator, {network: this.props.network}), // metamask name @@ -125,7 +125,6 @@ App.prototype.renderAppBar = function(){ width: 16, barHeight: 2, padding: 0, - paddingLeft: '200px', isOpen: state.menuOpen, color: 'rgb(247,146,30)', onClick: (event) => { From b81b3048c5e61a5f242d7d48810733c724d58f53 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 2 Jun 2016 11:46:35 -0700 Subject: [PATCH 04/23] Bump provider engine version --- CHANGELOG.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bf056b31..6efd0122b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - 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/package.json b/package.json index e9faea36c..f1307a1fd 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.7.2", "web3-stream-provider": "^2.0.1", "xtend": "^4.0.1" }, From 01e63d41ede22a7290553f888261583d22b24eda Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 2 Jun 2016 11:47:31 -0700 Subject: [PATCH 05/23] Version 2.2.0 --- CHANGELOG.md | 2 ++ app/manifest.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6efd0122b..cd5495c94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Current Master +## 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. 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": { From 9cc04be5e467ea6f41584c42cc742680163a6fe6 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 2 Jun 2016 16:52:18 -0700 Subject: [PATCH 06/23] Added seed word recovery to config screen --- CHANGELOG.md | 2 + app/scripts/background.js | 8 +- app/scripts/lib/idStore.js | 11 +- ui/app/actions.js | 27 ++++- ui/app/app.js | 4 + ui/app/config.js | 30 ++++-- ui/app/css/index.css | 4 + ui/app/recover-seed/confirmation.js | 150 ++++++++++++++++++++++++++++ ui/app/reducers/app.js | 14 ++- 9 files changed, 237 insertions(+), 13 deletions(-) create mode 100644 ui/app/recover-seed/confirmation.js diff --git a/CHANGELOG.md b/CHANGELOG.md index cd5495c94..c011ea6af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Current Master +- Added seed word recovery to config screen. + ## 2.2.0 2016-06-02 - Redesigned init, vault create, vault restore and seed confirmation screens. diff --git a/app/scripts/background.js b/app/scripts/background.js index bfd1fc92b..432040c53 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -145,7 +145,7 @@ function setupPublicConfig(stream){ } function setupProviderConnection(stream, originDomain){ - + stream.on('data', function onRpcRequest(payload){ // Append origin to rpc payload payload.origin = originDomain @@ -195,6 +195,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 +248,7 @@ function newUnsignedTransaction(txParams, cb){ }) var txId = idStore.addUnconfirmedTransaction(txParams, cb) } else { - addUnconfirmedTx(txParams, cb) + addUnconfirmedTx(txParams, cb) } } @@ -258,7 +260,7 @@ function newUnsignedMessage(msgParams, cb){ }) var msgId = idStore.addUnconfirmedMessage(msgParams, cb) } else { - addUnconfirmedMsg(msgParams, cb) + addUnconfirmedMsg(msgParams, cb) } } diff --git a/app/scripts/lib/idStore.js b/app/scripts/lib/idStore.js index 4ce4fd6f2..991827603 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) @@ -150,7 +157,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 +373,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/ui/app/actions.js b/ui/app/actions.js index ae6125b20..684d33f3d 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -29,6 +29,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', @@ -155,6 +159,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 +426,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..094cae76c 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -21,6 +21,7 @@ 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') @@ -232,6 +233,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/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 f56e9fbc4..4fd51ac95 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/recover-seed/confirmation.js b/ui/app/recover-seed/confirmation.js new file mode 100644 index 000000000..c7a99ad00 --- /dev/null +++ b/ui/app/recover-seed/confirmation.js @@ -0,0 +1,150 @@ +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', { + style: { + marginTop: '12px', + color: state && state.confirmationWrong ? 'red' : 'black', + } + }, `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..58303f630 100644 --- a/ui/app/reducers/app.js +++ b/ui/app/reducers/app.js @@ -85,7 +85,7 @@ function reduceApp(state, action) { name: 'config', context: appState.currentView.context, }, - transForward: true, + transForward: action.value, }) case actions.SHOW_INFO_PAGE: @@ -144,6 +144,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: From c04d33c6a54dfb5c6ba779a79e19d8f06910ed9f Mon Sep 17 00:00:00 2001 From: kumavis Date: Thu, 2 Jun 2016 16:59:02 -0700 Subject: [PATCH 07/23] hotfix for #236 - chrome notif api not avail --- app/scripts/lib/notifications.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/scripts/lib/notifications.js b/app/scripts/lib/notifications.js index d011d778b..b72939196 100644 --- a/app/scripts/lib/notifications.js +++ b/app/scripts/lib/notifications.js @@ -8,6 +8,9 @@ module.exports = { createMsgNotification: createMsgNotification, } +// 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] @@ -26,6 +29,8 @@ chrome.notifications.onClosed.addListener(function(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 +44,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 +74,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), From 07617dbb075bc0d98725e1116bc936aabda65486 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 2 Jun 2016 17:11:10 -0700 Subject: [PATCH 08/23] Add login check --- app/scripts/lib/idStore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/lib/idStore.js b/app/scripts/lib/idStore.js index 991827603..e9fc10cea 100644 --- a/app/scripts/lib/idStore.js +++ b/app/scripts/lib/idStore.js @@ -61,7 +61,7 @@ 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.')) + if (!this._idmgmt) return cb(new Error('Unauthenticated. Please sign in.')) var seedWords = this._idmgmt.getSeed() cb(null, seedWords) } From 10fec9052f2cd200868de25217bdaa4646fa0913 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 2 Jun 2016 17:11:12 -0700 Subject: [PATCH 09/23] A couple seed recovery enhancements - The seed words are no longer stored on `state.appState.currentView.context`, which caused view glitches since it was shared with other views' data. - The confirmation text warning color is now the same as other error messages'. --- ui/app/first-time/create-vault-complete.js | 2 +- ui/app/recover-seed/confirmation.js | 3 +-- ui/app/reducers/app.js | 6 ++++-- 3 files changed, 6 insertions(+), 5 deletions(-) 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 index c7a99ad00..0276d547d 100644 --- a/ui/app/recover-seed/confirmation.js +++ b/ui/app/recover-seed/confirmation.js @@ -65,10 +65,9 @@ RevealSeedConfirmatoin.prototype.render = function() { }, }), - h('h4', { + h(`h4${state && state.confirmationWrong ? '.error' : ''}`, { style: { marginTop: '12px', - color: state && state.confirmationWrong ? 'red' : 'black', } }, `Enter the phrase "I understand" to proceed.`), diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js index 58303f630..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, @@ -111,7 +112,7 @@ function reduceApp(state, action) { return extend(appState, { currentView: { name: 'createVaultComplete', - context: action.value, + seedWords: action.value, }, transForward: true, isLoading: false, @@ -210,6 +211,7 @@ function reduceApp(state, action) { return extend(appState, { currentView: { name: seedWords ? 'createVaultComplete' : 'accounts', + seedWords, }, transForward: true, isLoading: false, From 90f494c9a1f822436d4b3cd5f9ddd52f27e43378 Mon Sep 17 00:00:00 2001 From: kumavis Date: Thu, 2 Jun 2016 17:29:49 -0700 Subject: [PATCH 10/23] fix illegal return statement :( --- app/scripts/lib/notifications.js | 38 ++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/app/scripts/lib/notifications.js b/app/scripts/lib/notifications.js index b72939196..90edaea12 100644 --- a/app/scripts/lib/notifications.js +++ b/app/scripts/lib/notifications.js @@ -8,24 +8,30 @@ module.exports = { createMsgNotification: createMsgNotification, } -// guard for chrome bug https://github.com/MetaMask/metamask-plugin/issues/236 -if (!chrome.notifications) return console.error('Chrome notifications API missing...') +setupListeners() -// 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) -}) +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 teardown -chrome.notifications.onClosed.addListener(function(notificationId){ - delete notificationHandlers[notificationId] -}) + // 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] + }) + +} // creation helper function createUnlockRequestNotification(opts){ From 272bea31b5983a64c26fdc28c827ccd456bc778f Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 2 Jun 2016 18:42:09 -0700 Subject: [PATCH 11/23] Fix hashed address validation --- test/unit/util_test.js | 6 +++--- ui/app/util.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/unit/util_test.js b/test/unit/util_test.js index 6ad27ed81..12a16999e 100644 --- a/test/unit/util_test.js +++ b/test/unit/util_test.js @@ -78,11 +78,11 @@ describe('util', function() { }) it('should recognize this sample hashed address', function() { - const address = '0x5Fda30Bb72B8Dfe20e48A00dFc108d0915BE9BbA' + const address = '0x5Fda30Bb72B8Dfe20e48A00dFc108d0915BE9Bb0' const result = util.isValidAddress(address) - const hashed = ethUtil.toChecksumAddress(address) + const hashed = ethUtil.toChecksumAddress(address.toLowerCase()) assert.equal(hashed, address, 'example is hashed correctly') - assert.ok(result) + assert.ok(result, 'is valid by our check') }) }) 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) { From 66f06844acaec1498e324f48175c329ccb0fede0 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 3 Jun 2016 12:58:00 -0700 Subject: [PATCH 12/23] Remove metamask logo from header --- ui/app/app.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ui/app/app.js b/ui/app/app.js index 7ddfed2e6..0b2b69e7c 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -110,12 +110,6 @@ App.prototype.renderAppBar = function(){ }, }, state.isUnlocked && [ - // mini logo - h('img', { - height: 24, - width: 24, - src: '/images/icon-128.png', - }), h(NetworkIndicator, {network: this.props.network}), // metamask name From acc00b84ea1b224e39bca08bed88cd3b6770a9eb Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 3 Jun 2016 12:58:14 -0700 Subject: [PATCH 13/23] Make hover text more human friendly --- ui/app/components/network.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/app/components/network.js b/ui/app/components/network.js index ac749fe5e..34b4fab97 100644 --- a/ui/app/components/network.js +++ b/ui/app/components/network.js @@ -17,13 +17,13 @@ Network.prototype.render = function() { var imagePath = "/images/" if(networkNumber == undefined || networkNumber == "error"){ - networkName = "no-connection" + networkName = "No Blockchain Connection" }else if(networkNumber == 1){ - networkName = "ethereum-network" + networkName = "Main Ethereum Network" }else if(networkNumber == 2){ - networkName = "morden-test-network" + networkName = "Morden Test Network" }else{ - networkName = "unknown-private-network" + networkName = "Unknown Private Network" } return ( h('#network_component.flex-center', { From 3170e094c5963ccbde9e8e668bdcfe4f90600549 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 3 Jun 2016 12:59:56 -0700 Subject: [PATCH 14/23] Some minor linting --- ui/app/components/network.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/app/components/network.js b/ui/app/components/network.js index 34b4fab97..dbba2b494 100644 --- a/ui/app/components/network.js +++ b/ui/app/components/network.js @@ -16,13 +16,13 @@ Network.prototype.render = function() { var networkName; var imagePath = "/images/" - if(networkNumber == undefined || networkNumber == "error"){ + if (networkNumber == undefined || networkNumber == "error") { networkName = "No Blockchain Connection" - }else if(networkNumber == 1){ + } else if (networkNumber == 1) { networkName = "Main Ethereum Network" - }else if(networkNumber == 2){ + } else if (networkNumber == 2) { networkName = "Morden Test Network" - }else{ + } else { networkName = "Unknown Private Network" } return ( From d86d7b9ac77c04d4e5800d67f01ec1c859abfbf1 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 3 Jun 2016 13:08:49 -0700 Subject: [PATCH 15/23] Differentiate icon name from hover text --- ui/app/components/network.js | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/ui/app/components/network.js b/ui/app/components/network.js index dbba2b494..5f507f630 100644 --- a/ui/app/components/network.js +++ b/ui/app/components/network.js @@ -11,24 +11,28 @@ function Network() { } Network.prototype.render = function() { - var state = this.props - var networkNumber = state.network - var networkName; - var imagePath = "/images/" + const state = this.props + const networkNumber = state.network + let iconName, hoverText + const imagePath = "/images/" if (networkNumber == undefined || networkNumber == "error") { - networkName = "No Blockchain Connection" + hoverText = 'No Blockchain Connection' + iconName = 'no-connection' } else if (networkNumber == 1) { - networkName = "Main Ethereum Network" - } else if (networkNumber == 2) { - networkName = "Morden Test Network" - } else { - networkName = "Unknown Private Network" + hoverText = 'Main Ethereum Network' + iconName = 'ethereum-network' + }else if (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', { style: {}, - title: networkName - },[ h('img',{src: imagePath + networkName + ".jpg", width: '25px'}) ]) + title: hoverText, + },[ h('img',{src: imagePath + iconName + ".jpg", width: '25px'}) ]) ) } From 2422c78ce20a032a81545bf8cc03394e12c599d5 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 3 Jun 2016 13:58:09 -0700 Subject: [PATCH 16/23] Add network loading indication --- app/scripts/lib/idStore.js | 6 ++++++ ui/app/components/network.js | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/app/scripts/lib/idStore.js b/app/scripts/lib/idStore.js index 4ce4fd6f2..7f2659381 100644 --- a/app/scripts/lib/idStore.js +++ b/app/scripts/lib/idStore.js @@ -131,8 +131,10 @@ IdentityStore.prototype.revealAccount = function(cb) { } IdentityStore.prototype.getNetwork = function(tries) { + if (tries === 0) { this._currentState.network = 'error' + this._didUpdate() return } this.web3.version.getNetwork((err, network) => { @@ -140,7 +142,11 @@ IdentityStore.prototype.getNetwork = function(tries) { return this.getNetwork(tries - 1, cb) } this._currentState.network = network + this._didUpdate() }) + + this._currentState.network = 'loading' + this._didUpdate() } IdentityStore.prototype.setLocked = function(cb){ diff --git a/ui/app/components/network.js b/ui/app/components/network.js index 5f507f630..90c307ed6 100644 --- a/ui/app/components/network.js +++ b/ui/app/components/network.js @@ -19,6 +19,14 @@ Network.prototype.render = function() { if (networkNumber == undefined || networkNumber == "error") { hoverText = 'No Blockchain Connection' iconName = 'no-connection' + } else if (networkNumber == 'loading') { + return h('img', { + title: 'Contacting network...', + style: { + width: '27px', + }, + src: 'images/loading.svg', + }) } else if (networkNumber == 1) { hoverText = 'Main Ethereum Network' iconName = 'ethereum-network' From d49ef1a2e54b0a9ac7a3d23d7744990543246ec6 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 3 Jun 2016 15:18:20 -0700 Subject: [PATCH 17/23] Blockchain status now updates on availability change --- app/scripts/background.js | 19 +++++++++++++------ app/scripts/lib/idStore.js | 16 ++++++++-------- package.json | 2 +- ui/app/components/network.js | 10 ++++------ 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/app/scripts/background.js b/app/scripts/background.js index bfd1fc92b..209360a2d 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 @@ -246,7 +253,7 @@ function newUnsignedTransaction(txParams, cb){ }) var txId = idStore.addUnconfirmedTransaction(txParams, cb) } else { - addUnconfirmedTx(txParams, cb) + addUnconfirmedTx(txParams, cb) } } @@ -258,7 +265,7 @@ function newUnsignedMessage(msgParams, cb){ }) var msgId = idStore.addUnconfirmedMessage(msgParams, cb) } else { - addUnconfirmedMsg(msgParams, cb) + addUnconfirmedMsg(msgParams, cb) } } @@ -290,13 +297,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 7f2659381..85e8c8301 100644 --- a/app/scripts/lib/idStore.js +++ b/app/scripts/lib/idStore.js @@ -130,23 +130,23 @@ IdentityStore.prototype.revealAccount = function(cb) { cb(null) } -IdentityStore.prototype.getNetwork = function(tries) { +IdentityStore.prototype.getNetwork = function(err) { - if (tries === 0) { - this._currentState.network = 'error' + if (err) { + this._currentState.network = 'loading' this._didUpdate() - return } + 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() }) - - this._currentState.network = 'loading' - this._didUpdate() } IdentityStore.prototype.setLocked = function(cb){ 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/ui/app/components/network.js b/ui/app/components/network.js index 90c307ed6..37046fd30 100644 --- a/ui/app/components/network.js +++ b/ui/app/components/network.js @@ -16,14 +16,12 @@ Network.prototype.render = function() { let iconName, hoverText const imagePath = "/images/" - if (networkNumber == undefined || networkNumber == "error") { - hoverText = 'No Blockchain Connection' - iconName = 'no-connection' - } else if (networkNumber == 'loading') { + if (networkNumber == 'loading') { return h('img', { - title: 'Contacting network...', + title: 'Attempting to connect to blockchain.', style: { width: '27px', + marginRight: '-16px' }, src: 'images/loading.svg', }) @@ -39,7 +37,7 @@ Network.prototype.render = function() { } return ( h('#network_component.flex-center', { - style: {}, + style: { marginRight: '-16px' }, title: hoverText, },[ h('img',{src: imagePath + iconName + ".jpg", width: '25px'}) ]) ) From 5560ebba264d0ff5254562c700f86b2546afac4d Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 3 Jun 2016 17:09:18 -0700 Subject: [PATCH 18/23] Clicking network status indicator reveals provider menu --- ui/app/app.js | 62 +++++++++++++++++++++++++++++++++++- ui/app/components/network.js | 3 +- ui/app/css/lib.css | 4 +++ 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/ui/app/app.js b/ui/app/app.js index 7d5d2108a..56981e6fc 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -71,6 +71,7 @@ App.prototype.render = function() { // app bar this.renderAppBar(), + this.renderNetworkDropdown(), this.renderDropdown(), // panel content @@ -111,7 +112,14 @@ App.prototype.renderAppBar = function(){ }, }, state.isUnlocked && [ - h(NetworkIndicator, {network: this.props.network}), + h(NetworkIndicator, { + network: this.props.network, + onClick:(event) => { + event.preventDefault() + event.stopPropagation() + this.setState({ isNetworkMenuOpen: true }) + } + }), // metamask name h('h1', 'MetaMask'), @@ -133,6 +141,58 @@ 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) => { + event.preventDefault() + event.stopPropagation() + 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:() => state.dispatch(actions.setProviderType('mainnet')), + icon: h('img.menu-icon', { src: '/images/ethereum-network.jpg' }), + }), + + h(DropMenuItem, { + label: 'Morden Test Network', + closeMenu:() => this.setState({ isNetworkMenuOpen: false }), + action:() => state.dispatch(actions.setProviderType('testnet')), + icon: h('img.menu-icon', { src: '/images/morden-test-network.jpg' }), + }), + + h(DropMenuItem, { + label: 'Localhost 8545', + closeMenu:() => this.setState({ isNetworkMenuOpen: false }), + action:() => state.dispatch(actions.setRpcTarget('http://localhost:8545')), + icon: h('img.menu-icon', { src: '/images/unknown-private-network.jpg' }), + }), + ]) +} + App.prototype.renderDropdown = function() { const props = this.props return h(MenuDroppo, { diff --git a/ui/app/components/network.js b/ui/app/components/network.js index 37046fd30..e73ecb3f4 100644 --- a/ui/app/components/network.js +++ b/ui/app/components/network.js @@ -39,6 +39,7 @@ Network.prototype.render = function() { h('#network_component.flex-center', { style: { marginRight: '-16px' }, title: hoverText, - },[ h('img',{src: imagePath + iconName + ".jpg", width: '25px'}) ]) + onClick:(event) => this.props.onClick(event), + },[ h('img.menu-icon',{src: imagePath + iconName + ".jpg"}) ]) ) } diff --git a/ui/app/css/lib.css b/ui/app/css/lib.css index 865d8060c..855ed94d8 100644 --- a/ui/app/css/lib.css +++ b/ui/app/css/lib.css @@ -199,3 +199,7 @@ hr.horizontal-line { display: flex; align-items: center; } + +img.menu-icon { + width: 25px; +} From b9007ee8439ed0fbff5b0aede2d0e78d75601c45 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 3 Jun 2016 17:49:54 -0700 Subject: [PATCH 19/23] Add provider menu on clicking network status indicator. Also simplifies network status icons. --- CHANGELOG.md | 3 ++- ui/app/app.js | 37 ++++++++++++++++++------------------ ui/app/components/network.js | 32 +++++++++++++++++++++++++------ ui/app/css/lib.css | 19 ++++++++++++++++-- 4 files changed, 63 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 784c49cf5..64200fe6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,9 @@ ## Current Master -- show network status in title bar +- 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 diff --git a/ui/app/app.js b/ui/app/app.js index 56981e6fc..446c743a9 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -53,10 +53,9 @@ function mapStateToProps(state) { } 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 ( @@ -95,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 ( @@ -104,20 +105,20 @@ 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 && [ h(NetworkIndicator, { network: this.props.network, onClick:(event) => { event.preventDefault() event.stopPropagation() - this.setState({ isNetworkMenuOpen: true }) + this.setState({ isNetworkMenuOpen: !isNetworkMenuOpen }) } }), @@ -128,7 +129,7 @@ App.prototype.renderAppBar = function(){ width: 16, barHeight: 2, padding: 0, - isOpen: state.menuOpen, + isOpen: props.menuOpen, color: 'rgb(247,146,30)', onClick: (event) => { event.preventDefault() @@ -150,9 +151,7 @@ App.prototype.renderNetworkDropdown = function() { return h(MenuDroppo, { isOpen, - onClickOutside: (event) => { - event.preventDefault() - event.stopPropagation() + onClickOutside:(event) => { this.setState({ isNetworkMenuOpen: !isOpen }) }, style: { @@ -173,22 +172,22 @@ App.prototype.renderNetworkDropdown = function() { h(DropMenuItem, { label: 'Main Ethereum Network', closeMenu:() => this.setState({ isNetworkMenuOpen: false }), - action:() => state.dispatch(actions.setProviderType('mainnet')), - icon: h('img.menu-icon', { src: '/images/ethereum-network.jpg' }), + action:() => props.dispatch(actions.setProviderType('mainnet')), + icon: h('.menu-icon.ether-icon'), }), h(DropMenuItem, { label: 'Morden Test Network', closeMenu:() => this.setState({ isNetworkMenuOpen: false }), - action:() => state.dispatch(actions.setProviderType('testnet')), - icon: h('img.menu-icon', { src: '/images/morden-test-network.jpg' }), + action:() => props.dispatch(actions.setProviderType('testnet')), + icon: h('.menu-icon.morden-icon'), }), h(DropMenuItem, { label: 'Localhost 8545', closeMenu:() => this.setState({ isNetworkMenuOpen: false }), - action:() => state.dispatch(actions.setRpcTarget('http://localhost:8545')), - icon: h('img.menu-icon', { src: '/images/unknown-private-network.jpg' }), + action:() => props.dispatch(actions.setRpcTarget('http://localhost:8545')), + icon: h('i.fa.fa-question-circle.fa-lg', { ariaHidden: true }), }), ]) } diff --git a/ui/app/components/network.js b/ui/app/components/network.js index e73ecb3f4..5ad5f9351 100644 --- a/ui/app/components/network.js +++ b/ui/app/components/network.js @@ -21,14 +21,14 @@ Network.prototype.render = function() { title: 'Attempting to connect to blockchain.', style: { width: '27px', - marginRight: '-16px' + marginRight: '-27px' }, src: 'images/loading.svg', }) - } else if (networkNumber == 1) { + } else if (parseInt(networkNumber) == 1) { hoverText = 'Main Ethereum Network' iconName = 'ethereum-network' - }else if (networkNumber == 2) { + }else if (parseInt(networkNumber) == 2) { hoverText = "Morden Test Network" iconName = 'morden-test-network' }else { @@ -36,10 +36,30 @@ Network.prototype.render = function() { iconName = 'unknown-private-network' } return ( - h('#network_component.flex-center', { - style: { marginRight: '-16px' }, + h('#network_component.flex-center.pointer', { + style: { + marginRight: '-27px', + marginLeft: '-3px', + }, title: hoverText, onClick:(event) => this.props.onClick(event), - },[ h('img.menu-icon',{src: imagePath + iconName + ".jpg"}) ]) + },[ + 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/css/lib.css b/ui/app/css/lib.css index 855ed94d8..2a5ebe622 100644 --- a/ui/app/css/lib.css +++ b/ui/app/css/lib.css @@ -200,6 +200,21 @@ hr.horizontal-line { align-items: center; } -img.menu-icon { - width: 25px; +.menu-icon { + display: inline-block; + width: 14px; + height: 14px; + margin: 13px; +} +.ether-icon { + background: rgb(0, 163, 68); + border-radius: 20px; +} +.morden-icon { + background: #E20202; +} + +.drop-menu-item { + display: flex; + align-items: center; } From b1a4c19ddb0f5a5418cb3ef51b18eb79f0790785 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 3 Jun 2016 18:26:26 -0700 Subject: [PATCH 20/23] Make morden sail blue --- ui/app/css/lib.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/css/lib.css b/ui/app/css/lib.css index 2a5ebe622..f17882b9b 100644 --- a/ui/app/css/lib.css +++ b/ui/app/css/lib.css @@ -211,7 +211,7 @@ hr.horizontal-line { border-radius: 20px; } .morden-icon { - background: #E20202; + background: #2465E1; } .drop-menu-item { From 45ac0ef565abf441367068579569a025c9c359d9 Mon Sep 17 00:00:00 2001 From: kumavis Date: Sat, 4 Jun 2016 12:31:15 -0700 Subject: [PATCH 21/23] Update svg-notifications.md --- svg-notifications.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/svg-notifications.md b/svg-notifications.md index fd3b63f7a..2c9e5cc2c 100644 --- a/svg-notifications.md +++ b/svg-notifications.md @@ -8,13 +8,16 @@ Heres some utilities for preparing the data uri: 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) +`'data:image/svg+xml;charset=utf-8,'+encodeURIComponent(svgSrc)` +``` @@ -26,9 +29,10 @@ generate uri Gas: 0.025 Ether +``` or svg-embedded html: - +``` @@ -39,4 +43,5 @@ or svg-embedded html: - \ No newline at end of file + +``` From d64fef63330b3e9084d7cc3dd3741db2ab03fdf7 Mon Sep 17 00:00:00 2001 From: kumavis Date: Sat, 4 Jun 2016 12:32:31 -0700 Subject: [PATCH 22/23] Update svg-notifications.md --- svg-notifications.md | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/svg-notifications.md b/svg-notifications.md index 2c9e5cc2c..fdbb640c1 100644 --- a/svg-notifications.md +++ b/svg-notifications.md @@ -2,21 +2,8 @@ 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)` - ``` @@ -31,6 +18,9 @@ generate uri ``` +generate uri +`'data:image/svg+xml;charset=utf-8,'+encodeURIComponent(svgSrc)` + or svg-embedded html: ``` From 5d9ced3c058d44524f37600bea076ca09da1d9bd Mon Sep 17 00:00:00 2001 From: kumavis Date: Sat, 4 Jun 2016 12:32:54 -0700 Subject: [PATCH 23/23] Update svg-notifications.md --- svg-notifications.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/svg-notifications.md b/svg-notifications.md index fdbb640c1..577e7d07c 100644 --- a/svg-notifications.md +++ b/svg-notifications.md @@ -4,7 +4,7 @@ Taking advantage of this might allow us to show nicely formatted notifications build a template using pure svg: -``` +```svg @@ -22,7 +22,8 @@ generate uri `'data:image/svg+xml;charset=utf-8,'+encodeURIComponent(svgSrc)` or svg-embedded html: -``` + +```svg