diff --git a/src/AddThreepid.js b/src/AddThreepid.js
new file mode 100644
index 0000000000..31805aad11
--- /dev/null
+++ b/src/AddThreepid.js
@@ -0,0 +1,76 @@
+/*
+Copyright 2016 OpenMarket 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.
+*/
+
+var MatrixClientPeg = require("./MatrixClientPeg");
+
+/**
+ * Allows a user to add a third party identifier to their Home Server and,
+ * optionally, the identity servers.
+ *
+ * This involves getting an email token from the identity server to "prove" that
+ * the client owns the given email address, which is then passed to the
+ * add threepid API on the homeserver.
+ */
+class AddThreepid {
+ constructor() {
+ this.clientSecret = MatrixClientPeg.get().generateClientSecret();
+ }
+
+ /**
+ * Attempt to add an email threepid. This will trigger a side-effect of
+ * sending an email to the provided email address.
+ * @param {string} emailAddress The email address to add
+ * @param {boolean} bind If True, bind this email to this mxid on the Identity Server
+ * @return {Promise} Resolves when the email has been sent. Then call checkEmailLinkClicked().
+ */
+ addEmailAddress(emailAddress, bind) {
+ this.bind = bind;
+ return MatrixClientPeg.get().requestEmailToken(emailAddress, this.clientSecret, 1).then((res) => {
+ this.sessionId = res.sid;
+ return res;
+ }, function(err) {
+ if (err.httpStatus) {
+ err.message = err.message + ` (Status ${err.httpStatus})`;
+ }
+ throw err;
+ });
+ }
+
+ /**
+ * Checks if the email link has been clicked by attempting to add the threepid
+ * @return {Promise} Resolves if the password was reset. Rejects with an object
+ * with a "message" property which contains a human-readable message detailing why
+ * the reset failed, e.g. "There is no mapped matrix user ID for the given email address".
+ */
+ checkEmailLinkClicked() {
+ var identityServerDomain = MatrixClientPeg.get().idBaseUrl.split("://")[1];
+ return MatrixClientPeg.get().addThreePid({
+ sid: this.sessionId,
+ client_secret: this.clientSecret,
+ id_server: identityServerDomain
+ }, this.bind).catch(function(err) {
+ if (err.httpStatus === 401) {
+ err.message = "Failed to verify email address: make sure you clicked the link in the email";
+ }
+ else if (err.httpStatus) {
+ err.message += ` (Status ${err.httpStatus})`;
+ }
+ throw err;
+ });
+ }
+}
+
+module.exports = AddThreepid;
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index 44b7b9a973..8bb3cb5ecb 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
var React = require('react');
+var ReactDOM = require('react-dom');
var sdk = require('../../index');
var MatrixClientPeg = require("../../MatrixClientPeg");
var Modal = require('../../Modal');
@@ -22,6 +23,8 @@ var q = require('q');
var version = require('../../../package.json').version;
var UserSettingsStore = require('../../UserSettingsStore');
var GeminiScrollbar = require('react-gemini-scrollbar');
+var Email = require('../../email');
+var AddThreepid = require('../../AddThreepid');
module.exports = React.createClass({
displayName: 'UserSettings',
@@ -42,6 +45,7 @@ module.exports = React.createClass({
threePids: [],
clientVersion: version,
phase: "UserSettings.LOADING", // LOADING, DISPLAY
+ email_add_pending: false,
};
},
@@ -152,10 +156,85 @@ module.exports = React.createClass({
this.logoutModal.closeDialog();
},
+ onEnableNotificationsChange: function(event) {
+ UserSettingsStore.setEnableNotifications(event.target.checked);
+ },
+
+ onAddThreepidClicked: function(value, shouldSubmit) {
+ if (!shouldSubmit) return;
+ var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+ var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
+
+ var email_address = this.refs.add_threepid_input.value;
+ if (!Email.looksValid(email_address)) {
+ Modal.createDialog(ErrorDialog, {
+ title: "Invalid Email Address",
+ description: "This doesn't appear to be a valid email address",
+ });
+ return;
+ }
+ this.add_threepid = new AddThreepid();
+ // we always bind emails when registering, so let's do the
+ // same here.
+ this.add_threepid.addEmailAddress(email_address, true).then(() => {
+ Modal.createDialog(QuestionDialog, {
+ title: "Verification Pending",
+ description: "Please check your email and click on the link it contains. Once this is done, click continue.",
+ button: 'Continue',
+ onFinished: this.onEmailDialogFinished,
+ });
+ }, (err) => {
+ Modal.createDialog(ErrorDialog, {
+ title: "Unable to add email address",
+ description: err.toString()
+ });
+ });
+ ReactDOM.findDOMNode(this.refs.add_threepid_input).blur();
+ this.setState({email_add_pending: true});
+ },
+
+ onEmailDialogFinished: function(ok) {
+ if (ok) {
+ this.verifyEmailAddress();
+ } else {
+ this.setState({email_add_pending: false});
+ }
+ },
+
+ verifyEmailAddress: function() {
+ this.add_threepid.checkEmailLinkClicked().done(() => {
+ this.add_threepid = undefined;
+ this.setState({
+ phase: "UserSettings.LOADING",
+ });
+ this._refreshFromServer();
+ this.setState({email_add_pending: false});
+ }, (err) => {
+ if (err.errcode == 'M_THREEPID_AUTH_FAILED') {
+ var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
+ var message = "Unable to verify email address. "
+ message += "Please check your email and click on the link it contains. Once this is done, click continue."
+ Modal.createDialog(QuestionDialog, {
+ title: "Verification Pending",
+ description: message,
+ button: 'Continue',
+ onFinished: this.onEmailDialogFinished,
+ });
+ } else {
+ var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+ Modal.createDialog(ErrorDialog, {
+ title: "Unable to verify email address",
+ description: err.toString(),
+ });
+ }
+ });
+ },
+
render: function() {
+ var self = this;
+ var Loader = sdk.getComponent("elements.Spinner");
switch (this.state.phase) {
case "UserSettings.LOADING":
- var Loader = sdk.getComponent("elements.Spinner");
return (