Merge pull request #691 from matrix-org/dbkr/interactive_auth_nondialog
Split out InterActiveAuthDialog
This commit is contained in:
commit
17b08aedfc
6 changed files with 239 additions and 183 deletions
|
@ -31,6 +31,8 @@ import structures$CreateRoom from './components/structures/CreateRoom';
|
||||||
structures$CreateRoom && (module.exports.components['structures.CreateRoom'] = structures$CreateRoom);
|
structures$CreateRoom && (module.exports.components['structures.CreateRoom'] = structures$CreateRoom);
|
||||||
import structures$FilePanel from './components/structures/FilePanel';
|
import structures$FilePanel from './components/structures/FilePanel';
|
||||||
structures$FilePanel && (module.exports.components['structures.FilePanel'] = 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';
|
import structures$LoggedInView from './components/structures/LoggedInView';
|
||||||
structures$LoggedInView && (module.exports.components['structures.LoggedInView'] = structures$LoggedInView);
|
structures$LoggedInView && (module.exports.components['structures.LoggedInView'] = structures$LoggedInView);
|
||||||
import structures$MatrixChat from './components/structures/MatrixChat';
|
import structures$MatrixChat from './components/structures/MatrixChat';
|
||||||
|
|
152
src/components/structures/InteractiveAuth.js
Normal file
152
src/components/structures/InteractiveAuth.js
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
/*
|
||||||
|
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,
|
||||||
|
|
||||||
|
// callback called when the auth process has finished
|
||||||
|
// @param {bool} status True if the operation requiring
|
||||||
|
// auth was completed sucessfully, false if canceled.
|
||||||
|
// @param result The result of the authenticated call
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_submitAuthDict: function(authData) {
|
||||||
|
this._authLogic.submitAuthDict(authData);
|
||||||
|
},
|
||||||
|
|
||||||
|
_renderCurrentStage: function() {
|
||||||
|
const stage = this.state.authStage;
|
||||||
|
var StageComponent = getEntryComponentForLoginType(stage);
|
||||||
|
return (
|
||||||
|
<StageComponent ref="stageComponent"
|
||||||
|
loginType={stage}
|
||||||
|
authSessionId={this._authLogic.getSessionId()}
|
||||||
|
stageParams={this._authLogic.getStageParams(stage)}
|
||||||
|
submitAuthDict={this._submitAuthDict}
|
||||||
|
errorText={this.state.stageErrorText}
|
||||||
|
busy={this.state.busy}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
let error = null;
|
||||||
|
if (this.state.errorText) {
|
||||||
|
error = (
|
||||||
|
<div className="error">
|
||||||
|
{this.state.errorText}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
{this._renderCurrentStage()}
|
||||||
|
{error}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import * as KeyCode from '../../../KeyCode';
|
import * as KeyCode from '../../../KeyCode';
|
||||||
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic container for modal dialogs.
|
* Basic container for modal dialogs.
|
||||||
|
@ -59,9 +60,21 @@ export default React.createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onCancelClick: function(e) {
|
||||||
|
this.props.onFinished();
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
<div onKeyDown={this._onKeyDown} className={this.props.className}>
|
<div onKeyDown={this._onKeyDown} className={this.props.className}>
|
||||||
|
<AccessibleButton onClick={this._onCancelClick}
|
||||||
|
className="mx_Dialog_cancelButton"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="img/cancel.svg" width="18" height="18"
|
||||||
|
alt="Cancel" title="Cancel"
|
||||||
|
/>
|
||||||
|
</AccessibleButton>
|
||||||
<div className='mx_Dialog_title'>
|
<div className='mx_Dialog_title'>
|
||||||
{ this.props.title }
|
{ this.props.title }
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 OpenMarket Ltd
|
||||||
|
Copyright 2017 Vector Creations Ltd
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -15,13 +16,12 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Matrix from 'matrix-js-sdk';
|
import Matrix from 'matrix-js-sdk';
|
||||||
const InteractiveAuth = Matrix.InteractiveAuth;
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
|
|
||||||
import {getEntryComponentForLoginType} from '../login/InteractiveAuthEntryComponents';
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
|
|
||||||
export default React.createClass({
|
export default React.createClass({
|
||||||
displayName: 'InteractiveAuthDialog',
|
displayName: 'InteractiveAuthDialog',
|
||||||
|
@ -41,168 +41,29 @@ export default React.createClass({
|
||||||
onFinished: React.PropTypes.func.isRequired,
|
onFinished: React.PropTypes.func.isRequired,
|
||||||
|
|
||||||
title: React.PropTypes.string,
|
title: React.PropTypes.string,
|
||||||
submitButtonLabel: React.PropTypes.string,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
return {
|
return {
|
||||||
title: "Authentication",
|
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() {
|
|
||||||
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 (
|
|
||||||
<StageComponent ref="stageComponent"
|
|
||||||
loginType={stage}
|
|
||||||
authSessionId={this._authLogic.getSessionId()}
|
|
||||||
stageParams={this._authLogic.getStageParams(stage)}
|
|
||||||
submitAuthDict={this._submitAuthDict}
|
|
||||||
setSubmitButtonEnabled={this._setSubmitButtonEnabled}
|
|
||||||
errorText={this.state.stageErrorText}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
const Loader = sdk.getComponent("elements.Spinner");
|
const InteractiveAuth = sdk.getComponent("structures.InteractiveAuth");
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
|
|
||||||
let error = null;
|
|
||||||
if (this.state.errorText) {
|
|
||||||
error = (
|
|
||||||
<div className="error">
|
|
||||||
{this.state.errorText}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const submitLabel = this.state.busy ? <Loader /> : this.props.submitButtonLabel;
|
|
||||||
const submitEnabled = this.state.submitButtonEnabled && !this.state.busy;
|
|
||||||
|
|
||||||
const submitButton = (
|
|
||||||
<button className="mx_Dialog_primary"
|
|
||||||
onClick={this._onSubmit}
|
|
||||||
disabled={!submitEnabled}
|
|
||||||
>
|
|
||||||
{submitLabel}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
|
|
||||||
const cancelButton = (
|
|
||||||
<button onClick={this._onCancel}>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseDialog className="mx_InteractiveAuthDialog"
|
<BaseDialog className="mx_InteractiveAuthDialog"
|
||||||
onEnterPressed={this._onEnterPressed}
|
|
||||||
onFinished={this.props.onFinished}
|
onFinished={this.props.onFinished}
|
||||||
title={this.props.title}
|
title={this.props.title}
|
||||||
>
|
>
|
||||||
<div className="mx_Dialog_content">
|
<div>
|
||||||
<p>This operation requires additional authentication.</p>
|
<InteractiveAuth ref={this._collectInteractiveAuth}
|
||||||
{this._renderCurrentStage()}
|
authData={this.props.authData}
|
||||||
{error}
|
makeRequest={this.props.makeRequest}
|
||||||
</div>
|
onFinished={this.props.onFinished}
|
||||||
<div className="mx_Dialog_buttons">
|
/>
|
||||||
{submitButton}
|
|
||||||
{cancelButton}
|
|
||||||
</div>
|
</div>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 OpenMarket Ltd
|
||||||
|
Copyright 2017 Vector Creations Ltd
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -20,7 +21,7 @@ import sdk from '../../../index';
|
||||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
|
|
||||||
/* This file contains a collection of components which are used by the
|
/* This file contains a collection of components which are used by the
|
||||||
* InteractiveAuthDialog to prompt the user to enter the information needed
|
* InteractiveAuth to prompt the user to enter the information needed
|
||||||
* for an auth stage. (The intention is that they could also be used for other
|
* for an auth stage. (The intention is that they could also be used for other
|
||||||
* components, such as the registration flow).
|
* components, such as the registration flow).
|
||||||
*
|
*
|
||||||
|
@ -32,10 +33,10 @@ import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
* stageParams: params from the server for the stage being attempted
|
* stageParams: params from the server for the stage being attempted
|
||||||
* errorText: error message from a previous attempt to authenticate
|
* errorText: error message from a previous attempt to authenticate
|
||||||
* submitAuthDict: a function which will be called with the new auth dict
|
* submitAuthDict: a function which will be called with the new auth dict
|
||||||
* setSubmitButtonEnabled: a function which will enable/disable the 'submit' button
|
* busy: a boolean indicating whether the auth logic is doing something
|
||||||
|
* the user needs to wait for.
|
||||||
*
|
*
|
||||||
* Each component may also provide the following functions (beyond the standard React ones):
|
* Each component may also provide the following functions (beyond the standard React ones):
|
||||||
* onSubmitClick: handle a 'submit' button click
|
|
||||||
* focus: set the input focus appropriately in the form.
|
* focus: set the input focus appropriately in the form.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -48,12 +49,16 @@ export const PasswordAuthEntry = React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
submitAuthDict: React.PropTypes.func.isRequired,
|
submitAuthDict: React.PropTypes.func.isRequired,
|
||||||
setSubmitButtonEnabled: React.PropTypes.func.isRequired,
|
|
||||||
errorText: React.PropTypes.string,
|
errorText: React.PropTypes.string,
|
||||||
|
// is the auth logic currently waiting for something to
|
||||||
|
// happen?
|
||||||
|
busy: React.PropTypes.bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillMount: function() {
|
getInitialState: function() {
|
||||||
this.props.setSubmitButtonEnabled(false);
|
return {
|
||||||
|
passwordValid: false,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
focus: function() {
|
focus: function() {
|
||||||
|
@ -62,7 +67,10 @@ export const PasswordAuthEntry = React.createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onSubmitClick: function() {
|
_onSubmit: function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (this.props.busy) return;
|
||||||
|
|
||||||
this.props.submitAuthDict({
|
this.props.submitAuthDict({
|
||||||
type: PasswordAuthEntry.LOGIN_TYPE,
|
type: PasswordAuthEntry.LOGIN_TYPE,
|
||||||
user: MatrixClientPeg.get().credentials.userId,
|
user: MatrixClientPeg.get().credentials.userId,
|
||||||
|
@ -72,7 +80,9 @@ export const PasswordAuthEntry = React.createClass({
|
||||||
|
|
||||||
_onPasswordFieldChange: function(ev) {
|
_onPasswordFieldChange: function(ev) {
|
||||||
// enable the submit button iff the password is non-empty
|
// enable the submit button iff the password is non-empty
|
||||||
this.props.setSubmitButtonEnabled(Boolean(ev.target.value));
|
this.setState({
|
||||||
|
passwordValid: Boolean(this.refs.passwordField.value),
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
|
@ -82,16 +92,34 @@ export const PasswordAuthEntry = React.createClass({
|
||||||
passwordBoxClass = 'error';
|
passwordBoxClass = 'error';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let submitButtonOrSpinner;
|
||||||
|
if (this.props.busy) {
|
||||||
|
const Loader = sdk.getComponent("elements.Spinner");
|
||||||
|
submitButtonOrSpinner = <Loader />;
|
||||||
|
} else {
|
||||||
|
submitButtonOrSpinner = (
|
||||||
|
<input type="submit"
|
||||||
|
className="mx_Dialog_primary"
|
||||||
|
disabled={!this.state.passwordValid}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p>To continue, please enter your password.</p>
|
<p>To continue, please enter your password.</p>
|
||||||
<p>Password:</p>
|
<p>Password:</p>
|
||||||
<input
|
<form onSubmit={this._onSubmit}>
|
||||||
ref="passwordField"
|
<input
|
||||||
className={passwordBoxClass}
|
ref="passwordField"
|
||||||
onChange={this._onPasswordFieldChange}
|
className={passwordBoxClass}
|
||||||
type="password"
|
onChange={this._onPasswordFieldChange}
|
||||||
/>
|
type="password"
|
||||||
|
/>
|
||||||
|
<div className="mx_button_row">
|
||||||
|
{submitButtonOrSpinner}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
<div className="error">
|
<div className="error">
|
||||||
{this.props.errorText}
|
{this.props.errorText}
|
||||||
</div>
|
</div>
|
||||||
|
@ -110,14 +138,9 @@ export const RecaptchaAuthEntry = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
submitAuthDict: React.PropTypes.func.isRequired,
|
submitAuthDict: React.PropTypes.func.isRequired,
|
||||||
stageParams: React.PropTypes.object.isRequired,
|
stageParams: React.PropTypes.object.isRequired,
|
||||||
setSubmitButtonEnabled: React.PropTypes.func.isRequired,
|
|
||||||
errorText: React.PropTypes.string,
|
errorText: React.PropTypes.string,
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillMount: function() {
|
|
||||||
this.props.setSubmitButtonEnabled(false);
|
|
||||||
},
|
|
||||||
|
|
||||||
_onCaptchaResponse: function(response) {
|
_onCaptchaResponse: function(response) {
|
||||||
this.props.submitAuthDict({
|
this.props.submitAuthDict({
|
||||||
type: RecaptchaAuthEntry.LOGIN_TYPE,
|
type: RecaptchaAuthEntry.LOGIN_TYPE,
|
||||||
|
@ -148,7 +171,6 @@ export const FallbackAuthEntry = React.createClass({
|
||||||
authSessionId: React.PropTypes.string.isRequired,
|
authSessionId: React.PropTypes.string.isRequired,
|
||||||
loginType: React.PropTypes.string.isRequired,
|
loginType: React.PropTypes.string.isRequired,
|
||||||
submitAuthDict: React.PropTypes.func.isRequired,
|
submitAuthDict: React.PropTypes.func.isRequired,
|
||||||
setSubmitButtonEnabled: React.PropTypes.func.isRequired,
|
|
||||||
errorText: React.PropTypes.string,
|
errorText: React.PropTypes.string,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -156,7 +178,6 @@ export const FallbackAuthEntry = React.createClass({
|
||||||
// we have to make the user click a button, as browsers will block
|
// we have to make the user click a button, as browsers will block
|
||||||
// the popup if we open it immediately.
|
// the popup if we open it immediately.
|
||||||
this._popupWindow = null;
|
this._popupWindow = null;
|
||||||
this.props.setSubmitButtonEnabled(true);
|
|
||||||
window.addEventListener("message", this._onReceiveMessage);
|
window.addEventListener("message", this._onReceiveMessage);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -167,13 +188,12 @@ export const FallbackAuthEntry = React.createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onSubmitClick: function() {
|
_onShowFallbackClick: function() {
|
||||||
var url = MatrixClientPeg.get().getFallbackAuthUrl(
|
var url = MatrixClientPeg.get().getFallbackAuthUrl(
|
||||||
this.props.loginType,
|
this.props.loginType,
|
||||||
this.props.authSessionId
|
this.props.authSessionId
|
||||||
);
|
);
|
||||||
this._popupWindow = window.open(url);
|
this._popupWindow = window.open(url);
|
||||||
this.props.setSubmitButtonEnabled(false);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onReceiveMessage: function(event) {
|
_onReceiveMessage: function(event) {
|
||||||
|
@ -188,7 +208,7 @@ export const FallbackAuthEntry = React.createClass({
|
||||||
render: function() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
Click "Submit" to authenticate
|
<a onClick={this._onShowFallbackClick}>Start authentication</a>
|
||||||
<div className="error">
|
<div className="error">
|
||||||
{this.props.errorText}
|
{this.props.errorText}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -67,16 +67,24 @@ describe('InteractiveAuthDialog', function () {
|
||||||
onFinished={onFinished}
|
onFinished={onFinished}
|
||||||
/>, parentDiv);
|
/>, parentDiv);
|
||||||
|
|
||||||
// at this point there should be a password box
|
// at this point there should be a password box and a submit button
|
||||||
const passwordNode = ReactTestUtils.findRenderedDOMComponentWithTag(
|
const formNode = ReactTestUtils.findRenderedDOMComponentWithTag(dlg, "form");
|
||||||
|
const inputNodes = ReactTestUtils.scryRenderedDOMComponentsWithTag(
|
||||||
dlg, "input"
|
dlg, "input"
|
||||||
);
|
);
|
||||||
expect(passwordNode.type).toEqual("password");
|
let passwordNode;
|
||||||
|
let submitNode;
|
||||||
|
for (const node of inputNodes) {
|
||||||
|
if (node.type == 'password') {
|
||||||
|
passwordNode = node;
|
||||||
|
} else if (node.type == 'submit') {
|
||||||
|
submitNode = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect(passwordNode).toExist();
|
||||||
|
expect(submitNode).toExist();
|
||||||
|
|
||||||
// submit should be disabled
|
// submit should be disabled
|
||||||
const submitNode = ReactTestUtils.findRenderedDOMComponentWithClass(
|
|
||||||
dlg, "mx_Dialog_primary"
|
|
||||||
);
|
|
||||||
expect(submitNode.disabled).toBe(true);
|
expect(submitNode.disabled).toBe(true);
|
||||||
|
|
||||||
// put something in the password box, and hit enter; that should
|
// put something in the password box, and hit enter; that should
|
||||||
|
@ -84,9 +92,7 @@ describe('InteractiveAuthDialog', function () {
|
||||||
passwordNode.value = "s3kr3t";
|
passwordNode.value = "s3kr3t";
|
||||||
ReactTestUtils.Simulate.change(passwordNode);
|
ReactTestUtils.Simulate.change(passwordNode);
|
||||||
expect(submitNode.disabled).toBe(false);
|
expect(submitNode.disabled).toBe(false);
|
||||||
ReactTestUtils.Simulate.keyDown(passwordNode, {
|
ReactTestUtils.Simulate.submit(formNode, {});
|
||||||
key: "Enter", keyCode: 13, which: 13,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(doRequest.callCount).toEqual(1);
|
expect(doRequest.callCount).toEqual(1);
|
||||||
expect(doRequest.calledWithExactly({
|
expect(doRequest.calledWithExactly({
|
||||||
|
@ -96,8 +102,10 @@ describe('InteractiveAuthDialog', function () {
|
||||||
user: "@user:id",
|
user: "@user:id",
|
||||||
})).toBe(true);
|
})).toBe(true);
|
||||||
|
|
||||||
// the submit button should now be disabled (and be a spinner)
|
// there should now be a spinner
|
||||||
expect(submitNode.disabled).toBe(true);
|
ReactTestUtils.findRenderedComponentWithType(
|
||||||
|
dlg, sdk.getComponent('elements.Spinner'),
|
||||||
|
);
|
||||||
|
|
||||||
// let the request complete
|
// let the request complete
|
||||||
q.delay(1).then(() => {
|
q.delay(1).then(() => {
|
||||||
|
|
Loading…
Reference in a new issue