From 79d9deb339bdcde60e235b084e51a542ba3a1557 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 13 Feb 2017 16:03:21 +0000 Subject: [PATCH 1/6] Split out InterActiveAuthDialog Into a component that does Interactive Auth and a dialog that wraps it, so we can do interactive auth not necessarily in a dialog. As a side effect: * Put the buttons for each auth stage in the stage itself. Some stages don't have submit buttons (and it's very possible other stages may have other buttons entirely, like 'resend') so it makes more sense for the buttons to live in the stage components themselves. Plus it saves the slightly evil calling-functions-on-react-children thing we were doing (and indeed extending that to show the submit button at all). * Give all BaseDialogs a cross in the top right to cancel. They were all dismissable by clicking outside or pressing esc, so this adds a more visually obvious way of dismissing them. Plus, it means our InteractiveAuthDialog can have a way of canceling the whole operation separate from buttons for the individual stages. --- src/component-index.js | 2 + src/components/structures/InteractiveAuth.js | 153 +++++++++++++++++ src/components/views/dialogs/BaseDialog.js | 15 ++ .../views/dialogs/InteractiveAuthDialog.js | 154 +----------------- .../login/InteractiveAuthEntryComponents.js | 42 ++--- 5 files changed, 201 insertions(+), 165 deletions(-) create mode 100644 src/components/structures/InteractiveAuth.js 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 (
+ + Cancel +
{ 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) { From 77b226631a13c6c893c08943a781a2f0a1c0a874 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 13 Feb 2017 16:15:00 +0000 Subject: [PATCH 2/6] Copyright --- src/components/views/dialogs/InteractiveAuthDialog.js | 1 + src/components/views/login/InteractiveAuthEntryComponents.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/components/views/dialogs/InteractiveAuthDialog.js b/src/components/views/dialogs/InteractiveAuthDialog.js index 1c9c872070..ecca00358f 100644 --- a/src/components/views/dialogs/InteractiveAuthDialog.js +++ b/src/components/views/dialogs/InteractiveAuthDialog.js @@ -1,5 +1,6 @@ /* Copyright 2016 OpenMarket Ltd +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. diff --git a/src/components/views/login/InteractiveAuthEntryComponents.js b/src/components/views/login/InteractiveAuthEntryComponents.js index a8c58ea76f..e4c48b1b1b 100644 --- a/src/components/views/login/InteractiveAuthEntryComponents.js +++ b/src/components/views/login/InteractiveAuthEntryComponents.js @@ -1,5 +1,6 @@ /* Copyright 2016 OpenMarket Ltd +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. From 8fc31045079c3a5c5df130500c508e2404cffc68 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 13 Feb 2017 18:52:33 +0000 Subject: [PATCH 3/6] Replace submit button with a spinner when busy and update test accordingly --- src/components/structures/InteractiveAuth.js | 1 + .../login/InteractiveAuthEntryComponents.js | 30 ++++++++++++++----- .../dialogs/InteractiveAuthDialog-test.js | 30 ++++++++++++------- 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/src/components/structures/InteractiveAuth.js b/src/components/structures/InteractiveAuth.js index 205d2ca1b8..c29b0fe16a 100644 --- a/src/components/structures/InteractiveAuth.js +++ b/src/components/structures/InteractiveAuth.js @@ -125,6 +125,7 @@ export default React.createClass({ stageParams={this._authLogic.getStageParams(stage)} submitAuthDict={this._submitAuthDict} errorText={this.state.stageErrorText} + busy={this.state.busy} /> ); }, diff --git a/src/components/views/login/InteractiveAuthEntryComponents.js b/src/components/views/login/InteractiveAuthEntryComponents.js index e4c48b1b1b..4759a30c88 100644 --- a/src/components/views/login/InteractiveAuthEntryComponents.js +++ b/src/components/views/login/InteractiveAuthEntryComponents.js @@ -49,11 +49,14 @@ export const PasswordAuthEntry = React.createClass({ propTypes: { submitAuthDict: React.PropTypes.func.isRequired, errorText: React.PropTypes.string, + // is the auth logic currently waiting for something to + // happen? + busy: React.PropTypes.bool, }, getInitialState: function() { return { - enableSubmit: false, + passwordValid: false, }; }, @@ -63,7 +66,10 @@ export const PasswordAuthEntry = React.createClass({ } }, - _onSubmit: function() { + _onSubmit: function(e) { + e.preventDefault(); + if (this.props.busy) return; + this.props.submitAuthDict({ type: PasswordAuthEntry.LOGIN_TYPE, user: MatrixClientPeg.get().credentials.userId, @@ -74,7 +80,7 @@ export const PasswordAuthEntry = React.createClass({ _onPasswordFieldChange: function(ev) { // enable the submit button iff the password is non-empty this.setState({ - enableSubmit: Boolean(this.refs.passwordField.value), + passwordValid: Boolean(this.refs.passwordField.value), }); }, @@ -85,6 +91,19 @@ export const PasswordAuthEntry = React.createClass({ passwordBoxClass = 'error'; } + let submitButtonOrSpinner; + if (this.props.busy) { + const Loader = sdk.getComponent("elements.Spinner"); + submitButtonOrSpinner = ; + } else { + submitButtonOrSpinner = ( + + ); + } + return (

To continue, please enter your password.

@@ -97,10 +116,7 @@ export const PasswordAuthEntry = React.createClass({ type="password" />
- + {submitButtonOrSpinner}
diff --git a/test/components/views/dialogs/InteractiveAuthDialog-test.js b/test/components/views/dialogs/InteractiveAuthDialog-test.js index 35daace0f8..80f027ab44 100644 --- a/test/components/views/dialogs/InteractiveAuthDialog-test.js +++ b/test/components/views/dialogs/InteractiveAuthDialog-test.js @@ -67,16 +67,24 @@ describe('InteractiveAuthDialog', function () { onFinished={onFinished} />, parentDiv); - // at this point there should be a password box - const passwordNode = ReactTestUtils.findRenderedDOMComponentWithTag( + // at this point there should be a password box and a submit button + const formNode = ReactTestUtils.findRenderedDOMComponentWithTag(dlg, "form"); + const inputNodes = ReactTestUtils.scryRenderedDOMComponentsWithTag( 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 - const submitNode = ReactTestUtils.findRenderedDOMComponentWithClass( - dlg, "mx_Dialog_primary" - ); expect(submitNode.disabled).toBe(true); // put something in the password box, and hit enter; that should @@ -84,9 +92,7 @@ describe('InteractiveAuthDialog', function () { passwordNode.value = "s3kr3t"; ReactTestUtils.Simulate.change(passwordNode); expect(submitNode.disabled).toBe(false); - ReactTestUtils.Simulate.keyDown(passwordNode, { - key: "Enter", keyCode: 13, which: 13, - }); + ReactTestUtils.Simulate.submit(formNode, {}); expect(doRequest.callCount).toEqual(1); expect(doRequest.calledWithExactly({ @@ -96,8 +102,10 @@ describe('InteractiveAuthDialog', function () { user: "@user:id", })).toBe(true); - // the submit button should now be disabled (and be a spinner) - expect(submitNode.disabled).toBe(true); + // there should now be a spinner + ReactTestUtils.findRenderedComponentWithType( + dlg, sdk.getComponent('elements.Spinner'), + ); // let the request complete q.delay(1).then(() => { From 36d126f3a9f9aa07be401e0265f0c1d3cf0d7b54 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 13 Feb 2017 19:09:43 +0000 Subject: [PATCH 4/6] PR feedback --- src/components/structures/InteractiveAuth.js | 10 ++++------ src/components/views/dialogs/BaseDialog.js | 2 -- src/components/views/dialogs/InteractiveAuthDialog.js | 4 ---- .../views/login/InteractiveAuthEntryComponents.js | 6 +++--- 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/components/structures/InteractiveAuth.js b/src/components/structures/InteractiveAuth.js index c29b0fe16a..70b3c2e306 100644 --- a/src/components/structures/InteractiveAuth.js +++ b/src/components/structures/InteractiveAuth.js @@ -38,6 +38,10 @@ export default React.createClass({ // 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, }, @@ -107,10 +111,6 @@ export default React.createClass({ } }, - _onCancel: function() { - this.props.onFinished(false); - }, - _submitAuthDict: function(authData) { this._authLogic.submitAuthDict(authData); }, @@ -131,8 +131,6 @@ export default React.createClass({ }, render: function() { - const Loader = sdk.getComponent("elements.Spinner"); - let error = null; if (this.state.errorText) { error = ( diff --git a/src/components/views/dialogs/BaseDialog.js b/src/components/views/dialogs/BaseDialog.js index 01a16e86ac..e83403ef7c 100644 --- a/src/components/views/dialogs/BaseDialog.js +++ b/src/components/views/dialogs/BaseDialog.js @@ -61,8 +61,6 @@ export default React.createClass({ }, _onCancelClick: function(e) { - e.stopPropagation(); - e.preventDefault(); this.props.onFinished(); }, diff --git a/src/components/views/dialogs/InteractiveAuthDialog.js b/src/components/views/dialogs/InteractiveAuthDialog.js index ecca00358f..66b662b23d 100644 --- a/src/components/views/dialogs/InteractiveAuthDialog.js +++ b/src/components/views/dialogs/InteractiveAuthDialog.js @@ -49,10 +49,6 @@ export default React.createClass({ }; }, - _onCancelClick: function() { - this.props.onFinished(false); - }, - render: function() { const InteractiveAuth = sdk.getComponent("structures.InteractiveAuth"); const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); diff --git a/src/components/views/login/InteractiveAuthEntryComponents.js b/src/components/views/login/InteractiveAuthEntryComponents.js index 4759a30c88..62f59472ec 100644 --- a/src/components/views/login/InteractiveAuthEntryComponents.js +++ b/src/components/views/login/InteractiveAuthEntryComponents.js @@ -21,7 +21,7 @@ import sdk from '../../../index'; import MatrixClientPeg from '../../../MatrixClientPeg'; /* 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 * components, such as the registration flow). * @@ -187,7 +187,7 @@ export const FallbackAuthEntry = React.createClass({ } }, - onSubmitClick: function() { + _onShowFallbackClick: function() { var url = MatrixClientPeg.get().getFallbackAuthUrl( this.props.loginType, this.props.authSessionId @@ -207,7 +207,7 @@ export const FallbackAuthEntry = React.createClass({ render: function() { return (
- Click "Submit" to authenticate + Start authentication
{this.props.errorText}
From ba3e62e3950c8b9929f33d26b0166a8c0afdf488 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 14 Feb 2017 10:31:56 +0000 Subject: [PATCH 5/6] Remove old docs --- src/components/views/login/InteractiveAuthEntryComponents.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/views/login/InteractiveAuthEntryComponents.js b/src/components/views/login/InteractiveAuthEntryComponents.js index 62f59472ec..bde0da3020 100644 --- a/src/components/views/login/InteractiveAuthEntryComponents.js +++ b/src/components/views/login/InteractiveAuthEntryComponents.js @@ -35,7 +35,6 @@ import MatrixClientPeg from '../../../MatrixClientPeg'; * submitAuthDict: a function which will be called with the new auth dict * * 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. */ From 43a740df150ce70478e05c001e6f077bef575ec9 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 14 Feb 2017 10:34:43 +0000 Subject: [PATCH 6/6] Add busy param to docs --- src/components/views/login/InteractiveAuthEntryComponents.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/views/login/InteractiveAuthEntryComponents.js b/src/components/views/login/InteractiveAuthEntryComponents.js index bde0da3020..e18e60d7bc 100644 --- a/src/components/views/login/InteractiveAuthEntryComponents.js +++ b/src/components/views/login/InteractiveAuthEntryComponents.js @@ -33,6 +33,8 @@ 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 + * 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): * focus: set the input focus appropriately in the form.