diff options
-rw-r--r-- | CHANGELOG.md | 2 | ||||
-rw-r--r-- | app/scripts/lib/controllers/preferences.js | 37 | ||||
-rw-r--r-- | app/scripts/metamask-controller.js | 1 | ||||
-rw-r--r-- | development/states/first-time.json | 1 | ||||
-rw-r--r-- | test/unit/actions/config_test.js | 28 | ||||
-rw-r--r-- | ui/app/actions.js | 18 | ||||
-rw-r--r-- | ui/app/app.js | 37 | ||||
-rw-r--r-- | ui/app/config.js | 1 | ||||
-rw-r--r-- | ui/app/reducers/metamask.js | 3 |
9 files changed, 114 insertions, 14 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 761831b79..5d3d0c9cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Current Master + +- Add two most recently used custom RPCs to network dropdown menu. - Add personal_sign method support. ## 3.3.0 2017-2-20 diff --git a/app/scripts/lib/controllers/preferences.js b/app/scripts/lib/controllers/preferences.js index c5e93a5b9..9343fe67b 100644 --- a/app/scripts/lib/controllers/preferences.js +++ b/app/scripts/lib/controllers/preferences.js @@ -1,10 +1,12 @@ const ObservableStore = require('obs-store') const normalizeAddress = require('eth-sig-util').normalize +const extend = require('xtend') + class PreferencesController { constructor (opts = {}) { - const initState = opts.initState || {} + const initState = extend({ frequentRpcList: [] }, opts.initState) this.store = new ObservableStore(initState) } @@ -12,7 +14,7 @@ class PreferencesController { // PUBLIC METHODS // - setSelectedAddress(_address) { + setSelectedAddress (_address) { return new Promise((resolve, reject) => { const address = normalizeAddress(_address) this.store.updateState({ selectedAddress: address }) @@ -20,14 +22,43 @@ class PreferencesController { }) } - getSelectedAddress(_address) { + getSelectedAddress (_address) { return this.store.getState().selectedAddress } + updateFrequentRpcList (_url) { + return this.addToFrequentRpcList(_url) + .then((rpcList) => { + this.store.updateState({ frequentRpcList: rpcList }) + return rpcList + }) + } + + addToFrequentRpcList (_url) { + let rpcList = this.getFrequentRpcList() + let index = rpcList.findIndex((element) => { return element === _url }) + if (index !== -1) { + rpcList.splice(index, 1) + } + if (_url !== 'http://localhost:8545') { + rpcList.push(_url) + } + if (rpcList.length > 2) { + rpcList.shift() + } + return Promise.resolve(rpcList) + } + + getFrequentRpcList () { + return this.store.getState().frequentRpcList + } + // // PRIVATE METHODS // + + } module.exports = PreferencesController diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index f172c67a8..d8136667f 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -266,6 +266,7 @@ module.exports = class MetamaskController extends EventEmitter { // PreferencesController setSelectedAddress: nodeify(preferencesController.setSelectedAddress).bind(preferencesController), + updateFrequentRpcList: nodeify(preferencesController.updateFrequentRpcList).bind(preferencesController), // KeyringController setLocked: nodeify(keyringController.setLocked).bind(keyringController), diff --git a/development/states/first-time.json b/development/states/first-time.json index 108af9117..3554ee911 100644 --- a/development/states/first-time.json +++ b/development/states/first-time.json @@ -4,6 +4,7 @@ "isUnlocked": false, "rpcTarget": "https://rawtestrpc.metamask.io/", "identities": {}, + "frequentRpcList": [], "unapprovedTxs": {}, "currentFiat": "USD", "conversionRate": 12.7527416, diff --git a/test/unit/actions/config_test.js b/test/unit/actions/config_test.js index f851e4102..fea0cf0ba 100644 --- a/test/unit/actions/config_test.js +++ b/test/unit/actions/config_test.js @@ -11,6 +11,7 @@ describe ('config view actions', function() { var initialState = { metamask: { rpcTarget: 'foo', + frequentRpcList: [] }, appState: { currentView: { @@ -30,15 +31,36 @@ describe ('config view actions', function() { describe('SET_RPC_TARGET', function() { it('sets the state.metamask.rpcTarget property of the state to the action.value', function() { + const value = { + rpcTarget: 'foo', + frequentRpcList: ['foo'] + } const action = { type: actions.SET_RPC_TARGET, - value: 'bar', + value, } var result = reducers(initialState, action) assert.equal(result.metamask.provider.type, 'rpc') - assert.equal(result.metamask.provider.rpcTarget, action.value) + assert.equal(result.metamask.provider.rpcTarget, value.rpcTarget) + assert.equal(result.metamask.frequentRpcList[0], value.frequentRpcList[0]) + }) + + it('should handle multiple requests to change the rpc gracefully', function() { + const value = { + rpcTarget: 'foo', + frequentRpcList: ['foo'] + } + + const action = { + type: actions.SET_RPC_TARGET, + value, + } + + var result = reducers(initialState, action) + var secondResult = reducers(result, action) + assert.equal(secondResult.metamask.frequentRpcList.length, 1) }) }) -}) +}) diff --git a/ui/app/actions.js b/ui/app/actions.js index 89a4fadfa..4154c002c 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -659,11 +659,19 @@ function markAccountsFound() { // function setRpcTarget (newRpc) { - log.debug(`background.setRpcTarget`) - background.setRpcTarget(newRpc) - return { - type: actions.SET_RPC_TARGET, - value: newRpc, + return (dispatch) => { + log.debug(`background.setRpcTarget`) + background.setRpcTarget(newRpc) + background.updateFrequentRpcList(newRpc, (frequentRpcList) => { + const value = { + rpcTarget: newRpc, + frequentRpcList, + } + dispatch({ + type: actions.SET_RPC_TARGET, + value, + }) + }) } } diff --git a/ui/app/app.js b/ui/app/app.js index 63fab5db8..20f948770 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -58,6 +58,7 @@ function mapStateToProps (state) { forgottenPassword: state.appState.forgottenPassword, lastUnreadNotice: state.metamask.lastUnreadNotice, lostAccounts: state.metamask.lostAccounts, + frequentRpcList: state.metamask.frequentRpcList || [], } } @@ -211,6 +212,7 @@ App.prototype.renderAppBar = function () { App.prototype.renderNetworkDropdown = function () { const props = this.props + const rpcList = props.frequentRpcList const state = this.state || {} const isOpen = state.isNetworkMenuOpen @@ -256,12 +258,15 @@ App.prototype.renderNetworkDropdown = function () { h(DropMenuItem, { label: 'Localhost 8545', closeMenu: () => this.setState({ isNetworkMenuOpen: false }), - action: () => props.dispatch(actions.setRpcTarget('http://localhost:8545')), + action: () => { + props.dispatch(actions.setRpcTarget('http://localhost:8545')) + }, icon: h('i.fa.fa-question-circle.fa-lg'), activeNetworkRender: props.provider.rpcTarget, }), this.renderCustomOption(props.provider), + this.renderCommonRpc(rpcList, props.provider), props.isUnlocked && h(DropMenuItem, { label: 'Custom RPC', @@ -496,6 +501,12 @@ App.prototype.renderCustomOption = function (provider) { const { rpcTarget, type } = provider if (type !== 'rpc') return null + // Concatenate long URLs + let label = rpcTarget + if (rpcTarget.length > 31) { + label = label.substr(0, 34) + '...' + } + switch (rpcTarget) { case 'http://localhost:8545': @@ -503,10 +514,32 @@ App.prototype.renderCustomOption = function (provider) { default: return h(DropMenuItem, { - label: `${rpcTarget}`, + label, + key: rpcTarget, closeMenu: () => this.setState({ isNetworkMenuOpen: false }), icon: h('i.fa.fa-question-circle.fa-lg'), activeNetworkRender: 'custom', }) } } + +App.prototype.renderCommonRpc = function (rpcList, provider) { + const { rpcTarget } = provider + const props = this.props + + return rpcList.map((rpc) => { + if ((rpc === 'http://localhost:8545') || (rpc === rpcTarget)) { + return null + } else { + return h(DropMenuItem, { + label: rpc, + key: rpc, + closeMenu: () => this.setState({ isNetworkMenuOpen: false }), + action: () => props.dispatch(actions.setRpcTarget(rpc)), + icon: h('i.fa.fa-question-circle.fa-lg'), + activeNetworkRender: rpc, + }) + } + }) + +} diff --git a/ui/app/config.js b/ui/app/config.js index 65b1ed712..00a4cba88 100644 --- a/ui/app/config.js +++ b/ui/app/config.js @@ -5,6 +5,7 @@ const connect = require('react-redux').connect const actions = require('./actions') const currencies = require('./conversion.json').rows const validUrl = require('valid-url') + module.exports = connect(mapStateToProps)(ConfigScreen) function mapStateToProps (state) { diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js index 3875cf6d1..7bf2969e7 100644 --- a/ui/app/reducers/metamask.js +++ b/ui/app/reducers/metamask.js @@ -55,9 +55,10 @@ function reduceMetamask (state, action) { case actions.SET_RPC_TARGET: return extend(metamaskState, { + frequentRpcList: action.value.frequentRpcList, provider: { type: 'rpc', - rpcTarget: action.value, + rpcTarget: action.value.rpcTarget, }, }) |