diff --git a/src/interactive-auth.js b/src/interactive-auth.js index 200c5be58..90b17fc0d 100644 --- a/src/interactive-auth.js +++ b/src/interactive-auth.js @@ -55,6 +55,12 @@ const MSISDN_STAGE_TYPE = "m.login.msisdn"; * promise which resolves to the successful response or rejects with a * MatrixError. * + * @param {function(bool): module:client.Promise} opts.setBusy + * called whenever the interactive auth logic is busy submitting + * information provided by the user. After this has been called with + * true the UI should indicate that a request is in progress until it + * is called again with false. + * * @param {function(string, object?)} opts.stateUpdated * called when the status of the UI auth changes, ie. when the state of * an auth stage changes of when the auth flow moves to a new stage. @@ -101,6 +107,7 @@ function InteractiveAuth(opts) { this._matrixClient = opts.matrixClient; this._data = opts.authData || {}; this._requestCallback = opts.doRequest; + this._setBusyCallback = opts.setBusy; // startAuthStage included for backwards compat this._stateUpdatedCallback = opts.stateUpdated || opts.startAuthStage; this._resolveFunc = null; @@ -117,7 +124,9 @@ function InteractiveAuth(opts) { this._chosenFlow = null; this._currentStage = null; - this._polling = false; + // if we are currently trying to submit an auth dict (which includes polling) + // the promise the will resolve/reject when it completes + this._submitPromise = false; } InteractiveAuth.prototype = { @@ -138,7 +147,10 @@ InteractiveAuth.prototype = { // if we have no flows, try a request (we'll have // just a session ID in _data if resuming) if (!this._data.flows) { - this._doRequest(this._data); + this._setBusyCallback(true); + this._doRequest(this._data).finally(() => { + this._setBusyCallback(false); + }); } else { this._startNextAuthStage(); } @@ -152,7 +164,9 @@ InteractiveAuth.prototype = { */ poll: async function() { if (!this._data.session) return; - if (this._polling) return; + // if we currently have a request in flight, there's no point making + // another just to check what the status is + if (this._submitPromise) return; let authDict = {}; if (this._currentStage == EMAIL_STAGE_TYPE) { @@ -173,12 +187,7 @@ InteractiveAuth.prototype = { } } - this._polling = true; - try { - await this.submitAuthDict(authDict, true); - } finally { - this._polling = false; - } + this.submitAuthDict(authDict, true); }, /** @@ -235,13 +244,37 @@ InteractiveAuth.prototype = { throw new Error("submitAuthDict() called before attemptAuth()"); } + if (!background && this._setBusyCallback) { + this._setBusyCallback(true); + } + + // if we're currently trying a request, wait for it to finish + // as otherwise we can get multiple 200 responses which can mean + // things like multiple logins for register requests. + // (but discard any expections as we only care when its done, + // not whether it worked or not) + while (this._submitPromise) { + try { + await this._submitPromise; + } catch (e) { + } + } + // use the sessionid from the last request. const auth = { session: this._data.session, }; utils.extend(auth, authData); - await this._doRequest(auth, background); + try { + // NB. the 'background' flag is deprecated by the setBusy + // callback and is here for backwards compat + this._submitPromise = this._doRequest(auth, background); + await this._submitPromise; + } finally { + this._submitPromise = null; + if (!background) this._setBusyCallback(false); + } }, /**