Store for EditPassword Modal (#3979)
* External store (WIP) * address & meta * Add editable (WIP) * View converted (WIP) * Single API stub creation * Testing (WIP) * Simplified meta assign * Tests running * Fix duplicate exports * Fix tags not editable
This commit is contained in:
@@ -14,54 +14,55 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import ContentClear from 'material-ui/svg-icons/content/clear';
|
||||
import CheckIcon from 'material-ui/svg-icons/navigation/check';
|
||||
import SendIcon from 'material-ui/svg-icons/content/send';
|
||||
|
||||
import { Tabs, Tab } from 'material-ui/Tabs';
|
||||
import Paper from 'material-ui/Paper';
|
||||
import { Tabs, Tab } from 'material-ui/Tabs';
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { showSnackbar } from '~/redux/providers/snackbarActions';
|
||||
|
||||
import Form, { Input } from '~/ui/Form';
|
||||
import { newError, showSnackbar } from '~/redux/actions';
|
||||
import { Button, Modal, IdentityName, IdentityIcon } from '~/ui';
|
||||
import Form, { Input } from '~/ui/Form';
|
||||
import { CancelIcon, CheckIcon, SendIcon } from '~/ui/Icons';
|
||||
|
||||
import Store, { CHANGE_ACTION, TEST_ACTION } from './store';
|
||||
import styles from './passwordManager.css';
|
||||
|
||||
const TEST_ACTION = 'TEST_ACTION';
|
||||
const CHANGE_ACTION = 'CHANGE_ACTION';
|
||||
const MSG_SUCCESS_STYLE = {
|
||||
backgroundColor: 'rgba(174, 213, 129, 0.75)'
|
||||
};
|
||||
const MSG_FAILURE_STYLE = {
|
||||
backgroundColor: 'rgba(229, 115, 115, 0.75)'
|
||||
};
|
||||
const TABS_INKBAR_STYLE = {
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.55)'
|
||||
};
|
||||
const TABS_ITEM_STYLE = {
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.05)'
|
||||
};
|
||||
|
||||
class PasswordManager extends Component {
|
||||
@observer
|
||||
export default class PasswordManager extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
account: PropTypes.object.isRequired,
|
||||
showSnackbar: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func
|
||||
}
|
||||
|
||||
state = {
|
||||
action: TEST_ACTION,
|
||||
waiting: false,
|
||||
showMessage: false,
|
||||
message: { value: '', success: true },
|
||||
currentPass: '',
|
||||
newPass: '',
|
||||
repeatNewPass: '',
|
||||
repeatValid: true,
|
||||
passwordHint: this.props.account.meta && this.props.account.meta.passwordHint || ''
|
||||
}
|
||||
store = new Store(this.context.api, this.props.account);
|
||||
|
||||
render () {
|
||||
return (
|
||||
<Modal
|
||||
actions={ this.renderDialogActions() }
|
||||
title='Password Manager'
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='passwordChange.title'
|
||||
defaultMessage='Password Manager' />
|
||||
}
|
||||
visible>
|
||||
{ this.renderAccount() }
|
||||
{ this.renderPage() }
|
||||
@@ -71,150 +72,168 @@ class PasswordManager extends Component {
|
||||
}
|
||||
|
||||
renderMessage () {
|
||||
const { message, showMessage } = this.state;
|
||||
const { infoMessage } = this.store;
|
||||
|
||||
const style = message.success
|
||||
? {
|
||||
backgroundColor: 'rgba(174, 213, 129, 0.75)'
|
||||
}
|
||||
: {
|
||||
backgroundColor: 'rgba(229, 115, 115, 0.75)'
|
||||
};
|
||||
|
||||
const classes = [ styles.message ];
|
||||
|
||||
if (!showMessage) {
|
||||
classes.push(styles.hideMessage);
|
||||
if (!infoMessage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper
|
||||
zDepth={ 1 }
|
||||
style={ style }
|
||||
className={ classes.join(' ') }>
|
||||
{ message.value }
|
||||
className={ `${styles.message}` }
|
||||
style={
|
||||
infoMessage.success
|
||||
? MSG_SUCCESS_STYLE
|
||||
: MSG_FAILURE_STYLE
|
||||
}
|
||||
zDepth={ 1 }>
|
||||
{ infoMessage.value }
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
renderAccount () {
|
||||
const { account } = this.props;
|
||||
const { address, meta } = account;
|
||||
|
||||
const passwordHint = meta && meta.passwordHint
|
||||
? (
|
||||
<span className={ styles.passwordHint }>
|
||||
<span className={ styles.hintLabel }>Hint </span>
|
||||
{ meta.passwordHint }
|
||||
</span>
|
||||
)
|
||||
: null;
|
||||
const { address, passwordHint } = this.store;
|
||||
|
||||
return (
|
||||
<div className={ styles.accountContainer }>
|
||||
<IdentityIcon
|
||||
address={ address }
|
||||
/>
|
||||
<IdentityIcon address={ address } />
|
||||
<div className={ styles.accountInfos }>
|
||||
<IdentityName
|
||||
className={ styles.accountName }
|
||||
address={ address }
|
||||
unknown
|
||||
/>
|
||||
className={ styles.accountName }
|
||||
unknown />
|
||||
<span className={ styles.accountAddress }>
|
||||
{ address }
|
||||
</span>
|
||||
{ passwordHint }
|
||||
<span className={ styles.passwordHint }>
|
||||
<span className={ styles.hintLabel }>Hint </span>
|
||||
{ passwordHint || '-' }
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderPage () {
|
||||
const { account } = this.props;
|
||||
const { waiting, repeatValid } = this.state;
|
||||
const disabled = !!waiting;
|
||||
|
||||
const repeatError = repeatValid
|
||||
? null
|
||||
: 'the two passwords differ';
|
||||
|
||||
const { meta } = account;
|
||||
const passwordHint = meta && meta.passwordHint || '';
|
||||
const { busy, isRepeatValid, passwordHint } = this.store;
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
inkBarStyle={ {
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.55)'
|
||||
} }
|
||||
tabItemContainerStyle={ {
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.05)'
|
||||
} }
|
||||
>
|
||||
inkBarStyle={ TABS_INKBAR_STYLE }
|
||||
tabItemContainerStyle={ TABS_ITEM_STYLE }>
|
||||
<Tab
|
||||
onActive={ this.handleTestActive }
|
||||
label='Test Password'
|
||||
>
|
||||
<Form
|
||||
className={ styles.form }
|
||||
>
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='passwordChange.tabTest.label'
|
||||
defaultMessage='Test Password' />
|
||||
}
|
||||
onActive={ this.onActivateTestTab }>
|
||||
<Form className={ styles.form }>
|
||||
<div>
|
||||
<Input
|
||||
label='password'
|
||||
hint='your current password for this account'
|
||||
type='password'
|
||||
disabled={ busy }
|
||||
hint={
|
||||
<FormattedMessage
|
||||
id='passwordChange.testPassword.hint'
|
||||
defaultMessage='your account password' />
|
||||
}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='passwordChange.testPassword.label'
|
||||
defaultMessage='password' />
|
||||
}
|
||||
onChange={ this.onEditTestPassword }
|
||||
onSubmit={ this.testPassword }
|
||||
submitOnBlur={ false }
|
||||
disabled={ disabled }
|
||||
onSubmit={ this.handleTestPassword }
|
||||
onChange={ this.onEditCurrent } />
|
||||
type='password' />
|
||||
</div>
|
||||
</Form>
|
||||
</Tab>
|
||||
<Tab
|
||||
onActive={ this.handleChangeActive }
|
||||
label='Change Password'
|
||||
>
|
||||
<Form
|
||||
className={ styles.form }
|
||||
>
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='passwordChange.tabChange.label'
|
||||
defaultMessage='Change Password' />
|
||||
}
|
||||
onActive={ this.onActivateChangeTab }>
|
||||
<Form className={ styles.form }>
|
||||
<div>
|
||||
<Input
|
||||
label='current password'
|
||||
hint='your current password for this account'
|
||||
type='password'
|
||||
disabled={ busy }
|
||||
hint={
|
||||
<FormattedMessage
|
||||
id='passwordChange.currentPassword.hint'
|
||||
defaultMessage='your current password for this account' />
|
||||
}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='passwordChange.currentPassword.label'
|
||||
defaultMessage='current password' />
|
||||
}
|
||||
onChange={ this.onEditCurrentPassword }
|
||||
onSubmit={ this.changePassword }
|
||||
submitOnBlur={ false }
|
||||
disabled={ disabled }
|
||||
onSubmit={ this.handleChangePassword }
|
||||
onChange={ this.onEditCurrent } />
|
||||
type='password' />
|
||||
<Input
|
||||
label='(optional) new password hint'
|
||||
hint='hint for the new password'
|
||||
disabled={ busy }
|
||||
hint={
|
||||
<FormattedMessage
|
||||
id='passwordChange.passwordHint.hint'
|
||||
defaultMessage='hint for the new password' />
|
||||
}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='passwordChange.passwordHint.label'
|
||||
defaultMessage='(optional) new password hint' />
|
||||
}
|
||||
onChange={ this.onEditNewPasswordHint }
|
||||
onSubmit={ this.changePassword }
|
||||
submitOnBlur={ false }
|
||||
value={ passwordHint }
|
||||
disabled={ disabled }
|
||||
onSubmit={ this.handleChangePassword }
|
||||
onChange={ this.onEditHint } />
|
||||
value={ passwordHint } />
|
||||
<div className={ styles.passwords }>
|
||||
<div className={ styles.password }>
|
||||
<Input
|
||||
label='new password'
|
||||
hint='the new password for this account'
|
||||
type='password'
|
||||
disabled={ busy }
|
||||
hint={
|
||||
<FormattedMessage
|
||||
id='passwordChange.newPassword.hint'
|
||||
defaultMessage='the new password for this account' />
|
||||
}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='passwordChange.newPassword.label'
|
||||
defaultMessage='new password' />
|
||||
}
|
||||
onChange={ this.onEditNewPassword }
|
||||
onSubmit={ this.changePassword }
|
||||
submitOnBlur={ false }
|
||||
disabled={ disabled }
|
||||
onSubmit={ this.handleChangePassword }
|
||||
onChange={ this.onEditNew } />
|
||||
type='password' />
|
||||
</div>
|
||||
<div className={ styles.password }>
|
||||
<Input
|
||||
label='repeat new password'
|
||||
hint='repeat the new password for this account'
|
||||
type='password'
|
||||
disabled={ busy }
|
||||
error={
|
||||
isRepeatValid
|
||||
? null
|
||||
: <FormattedMessage
|
||||
id='passwordChange.repeatPassword.error'
|
||||
defaultMessage='the supplied passwords do not match' />
|
||||
}
|
||||
hint={
|
||||
<FormattedMessage
|
||||
id='passwordChange.repeatPassword.hint'
|
||||
defaultMessage='repeat the new password for this account' />
|
||||
}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='passwordChange.repeatPassword.label'
|
||||
defaultMessage='repeat new password' />
|
||||
}
|
||||
onChange={ this.onEditNewPasswordRepeat }
|
||||
onSubmit={ this.changePassword }
|
||||
submitOnBlur={ false }
|
||||
error={ repeatError }
|
||||
disabled={ disabled }
|
||||
onSubmit={ this.handleChangePassword }
|
||||
onChange={ this.onEditRepeatNew } />
|
||||
type='password' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -225,176 +244,118 @@ class PasswordManager extends Component {
|
||||
}
|
||||
|
||||
renderDialogActions () {
|
||||
const { actionTab, busy, isRepeatValid } = this.store;
|
||||
const { onClose } = this.props;
|
||||
const { action, waiting, repeatValid } = this.state;
|
||||
|
||||
const cancelBtn = (
|
||||
<Button
|
||||
icon={ <ContentClear /> }
|
||||
label='Cancel'
|
||||
icon={ <CancelIcon /> }
|
||||
key='cancel'
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='passwordChange.button.cancel'
|
||||
defaultMessage='Cancel' />
|
||||
}
|
||||
onClick={ onClose } />
|
||||
);
|
||||
|
||||
if (waiting) {
|
||||
const waitingBtn = (
|
||||
if (busy) {
|
||||
return [
|
||||
cancelBtn,
|
||||
<Button
|
||||
disabled
|
||||
label='Wait...' />
|
||||
);
|
||||
|
||||
return [ cancelBtn, waitingBtn ];
|
||||
key='wait'
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='passwordChange.button.wait'
|
||||
defaultMessage='Wait...' />
|
||||
} />
|
||||
];
|
||||
}
|
||||
|
||||
if (action === TEST_ACTION) {
|
||||
const testBtn = (
|
||||
if (actionTab === TEST_ACTION) {
|
||||
return [
|
||||
cancelBtn,
|
||||
<Button
|
||||
icon={ <CheckIcon /> }
|
||||
label='Test'
|
||||
onClick={ this.handleTestPassword } />
|
||||
);
|
||||
|
||||
return [ cancelBtn, testBtn ];
|
||||
key='test'
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='passwordChange.button.test'
|
||||
defaultMessage='Test' />
|
||||
}
|
||||
onClick={ this.testPassword } />
|
||||
];
|
||||
}
|
||||
|
||||
const changeBtn = (
|
||||
return [
|
||||
cancelBtn,
|
||||
<Button
|
||||
disabled={ !repeatValid }
|
||||
disabled={ !isRepeatValid }
|
||||
icon={ <SendIcon /> }
|
||||
label='Change'
|
||||
onClick={ this.handleChangePassword } />
|
||||
);
|
||||
|
||||
return [ cancelBtn, changeBtn ];
|
||||
}
|
||||
|
||||
onEditCurrent = (event, value) => {
|
||||
this.setState({
|
||||
currentPass: value,
|
||||
showMessage: false
|
||||
});
|
||||
}
|
||||
|
||||
onEditNew = (event, value) => {
|
||||
const repeatValid = value === this.state.repeatNewPass;
|
||||
|
||||
this.setState({
|
||||
newPass: value,
|
||||
showMessage: false,
|
||||
repeatValid
|
||||
});
|
||||
}
|
||||
|
||||
onEditRepeatNew = (event, value) => {
|
||||
const repeatValid = value === this.state.newPass;
|
||||
|
||||
this.setState({
|
||||
repeatNewPass: value,
|
||||
showMessage: false,
|
||||
repeatValid
|
||||
});
|
||||
}
|
||||
|
||||
onEditHint = (event, value) => {
|
||||
this.setState({
|
||||
passwordHint: value,
|
||||
showMessage: false
|
||||
});
|
||||
}
|
||||
|
||||
handleTestActive = () => {
|
||||
this.setState({
|
||||
action: TEST_ACTION,
|
||||
showMessage: false
|
||||
});
|
||||
}
|
||||
|
||||
handleChangeActive = () => {
|
||||
this.setState({
|
||||
action: CHANGE_ACTION,
|
||||
showMessage: false
|
||||
});
|
||||
}
|
||||
|
||||
handleTestPassword = () => {
|
||||
const { account } = this.props;
|
||||
const { currentPass } = this.state;
|
||||
|
||||
this.setState({ waiting: true, showMessage: false });
|
||||
|
||||
this.context
|
||||
.api.parity
|
||||
.testPassword(account.address, currentPass)
|
||||
.then(correct => {
|
||||
const message = correct
|
||||
? { value: 'This password is correct', success: true }
|
||||
: { value: 'This password is not correct', success: false };
|
||||
|
||||
this.setState({ waiting: false, message, showMessage: true });
|
||||
})
|
||||
.catch(e => {
|
||||
console.error('passwordManager::handleTestPassword', e);
|
||||
this.setState({ waiting: false });
|
||||
});
|
||||
}
|
||||
|
||||
handleChangePassword = () => {
|
||||
const { account, showSnackbar, onClose } = this.props;
|
||||
const { currentPass, newPass, repeatNewPass, passwordHint } = this.state;
|
||||
|
||||
if (repeatNewPass !== newPass) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({ waiting: true, showMessage: false });
|
||||
|
||||
this.context
|
||||
.api.parity
|
||||
.testPassword(account.address, currentPass)
|
||||
.then(correct => {
|
||||
if (!correct) {
|
||||
const message = {
|
||||
value: 'This provided current password is not correct',
|
||||
success: false
|
||||
};
|
||||
|
||||
this.setState({ waiting: false, message, showMessage: true });
|
||||
|
||||
return false;
|
||||
key='change'
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='passwordChange.button.change'
|
||||
defaultMessage='Change' />
|
||||
}
|
||||
onClick={ this.changePassword } />
|
||||
];
|
||||
}
|
||||
|
||||
const meta = Object.assign({}, account.meta, {
|
||||
passwordHint
|
||||
});
|
||||
onActivateChangeTab = () => {
|
||||
this.store.setActionTab(CHANGE_ACTION);
|
||||
}
|
||||
|
||||
return Promise.all([
|
||||
this.context
|
||||
.api.parity
|
||||
.setAccountMeta(account.address, meta),
|
||||
onActivateTestTab = () => {
|
||||
this.store.setActionTab(TEST_ACTION);
|
||||
}
|
||||
|
||||
this.context
|
||||
.api.parity
|
||||
.changePassword(account.address, currentPass, newPass)
|
||||
])
|
||||
.then(() => {
|
||||
showSnackbar(<div>Your password has been successfully changed.</div>);
|
||||
this.setState({ waiting: false, showMessage: false });
|
||||
onClose();
|
||||
});
|
||||
onEditCurrentPassword = (event, password) => {
|
||||
this.store.setPassword(password);
|
||||
}
|
||||
|
||||
onEditNewPassword = (event, password) => {
|
||||
this.store.setNewPassword(password);
|
||||
}
|
||||
|
||||
onEditNewPasswordHint = (event, passwordHint) => {
|
||||
this.store.setNewPasswordHint(passwordHint);
|
||||
}
|
||||
|
||||
onEditNewPasswordRepeat = (event, password) => {
|
||||
this.store.setNewPasswordRepeat(password);
|
||||
}
|
||||
|
||||
onEditTestPassword = (event, password) => {
|
||||
this.store.setValidatePassword(password);
|
||||
}
|
||||
|
||||
changePassword = () => {
|
||||
return this.store
|
||||
.changePassword()
|
||||
.then((result) => {
|
||||
if (result) {
|
||||
showSnackbar(
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id='passwordChange.success'
|
||||
defaultMessage='Your password has been successfully changed' />
|
||||
</div>
|
||||
);
|
||||
this.props.onClose();
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
console.error('passwordManager::handleChangePassword', e);
|
||||
this.setState({ waiting: false });
|
||||
.catch((error) => {
|
||||
newError(error);
|
||||
});
|
||||
}
|
||||
|
||||
testPassword = () => {
|
||||
return this.store
|
||||
.testPassword()
|
||||
.catch((error) => {
|
||||
newError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return bindActionCreators({
|
||||
showSnackbar
|
||||
}, dispatch);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
null,
|
||||
mapDispatchToProps
|
||||
)(PasswordManager);
|
||||
|
||||
Reference in New Issue
Block a user