diff --git a/src/component-index.js b/src/component-index.js
index 5b28be0627..b357472ad7 100644
--- a/src/component-index.js
+++ b/src/component-index.js
@@ -31,6 +31,8 @@ import structures$CreateRoom from './components/structures/CreateRoom';
structures$CreateRoom && (module.exports.components['structures.CreateRoom'] = structures$CreateRoom);
import structures$FilePanel from './components/structures/FilePanel';
structures$FilePanel && (module.exports.components['structures.FilePanel'] = structures$FilePanel);
+import structures$InteractiveAuth from './components/structures/InteractiveAuth';
+structures$InteractiveAuth && (module.exports.components['structures.InteractiveAuth'] = structures$InteractiveAuth);
import structures$LoggedInView from './components/structures/LoggedInView';
structures$LoggedInView && (module.exports.components['structures.LoggedInView'] = structures$LoggedInView);
import structures$MatrixChat from './components/structures/MatrixChat';
diff --git a/src/components/structures/InteractiveAuth.js b/src/components/structures/InteractiveAuth.js
new file mode 100644
index 0000000000..205d2ca1b8
--- /dev/null
+++ b/src/components/structures/InteractiveAuth.js
@@ -0,0 +1,153 @@
+/*
+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 Matrix from 'matrix-js-sdk';
+const InteractiveAuth = Matrix.InteractiveAuth;
+
+import React from 'react';
+
+import sdk from '../../index';
+
+import {getEntryComponentForLoginType} from '../views/login/InteractiveAuthEntryComponents';
+
+export default React.createClass({
+ displayName: 'InteractiveAuth',
+
+ propTypes: {
+ // response from initial request. If not supplied, will do a request on
+ // mount.
+ authData: React.PropTypes.shape({
+ flows: React.PropTypes.array,
+ params: React.PropTypes.object,
+ session: React.PropTypes.string,
+ }),
+
+ // callback
+ makeRequest: React.PropTypes.func.isRequired,
+
+ onFinished: React.PropTypes.func.isRequired,
+ },
+
+ getInitialState: function() {
+ return {
+ authStage: null,
+ busy: false,
+ errorText: null,
+ stageErrorText: null,
+ submitButtonEnabled: false,
+ };
+ },
+
+ componentWillMount: function() {
+ this._unmounted = false;
+ this._authLogic = new InteractiveAuth({
+ authData: this.props.authData,
+ doRequest: this._requestCallback,
+ startAuthStage: this._startAuthStage,
+ });
+
+ this._authLogic.attemptAuth().then((result) => {
+ this.props.onFinished(true, result);
+ }).catch((error) => {
+ console.error("Error during user-interactive auth:", error);
+ if (this._unmounted) {
+ return;
+ }
+
+ const msg = error.message || error.toString();
+ this.setState({
+ errorText: msg
+ });
+ }).done();
+ },
+
+ componentWillUnmount: function() {
+ this._unmounted = true;
+ },
+
+ _startAuthStage: function(stageType, error) {
+ this.setState({
+ authStage: stageType,
+ errorText: error ? error.error : null,
+ }, this._setFocus);
+ },
+
+ _requestCallback: function(auth) {
+ this.setState({
+ busy: true,
+ errorText: null,
+ stageErrorText: null,
+ });
+ return this.props.makeRequest(auth).finally(() => {
+ if (this._unmounted) {
+ return;
+ }
+ this.setState({
+ busy: false,
+ });
+ });
+ },
+
+ _setFocus: function() {
+ if (this.refs.stageComponent && this.refs.stageComponent.focus) {
+ this.refs.stageComponent.focus();
+ }
+ },
+
+ _onCancel: function() {
+ this.props.onFinished(false);
+ },
+
+ _submitAuthDict: function(authData) {
+ this._authLogic.submitAuthDict(authData);
+ },
+
+ _renderCurrentStage: function() {
+ const stage = this.state.authStage;
+ var StageComponent = getEntryComponentForLoginType(stage);
+ return (
+
+ );
+ },
+
+ render: function() {
+ const Loader = sdk.getComponent("elements.Spinner");
+
+ let error = null;
+ if (this.state.errorText) {
+ error = (
+
+ {this.state.errorText}
+
+ );
+ }
+
+ return (
+
+
+ {this._renderCurrentStage()}
+ {error}
+
+
+ );
+ },
+});
diff --git a/src/components/views/dialogs/BaseDialog.js b/src/components/views/dialogs/BaseDialog.js
index 2b3980c536..01a16e86ac 100644
--- a/src/components/views/dialogs/BaseDialog.js
+++ b/src/components/views/dialogs/BaseDialog.js
@@ -17,6 +17,7 @@ limitations under the License.
import React from 'react';
import * as KeyCode from '../../../KeyCode';
+import AccessibleButton from '../elements/AccessibleButton';
/**
* Basic container for modal dialogs.
@@ -59,9 +60,23 @@ export default React.createClass({
}
},
+ _onCancelClick: function(e) {
+ e.stopPropagation();
+ e.preventDefault();
+ this.props.onFinished();
+ },
+
render: function() {
return (
+
+
+
{ this.props.title }
diff --git a/src/components/views/dialogs/InteractiveAuthDialog.js b/src/components/views/dialogs/InteractiveAuthDialog.js
index a4abbb17d9..1c9c872070 100644
--- a/src/components/views/dialogs/InteractiveAuthDialog.js
+++ b/src/components/views/dialogs/InteractiveAuthDialog.js
@@ -15,13 +15,12 @@ limitations under the License.
*/
import Matrix from 'matrix-js-sdk';
-const InteractiveAuth = Matrix.InteractiveAuth;
import React from 'react';
import sdk from '../../../index';
-import {getEntryComponentForLoginType} from '../login/InteractiveAuthEntryComponents';
+import AccessibleButton from '../elements/AccessibleButton';
export default React.createClass({
displayName: 'InteractiveAuthDialog',
@@ -41,168 +40,33 @@ export default React.createClass({
onFinished: React.PropTypes.func.isRequired,
title: React.PropTypes.string,
- submitButtonLabel: React.PropTypes.string,
},
getDefaultProps: function() {
return {
title: "Authentication",
- submitButtonLabel: "Submit",
};
},
- getInitialState: function() {
- return {
- authStage: null,
- busy: false,
- errorText: null,
- stageErrorText: null,
- submitButtonEnabled: false,
- };
- },
-
- componentWillMount: function() {
- this._unmounted = false;
- this._authLogic = new InteractiveAuth({
- authData: this.props.authData,
- doRequest: this._requestCallback,
- startAuthStage: this._startAuthStage,
- });
-
- this._authLogic.attemptAuth().then((result) => {
- this.props.onFinished(true, result);
- }).catch((error) => {
- console.error("Error during user-interactive auth:", error);
- if (this._unmounted) {
- return;
- }
-
- const msg = error.message || error.toString();
- this.setState({
- errorText: msg
- });
- }).done();
- },
-
- componentWillUnmount: function() {
- this._unmounted = true;
- },
-
- _startAuthStage: function(stageType, error) {
- this.setState({
- authStage: stageType,
- errorText: error ? error.error : null,
- }, this._setFocus);
- },
-
- _requestCallback: function(auth) {
- this.setState({
- busy: true,
- errorText: null,
- stageErrorText: null,
- });
- return this.props.makeRequest(auth).finally(() => {
- if (this._unmounted) {
- return;
- }
- this.setState({
- busy: false,
- });
- });
- },
-
- _onEnterPressed: function(e) {
- if (this.state.submitButtonEnabled && !this.state.busy) {
- this._onSubmit();
- }
- },
-
- _onSubmit: function() {
- if (this.refs.stageComponent && this.refs.stageComponent.onSubmitClick) {
- this.refs.stageComponent.onSubmitClick();
- }
- },
-
- _setFocus: function() {
- if (this.refs.stageComponent && this.refs.stageComponent.focus) {
- this.refs.stageComponent.focus();
- }
- },
-
- _onCancel: function() {
+ _onCancelClick: function() {
this.props.onFinished(false);
},
- _setSubmitButtonEnabled: function(enabled) {
- this.setState({
- submitButtonEnabled: enabled,
- });
- },
-
- _submitAuthDict: function(authData) {
- this._authLogic.submitAuthDict(authData);
- },
-
- _renderCurrentStage: function() {
- const stage = this.state.authStage;
- var StageComponent = getEntryComponentForLoginType(stage);
- return (
-
- );
- },
-
render: function() {
- const Loader = sdk.getComponent("elements.Spinner");
+ const InteractiveAuth = sdk.getComponent("structures.InteractiveAuth");
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
- let error = null;
- if (this.state.errorText) {
- error = (
-
- {this.state.errorText}
-
- );
- }
-
- const submitLabel = this.state.busy ?
: this.props.submitButtonLabel;
- const submitEnabled = this.state.submitButtonEnabled && !this.state.busy;
-
- const submitButton = (
-
- );
-
- const cancelButton = (
-
- );
-
return (
-
-
This operation requires additional authentication.
- {this._renderCurrentStage()}
- {error}
-
-
- {submitButton}
- {cancelButton}
+
+
);
diff --git a/src/components/views/login/InteractiveAuthEntryComponents.js b/src/components/views/login/InteractiveAuthEntryComponents.js
index ec184ca09f..a8c58ea76f 100644
--- a/src/components/views/login/InteractiveAuthEntryComponents.js
+++ b/src/components/views/login/InteractiveAuthEntryComponents.js
@@ -32,7 +32,6 @@ import MatrixClientPeg from '../../../MatrixClientPeg';
* stageParams: params from the server for the stage being attempted
* errorText: error message from a previous attempt to authenticate
* submitAuthDict: a function which will be called with the new auth dict
- * setSubmitButtonEnabled: a function which will enable/disable the 'submit' button
*
* Each component may also provide the following functions (beyond the standard React ones):
* onSubmitClick: handle a 'submit' button click
@@ -48,12 +47,13 @@ export const PasswordAuthEntry = React.createClass({
propTypes: {
submitAuthDict: React.PropTypes.func.isRequired,
- setSubmitButtonEnabled: React.PropTypes.func.isRequired,
errorText: React.PropTypes.string,
},
- componentWillMount: function() {
- this.props.setSubmitButtonEnabled(false);
+ getInitialState: function() {
+ return {
+ enableSubmit: false,
+ };
},
focus: function() {
@@ -62,7 +62,7 @@ export const PasswordAuthEntry = React.createClass({
}
},
- onSubmitClick: function() {
+ _onSubmit: function() {
this.props.submitAuthDict({
type: PasswordAuthEntry.LOGIN_TYPE,
user: MatrixClientPeg.get().credentials.userId,
@@ -72,7 +72,9 @@ export const PasswordAuthEntry = React.createClass({
_onPasswordFieldChange: function(ev) {
// enable the submit button iff the password is non-empty
- this.props.setSubmitButtonEnabled(Boolean(ev.target.value));
+ this.setState({
+ enableSubmit: Boolean(this.refs.passwordField.value),
+ });
},
render: function() {
@@ -86,12 +88,20 @@ export const PasswordAuthEntry = React.createClass({
To continue, please enter your password.
Password:
-
+
{this.props.errorText}
@@ -110,14 +120,9 @@ export const RecaptchaAuthEntry = React.createClass({
propTypes: {
submitAuthDict: React.PropTypes.func.isRequired,
stageParams: React.PropTypes.object.isRequired,
- setSubmitButtonEnabled: React.PropTypes.func.isRequired,
errorText: React.PropTypes.string,
},
- componentWillMount: function() {
- this.props.setSubmitButtonEnabled(false);
- },
-
_onCaptchaResponse: function(response) {
this.props.submitAuthDict({
type: RecaptchaAuthEntry.LOGIN_TYPE,
@@ -148,7 +153,6 @@ export const FallbackAuthEntry = React.createClass({
authSessionId: React.PropTypes.string.isRequired,
loginType: React.PropTypes.string.isRequired,
submitAuthDict: React.PropTypes.func.isRequired,
- setSubmitButtonEnabled: React.PropTypes.func.isRequired,
errorText: React.PropTypes.string,
},
@@ -156,7 +160,6 @@ export const FallbackAuthEntry = React.createClass({
// we have to make the user click a button, as browsers will block
// the popup if we open it immediately.
this._popupWindow = null;
- this.props.setSubmitButtonEnabled(true);
window.addEventListener("message", this._onReceiveMessage);
},
@@ -173,7 +176,6 @@ export const FallbackAuthEntry = React.createClass({
this.props.authSessionId
);
this._popupWindow = window.open(url);
- this.props.setSubmitButtonEnabled(false);
},
_onReceiveMessage: function(event) {