瀏覽代碼

Turn login and register panels into element views

JC Brand 4 年之前
父節點
當前提交
bb7d1c5cba

+ 1 - 1
spec/register.js

@@ -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,

+ 2 - 4
src/plugins/controlbox/index.js

@@ -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;
 

+ 31 - 28
src/plugins/controlbox/loginpanel.js

@@ -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);

+ 11 - 5
src/plugins/controlbox/templates/controlbox.js

@@ -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>
 `;

+ 9 - 32
src/plugins/controlbox/view.js

@@ -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();
     }
 
     /**

+ 0 - 35
src/plugins/register/controlbox-mixin.js

@@ -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;

+ 0 - 21
src/plugins/register/index.js

@@ -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(() => {

+ 40 - 38
src/plugins/register/panel.js

@@ -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);