Turn login and register panels into element views

This commit is contained in:
JC Brand 2021-01-22 15:31:59 +01:00
parent 0ecd86ecf7
commit bb7d1c5cba
8 changed files with 94 additions and 164 deletions

View File

@ -7,7 +7,7 @@ const u = converse.env.utils;
describe("The Registration Panel", function () {
it("is not available unless allow_registration=true",
fit("is not available unless allow_registration=true",
mock.initConverse(
['chatBoxesInitialized'],
{ auto_login: false,

View File

@ -5,15 +5,15 @@
*/
import "../../components/brand-heading";
import "../chatview/index.js";
import './loginpanel.js';
import ControlBoxMixin from './model.js';
import ControlBoxPane from './pane.js';
import ControlBoxToggle from './toggle.js';
import ControlBoxView from './view.js';
import controlbox_api from './api.js';
import log from '@converse/headless/log';
import { LoginPanelModel, LoginPanel } from './loginpanel.js';
import { _converse, api, converse } from '@converse/headless/core';
import { addControlBox } from './utils.js';
import controlbox_api from './api.js';
const u = converse.env.utils;
@ -96,8 +96,6 @@ converse.plugins.add('converse-controlbox', {
_converse.ControlBoxView = ControlBoxView;
_converse.ControlBox = _converse.ChatBox.extend(ControlBoxMixin);
_converse.LoginPanelModel = LoginPanelModel;
_converse.LoginPanel = LoginPanel;
_converse.ControlBoxPane = ControlBoxPane;
_converse.ControlBoxToggle = ControlBoxToggle;

View File

@ -1,9 +1,10 @@
import bootstrap from "bootstrap.native";
import tpl_login_panel from "./templates/loginpanel.js";
import { ElementView } from "@converse/skeletor/src/element";
import { Model } from '@converse/skeletor/src/model.js';
import { View } from "@converse/skeletor/src/view";
import { __ } from '../../i18n';
import { __ } from 'i18n';
import { _converse, api, converse } from "@converse/headless/core";
import { render } from 'lit-html';
const u = converse.env.utils;
const { Strophe } = converse.env;
@ -47,38 +48,38 @@ const CONNECTION_STATUS_CSS_CLASS = {
};
export const LoginPanelModel = Model.extend({
const LoginPanelModel = Model.extend({
defaults: {
// Passed-by-reference. Fine in this case because there's
// only one such model.
// Passed-by-reference. Fine in this case because there's only one such model.
'errors': [],
}
});
export const LoginPanel = View.extend({
tagName: 'div',
id: "converse-login-panel",
className: 'controlbox-pane fade-in row no-gutters',
events: {
class LoginPanel extends ElementView {
id = "converse-login-panel"
className = 'controlbox-pane fade-in row no-gutters'
events = {
'submit form#converse-login': 'authenticate',
'change input': 'validate'
},
}
initialize () {
this.model = new LoginPanelModel();
this.listenTo(this.model, 'change', this.render)
this.listenTo(_converse.connfeedback, 'change', this.render);
this.render();
},
this.initPopovers();
}
toHTML () {
render () {
const connection_status = _converse.connfeedback.get('connection_status');
let feedback_class, pretty_status;
if (REPORTABLE_STATUSES.includes(connection_status)) {
pretty_status = PRETTY_CONNECTION_STATUS[connection_status];
feedback_class = CONNECTION_STATUS_CSS_CLASS[pretty_status];
}
return tpl_login_panel(
render(tpl_login_panel(
Object.assign(this.model.toJSON(), {
'_converse': _converse,
'ANONYMOUS': _converse.ANONYMOUS,
@ -95,21 +96,21 @@ export const LoginPanel = View.extend({
__('Username') || __('user@domain'),
'show_trust_checkbox': api.settings.get('allow_user_trust_override')
})
);
},
), this);
}
initPopovers () {
Array.from(this.el.querySelectorAll('[data-title]')).forEach(el => {
Array.from(this.querySelectorAll('[data-title]')).forEach(el => {
new bootstrap.Popover(el, {
'trigger': api.settings.get("view_mode") === 'mobile' && 'click' || 'hover',
'dismissible': api.settings.get("view_mode") === 'mobile' && true || false,
'container': this.el.parentElement.parentElement.parentElement
'container': this.parentElement.parentElement.parentElement
})
});
},
}
validate () {
const form = this.el.querySelector('form');
const form = this.querySelector('form');
const jid_element = form.querySelector('input[name=jid]');
if (jid_element.value &&
!api.settings.get('locked_domain') &&
@ -120,14 +121,14 @@ export const LoginPanel = View.extend({
}
jid_element.setCustomValidity('');
return true;
},
}
/**
* Authenticate the user based on a form submission event.
* @param { Event } ev
*/
* Authenticate the user based on a form submission event.
* @param { Event } ev
*/
authenticate (ev) {
if (ev && ev.preventDefault) { ev.preventDefault(); }
ev?.preventDefault();
if (api.settings.get("authentication") === _converse.ANONYMOUS) {
return this.connect(_converse.jid, null);
}
@ -147,13 +148,15 @@ export const LoginPanel = View.extend({
jid = jid + '@' + api.settings.get('default_domain');
}
this.connect(jid, form_data.get('password'));
},
}
connect (jid, password) {
connect (jid, password) { // eslint-disable-line class-methods-use-this
if (["converse/login", "converse/register"].includes(_converse.router.history.getFragment())) {
_converse.router.navigate('', {'replace': true});
}
_converse.connection && _converse.connection.reset();
api.user.login(jid, password);
}
});
}
api.elements.define('converse-login-panel', LoginPanel);

View File

@ -1,5 +1,4 @@
import { html } from 'lit-html';
import { api } from '@converse/headless/core';
export default (o) => html`
<div class="flyout box-flyout">
@ -8,10 +7,17 @@ export default (o) => html`
${o.sticky_controlbox ? '' : html`<a class="chatbox-btn close-chatbox-button fa fa-times"></a>` }
</div>
<div class="controlbox-panes">
${ api.connection.connected() ? html`
<converse-headlines-panel></converse-headlines-panel>
<converse-rooms-list></converse-rooms-list>
<converse-bookmarks></converse-bookmarks>` : '' }
${ o.connected
? html`
<converse-headlines-panel></converse-headlines-panel>
<converse-rooms-list></converse-rooms-list>
<converse-bookmarks></converse-bookmarks>`
: (
o['active-form'] === 'register'
? html`<converse-login-panel></converse-login-panel>`
: html`<converse-register-panel></converse-headlines-panel>`
)
}
</div>
</div>
`;

View File

@ -23,6 +23,7 @@ class ControlBoxView extends ElementView {
// this.listenTo(this.model, 'hide', this.hide);
this.listenTo(this.model, 'show', this.show);
this.render();
_converse.chatboxviews.add('controlbox', this);
/**
* Triggered when the _converse.ControlBoxView has been initialized and therefore
* exists. The controlbox contains the login and register forms when the user is
@ -35,49 +36,25 @@ class ControlBoxView extends ElementView {
}
render () {
_converse.chatboxviews.add('controlbox', this);
if (this.model.get('connected')) {
if (this.model.get('closed') === undefined) {
this.model.set('closed', !api.settings.get('show_controlbox_by_default'));
}
if (this.model.get('connected') && this.model.get('closed') === undefined) {
this.model.set('closed', !api.settings.get('show_controlbox_by_default'));
}
const tpl_result = tpl_controlbox({
render(tpl_controlbox({
'sticky_controlbox': api.settings.get('sticky_controlbox'),
...this.model.toJSON()
});
render(tpl_result, this);
}), this);
const connection = _converse?.connection || {};
if (!connection.connected || !connection.authenticated || connection.disconnecting) {
this.renderLoginPanel();
const connection = _converse?.connection;
if (!connection?.connected || !connection?.authenticated || connection?.disconnecting) {
this.classList.add('logged-out');
} else if (this.model.get('connected')) {
this.renderControlBoxPane();
}
return this;
}
onConnected () {
if (this.model.get('connected')) {
this.render();
}
}
renderLoginPanel () {
this.classList.add('logged-out');
if (this.loginpanel) {
this.loginpanel.render();
} else {
this.loginpanel = new _converse.LoginPanel({
'model': new _converse.LoginPanelModel()
});
const panes = this.querySelector('.controlbox-panes');
panes.innerHTML = '';
panes.appendChild(this.loginpanel.render().el);
}
this.loginpanel.initPopovers();
return this;
this.model.get('connected') && this.render();
}
/**

View File

@ -1,35 +0,0 @@
import { _converse, api } from '@converse/headless/core';
const ControlBoxRegistrationMixin = {
showLoginOrRegisterForm () {
if (!this.registerpanel) {
return;
}
if (this.model.get('active-form') == 'register') {
this.loginpanel.el.classList.add('hidden');
this.registerpanel.el.classList.remove('hidden');
} else {
this.loginpanel.el.classList.remove('hidden');
this.registerpanel.el.classList.add('hidden');
}
},
renderRegistrationPanel () {
if (api.settings.get('allow_registration')) {
this.registerpanel = new _converse.RegisterPanel({
'model': this.model
});
this.registerpanel.render();
this.registerpanel.el.classList.add('hidden');
const login_panel = this.querySelector('#converse-login-panel');
if (login_panel) {
login_panel.insertAdjacentElement('afterend', this.registerpanel.el);
}
this.showLoginOrRegisterForm();
}
return this;
}
};
export default ControlBoxRegistrationMixin;

View File

@ -7,8 +7,6 @@
* @license Mozilla Public License (MPLv2)
*/
import '../controlbox/index.js';
import ControlBoxRegistrationMixin from './controlbox-mixin.js';
import RegisterPanel from './panel.js';
import log from '@converse/headless/log';
import { __ } from 'i18n';
import { _converse, api, converse } from '@converse/headless/core';
@ -31,21 +29,6 @@ converse.plugins.add('converse-register', {
return true;
},
overrides: {
// Overrides mentioned here will be picked up by converse.js's
// plugin architecture they will replace existing methods on the
// relevant objects or classes.
ControlBoxView: {
renderLoginPanel () {
// Also render a registration panel, when rendering the login panel.
this.__super__.renderLoginPanel.apply(this, arguments);
this.renderRegistrationPanel();
return this;
}
}
},
initialize () {
_converse.CONNECTION_STATUS[Strophe.Status.REGIFAIL] = 'REGIFAIL';
_converse.CONNECTION_STATUS[Strophe.Status.REGISTERED] = 'REGISTERED';
@ -59,10 +42,6 @@ converse.plugins.add('converse-register', {
'registration_domain': ''
});
Object.assign(_converse.ControlBoxView.prototype, ControlBoxRegistrationMixin);
_converse.RegisterPanel = RegisterPanel;
function setActiveForm (value) {
api.waitUntil('controlBoxInitialized')
.then(() => {

View File

@ -5,7 +5,7 @@ import tpl_form_username from "templates/form_username.js";
import tpl_register_panel from "./templates/register_panel.js";
import tpl_spinner from "templates/spinner.js";
import utils from "@converse/headless/utils/form";
import { View } from "@converse/skeletor/src/view";
import { ElementView } from "@converse/skeletor/src/element";
import { __ } from 'i18n';
import { _converse, api, converse } from "@converse/headless/core";
import { pick } from "lodash-es";
@ -26,17 +26,17 @@ const REGISTRATION_FORM = 2;
* @namespace _converse.RegisterPanel
* @memberOf _converse
*/
const RegisterPanel = View.extend({
tagName: 'div',
id: "converse-register-panel",
className: 'controlbox-pane fade-in',
events: {
class RegisterPanel extends ElementView {
id = "converse-register-panel"
className = 'controlbox-pane fade-in'
events = {
'submit form#converse-register': 'onFormSubmission',
'click .button-cancel': 'renderProviderChoiceForm',
},
}
initialize () {
this.reset();
this.model = _converse.controlbox;
api.listen.on('connectionInitialized', () => this.registerHooks());
this.listenTo(this.model, 'change:registration_status', this.render);
@ -46,7 +46,9 @@ const RegisterPanel = View.extend({
} else {
this.model.set('registration_status', CHOOSE_PROVIDER);
}
},
this.initPopovers();
}
render () {
render(tpl_register_panel({
@ -56,9 +58,9 @@ const RegisterPanel = View.extend({
'instructions': this.instructions,
'model': this.model,
'title': this.title,
}), this.el);
}), this);
return this;
},
}
/**
* Hook into Strophe's _connect_cb, so that we can send an IQ
@ -76,7 +78,7 @@ const RegisterPanel = View.extend({
}
}
};
},
}
/**
* Send an IQ stanza to the XMPP server asking for the registration fields.
@ -118,7 +120,7 @@ const RegisterPanel = View.extend({
conn.send(stanza);
conn.connected = false;
return true;
},
}
/**
* Handler for {@link _converse.RegisterPanel#getRegistrationFields}
@ -147,7 +149,7 @@ const RegisterPanel = View.extend({
this.renderRegistrationForm(stanza);
}
return false;
},
}
reset (settings) {
const defaults = {
@ -164,7 +166,7 @@ const RegisterPanel = View.extend({
if (settings) {
Object.assign(this, pick(settings, Object.keys(defaults)));
}
},
}
/**
* Event handler when the #converse-register form is submitted.
@ -180,7 +182,7 @@ const RegisterPanel = View.extend({
this.onProviderChosen(ev.target);
}
},
}
/**
* Callback method that gets called when the user has chosen an XMPP provider
@ -198,7 +200,7 @@ const RegisterPanel = View.extend({
}
form.querySelector('input[type=submit]').classList.add('hidden');
this.fetchRegistrationForm(domain.trim());
},
}
/**
* Fetch a registration form from the requested domain
@ -217,27 +219,27 @@ const RegisterPanel = View.extend({
// above finishes. So we use optional chaining here
_converse.connection?.connect(this.domain, "", status => this.onConnectStatusChanged(status));
return false;
},
}
giveFeedback (message, klass) {
let feedback = this.el.querySelector('.reg-feedback');
let feedback = this.querySelector('.reg-feedback');
if (feedback !== null) {
feedback.parentNode.removeChild(feedback);
}
const form = this.el.querySelector('form');
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.el.querySelector('form');
const form = this.querySelector('form');
render(tpl_spinner(), form);
return this;
},
}
/**
* Callback function called by Strophe whenever the connection status changes.
@ -282,7 +284,7 @@ const RegisterPanel = View.extend({
}
this.reset();
}
},
}
getLegacyFormFields () {
const input_fields = Object.keys(this.fields).map(key => {
@ -308,7 +310,7 @@ const RegisterPanel = View.extend({
});
const urls = this.urls.map(u => tpl_form_url({'label': '', 'value': u}));
return [...input_fields, ...urls];
},
}
getFormFields (stanza) {
if (this.form_type === 'xform') {
@ -318,7 +320,7 @@ const RegisterPanel = View.extend({
} else {
return this.getLegacyFormFields();
}
},
}
/**
* Renders the registration form based on the XForm fields
@ -330,10 +332,10 @@ const RegisterPanel = View.extend({
renderRegistrationForm (stanza) {
this.form_fields = this.getFormFields(stanza);
this.model.set('registration_status', REGISTRATION_FORM);
},
}
showValidationError (message) {
const form = this.el.querySelector('form');
const form = this.querySelector('form');
let flash = form.querySelector('.form-errors');
if (flash === null) {
flash = '<div class="form-errors hidden"></div>';
@ -352,7 +354,7 @@ const RegisterPanel = View.extend({
'<p class="form-help error">'+message+'</p>'
);
flash.classList.remove('hidden');
},
}
/**
* Report back to the user any error messages received from the
@ -369,14 +371,14 @@ const RegisterPanel = View.extend({
'Please check the values you entered for correctness.');
this.showValidationError(message);
}
},
}
renderProviderChoiceForm (ev) {
if (ev && ev.preventDefault) { ev.preventDefault(); }
_converse.connection._proto._abortAllRequests();
_converse.connection.reset();
this.render();
},
}
abortRegistration () {
_converse.connection._proto._abortAllRequests();
@ -388,7 +390,7 @@ const RegisterPanel = View.extend({
} else {
this.render();
}
},
}
/**
* Handler, when the user submits the registration form.
@ -398,7 +400,7 @@ const RegisterPanel = View.extend({
* @param { HTMLElement } form - The HTML form that was submitted
*/
submitRegistrationForm (form) {
const has_empty_inputs = Array.from(this.el.querySelectorAll('input.required'))
const has_empty_inputs = Array.from(this.querySelectorAll('input.required'))
.reduce((result, input) => {
if (input.value === '') {
input.classList.add('error');
@ -423,7 +425,7 @@ const RegisterPanel = View.extend({
_converse.connection._addSysHandler(this._onRegisterIQ.bind(this), null, "iq", null, null);
_converse.connection.send(iq);
this.setFields(iq.tree());
},
}
/* Stores the values that will be sent to the XMPP server during attempted registration.
* @private
@ -438,7 +440,7 @@ const RegisterPanel = View.extend({
} else {
this._setFieldsFromLegacy(query);
}
},
}
_setFieldsFromLegacy (query) {
[].forEach.call(query.children, field => {
@ -454,7 +456,7 @@ const RegisterPanel = View.extend({
this.fields[field.tagName.toLowerCase()] = Strophe.getText(field);
});
this.form_type = 'legacy';
},
}
_setFieldsFromXForm (xform) {
this.title = xform.querySelector('title')?.textContent;
@ -469,7 +471,7 @@ const RegisterPanel = View.extend({
}
});
this.form_type = 'xform';
},
}
/**
* Callback method that gets called when a return IQ stanza
@ -502,6 +504,6 @@ const RegisterPanel = View.extend({
}
return false;
}
});
}
export default RegisterPanel;
api.elements.define('converse-register-panel', RegisterPanel);