Fixes #1490: Busy-loop when fetching registration form fails

This commit is contained in:
JC Brand 2023-02-17 10:20:17 +01:00
parent 7b8b32638c
commit 5e3139f563
5 changed files with 55 additions and 71 deletions

View File

@ -2,8 +2,10 @@
## 10.1.2 (Unreleased) ## 10.1.2 (Unreleased)
- #1490: Busy-loop when fetching registration form fails
- #1556: Can't switch to registration form afrer logout - #1556: Can't switch to registration form afrer logout
- #3137: Various UI/UX bugfixes regarding the registration form - #3137: Various UI/UX bugfixes regarding the registration form
- XEP-0437: Room Activity Indicators (RAI) optimizations
## 10.1.1 (2023-02-15) ## 10.1.1 (2023-02-15)

View File

@ -3,12 +3,10 @@ import tplFormInput from "templates/form_input.js";
import tplFormUrl from "templates/form_url.js"; import tplFormUrl from "templates/form_url.js";
import tplFormUsername from "templates/form_username.js"; import tplFormUsername from "templates/form_username.js";
import tplRegisterPanel from "./templates/register_panel.js"; import tplRegisterPanel from "./templates/register_panel.js";
import tplSpinner from "templates/spinner.js";
import { CustomElement } from 'shared/components/element.js'; import { CustomElement } from 'shared/components/element.js';
import { __ } from 'i18n'; import { __ } from 'i18n';
import { _converse, api, converse } from "@converse/headless/core.js"; import { _converse, api, converse } from "@converse/headless/core.js";
import { initConnection } from '@converse/headless/utils/init.js'; import { initConnection } from '@converse/headless/utils/init.js';
import { render } from 'lit';
import { setActiveForm } from './utils.js'; import { setActiveForm } from './utils.js';
import { webForm2xForm } from "@converse/headless/utils/form"; import { webForm2xForm } from "@converse/headless/utils/form";
@ -22,6 +20,7 @@ const u = converse.env.utils;
const CHOOSE_PROVIDER = 0; const CHOOSE_PROVIDER = 0;
const FETCHING_FORM = 1; const FETCHING_FORM = 1;
const REGISTRATION_FORM = 2; const REGISTRATION_FORM = 2;
const REGISTRATION_FORM_ERROR = 3;
/** /**
@ -34,10 +33,18 @@ class RegisterPanel extends CustomElement {
static get properties () { static get properties () {
return { return {
status : { type: String }, status : { type: String },
error_message: { type: String }, alert_message: { type: String },
alert_type: { type: String },
} }
} }
constructor () {
super();
this.alert_type = 'info';
this.setErrorMessage = (m) => this.setMessage(m, 'danger');
this.setFeedbackMessage = (m) => this.setMessage(m, 'info');
}
initialize () { initialize () {
this.reset(); this.reset();
this.listenTo(_converse, 'connectionInitialized', () => this.registerHooks()); this.listenTo(_converse, 'connectionInitialized', () => this.registerHooks());
@ -54,6 +61,11 @@ class RegisterPanel extends CustomElement {
return tplRegisterPanel(this); return tplRegisterPanel(this);
} }
setMessage(message, type) {
this.alert_type = type;
this.alert_message = message;
}
/** /**
* Hook into Strophe's _connect_cb, so that we can send an IQ * Hook into Strophe's _connect_cb, so that we can send an IQ
* requesting the registration fields. * requesting the registration fields.
@ -64,17 +76,14 @@ class RegisterPanel extends CustomElement {
conn._connect_cb = (req, callback, raw) => { conn._connect_cb = (req, callback, raw) => {
if (!this._registering) { if (!this._registering) {
connect_cb(req, callback, raw); connect_cb(req, callback, raw);
} else { } else if (this.getRegistrationFields(req, callback)) {
if (this.getRegistrationFields(req, callback)) { this._registering = false;
this._registering = false;
}
} }
}; };
} }
/** /**
* Send an IQ stanza to the XMPP server asking for the registration fields. * Send an IQ stanza to the XMPP server asking for the registration fields.
* @private
* @method _converse.RegisterPanel#getRegistrationFields * @method _converse.RegisterPanel#getRegistrationFields
* @param { Strophe.Request } req - The current request * @param { Strophe.Request } req - The current request
* @param { Function } callback - The callback function * @param { Function } callback - The callback function
@ -87,7 +96,7 @@ class RegisterPanel extends CustomElement {
if (!body) { return; } if (!body) { return; }
if (conn._proto._connect_cb(body) === Strophe.Status.CONNFAIL) { if (conn._proto._connect_cb(body) === Strophe.Status.CONNFAIL) {
this.status = CHOOSE_PROVIDER; this.status = CHOOSE_PROVIDER;
this.error_message = __("Sorry, we're unable to connect to your chosen provider."); this.setErrorMessage(__("Sorry, we're unable to connect to your chosen provider."));
return false; return false;
} }
const register = body.getElementsByTagName("register"); const register = body.getElementsByTagName("register");
@ -98,10 +107,11 @@ class RegisterPanel extends CustomElement {
} }
if (register.length === 0) { if (register.length === 0) {
conn._changeConnectStatus(Strophe.Status.REGIFAIL); conn._changeConnectStatus(Strophe.Status.REGIFAIL);
this.error_message = this.alert_type = 'danger';
this.setErrorMessage(
__("Sorry, the given provider does not support in "+ __("Sorry, the given provider does not support in "+
"band account registration. Please try with a "+ "band account registration. Please try with a "+
"different provider."); "different provider."));
return true; return true;
} }
// Send an IQ stanza to get all required data fields // Send an IQ stanza to get all required data fields
@ -115,24 +125,17 @@ class RegisterPanel extends CustomElement {
/** /**
* Handler for {@link _converse.RegisterPanel#getRegistrationFields} * Handler for {@link _converse.RegisterPanel#getRegistrationFields}
* @private
* @method _converse.RegisterPanel#onRegistrationFields * @method _converse.RegisterPanel#onRegistrationFields
* @param { XMLElement } stanza - The query stanza. * @param { XMLElement } stanza - The query stanza.
*/ */
onRegistrationFields (stanza) { onRegistrationFields (stanza) {
if (stanza.getAttribute("type") === "error") { if (stanza.getAttribute("type") === "error") {
_converse.connection._changeConnectStatus( this.reportErrors(stanza);
Strophe.Status.REGIFAIL, if (api.settings.get('registration_domain')) {
__('Something went wrong while establishing a connection with "%1$s". '+ this.status = REGISTRATION_FORM_ERROR;
'Are you sure it exists?', this.domain) } else {
); this.status = CHOOSE_PROVIDER;
return false; }
}
if (stanza.getElementsByTagName("query").length !== 1) {
_converse.connection._changeConnectStatus(
Strophe.Status.REGIFAIL,
"unknown"
);
return false; return false;
} }
this.setFields(stanza); this.setFields(stanza);
@ -160,7 +163,6 @@ class RegisterPanel extends CustomElement {
/** /**
* Event handler when the #converse-register form is submitted. * Event handler when the #converse-register form is submitted.
* Depending on the available input fields, we delegate to other methods. * Depending on the available input fields, we delegate to other methods.
* @private
* @param { Event } ev * @param { Event } ev
*/ */
onFormSubmission (ev) { onFormSubmission (ev) {
@ -175,7 +177,6 @@ class RegisterPanel extends CustomElement {
/** /**
* Callback method that gets called when the user has chosen an XMPP provider * Callback method that gets called when the user has chosen an XMPP provider
* @private
* @method _converse.RegisterPanel#onProviderChosen * @method _converse.RegisterPanel#onProviderChosen
* @param { HTMLElement } form - The form that was submitted * @param { HTMLElement } form - The form that was submitted
*/ */
@ -186,7 +187,6 @@ class RegisterPanel extends CustomElement {
/** /**
* Fetch a registration form from the requested domain * Fetch a registration form from the requested domain
* @private
* @method _converse.RegisterPanel#fetchRegistrationForm * @method _converse.RegisterPanel#fetchRegistrationForm
* @param { String } domain_name - XMPP server domain * @param { String } domain_name - XMPP server domain
*/ */
@ -203,30 +203,9 @@ class RegisterPanel extends CustomElement {
return false; return false;
} }
giveFeedback (message, klass) {
let feedback = this.querySelector('.reg-feedback');
if (feedback !== null) {
feedback.parentNode.removeChild(feedback);
}
const form = this.querySelector('form');
form.insertAdjacentHTML('afterbegin', '<span class="reg-feedback"></span>');
feedback = form.querySelector('.reg-feedback');
feedback.textContent = message;
if (klass) {
feedback.classList.add(klass);
}
}
showSpinner () {
const form = this.querySelector('form');
render(tplSpinner(), form);
return this;
}
/** /**
* Callback function called by Strophe whenever the connection status changes. * Callback function called by Strophe whenever the connection status changes.
* Passed to Strophe specifically during a registration attempt. * Passed to Strophe specifically during a registration attempt.
* @private
* @method _converse.RegisterPanel#onConnectStatusChanged * @method _converse.RegisterPanel#onConnectStatusChanged
* @param { integer } status_code - The Strophe.Status status code * @param { integer } status_code - The Strophe.Status status code
*/ */
@ -246,11 +225,11 @@ class RegisterPanel extends CustomElement {
} else if (status_code === Strophe.Status.REGISTERED) { } else if (status_code === Strophe.Status.REGISTERED) {
log.debug("Registered successfully."); log.debug("Registered successfully.");
_converse.connection.reset(); _converse.connection.reset();
this.showSpinner();
if (["converse/login", "converse/register"].includes(_converse.router.history.getFragment())) { if (["converse/login", "converse/register"].includes(_converse.router.history.getFragment())) {
_converse.router.navigate('', {'replace': true}); _converse.router.navigate('', {'replace': true});
} }
setActiveForm('login');
if (this.fields.password && this.fields.username) { if (this.fields.password && this.fields.username) {
// automatically log the user in // automatically log the user in
@ -259,11 +238,9 @@ class RegisterPanel extends CustomElement {
this.fields.password, this.fields.password,
_converse.onConnectStatusChanged _converse.onConnectStatusChanged
); );
setActiveForm('login'); this.setFeedbackMessage(__('Now logging you in'));
_converse.router.navigate('');
this.giveFeedback(__('Now logging you in'), 'info');
} else { } else {
_converse.giveFeedback(__('Registered successfully')); this.setFeedbackMessage(__('Registered successfully'));
} }
this.reset(); this.reset();
} }
@ -308,7 +285,6 @@ class RegisterPanel extends CustomElement {
/** /**
* Renders the registration form based on the XForm fields * Renders the registration form based on the XForm fields
* received from the XMPP server. * received from the XMPP server.
* @private
* @method _converse.RegisterPanel#renderRegistrationForm * @method _converse.RegisterPanel#renderRegistrationForm
* @param { XMLElement } stanza - The IQ stanza received from the XMPP server. * @param { XMLElement } stanza - The IQ stanza received from the XMPP server.
*/ */
@ -320,17 +296,16 @@ class RegisterPanel extends CustomElement {
/** /**
* Report back to the user any error messages received from the * Report back to the user any error messages received from the
* XMPP server after attempted registration. * XMPP server after attempted registration.
* @private
* @method _converse.RegisterPanel#reportErrors * @method _converse.RegisterPanel#reportErrors
* @param { XMLElement } stanza - The IQ stanza received from the XMPP server * @param { XMLElement } stanza - The IQ stanza received from the XMPP server
*/ */
reportErrors (stanza) { reportErrors (stanza) {
const errors = Array.from(stanza.querySelectorAll('error')); const errors = Array.from(stanza.querySelectorAll('error'));
if (errors.length) { if (errors.length) {
this.error_message = errors.reduce((result, e) => `${result}\n${e.textContent}`, ''); this.setErrorMessage(errors.reduce((result, e) => `${result}\n${e.textContent}`, ''));
} else { } else {
this.error_message = __('The provider rejected your registration attempt. '+ this.setErrorMessage(__('The provider rejected your registration attempt. '+
'Please check the values you entered for correctness.'); 'Please check the values you entered for correctness.'));
} }
} }
@ -356,7 +331,6 @@ class RegisterPanel extends CustomElement {
/** /**
* Handler, when the user submits the registration form. * Handler, when the user submits the registration form.
* Provides form error feedback or starts the registration process. * Provides form error feedback or starts the registration process.
* @private
* @method _converse.RegisterPanel#submitRegistrationForm * @method _converse.RegisterPanel#submitRegistrationForm
* @param { HTMLElement } form - The HTML form that was submitted * @param { HTMLElement } form - The HTML form that was submitted
*/ */
@ -378,8 +352,8 @@ class RegisterPanel extends CustomElement {
this.setFields(iq.tree()); this.setFields(iq.tree());
} }
/* Stores the values that will be sent to the XMPP server during attempted registration. /**
* @private * Stores the values that will be sent to the XMPP server during attempted registration.
* @method _converse.RegisterPanel#setFields * @method _converse.RegisterPanel#setFields
* @param { XMLElement } stanza - the IQ stanza that will be sent to the XMPP server. * @param { XMLElement } stanza - the IQ stanza that will be sent to the XMPP server.
*/ */
@ -428,7 +402,6 @@ class RegisterPanel extends CustomElement {
* Callback method that gets called when a return IQ stanza * Callback method that gets called when a return IQ stanza
* is received from the XMPP server, after attempting to * is received from the XMPP server, after attempting to
* register a new user. * register a new user.
* @private
* @method _converse.RegisterPanel#reportErrors * @method _converse.RegisterPanel#reportErrors
* @param { XMLElement } stanza - The IQ stanza. * @param { XMLElement } stanza - The IQ stanza.
*/ */

View File

@ -1,17 +1,16 @@
import tplRegistrationForm from './registration_form.js'; import tplRegistrationForm from './registration_form.js';
import tplSpinner from 'templates/spinner.js'; import tplSpinner from 'templates/spinner.js';
import tplSwitchForm from './switch_form.js';
import { __ } from 'i18n'; import { __ } from 'i18n';
import { api } from '@converse/headless/core'; import { api } from '@converse/headless/core';
import { html } from 'lit'; import { html } from 'lit';
const tplFormRequest = (el) => { const tplFormRequest = (el) => {
const default_domain = api.settings.get('registration_domain'); const default_domain = api.settings.get('registration_domain');
const i18n_fetch_form = __("Hold tight, we're fetching the registration form…");
const i18n_cancel = __('Cancel'); const i18n_cancel = __('Cancel');
return html` return html`
<form id="converse-register" class="converse-form no-scrolling" @submit=${ev => el.onFormSubmission(ev)}> <form id="converse-register" class="converse-form no-scrolling" @submit=${ev => el.onFormSubmission(ev)}>
${tplSpinner({ 'classes': 'hor_centered' })} ${tplSpinner({ 'classes': 'hor_centered' })}
<p class="info">${i18n_fetch_form}</p>
${default_domain ${default_domain
? '' ? ''
: html` : html`
@ -73,13 +72,15 @@ const tplChooseProvider = (el) => {
const CHOOSE_PROVIDER = 0; const CHOOSE_PROVIDER = 0;
const FETCHING_FORM = 1; const FETCHING_FORM = 1;
const REGISTRATION_FORM = 2; const REGISTRATION_FORM = 2;
const REGISTRATION_FORM_ERROR = 3;
export default (el) => { export default (el) => {
return html` return html`
<converse-brand-logo></converse-brand-logo> <converse-brand-logo></converse-brand-logo>
${ el.error_message ? html`<div class="alert alert-danger" role="alert">${el.error_message}</div>` : '' } ${ el.alert_message ? html`<div class="alert alert-${el.alert_type}" role="alert">${el.alert_message}</div>` : '' }
${el.status === CHOOSE_PROVIDER ? tplChooseProvider(el) : ''} ${el.status === CHOOSE_PROVIDER ? tplChooseProvider(el) : ''}
${el.status === FETCHING_FORM ? tplFormRequest(el) : ''} ${el.status === FETCHING_FORM ? tplFormRequest(el) : ''}
${el.status === REGISTRATION_FORM ? tplRegistrationForm(el) : ''} ${el.status === REGISTRATION_FORM ? tplRegistrationForm(el) : ''}
${el.status === REGISTRATION_FORM_ERROR ? tplSwitchForm() : '' }
`; `;
}; };

View File

@ -1,12 +1,11 @@
import tplSwitchForm from './switch_form.js';
import { __ } from 'i18n'; import { __ } from 'i18n';
import { api } from '@converse/headless/core'; import { api } from '@converse/headless/core';
import { html } from 'lit'; import { html } from 'lit';
export default (el) => { export default (el) => {
const i18n_choose_provider = __('Choose a different provider'); const i18n_choose_provider = __('Choose a different provider');
const i18n_has_account = __('Already have a chat account?');
const i18n_legend = __('Account Registration:'); const i18n_legend = __('Account Registration:');
const i18n_login = __('Log in here');
const i18n_register = __('Register'); const i18n_register = __('Register');
const registration_domain = api.settings.get('registration_domain'); const registration_domain = api.settings.get('registration_domain');
@ -34,10 +33,7 @@ export default (el) => {
@click=${ev => el.renderProviderChoiceForm(ev)} @click=${ev => el.renderProviderChoiceForm(ev)}
/> />
`} `}
<div class="switch-form"> ${ tplSwitchForm() }
<p>${i18n_has_account}</p>
<p><a class="login-here toggle-register-login" href="#converse/login">${i18n_login}</a></p>
</div>
</fieldset> </fieldset>
</form> </form>
`; `;

View File

@ -0,0 +1,12 @@
import { __ } from 'i18n';
import { html } from 'lit';
export default () => {
const i18n_has_account = __('Already have a chat account?');
const i18n_login = __('Log in here');
return html`
<div class="switch-form">
<p>${i18n_has_account}</p>
<p><a class="login-here toggle-register-login" href="#converse/login">${i18n_login}</a></p>
</div>`;
}