From 4344af58ee6ed03701197bf546c4a2e26a8cd9cb Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 14 Jun 2017 09:31:16 +0100 Subject: [PATCH] Ask for email address after setting password for the first time So that the user can reset their password. --- src/Modal.js | 6 +- .../views/dialogs/SetEmailDialog.js | 164 ++++++++++++++++++ .../views/settings/ChangePassword.js | 26 ++- 3 files changed, 193 insertions(+), 3 deletions(-) create mode 100644 src/components/views/dialogs/SetEmailDialog.js diff --git a/src/Modal.js b/src/Modal.js index 8d53b2da7d..e100105a88 100644 --- a/src/Modal.js +++ b/src/Modal.js @@ -64,7 +64,6 @@ const AsyncWrapper = React.createClass({ render: function() { const {loader, ...otherProps} = this.props; - if (this.state.component) { const Component = this.state.component; return ; @@ -199,4 +198,7 @@ class ModalManager { } } -export default new ModalManager(); +if (!global.singletonModalManager) { + global.singletonModalManager = new ModalManager(); +} +export default global.singletonModalManager; diff --git a/src/components/views/dialogs/SetEmailDialog.js b/src/components/views/dialogs/SetEmailDialog.js new file mode 100644 index 0000000000..6d0e8468ca --- /dev/null +++ b/src/components/views/dialogs/SetEmailDialog.js @@ -0,0 +1,164 @@ +/* +Copyright 2017 Vector Creations Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import q from 'q'; +import React from 'react'; +import sdk from '../../../index'; +import MatrixClientPeg from '../../../MatrixClientPeg'; +import classnames from 'classnames'; +import KeyCode from '../../../KeyCode'; +import Email from '../../../email'; +import AddThreepid from '../../../AddThreepid'; +import { _t, _tJsx } from '../../../languageHandler'; +import Modal from '../../../Modal'; + + +/** + * Prompt the user to set an email address. + * + * On success, `onFinished(true)` is called. + */ +export default React.createClass({ + displayName: 'SetEmailDialog', + propTypes: { + onFinished: React.PropTypes.func.isRequired, + }, + + getInitialState: function() { + return { + emailAddress: null, + emailBusy: false, + }; + }, + + componentDidMount: function() { + }, + + onEmailAddressChanged: function(value) { + this.setState({ + emailAddress: value, + }); + }, + + onSubmit: function() { + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); + + const emailAddress = this.state.emailAddress; + if (!Email.looksValid(emailAddress)) { + Modal.createDialog(ErrorDialog, { + title: _t("Invalid Email Address"), + description: _t("This doesn't appear to be a valid email address"), + }); + return; + } + this._addThreepid = new AddThreepid(); + // we always bind emails when registering, so let's do the + // same here. + this._addThreepid.addEmailAddress(emailAddress, true).done(() => { + Modal.createDialog(QuestionDialog, { + title: _t("Verification Pending"), + description: _t( + "Please check your email and click on the link it contains. Once this " + + "is done, click continue.", + ), + button: _t('Continue'), + onFinished: this.onEmailDialogFinished, + }); + }, (err) => { + this.setState({emailBusy: false}); + console.error("Unable to add email address " + emailAddress + " " + err); + Modal.createDialog(ErrorDialog, { + title: _t("Unable to add email address"), + description: ((err && err.message) ? err.message : _t("Operation failed")), + }); + }); + this.setState({emailBusy: true}); + }, + + onEmailDialogFinished: function(ok) { + if (ok) { + this.verifyEmailAddress(); + } else { + this.setState({emailBusy: false}); + } + }, + + verifyEmailAddress: function() { + this._addThreepid.checkEmailLinkClicked().done(() => { + this.props.onFinished(true); + }, (err) => { + this.setState({emailBusy: false}); + if (err.errcode == 'M_THREEPID_AUTH_FAILED') { + const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); + const message = _t("Unable to verify email address.") + " " + + _t("Please check your email and click on the link it contains. Once this is done, click continue."); + Modal.createDialog(QuestionDialog, { + title: _t("Verification Pending"), + description: message, + button: _t('Continue'), + onFinished: this.onEmailDialogFinished, + }); + } else { + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + console.error("Unable to verify email address: " + err); + Modal.createDialog(ErrorDialog, { + title: _t("Unable to verify email address."), + description: ((err && err.message) ? err.message : _t("Operation failed")), + }); + } + }); + }, + + render: function() { + const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); + const Spinner = sdk.getComponent('elements.Spinner'); + const EditableText = sdk.getComponent('elements.EditableText'); + + const emailInput = this.state.emailBusy ? : ; + + return ( + +
+

+ { _t('This will allow you to reset your password and receive notifications.') } +

+ { emailInput } +
+
+ + +
+
+ ); + }, +}); diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js index 7c79c4001b..ebd703a0bc 100644 --- a/src/components/views/settings/ChangePassword.js +++ b/src/components/views/settings/ChangePassword.js @@ -20,6 +20,8 @@ var React = require('react'); var MatrixClientPeg = require("../../../MatrixClientPeg"); var Modal = require("../../../Modal"); var sdk = require("../../../index"); + +import q from 'q'; import AccessibleButton from '../elements/AccessibleButton'; import { _t } from '../../../languageHandler'; @@ -140,7 +142,15 @@ module.exports = React.createClass({ }); cli.setPassword(authDict, newPassword).then(() => { - this.props.onFinished(); + if (this.props.shouldAskForEmail) { + return this._optionallySetEmail().then((confirmed) => { + this.props.onFinished({ + didSetEmail: confirmed, + }); + }); + } else { + this.props.onFinished(); + } }, (err) => { this.props.onError(err); }).finally(() => { @@ -150,6 +160,20 @@ module.exports = React.createClass({ }).done(); }, + _optionallySetEmail: function() { + const deferred = q.defer(); + // Ask for an email otherwise the user has no way to reset their password + const SetEmailDialog = sdk.getComponent("dialogs.SetEmailDialog"); + Modal.createDialog(SetEmailDialog, { + title: _t('Do you want to set an email address?'), + onFinished: (confirmed) => { + // ignore confirmed, setting an email is optional + deferred.resolve(confirmed); + }, + }); + return deferred.promise; + }, + _onExportE2eKeysClicked: function() { Modal.createDialogAsync( (cb) => {