From 22d9e3a7e6dfd21b3ea52007030d71f53e29b851 Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Wed, 25 Oct 2017 18:23:46 -0700 Subject: [PATCH] Allow editing account name in account details modal --- ui/app/actions.js | 26 ++-- ui/app/components/editable-label.js | 115 +++++++++++------- .../modals/account-details-modal.js | 21 ++-- ui/app/components/qr-code.js | 10 +- .../css/itcss/components/editable-label.scss | 34 ++++++ ui/app/css/itcss/components/index.scss | 2 + ui/app/css/itcss/components/modal.scss | 5 + 7 files changed, 149 insertions(+), 64 deletions(-) create mode 100644 ui/app/css/itcss/components/editable-label.scss diff --git a/ui/app/actions.js b/ui/app/actions.js index 445d19529..2e9b34c58 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -1034,7 +1034,7 @@ function setProviderType (type) { dispatch(actions.updateProviderType(type)) dispatch(actions.setSelectedToken()) }) - + } } @@ -1213,14 +1213,22 @@ function saveAccountLabel (account, label) { return (dispatch) => { dispatch(actions.showLoadingIndication()) log.debug(`background.saveAccountLabel`) - background.saveAccountLabel(account, label, (err) => { - dispatch(actions.hideLoadingIndication()) - if (err) { - return dispatch(actions.displayWarning(err.message)) - } - dispatch({ - type: actions.SAVE_ACCOUNT_LABEL, - value: { account, label }, + + return new Promise((resolve, reject) => { + background.saveAccountLabel(account, label, (err) => { + dispatch(actions.hideLoadingIndication()) + + if (err) { + dispatch(actions.displayWarning(err.message)) + reject(err) + } + + dispatch({ + type: actions.SAVE_ACCOUNT_LABEL, + value: { account, label }, + }) + + resolve(account) }) }) } diff --git a/ui/app/components/editable-label.js b/ui/app/components/editable-label.js index 8a5c8954f..eb41ec50c 100644 --- a/ui/app/components/editable-label.js +++ b/ui/app/components/editable-label.js @@ -1,57 +1,88 @@ -const Component = require('react').Component +const { Component } = require('react') +const PropTypes = require('prop-types') const h = require('react-hyperscript') -const inherits = require('util').inherits -const findDOMNode = require('react-dom').findDOMNode +const classnames = require('classnames') -module.exports = EditableLabel +class EditableLabel extends Component { + constructor (props) { + super(props) -inherits(EditableLabel, Component) -function EditableLabel () { - Component.call(this) -} + this.state = { + isEditing: false, + value: props.defaultValue || '', + } + } + + handleSubmit () { + const { value } = this.state + + if (value === '') { + return + } + + Promise.resolve(this.props.onSubmit(value)) + .then(() => this.setState({ isEditing: false })) + } + + saveIfEnter (event) { + if (event.key === 'Enter') { + this.handleSubmit() + } + } -EditableLabel.prototype.render = function () { - const props = this.props - const state = this.state + renderEditing () { + const { value } = this.state - if (state && state.isEditingLabel) { - return h('div.editable-label', [ - h('input.sizing-input', { - defaultValue: props.textValue, - maxLength: '20', + return ([ + h('input.large-input.editable-label__input', { + type: 'text', + required: true, + value: this.state.value, onKeyPress: (event) => { - this.saveIfEnter(event) + if (event.key === 'Enter') { + this.handleSubmit() + } }, + onChange: event => this.setState({ value: event.target.value }), + className: classnames({ 'editable-label__input--error': value === '' }), }), - h('button.editable-button', { - onClick: () => this.saveText(), - }, 'Save'), + h('div.editable-label__icon-wrapper', [ + h('i.fa.fa-check.editable-label__icon', { + onClick: () => this.handleSubmit(), + }), + ]), ]) - } else { - return h('div.name-label', { - onClick: (event) => { - const nameAttribute = event.target.getAttribute('name') - // checks for class to handle smaller CTA above the account name - const classAttribute = event.target.getAttribute('class') - if (nameAttribute === 'edit' || classAttribute === 'edit-text') { - this.setState({ isEditingLabel: true }) - } - }, - }, this.props.children) } -} -EditableLabel.prototype.saveIfEnter = function (event) { - if (event.key === 'Enter') { - this.saveText() + renderReadonly () { + return ([ + h('div.editable-label__value', this.state.value), + h('div.editable-label__icon-wrapper', [ + h('i.fa.fa-pencil.editable-label__icon', { + onClick: () => this.setState({ isEditing: true }), + }), + ]), + ]) + } + + render () { + const { isEditing } = this.state + const { className } = this.props + + return ( + h('div.editable-label', { className: classnames(className) }, + isEditing + ? this.renderEditing() + : this.renderReadonly() + ) + ) } } -EditableLabel.prototype.saveText = function () { - // eslint-disable-next-line react/no-find-dom-node - var container = findDOMNode(this) - var text = container.querySelector('.editable-label input').value - var truncatedText = text.substring(0, 20) - this.props.saveText(truncatedText) - this.setState({ isEditingLabel: false, textLabel: truncatedText }) +EditableLabel.propTypes = { + onSubmit: PropTypes.func.isRequired, + defaultValue: PropTypes.string, + className: PropTypes.string, } + +module.exports = EditableLabel diff --git a/ui/app/components/modals/account-details-modal.js b/ui/app/components/modals/account-details-modal.js index 37a62e1c0..e3c936702 100644 --- a/ui/app/components/modals/account-details-modal.js +++ b/ui/app/components/modals/account-details-modal.js @@ -7,6 +7,7 @@ const AccountModalContainer = require('./account-modal-container') const { getSelectedIdentity, getSelectedAddress } = require('../../selectors') const genAccountLink = require('../../../lib/account-link.js') const QrView = require('../qr-code') +const EditableLabel = require('../editable-label') function mapStateToProps (state) { return { @@ -23,6 +24,7 @@ function mapDispatchToProps (dispatch) { dispatch(actions.showModal({ name: 'EXPORT_PRIVATE_KEY' })) }, hideModal: () => dispatch(actions.hideModal()), + saveAccountLabel: (address, label) => dispatch(actions.saveAccountLabel(address, label)), } } @@ -41,14 +43,19 @@ AccountDetailsModal.prototype.render = function () { selectedIdentity, network, showExportPrivateKeyModal, - hideModal, + saveAccountLabel, } = this.props const { name, address } = selectedIdentity return h(AccountModalContainer, {}, [ + h(EditableLabel, { + className: 'account-modal__name', + defaultValue: name, + onSubmit: label => saveAccountLabel(address, label), + }), + h(QrView, { Qr: { - message: name, data: address, }, }), @@ -57,14 +64,12 @@ AccountDetailsModal.prototype.render = function () { h('button.btn-clear', { onClick: () => global.platform.openWindow({ url: genAccountLink(address, network) }), - }, [ 'View account on Etherscan' ]), + }, 'View account on Etherscan'), // Holding on redesign for Export Private Key functionality h('button.btn-clear', { - onClick: () => { - showExportPrivateKeyModal() - }, - }, [ 'Export private key' ]), - + onClick: () => showExportPrivateKeyModal(), + }, 'Export private key'), + ]) } diff --git a/ui/app/components/qr-code.js b/ui/app/components/qr-code.js index cc723df14..83885539c 100644 --- a/ui/app/components/qr-code.js +++ b/ui/app/components/qr-code.js @@ -29,11 +29,11 @@ QrCodeView.prototype.render = function () { const qrImage = qrCode(4, 'M') qrImage.addData(address) qrImage.make() - return h('.div.flex-column.flex-center', { - style: { - }, - }, [ - Array.isArray(Qr.message) ? h('.message-container', this.renderMultiMessage()) : h('.qr-header', Qr.message), + + return h('.div.flex-column.flex-center', [ + Array.isArray(Qr.message) + ? h('.message-container', this.renderMultiMessage()) + : Qr.message && h('.qr-header', Qr.message), this.props.warning ? this.props.warning && h('span.error.flex-center', { style: { diff --git a/ui/app/css/itcss/components/editable-label.scss b/ui/app/css/itcss/components/editable-label.scss new file mode 100644 index 000000000..13570610c --- /dev/null +++ b/ui/app/css/itcss/components/editable-label.scss @@ -0,0 +1,34 @@ +.editable-label { + display: flex; + align-items: center; + justify-content: center; + position: relative; + + &__value { + max-width: 250px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + &__input { + width: 250px; + font-size: 14px; + text-align: center; + + &--error { + border: 1px solid $monzo; + } + } + + &__icon-wrapper { + position: absolute; + margin-left: 10px; + left: 100%; + } + + &__icon { + cursor: pointer; + color: $dusty-gray; + } +} diff --git a/ui/app/css/itcss/components/index.scss b/ui/app/css/itcss/components/index.scss index 03c59cacf..4ba02be67 100644 --- a/ui/app/css/itcss/components/index.scss +++ b/ui/app/css/itcss/components/index.scss @@ -47,3 +47,5 @@ @import './request-signature.scss'; @import './account-dropdown-mini.scss'; + +@import './editable-label.scss'; diff --git a/ui/app/css/itcss/components/modal.scss b/ui/app/css/itcss/components/modal.scss index cd1039d0a..b69bd5c7e 100644 --- a/ui/app/css/itcss/components/modal.scss +++ b/ui/app/css/itcss/components/modal.scss @@ -294,6 +294,11 @@ font-size: 18px; } +.account-modal__name { + margin-top: 9px; + font-size: 20px; +} + .private-key-password { display: flex; flex-direction: column;