Browse Source

Use LitElement for modals

JC Brand 5 months ago
parent
commit
c9a1814b16

+ 1 - 1
src/plugins/modal/alert.js

@@ -7,7 +7,7 @@ export default class Alert extends BaseModal {
 
 
     initialize () {
     initialize () {
         super.initialize();
         super.initialize();
-        this.listenTo(this.model, 'change', () => this.render())
+        this.listenTo(this.model, 'change', () => this.requestUpdate())
         this.addEventListener('hide.bs.modal', () => this.remove(), false);
         this.addEventListener('hide.bs.modal', () => this.remove(), false);
     }
     }
 
 

+ 1 - 1
src/plugins/modal/confirm.js

@@ -11,7 +11,7 @@ export default class Confirm extends BaseModal {
 
 
     initialize() {
     initialize() {
         super.initialize();
         super.initialize();
-        this.listenTo(this.model, 'change', () => this.render());
+        this.listenTo(this.model, 'change', () => this.requestUpdate());
         this.addEventListener(
         this.addEventListener(
             'hide.bs.modal',
             'hide.bs.modal',
             () => {
             () => {

+ 19 - 10
src/plugins/modal/modal.js

@@ -1,18 +1,21 @@
 import { html } from 'lit';
 import { html } from 'lit';
 import { getOpenPromise } from '@converse/openpromise';
 import { getOpenPromise } from '@converse/openpromise';
 import { Modal } from "bootstrap";
 import { Modal } from "bootstrap";
-import { ElementView } from '@converse/skeletor';
+import { CustomElement } from 'shared/components/element.js';
 import { u } from '@converse/headless';
 import { u } from '@converse/headless';
 import { modal_close_button } from "./templates/buttons.js";
 import { modal_close_button } from "./templates/buttons.js";
 import tplModal from './templates/modal.js';
 import tplModal from './templates/modal.js';
 
 
 import './styles/_modal.scss';
 import './styles/_modal.scss';
 
 
-class BaseModal extends ElementView {
+class BaseModal extends CustomElement {
     /**
     /**
      * @typedef {import('lit').TemplateResult} TemplateResult
      * @typedef {import('lit').TemplateResult} TemplateResult
      */
      */
 
 
+    /** @type {Modal} */
+    #modal;
+
     /**
     /**
      * @param {Object} options
      * @param {Object} options
      */
      */
@@ -37,12 +40,18 @@ class BaseModal extends ElementView {
         });
         });
     }
     }
 
 
+    get modal () {
+        if (!this.#modal) {
+            this.#modal = new Modal(this, {
+                backdrop: u.isTestEnv() ? false : true,
+                keyboard: true
+            });
+        }
+        return this.#modal;
+    }
+
     initialize () {
     initialize () {
-        this.render()
-        this.modal = new Modal(this, {
-            backdrop: u.isTestEnv() ? false : true,
-            keyboard: true
-        });
+        this.requestUpdate()
         this.initialized.resolve();
         this.initialized.resolve();
     }
     }
 
 
@@ -60,7 +69,7 @@ class BaseModal extends ElementView {
         return html`<div class="modal-footer">${ modal_close_button }</div>`;
         return html`<div class="modal-footer">${ modal_close_button }</div>`;
     }
     }
 
 
-    toHTML () {
+    render () {
         return tplModal(this);
         return tplModal(this);
     }
     }
 
 
@@ -79,7 +88,7 @@ class BaseModal extends ElementView {
         ev?.stopPropagation();
         ev?.stopPropagation();
         ev?.preventDefault();
         ev?.preventDefault();
         this.tab = /** @type {HTMLElement} */(ev.target).getAttribute('data-name');
         this.tab = /** @type {HTMLElement} */(ev.target).getAttribute('data-name');
-        this.render();
+        this.requestUpdate();
     }
     }
 
 
     close () {
     close () {
@@ -105,7 +114,7 @@ class BaseModal extends ElementView {
     async show () {
     async show () {
         await this.initialized;
         await this.initialized;
         this.modal.show();
         this.modal.show();
-        this.render();
+        this.requestUpdate();
     }
     }
 }
 }
 
 

+ 2 - 2
src/plugins/muc-views/modals/add-muc.js

@@ -11,11 +11,11 @@ const { Strophe } = converse.env;
 export default class AddMUCModal extends BaseModal {
 export default class AddMUCModal extends BaseModal {
     initialize() {
     initialize() {
         super.initialize();
         super.initialize();
-        this.render();
+        this.requestUpdate();
         this.addEventListener(
         this.addEventListener(
             'shown.bs.modal',
             'shown.bs.modal',
             () => {
             () => {
-                /** @type {HTMLInputElement} */ (this.querySelector('input[name="chatroom"]')).focus();
+                /** @type {HTMLInputElement} */ (this.querySelector('input[name="chatroom"]'))?.focus();
             },
             },
             false
             false
         );
         );

+ 8 - 9
src/plugins/muc-views/modals/config.js

@@ -1,16 +1,15 @@
-/**
- * @typedef {import('@converse/headless/types/plugins/vcard/api').VCardData} VCardData
- */
+import { api, converse, log } from '@converse/headless';
 import tplMUCConfigForm from './templates/muc-config.js';
 import tplMUCConfigForm from './templates/muc-config.js';
 import BaseModal from 'plugins/modal/modal.js';
 import BaseModal from 'plugins/modal/modal.js';
 import { __ } from 'i18n';
 import { __ } from 'i18n';
-import { api, converse, log } from '@converse/headless';
 import { compressImage, isImageWithAlphaChannel } from 'utils/file.js';
 import { compressImage, isImageWithAlphaChannel } from 'utils/file.js';
 
 
-const { sizzle } = converse.env;
-const u = converse.env.utils;
+const { sizzle, u } = converse.env;
 
 
 export default class MUCConfigModal extends BaseModal {
 export default class MUCConfigModal extends BaseModal {
+    /**
+     * @typedef {import('@converse/headless/types/plugins/vcard/api').VCardData} VCardData
+     */
 
 
     constructor (options) {
     constructor (options) {
         super(options);
         super(options);
@@ -19,9 +18,9 @@ export default class MUCConfigModal extends BaseModal {
 
 
     initialize () {
     initialize () {
         super.initialize();
         super.initialize();
-        this.listenTo(this.model, 'change', () => this.render());
-        this.listenTo(this.model.features, 'change:passwordprotected', () => this.render());
-        this.listenTo(this.model.session, 'change:config_stanza', () => this.render());
+        this.listenTo(this.model, 'change', () => this.requestUpdate());
+        this.listenTo(this.model.features, 'change:passwordprotected', () => this.requestUpdate());
+        this.listenTo(this.model.session, 'change:config_stanza', () => this.requestUpdate());
     }
     }
 
 
     renderModal () {
     renderModal () {

+ 4 - 4
src/plugins/muc-views/modals/muc-details.js

@@ -10,10 +10,10 @@ export default class MUCDetailsModal extends BaseModal {
 
 
     initialize () {
     initialize () {
         super.initialize();
         super.initialize();
-        this.listenTo(this.model, 'change', () => this.render());
-        this.listenTo(this.model.features, 'change', () => this.render());
-        this.listenTo(this.model.occupants, 'add', () => this.render());
-        this.listenTo(this.model.occupants, 'change', () => this.render());
+        this.listenTo(this.model, 'change', () => this.requestUpdate());
+        this.listenTo(this.model.features, 'change', () => this.requestUpdate());
+        this.listenTo(this.model.occupants, 'add', () => this.requestUpdate());
+        this.listenTo(this.model.occupants, 'change', () => this.requestUpdate());
     }
     }
 
 
     renderModal () {
     renderModal () {

+ 1 - 1
src/plugins/muc-views/modals/muc-invite.js

@@ -15,7 +15,7 @@ export default class MUCInviteModal extends BaseModal {
 
 
     initialize () {
     initialize () {
         super.initialize();
         super.initialize();
-        this.listenTo(this.model, 'change', () => this.render());
+        this.listenTo(this.model, 'change', () => this.requestUpdate());
     }
     }
 
 
     renderModal () {
     renderModal () {

+ 3 - 3
src/plugins/muc-views/modals/muc-list.js

@@ -72,7 +72,7 @@ export default class MUCListModal extends BaseModal {
     initialize () {
     initialize () {
         super.initialize();
         super.initialize();
         this.listenTo(this.model, 'change:muc_domain', this.onDomainChange);
         this.listenTo(this.model, 'change:muc_domain', this.onDomainChange);
-        this.listenTo(this.model, 'change:feedback_text', () => this.render());
+        this.listenTo(this.model, 'change:feedback_text', () => this.requestUpdate());
 
 
         this.addEventListener('shown.bs.modal', () => api.settings.get('locked_muc_domain') && this.updateRoomsList());
         this.addEventListener('shown.bs.modal', () => api.settings.get('locked_muc_domain') && this.updateRoomsList());
 
 
@@ -130,7 +130,7 @@ export default class MUCListModal extends BaseModal {
             this.items = [];
             this.items = [];
             this.model.set({'feedback_text': __('No groupchats found')}, {'silent': true});
             this.model.set({'feedback_text': __('No groupchats found')}, {'silent': true});
         }
         }
-        this.render();
+        this.requestUpdate();
         return true;
         return true;
     }
     }
 
 
@@ -153,7 +153,7 @@ export default class MUCListModal extends BaseModal {
     showRooms (ev) {
     showRooms (ev) {
         ev.preventDefault();
         ev.preventDefault();
         this.loading_items = true;
         this.loading_items = true;
-        this.render();
+        this.requestUpdate();
 
 
         const data = new FormData(ev.target);
         const data = new FormData(ev.target);
         this.model.setDomain(data.get('server'));
         this.model.setDomain(data.get('server'));

+ 2 - 2
src/plugins/muc-views/modals/occupant.js

@@ -18,7 +18,7 @@ export default class OccupantModal extends BaseModal {
     initialize () {
     initialize () {
         super.initialize()
         super.initialize()
         const model = this.model ?? this.message;
         const model = this.model ?? this.message;
-        this.listenTo(model, 'change', () => this.render());
+        this.listenTo(model, 'change', () => this.requestUpdate());
         /**
         /**
          * Triggered once the OccupantModal has been initialized
          * Triggered once the OccupantModal has been initialized
          * @event _converse#occupantModalInitialized
          * @event _converse#occupantModalInitialized
@@ -61,7 +61,7 @@ export default class OccupantModal extends BaseModal {
         } else {
         } else {
             this.show_affiliation_form = !this.show_affiliation_form;
             this.show_affiliation_form = !this.show_affiliation_form;
         }
         }
-        this.render();
+        this.requestUpdate();
     }
     }
 }
 }
 
 

+ 1 - 1
src/plugins/muc-views/modtools.js

@@ -160,7 +160,7 @@ export default class ModeratorTools extends CustomElement {
 
 
     filterRoleResults (ev) {
     filterRoleResults (ev) {
         this.roles_filter = ev.target.value;
         this.roles_filter = ev.target.value;
-        this.render();
+        this.requestUpdate();
     }
     }
 
 
     filterAffiliationResults (ev) {
     filterAffiliationResults (ev) {

+ 1 - 1
src/plugins/muc-views/occupant.js

@@ -82,7 +82,7 @@ export default class MUCOccupant extends CustomElement {
         } else {
         } else {
             this.show_affiliation_form = !this.show_affiliation_form;
             this.show_affiliation_form = !this.show_affiliation_form;
         }
         }
-        this.render();
+        this.requestUpdate();
     }
     }
 }
 }
 
 

+ 2 - 2
src/plugins/muc-views/password-form.js

@@ -18,8 +18,8 @@ class MUCPasswordForm extends CustomElement {
         super.connectedCallback();
         super.connectedCallback();
         const { chatboxes } = _converse.state;
         const { chatboxes } = _converse.state;
         this.model = chatboxes.get(this.jid);
         this.model = chatboxes.get(this.jid);
-        this.listenTo(this.model, 'change:password_validation_message', this.render);
-        this.render();
+        this.listenTo(this.model, 'change:password_validation_message', () => this.requestUpdate());
+        this.requestUpdate();
     }
     }
 
 
     render () {
     render () {

+ 1 - 1
src/plugins/muc-views/tests/commands.js

@@ -878,7 +878,7 @@ describe("Groupchats", function () {
             submit.click();
             submit.click();
 
 
             expect(u.isVisible(modal)).toBeTruthy();
             expect(u.isVisible(modal)).toBeTruthy();
-            expect(u.hasClass('error', challenge_el)).toBeTruthy();
+            await u.waitUntil(() => u.hasClass('error', challenge_el));
             challenge_el.value = muc_jid;
             challenge_el.value = muc_jid;
             submit.click();
             submit.click();
 
 

+ 2 - 2
src/plugins/muc-views/tests/muc-avatar.js

@@ -150,9 +150,10 @@ describe('Groupchats', () => {
                     expect(els[3].textContent).toBe('Online users: 2');
                     expect(els[3].textContent).toBe('Online users: 2');
 
 
                     view.model.set({ 'subject': { 'author': 'someone', 'text': 'Hatching dark plots' } });
                     view.model.set({ 'subject': { 'author': 'someone', 'text': 'Hatching dark plots' } });
+                    await u.waitUntil(() => modal.querySelectorAll('p.room-info').length === 7);
+
                     els = modal.querySelectorAll('p.room-info');
                     els = modal.querySelectorAll('p.room-info');
                     expect(els[0].textContent).toBe('Name: A Dark Cave');
                     expect(els[0].textContent).toBe('Name: A Dark Cave');
-
                     expect(els[1].querySelector('strong').textContent).toBe('XMPP address:');
                     expect(els[1].querySelector('strong').textContent).toBe('XMPP address:');
                     expect(els[1].querySelector('converse-texture').textContent.trim()).toBe(
                     expect(els[1].querySelector('converse-texture').textContent.trim()).toBe(
                         'xmpp:coven@chat.shakespeare.lit?join'
                         'xmpp:coven@chat.shakespeare.lit?join'
@@ -163,7 +164,6 @@ describe('Groupchats', () => {
                     await u.waitUntil(
                     await u.waitUntil(
                         () => els[3].querySelector('converse-texture').textContent === 'Hatching dark plots'
                         () => els[3].querySelector('converse-texture').textContent === 'Hatching dark plots'
                     );
                     );
-
                     expect(els[4].textContent).toBe('Topic author: someone');
                     expect(els[4].textContent).toBe('Topic author: someone');
                     expect(els[5].textContent).toBe('Online users: 2');
                     expect(els[5].textContent).toBe('Online users: 2');
                 }
                 }

+ 1 - 1
src/plugins/profile/modals/chat-status.js

@@ -10,7 +10,7 @@ const u = converse.env.utils;
 export default class ChatStatusModal extends BaseModal {
 export default class ChatStatusModal extends BaseModal {
     initialize () {
     initialize () {
         super.initialize();
         super.initialize();
-        this.render();
+        this.requestUpdate();
         this.addEventListener(
         this.addEventListener(
             'shown.bs.modal',
             'shown.bs.modal',
             () => {
             () => {

+ 3 - 3
src/plugins/rosterview/modals/add-contact.js

@@ -9,8 +9,8 @@ import { getNamesAutoCompleteList } from '../utils.js';
 export default class AddContactModal extends BaseModal {
 export default class AddContactModal extends BaseModal {
     initialize () {
     initialize () {
         super.initialize();
         super.initialize();
-        this.listenTo(this.model, 'change', () => this.render());
-        this.render();
+        this.listenTo(this.model, 'change', () => this.requestUpdate());
+        this.requestUpdate();
         this.addEventListener(
         this.addEventListener(
             'shown.bs.modal',
             'shown.bs.modal',
             () => /** @type {HTMLInputElement} */ (this.querySelector('input[name="jid"]'))?.focus(),
             () => /** @type {HTMLInputElement} */ (this.querySelector('input[name="jid"]'))?.focus(),
@@ -73,7 +73,7 @@ export default class AddContactModal extends BaseModal {
             const list = await getNamesAutoCompleteList(name);
             const list = await getNamesAutoCompleteList(name);
             if (list.length !== 1) {
             if (list.length !== 1) {
                 this.model.set('error', __('Sorry, could not find a contact with that name'));
                 this.model.set('error', __('Sorry, could not find a contact with that name'));
-                this.render();
+                this.requestUpdate();
                 return;
                 return;
             }
             }
             jid = list[0].value;
             jid = list[0].value;

+ 2 - 2
src/plugins/rosterview/modals/new-chat.js

@@ -6,8 +6,8 @@ import { __ } from 'i18n';
 export default class NewChatModal extends BaseModal {
 export default class NewChatModal extends BaseModal {
     initialize() {
     initialize() {
         super.initialize();
         super.initialize();
-        this.listenTo(this.model, 'change', () => this.render());
-        this.render();
+        this.listenTo(this.model, 'change', () => this.requestUpdate());
+        this.requestUpdate();
         this.addEventListener(
         this.addEventListener(
             'shown.bs.modal',
             'shown.bs.modal',
             () => /** @type {HTMLInputElement} */ (this.querySelector('input[name="jid"]'))?.focus(),
             () => /** @type {HTMLInputElement} */ (this.querySelector('input[name="jid"]'))?.focus(),

+ 0 - 1
src/plugins/rosterview/tests/protocol.js

@@ -58,7 +58,6 @@ describe("Presence subscriptions", function () {
 
 
             const modal = _converse.api.modal.get('converse-add-contact-modal');
             const modal = _converse.api.modal.get('converse-add-contact-modal');
             await u.waitUntil(() => u.isVisible(modal), 1000);
             await u.waitUntil(() => u.isVisible(modal), 1000);
-            modal.delegateEvents();
 
 
             // Fill in the form and submit
             // Fill in the form and submit
             const form = modal.querySelector('form.add-xmpp-contact');
             const form = modal.querySelector('form.add-xmpp-contact');

+ 21 - 10
src/types/plugins/modal/modal.d.ts

@@ -1,28 +1,38 @@
 export default BaseModal;
 export default BaseModal;
-declare class BaseModal extends ElementView {
-    /**
-     * @typedef {import('lit').TemplateResult} TemplateResult
-     */
+declare class BaseModal extends CustomElement {
     /**
     /**
      * @param {Object} options
      * @param {Object} options
      */
      */
     constructor(options: any);
     constructor(options: any);
     model: any;
     model: any;
     initialized: any;
     initialized: any;
-    modal: Modal;
+    get modal(): Modal;
+    initialize(): void;
     /**
     /**
      * @returns {TemplateResult|string}
      * @returns {TemplateResult|string}
      */
      */
-    renderModal(): import("lit").TemplateResult<1 | 2> | string;
+    renderModal(): {
+        _$litType$: 1 | 2;
+        strings: TemplateStringsArray;
+        values: unknown[];
+    } | string;
     /**
     /**
      * @returns {TemplateResult|string}
      * @returns {TemplateResult|string}
      */
      */
-    renderModalFooter(): import("lit").TemplateResult<1 | 2> | string;
-    toHTML(): import("lit").TemplateResult<1>;
+    renderModalFooter(): {
+        _$litType$: 1 | 2;
+        strings: TemplateStringsArray;
+        values: unknown[];
+    } | string;
+    render(): import("lit").TemplateResult<1>;
     /**
     /**
      * @returns {string|TemplateResult}
      * @returns {string|TemplateResult}
      */
      */
-    getModalTitle(): string | import("lit").TemplateResult<1 | 2>;
+    getModalTitle(): string | {
+        _$litType$: 1 | 2;
+        strings: TemplateStringsArray;
+        values: unknown[];
+    };
     /**
     /**
      * @param {Event} [ev]
      * @param {Event} [ev]
      */
      */
@@ -36,7 +46,8 @@ declare class BaseModal extends ElementView {
      */
      */
     alert(message: string, type?: "primary" | "secondary" | "danger"): void;
     alert(message: string, type?: "primary" | "secondary" | "danger"): void;
     show(): Promise<void>;
     show(): Promise<void>;
+    #private;
 }
 }
-import { ElementView } from '@converse/skeletor';
+import { CustomElement } from 'shared/components/element.js';
 import { Modal } from "bootstrap";
 import { Modal } from "bootstrap";
 //# sourceMappingURL=modal.d.ts.map
 //# sourceMappingURL=modal.d.ts.map

+ 3 - 1
src/types/plugins/muc-views/modals/config.d.ts

@@ -1,4 +1,7 @@
 export default class MUCConfigModal extends BaseModal {
 export default class MUCConfigModal extends BaseModal {
+    /**
+     * @typedef {import('@converse/headless/types/plugins/vcard/api').VCardData} VCardData
+     */
     constructor(options: any);
     constructor(options: any);
     renderModal(): import("lit").TemplateResult<1>;
     renderModal(): import("lit").TemplateResult<1>;
     connectedCallback(): void;
     connectedCallback(): void;
@@ -13,6 +16,5 @@ export default class MUCConfigModal extends BaseModal {
      */
      */
     submitConfigForm(ev: SubmitEvent): Promise<void>;
     submitConfigForm(ev: SubmitEvent): Promise<void>;
 }
 }
-export type VCardData = import("@converse/headless/types/plugins/vcard/api").VCardData;
 import BaseModal from 'plugins/modal/modal.js';
 import BaseModal from 'plugins/modal/modal.js';
 //# sourceMappingURL=config.d.ts.map
 //# sourceMappingURL=config.d.ts.map