diff --git a/src/actions/OptionsActions.js b/src/actions/OptionsActions.js index df1e03b..583c669 100644 --- a/src/actions/OptionsActions.js +++ b/src/actions/OptionsActions.js @@ -13,12 +13,23 @@ class OptionsActions { 'activateShortLoginFailure', 'deactivateShortLoginSuccess', 'deactivateShortLoginFailure', + 'showImportKeepass', + 'hideImportKeepass', + 'showChangePassword', + 'hideChangePassword', + 'importKeepassProgress', + 'importKeepassSuccess', + 'importKeepassFailure', 'hideQRCode', 'hideShortLogin', 'changeDelaySuccess', 'changeDelayFailure', 'showRescueCodesSuccess', - 'hideRescueCodes' + 'hideRescueCodes', + 'changeNewPass1', + 'changeNewPass2', + 'changePasswordSuccess', + 'changePasswordFailure' ); } @@ -87,6 +98,20 @@ class OptionsActions { }; } + changePassword({ newPass }) { + return dispatch => { + dispatch(); + secretin + .changePassword(newPass) + .then(() => { + this.changePasswordSuccess(); + }) + .catch(() => { + this.changePasswordFailure(); + }); + }; + } + toggleTotp({ checked }) { if (checked) { return true; diff --git a/src/components/options/ChangePasswordShow.js b/src/components/options/ChangePasswordShow.js new file mode 100644 index 0000000..701785a --- /dev/null +++ b/src/components/options/ChangePasswordShow.js @@ -0,0 +1,138 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import connectToStores from 'alt-utils/lib/connectToStores'; + +import Modal from 'components/utilities/Modal'; +import Button from 'components/utilities/Button'; +import Input from 'components/utilities/Input'; + +import OptionsStore from 'stores/OptionsStore'; +import OptionsActions from 'actions/OptionsActions'; + +class ChangePasswordShow extends Component { + static propTypes = { + shown: PropTypes.bool, + loading: PropTypes.bool, + error: PropTypes.string, + newPass1: PropTypes.string, + newPass2: PropTypes.string, + }; + + static defaultProps = { + shown: false, + loading: false, + newPass1: '', + newPass2: '', + error: '', + }; + + static getStores() { + return [OptionsStore]; + } + + static getPropsFromStores() { + const state = OptionsStore.getNewPass(); + + return { + errors: state.get('error'), + shown: state.get('shown'), + loading: state.get('loading'), + newPass1: state.get('newPass1'), + newPass2: state.get('newPass2'), + }; + } + + constructor(props) { + super(props); + + this.state = { + success: false, + }; + this.handleChangePassword = this.handleChangePassword.bind(this); + } + + handleChangePassword() { + OptionsActions.changePassword({ + newPass: this.props.newPass1, + }); + } + + componentWillReceiveProps(nextProps) { + if (this.props.loading && !nextProps.loading && nextProps.error === '') { + this.setState({ + success: true, + }); + } else { + this.setState({ + success: false, + }); + } + } + + render() { + return ( + + + Change master password + + + {!this.state.success + ? + + {this.props.newPass1.length > 0 && + + + } +
+ {this.props.error === '' && + this.props.newPass1.length > 0 && + this.props.newPass1 !== this.props.newPass2 && + 'Passwords mismatch'} + {this.props.error !== '' && this.props.error} +
+
+ : +
Success
+
} + + + + {this.props.newPass1.length > 0 && + } + +
+ ); + } +} + +export default connectToStores(ChangePasswordShow); diff --git a/src/components/options/OptionsContainer.js b/src/components/options/OptionsContainer.js index 3c7c5fe..ff93fed 100644 --- a/src/components/options/OptionsContainer.js +++ b/src/components/options/OptionsContainer.js @@ -7,6 +7,7 @@ import AppUIStore from 'stores/AppUIStore'; import ShortLoginShow from 'components/options/ShortLoginShow'; import QRCodeShow from 'components/options/QRCodeShow'; import RescueCodesShow from 'components/options/RescueCodesShow'; +import ChangePasswordShow from 'components/options/ChangePasswordShow'; import Title from 'components/utilities/Title'; import Checkbox from 'components/utilities/Checkbox'; import Input from 'components/utilities/Input'; @@ -19,6 +20,7 @@ import OptionsStore from 'stores/OptionsStore'; class OptionsContainer extends Component { static propTypes = { options: PropTypes.instanceOf(Immutable.Map), + newPass: PropTypes.instanceOf(Immutable.Map), }; static getStores() { @@ -39,7 +41,6 @@ class OptionsContainer extends Component { render() { const { options } = this.props; - return (
@@ -117,8 +118,17 @@ class OptionsContainer extends Component {
-

Password generation

-
SOON BY @dqms
+
+ + +
diff --git a/src/components/utilities/Button.js b/src/components/utilities/Button.js index ab5b3e0..c0be20a 100644 --- a/src/components/utilities/Button.js +++ b/src/components/utilities/Button.js @@ -11,7 +11,13 @@ class Button extends Component { form: PropTypes.string, disabled: PropTypes.bool, size: PropTypes.string, - buttonStyle: PropTypes.oneOf(['default', 'primary', 'icon']), + buttonStyle: PropTypes.oneOf([ + 'default', + 'primary', + 'icon', + 'success', + 'warning', + ]), to: PropTypes.string, onClick: PropTypes.func, children: PropTypes.oneOfType([ diff --git a/src/stores/OptionsStore.js b/src/stores/OptionsStore.js index f17a921..f2fc458 100644 --- a/src/stores/OptionsStore.js +++ b/src/stores/OptionsStore.js @@ -11,6 +11,13 @@ const OptionsState = new Record({ showQRCode: false, showShortLogin: false, showRescueCodes: false, + newPass: new Immutable.Map({ + shown: false, + newPass1: '', + newPass2: '', + error: '', + loading: false, + }), rescueCodes: new Immutable.List(), loading: false, }); @@ -108,6 +115,62 @@ class OptionsStore { this.setState(this.state.setIn(['options', 'timeToClose'], timeToClose)); } + onChangeNewPass1(newPass1) { + this.setState(this.state.setIn(['newPass', 'newPass1'], newPass1.value)); + } + + onChangeNewPass2(newPass2) { + this.setState(this.state.setIn(['newPass', 'newPass2'], newPass2.value)); + } + + onShowChangePassword() { + this.setState( + this.state + .setIn(['newPass', 'newPass1'], '') + .setIn(['newPass', 'newPass2'], '') + .setIn(['newPass', 'error'], '') + .setIn(['newPass', 'loading'], false) + .setIn(['newPass', 'shown'], true) + ); + } + + onHideChangePassword() { + this.setState( + this.state + .setIn(['newPass', 'newPass1'], '') + .setIn(['newPass', 'newPass2'], '') + .setIn(['newPass', 'error'], '') + .setIn(['newPass', 'loading'], false) + .setIn(['newPass', 'shown'], false) + ); + } + + onChangePassword() { + this.setState( + this.state + .setIn(['newPass', 'loading'], true) + .setIn(['newPass', 'error'], '') + ); + } + + onChangePasswordSuccess() { + this.setState( + this.state + .setIn(['newPass', 'newPass1'], '') + .setIn(['newPass', 'newPass2'], '') + .setIn(['newPass', 'error'], '') + .setIn(['newPass', 'loading'], false) + ); + } + + onChangePasswordFailure() { + this.setState( + this.state + .setIn(['newPass', 'error'], 'Password change failed') + .setIn(['newPass', 'loading'], false) + ); + } + onShowRescueCodesSuccess({ rescueCodes }) { this.setState( this.state.merge({ @@ -129,6 +192,10 @@ class OptionsStore { static getOptions() { return this.getState().get('options'); } + + static getNewPass() { + return this.getState().get('newPass'); + } } export default alt.createStore(makeImmutable(OptionsStore), 'OptionsStore'); diff --git a/src/stylesheets/application.sass b/src/stylesheets/application.sass index f14c0d3..643a777 100644 --- a/src/stylesheets/application.sass +++ b/src/stylesheets/application.sass @@ -30,5 +30,6 @@ @import 'components/users/UserConnect' @import 'components/options/OptionsContainer' +@import 'components/options/ChangePassword' @import 'components/options/OptionsTOTP' @import 'components/options/ImportKeepass' diff --git a/src/stylesheets/components/options/ChangePassword.sass b/src/stylesheets/components/options/ChangePassword.sass new file mode 100644 index 0000000..ef27225 --- /dev/null +++ b/src/stylesheets/components/options/ChangePassword.sass @@ -0,0 +1,10 @@ +.options-changepassword + margin-left: 20px + +.options-changepassword-infos + margin-top: 10px + +.options-changepassword-success + color: $color-nephritis + text-align: center + font-size: 20px \ No newline at end of file diff --git a/src/stylesheets/components/utilities/Button.sass b/src/stylesheets/components/utilities/Button.sass index 8aaa2e7..8bd5b0d 100644 --- a/src/stylesheets/components/utilities/Button.sass +++ b/src/stylesheets/components/utilities/Button.sass @@ -40,8 +40,17 @@ background-color: $color-peter-river color: white +.button--style-warning + @extend .button--style-primary + background-color: $color-pomegranate + +.button--style-success + @extend .button--style-primary + background-color: $color-nephritis + .button--style-default, -.button--style-primary +.button--style-primary, +.button--style-warning &.button--size-base .icon margin-left: -12px