aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Finlay <dan@danfinlay.com>2016-05-21 07:18:54 +0800
committerDan Finlay <dan@danfinlay.com>2016-05-21 07:18:54 +0800
commit95a3cfe3fcffee2ffabd4cf71e568ae94693b10f (patch)
tree9c19992a823fb76a790cfdcbabb11ac1dc9b8fc1
parent24fc5f9ea3a8cddfbf3993bdf0b18187a0787a64 (diff)
downloadtangerine-wallet-browser-95a3cfe3fcffee2ffabd4cf71e568ae94693b10f.tar
tangerine-wallet-browser-95a3cfe3fcffee2ffabd4cf71e568ae94693b10f.tar.gz
tangerine-wallet-browser-95a3cfe3fcffee2ffabd4cf71e568ae94693b10f.tar.bz2
tangerine-wallet-browser-95a3cfe3fcffee2ffabd4cf71e568ae94693b10f.tar.lz
tangerine-wallet-browser-95a3cfe3fcffee2ffabd4cf71e568ae94693b10f.tar.xz
tangerine-wallet-browser-95a3cfe3fcffee2ffabd4cf71e568ae94693b10f.tar.zst
tangerine-wallet-browser-95a3cfe3fcffee2ffabd4cf71e568ae94693b10f.zip
Added ability to nickname wallets locally
The changes are persisted to localstorage, so they cannot be restored on a new computer, but for right now it's a nice organizational feature.
-rw-r--r--app/scripts/background.js1
-rw-r--r--app/scripts/lib/config-manager.js20
-rw-r--r--app/scripts/lib/idStore.js12
-rw-r--r--test/unit/actions/save_account_label_test.js36
-rw-r--r--test/unit/config-manager-test.js21
-rw-r--r--ui/app/account-detail.js40
-rw-r--r--ui/app/actions.js18
-rw-r--r--ui/app/components/editable-label.js52
-rw-r--r--ui/app/reducers/metamask.js8
9 files changed, 192 insertions, 16 deletions
diff --git a/app/scripts/background.js b/app/scripts/background.js
index e77df1519..f79047db4 100644
--- a/app/scripts/background.js
+++ b/app/scripts/background.js
@@ -183,6 +183,7 @@ function setupControllerConnection(stream){
clearSeedWordCache: idStore.clearSeedWordCache.bind(idStore),
exportAccount: idStore.exportAccount.bind(idStore),
revealAccount: idStore.revealAccount.bind(idStore),
+ saveAccountLabel: idStore.saveAccountLabel.bind(idStore),
})
stream.pipe(dnode).pipe(stream)
dnode.on('remote', function(remote){
diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js
index 7b2f2f1f8..f5e1cf38d 100644
--- a/app/scripts/lib/config-manager.js
+++ b/app/scripts/lib/config-manager.js
@@ -230,6 +230,26 @@ ConfigManager.prototype.updateTx = function(tx) {
this._saveTxList(transactions)
}
+// wallet nickname methods
+
+ConfigManager.prototype.getWalletNicknames = function() {
+ var data = this.getData()
+ let nicknames = ('walletNicknames' in data) ? data.walletNicknames : {}
+ return nicknames
+}
+
+ConfigManager.prototype.nicknameForWallet = function(account) {
+ let nicknames = this.getWalletNicknames()
+ return nicknames[account]
+}
+
+ConfigManager.prototype.setNicknameForWallet = function(account, nickname) {
+ let nicknames = this.getWalletNicknames()
+ nicknames[account] = nickname
+ var data = this.getData()
+ data.walletNicknames = nicknames
+ this.setData(data)
+}
// observable
diff --git a/app/scripts/lib/idStore.js b/app/scripts/lib/idStore.js
index 0604c4bca..9d2552e8b 100644
--- a/app/scripts/lib/idStore.js
+++ b/app/scripts/lib/idStore.js
@@ -325,9 +325,10 @@ IdentityStore.prototype._loadIdentities = function(){
// // add to ethStore
this._ethStore.addAccount(address)
// add to identities
+ const defaultLabel = 'Wallet ' + (i+1)
+ const nickname = configManager.nicknameForWallet(address)
var identity = {
- name: 'Wallet ' + (i+1),
- img: 'QmW6hcwYzXrNkuHrpvo58YeZvbZxUddv69ATSHY3BHpPdd',
+ name: nickname || defaultLabel,
address: address,
mayBeFauceting: this._mayBeFauceting(i),
}
@@ -336,6 +337,13 @@ IdentityStore.prototype._loadIdentities = function(){
this._didUpdate()
}
+IdentityStore.prototype.saveAccountLabel = function(account, label, cb) {
+ configManager.setNicknameForWallet(account, label)
+ this._loadIdentities()
+ cb(null, label)
+ this._didUpdate()
+}
+
// mayBeFauceting
// If on testnet, index 0 may be fauceting.
// The UI will have to check the balance to know.
diff --git a/test/unit/actions/save_account_label_test.js b/test/unit/actions/save_account_label_test.js
new file mode 100644
index 000000000..1df428b1d
--- /dev/null
+++ b/test/unit/actions/save_account_label_test.js
@@ -0,0 +1,36 @@
+var jsdom = require('mocha-jsdom')
+var assert = require('assert')
+var freeze = require('deep-freeze-strict')
+var path = require('path')
+
+var actions = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'actions.js'))
+var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'reducers.js'))
+
+describe('SAVE_ACCOUNT_LABEL', function() {
+
+ it('updates the state.metamask.identities[:i].name property of the state to the action.value.label', function() {
+ var initialState = {
+ metamask: {
+ identities: {
+ foo: {
+ name: 'bar'
+ }
+ },
+ }
+ }
+ freeze(initialState)
+
+ const action = {
+ type: actions.SAVE_ACCOUNT_LABEL,
+ value: {
+ account: 'foo',
+ label: 'baz'
+ },
+ }
+ freeze(action)
+
+ var resultingState = reducers(initialState, action)
+ assert.equal(resultingState.metamask.identities.foo.name, action.value.label)
+ });
+});
+
diff --git a/test/unit/config-manager-test.js b/test/unit/config-manager-test.js
index e414ecb9e..aa94dc385 100644
--- a/test/unit/config-manager-test.js
+++ b/test/unit/config-manager-test.js
@@ -54,6 +54,27 @@ describe('config-manager', function() {
})
})
+ describe('wallet nicknames', function() {
+ it('should return null when no nicknames are saved', function() {
+ var nick = configManager.nicknameForWallet('0x0')
+ assert.equal(nick, null, 'no nickname returned')
+ })
+
+ it('should persist nicknames', function() {
+ var account = '0x0'
+ var nick1 = 'foo'
+ var nick2 = 'bar'
+ configManager.setNicknameForWallet(account, nick1)
+
+ var result1 = configManager.nicknameForWallet(account)
+ assert.equal(result1, nick1)
+
+ configManager.setNicknameForWallet(account, nick2)
+ var result2 = configManager.nicknameForWallet(account)
+ assert.equal(result2, nick2)
+ })
+ })
+
describe('rpc manipulations', function() {
it('changing rpc should return a different rpc', function() {
var firstRpc = 'first'
diff --git a/ui/app/account-detail.js b/ui/app/account-detail.js
index c708580c4..bae44ec85 100644
--- a/ui/app/account-detail.js
+++ b/ui/app/account-detail.js
@@ -8,12 +8,12 @@ const actions = require('./actions')
const addressSummary = require('./util').addressSummary
const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
-const AccountPanel = require('./components/account-panel')
const Identicon = require('./components/identicon')
const EtherBalance = require('./components/eth-balance')
const transactionList = require('./components/transaction-list')
const ExportAccountView = require('./components/account-export')
const ethUtil = require('ethereumjs-util')
+const EditableLabel = require('./components/editable-label')
module.exports = connect(mapStateToProps)(AccountDetailScreen)
@@ -34,12 +34,12 @@ function AccountDetailScreen() {
}
AccountDetailScreen.prototype.render = function() {
- var state = this.props
- var selected = state.address || Object.keys(state.accounts)[0]
- var identity = state.identities[selected]
- var account = state.accounts[selected]
- var accountDetail = state.accountDetail
- var transactions = state.transactions
+ var props = this.props
+ var selected = props.address || Object.keys(props.accounts)[0]
+ var identity = props.identities[selected]
+ var account = props.accounts[selected]
+ var accountDetail = props.accountDetail
+ var transactions = props.transactions
return (
@@ -78,16 +78,28 @@ AccountDetailScreen.prototype.render = function() {
h('i.fa.fa-users.fa-lg.cursor-pointer.color-orange', {
onClick: this.navigateToAccounts.bind(this),
}),
-
]),
- // account label
- h('h2.font-medium.color-forest.flex-center', {
+ h('.flex-center', {
style: {
- paddingTop: 8,
- marginBottom: 32,
- },
- }, identity && identity.name),
+ height: '62px',
+ paddingTop: '8px',
+ }
+ }, [
+ h(EditableLabel, {
+ textValue: identity ? identity.name : '',
+ state: {
+ isEditingLabel: false,
+ },
+ saveText: (text) => {
+ props.dispatch(actions.saveAccountLabel(selected, text))
+ },
+ }, [
+
+ // What is shown when not editing:
+ h('h2.font-medium.color-forest', identity && identity.name)
+ ]),
+ ]),
// address and getter actions
h('.flex-row.flex-space-between', {
diff --git a/ui/app/actions.js b/ui/app/actions.js
index 5d6f503e2..9ff05c460 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -59,6 +59,8 @@ var actions = {
exportAccount: exportAccount,
SHOW_PRIVATE_KEY: 'SHOW_PRIVATE_KEY',
showPrivateKey: showPrivateKey,
+ SAVE_ACCOUNT_LABEL: 'SAVE_ACCOUNT_LABEL',
+ saveAccountLabel: saveAccountLabel,
// tx conf screen
COMPLETED_TX: 'COMPLETED_TX',
TRANSACTION_ERROR: 'TRANSACTION_ERROR',
@@ -481,6 +483,22 @@ function showPrivateKey(key) {
}
}
+function saveAccountLabel(account, label) {
+ return (dispatch) => {
+ dispatch(this.showLoadingIndication())
+ _accountManager.saveAccountLabel(account, label, (err) => {
+ dispatch(this.hideLoadingIndication())
+ if (err) {
+ return dispatch(this.showWarning(err.message))
+ }
+ dispatch({
+ type: this.SAVE_ACCOUNT_LABEL,
+ value: { account, label },
+ })
+ })
+ }
+}
+
function showSendPage() {
return {
type: this.SHOW_SEND_PAGE,
diff --git a/ui/app/components/editable-label.js b/ui/app/components/editable-label.js
new file mode 100644
index 000000000..20e24a9c7
--- /dev/null
+++ b/ui/app/components/editable-label.js
@@ -0,0 +1,52 @@
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const inherits = require('util').inherits
+const findDOMNode = require('react-dom').findDOMNode
+
+module.exports = EditableLabel
+
+
+inherits(EditableLabel, Component)
+function EditableLabel() {
+ Component.call(this)
+}
+
+EditableLabel.prototype.render = function() {
+ const props = this.props
+ let state = this.state
+
+ if (state && state.isEditingLabel) {
+
+ return h('div.editable-label', [
+ h('input', {
+ defaultValue: props.textValue,
+ onKeyPress:(event) => {
+ this.saveIfEnter(event)
+ },
+ }),
+ h('button', {
+ onClick:() => this.saveText(),
+ }, 'Save')
+ ])
+
+ } else {
+ return h('div', {
+ onClick:(event) => {
+ this.setState({ isEditingLabel: true })
+ },
+ }, this.props.children)
+ }
+}
+
+EditableLabel.prototype.saveIfEnter = function(event) {
+ if (event.key === 'Enter') {
+ this.saveText()
+ }
+}
+
+EditableLabel.prototype.saveText = function() {
+ var container = findDOMNode(this)
+ var text = container.querySelector('.editable-label input').value
+ this.props.saveText(text)
+ this.setState({ isEditingLabel: false, textLabel: text })
+}
diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js
index 8628e84d2..a45327189 100644
--- a/ui/app/reducers/metamask.js
+++ b/ui/app/reducers/metamask.js
@@ -95,6 +95,14 @@ function reduceMetamask(state, action) {
delete newState.seedWords
return newState
+ case actions.SAVE_ACCOUNT_LABEL:
+ const account = action.value.account
+ const name = action.value.label
+ var id = {}
+ id[account] = extend(metamaskState.identities[account], { name })
+ var identities = extend(metamaskState.identities, id)
+ return extend(metamaskState, { identities })
+
default:
return metamaskState