From e0965e26d6ca4eb0e79aef244c7b38a0244ba83c Mon Sep 17 00:00:00 2001 From: JC Brand Date: Tue, 23 Jan 2024 20:57:42 +0100 Subject: [PATCH] Fixup --- dev.html | 2 +- .../plugins/muc/affiliations/utils.js | 4 +- src/plugins/adhoc-views/adhoc-commands.js | 23 +++++++--- .../components/bookmark-form.js | 5 +++ .../bookmark-views/modals/bookmark-form.js | 5 +++ src/plugins/chatview/bottom-panel.js | 22 ++++----- src/plugins/chatview/heading.js | 33 ++++++++------ src/plugins/chatview/message-form.js | 32 ++++++++----- src/plugins/controlbox/api.js | 5 ++- src/plugins/controlbox/navback.js | 5 +++ src/plugins/dragresize/index.js | 10 ++--- src/plugins/headlines-view/heading.js | 5 +++ src/plugins/modal/api.js | 23 +++++----- src/plugins/modal/confirm.js | 4 +- src/plugins/muc-views/affiliation-form.js | 10 ++++- src/plugins/muc-views/bottom-panel.js | 3 +- src/plugins/muc-views/chatarea.js | 8 +++- src/plugins/muc-views/config-form.js | 5 +++ src/plugins/muc-views/destroyed.js | 5 +++ src/plugins/muc-views/disconnected.js | 5 +++ src/plugins/muc-views/message-form.js | 4 +- src/plugins/muc-views/modals/add-muc.js | 45 ++++++++++--------- .../muc-views/modals/moderator-tools.js | 2 + src/plugins/muc-views/modals/muc-invite.js | 21 +++++---- src/plugins/muc-views/nickname-form.js | 5 +++ src/plugins/muc-views/password-form.js | 21 +++++---- src/plugins/muc-views/role-form.js | 6 +++ src/plugins/muc-views/sidebar.js | 5 +++ src/plugins/omemo/api.js | 4 +- src/plugins/omemo/device.js | 2 +- src/plugins/omemo/errors.js | 2 +- src/plugins/omemo/fingerprints.js | 5 +++ src/plugins/profile/modals/chat-status.js | 25 ++++++----- src/plugins/profile/modals/profile.js | 4 +- src/plugins/rosterview/modals/add-contact.js | 24 +++++----- src/plugins/rosterview/rosterview.js | 2 +- src/shared/autocomplete/suggestion.js | 2 +- src/shared/avatar/avatar.js | 1 + src/shared/chat/baseview.js | 2 +- src/shared/chat/chat-content.js | 5 +++ src/shared/chat/emoji-dropdown.js | 3 +- src/shared/chat/emoji-picker.js | 1 + src/shared/chat/help-messages.js | 7 +++ src/shared/chat/message-history.js | 6 +++ src/shared/chat/message-limit.js | 5 +++ src/shared/chat/message.js | 6 +++ src/shared/chat/unfurl.js | 9 ++++ src/shared/chat/utils.js | 7 ++- src/shared/components/dropdownbase.js | 20 ++++----- src/shared/components/gif.js | 1 + src/shared/components/image-picker.js | 8 +++- src/shared/components/rich-text.js | 4 ++ src/shared/directives/retraction.js | 7 +-- 53 files changed, 329 insertions(+), 156 deletions(-) diff --git a/dev.html b/dev.html index 99b78dd6ae..35d07cf75b 100644 --- a/dev.html +++ b/dev.html @@ -43,7 +43,7 @@ websocket_url: 'wss://conversejs.org/xmpp-websocket', // websocket_url: 'ws://chat.example.org:5380/xmpp-websocket', whitelisted_plugins: ['converse-debug'], - connection_options: { worker: '/dist/shared-connection-worker.js' } + // connection_options: { worker: '/dist/shared-connection-worker.js' } }); diff --git a/src/headless/plugins/muc/affiliations/utils.js b/src/headless/plugins/muc/affiliations/utils.js index f4b7b559d5..8b7937edc8 100644 --- a/src/headless/plugins/muc/affiliations/utils.js +++ b/src/headless/plugins/muc/affiliations/utils.js @@ -88,7 +88,7 @@ export function setAffiliations (muc_jid, users) { * a separate stanza for each JID. * Related ticket: https://issues.prosody.im/345 * - * @param {typeof AFFILIATIONS} affiliation - The affiliation to be set + * @param {typeof AFFILIATIONS[number]} affiliation - The affiliation to be set * @param {String|Array} muc_jids - The JID(s) of the MUCs in which the * affiliations need to be set. * @param {object} members - A map of jids, affiliations and @@ -108,7 +108,7 @@ export function setAffiliation (affiliation, muc_jids, members) { /** * Send an IQ stanza specifying an affiliation change. - * @param {typeof AFFILIATIONS} affiliation: affiliation (could also be stored on the member object). + * @param {typeof AFFILIATIONS[number]} affiliation: affiliation (could also be stored on the member object). * @param {string} muc_jid: The JID of the MUC in which the affiliation should be set. * @param {object} member: Map containing the member's jid and optionally a reason and affiliation. */ diff --git a/src/plugins/adhoc-views/adhoc-commands.js b/src/plugins/adhoc-views/adhoc-commands.js index 1bf44ddb79..0c2b186477 100644 --- a/src/plugins/adhoc-views/adhoc-commands.js +++ b/src/plugins/adhoc-views/adhoc-commands.js @@ -32,15 +32,24 @@ export default class AdHocCommands extends CustomElement { return tplAdhoc(this) } + /** + * @param {SubmitEvent} ev + */ async fetchCommands (ev) { ev.preventDefault(); - delete this.alert_type; - delete this.alert; + + if (!(ev.target instanceof HTMLFormElement)) { + this.alert_type = 'danger'; + this.alert = 'Form could not be submitted' + return; + } this.fetching = true; + delete this.alert_type; + delete this.alert; const form_data = new FormData(ev.target); - const jid = form_data.get('jid').trim(); + const jid = /** @type {string} */(form_data.get('jid')).trim(); let supported; try { supported = await api.disco.supports(Strophe.NS.ADHOC, jid); @@ -109,8 +118,8 @@ export default class AdHocCommands extends CustomElement { async runCommand (form, action) { const form_data = new FormData(form); - const jid = form_data.get('command_jid').trim(); - const node = form_data.get('command_node').trim(); + const jid = /** @type {string} */(form_data.get('command_jid')).trim(); + const node = /** @type {string} */(form_data.get('command_node')).trim(); const cmd = this.commands.filter(c => c.node === node)[0]; delete cmd.alert; @@ -159,8 +168,8 @@ export default class AdHocCommands extends CustomElement { this.requestUpdate(); const form_data = new FormData(ev.target.form); - const jid = form_data.get('command_jid').trim(); - const node = form_data.get('command_node').trim(); + const jid = /** @type {string} */(form_data.get('command_jid')).trim(); + const node = /** @type {string} */(form_data.get('command_node')).trim(); const cmd = this.commands.filter(c => c.node === node)[0]; delete cmd.alert; diff --git a/src/plugins/bookmark-views/components/bookmark-form.js b/src/plugins/bookmark-views/components/bookmark-form.js index e79e4dbc28..5b63d0fa15 100644 --- a/src/plugins/bookmark-views/components/bookmark-form.js +++ b/src/plugins/bookmark-views/components/bookmark-form.js @@ -5,6 +5,11 @@ import { _converse, api } from "@converse/headless"; class MUCBookmarkForm extends CustomElement { + constructor () { + super(); + this.jid = null; + } + static get properties () { return { 'jid': { type: String } diff --git a/src/plugins/bookmark-views/modals/bookmark-form.js b/src/plugins/bookmark-views/modals/bookmark-form.js index 65df95c341..15319b17fc 100644 --- a/src/plugins/bookmark-views/modals/bookmark-form.js +++ b/src/plugins/bookmark-views/modals/bookmark-form.js @@ -6,6 +6,11 @@ import { api } from "@converse/headless"; export default class BookmarkFormModal extends BaseModal { + constructor (options) { + super(options); + this.jid = null; + } + renderModal () { return html` diff --git a/src/plugins/chatview/bottom-panel.js b/src/plugins/chatview/bottom-panel.js index 0fd0e5cfb7..34ef857139 100644 --- a/src/plugins/chatview/bottom-panel.js +++ b/src/plugins/chatview/bottom-panel.js @@ -1,3 +1,8 @@ +/** + * @typedef {import('shared/chat/emoji-picker.js').default} EmojiPicker + * @typedef {import('shared/chat/emoji-dropdown.js').default} EmojiDropdown + * @typedef {import('./message-form.js').default} MessageForm + */ import './message-form.js'; import tplBottomPanel from './templates/bottom-panel.js'; import { CustomElement } from 'shared/components/element.js'; @@ -38,7 +43,8 @@ export default class ChatBottomPanel extends CustomElement { sendButtonClicked (ev) { if (ev.delegateTarget?.dataset.action === 'sendMessage') { - this.querySelector('converse-message-form')?.onFormSubmitted(ev); + const form = /** @type {MessageForm} */(this.querySelector('converse-message-form')); + form?.onFormSubmitted(ev); } } @@ -55,16 +61,6 @@ export default class ChatBottomPanel extends CustomElement { _converse.chatboxviews.get(this.getAttribute('jid'))?.emitBlurred(ev); } - onDrop (evt) { - if (evt.dataTransfer.files.length == 0) { - // There are no files to be dropped, so this isn’t a file - // transfer operation. - return; - } - evt.preventDefault(); - this.model.sendFiles(evt.dataTransfer.files); - } - onDragOver (ev) { // eslint-disable-line class-methods-use-this ev.preventDefault(); } @@ -76,14 +72,14 @@ export default class ChatBottomPanel extends CustomElement { async autocompleteInPicker (input, value) { await api.emojis.initialize(); - const emoji_picker = this.querySelector('converse-emoji-picker'); + const emoji_picker = /** @type {EmojiPicker} */(this.querySelector('converse-emoji-picker')); if (emoji_picker) { emoji_picker.model.set({ 'ac_position': input.selectionStart, 'autocompleting': value, 'query': value }); - const emoji_dropdown = this.querySelector('converse-emoji-dropdown'); + const emoji_dropdown = /** @type {EmojiDropdown} */(this.querySelector('converse-emoji-dropdown')); emoji_dropdown?.showMenu(); } } diff --git a/src/plugins/chatview/heading.js b/src/plugins/chatview/heading.js index ccf57b911b..518c80dddb 100644 --- a/src/plugins/chatview/heading.js +++ b/src/plugins/chatview/heading.js @@ -1,3 +1,17 @@ +/** + * @typedef { Object } HeadingButtonAttributes + * An object representing a chat heading button + * @property { Boolean } standalone + * True if shown on its own, false if it must be in the dropdown menu. + * @property { Function } handler + * A handler function to be called when the button is clicked. + * @property { String } a_class - HTML classes to show on the button + * @property { String } i18n_text - The user-visiible name of the button + * @property { String } i18n_title - The tooltip text for this button + * @property { String } icon_class - What kind of CSS class to use for the icon + * @property { String } name - The internal name of the button + */ + import 'shared/modals/user-details.js'; import tplChatboxHead from './templates/chat-head.js'; import { CustomElement } from 'shared/components/element.js'; @@ -9,6 +23,11 @@ import './styles/chat-head.scss'; export default class ChatHeading extends CustomElement { + constructor () { + super(); + this.jid = null; + } + static get properties () { return { 'jid': { type: String }, @@ -54,19 +73,7 @@ export default class ChatHeading extends CustomElement { */ getHeadingButtons () { const buttons = [ - /** - * @typedef { Object } HeadingButtonAttributes - * An object representing a chat heading button - * @property { Boolean } standalone - * True if shown on its own, false if it must be in the dropdown menu. - * @property { Function } handler - * A handler function to be called when the button is clicked. - * @property { String } a_class - HTML classes to show on the button - * @property { String } i18n_text - The user-visiible name of the button - * @property { String } i18n_title - The tooltip text for this button - * @property { String } icon_class - What kind of CSS class to use for the icon - * @property { String } name - The internal name of the button - */ + /** @type {HeadingButtonAttributes} */ { 'a_class': 'show-user-details-modal', 'handler': ev => this.showUserDetailsModal(ev), diff --git a/src/plugins/chatview/message-form.js b/src/plugins/chatview/message-form.js index 2616c96bc0..a93c300ebd 100644 --- a/src/plugins/chatview/message-form.js +++ b/src/plugins/chatview/message-form.js @@ -1,3 +1,6 @@ +/** + * @typedef {import('shared/chat/emoji-dropdown.js').default} EmojiDropdown + */ import tplMessageForm from './templates/message-form.js'; import { CustomElement } from 'shared/components/element.js'; import { __ } from 'i18n'; @@ -34,13 +37,12 @@ export default class MessageForm extends CustomElement { return tplMessageForm( Object.assign(this.model.toJSON(), { 'onDrop': ev => this.onDrop(ev), - 'hint_value': this.querySelector('.spoiler-hint')?.value, - 'message_value': this.querySelector('.chat-textarea')?.value, + 'hint_value': /** @type {HTMLInputElement} */(this.querySelector('.spoiler-hint'))?.value, + 'message_value': /** @type {HTMLTextAreaElement} */(this.querySelector('.chat-textarea'))?.value, 'onChange': ev => this.model.set({'draft': ev.target.value}), 'onKeyDown': ev => this.onKeyDown(ev), 'onKeyUp': ev => this.onKeyUp(ev), - 'onPaste': ev => this.onPaste(ev), - 'viewUnreadMessages': ev => this.viewUnreadMessages(ev) + 'onPaste': ev => this.onPaste(ev) }) ); } @@ -56,7 +58,7 @@ export default class MessageForm extends CustomElement { * replaced with the new value. */ insertIntoTextArea (value, replace = false, correcting = false, position) { - const textarea = this.querySelector('.chat-textarea'); + const textarea = /** @type {HTMLTextAreaElement} */(this.querySelector('.chat-textarea')); if (correcting) { u.addClass('correcting', textarea); } else { @@ -120,6 +122,16 @@ export default class MessageForm extends CustomElement { this.model.set({'draft': ev.clipboardData.getData('text/plain')}); } + onDrop (evt) { + if (evt.dataTransfer.files.length == 0) { + // There are no files to be dropped, so this isn’t a file + // transfer operation. + return; + } + evt.preventDefault(); + this.model.sendFiles(evt.dataTransfer.files); + } + onKeyUp (ev) { this.model.set({'draft': ev.target.value}); } @@ -141,11 +153,11 @@ export default class MessageForm extends CustomElement { // Forward slash is used to run commands. Nothing to do here. return; } else if (ev.keyCode === converse.keycodes.ESCAPE) { - return this.onEscapePressed(ev, this); + return this.onEscapePressed(ev); } else if (ev.keyCode === converse.keycodes.ENTER) { return this.onFormSubmitted(ev); } else if (ev.keyCode === converse.keycodes.UP_ARROW && !ev.target.selectionEnd) { - const textarea = this.querySelector('.chat-textarea'); + const textarea = /** @type {HTMLTextAreaElement} */(this.querySelector('.chat-textarea')); if (!textarea.value || u.hasClass('correcting', textarea)) { return this.model.editEarlierMessage(); } @@ -178,7 +190,7 @@ export default class MessageForm extends CustomElement { async onFormSubmitted (ev) { ev?.preventDefault?.(); - const textarea = this.querySelector('.chat-textarea'); + const textarea = /** @type {HTMLTextAreaElement} */(this.querySelector('.chat-textarea')); const message_text = textarea.value.trim(); if ( (api.settings.get('message_limit') && message_text.length > api.settings.get('message_limit')) || @@ -195,12 +207,12 @@ export default class MessageForm extends CustomElement { let spoiler_hint, hint_el = {}; if (this.model.get('composing_spoiler')) { - hint_el = this.querySelector('form.sendXMPPMessage input.spoiler-hint'); + hint_el = /** @type {HTMLInputElement} */(this.querySelector('form.sendXMPPMessage input.spoiler-hint')); spoiler_hint = hint_el.value; } u.addClass('disabled', textarea); textarea.setAttribute('disabled', 'disabled'); - this.querySelector('converse-emoji-dropdown')?.hideMenu(); + /** @type {EmojiDropdown} */(this.querySelector('converse-emoji-dropdown'))?.hideMenu(); const is_command = await parseMessageForCommands(this.model, message_text); const message = is_command ? null : await this.model.sendMessage({'body': message_text, spoiler_hint}); diff --git a/src/plugins/controlbox/api.js b/src/plugins/controlbox/api.js index 8e186efbf7..0d7a002ee9 100644 --- a/src/plugins/controlbox/api.js +++ b/src/plugins/controlbox/api.js @@ -1,3 +1,6 @@ +/** + * @typedef {import('./controlbox.js').default} ControlBox + */ import { _converse, api, converse } from "@converse/headless"; const { u } = converse.env; @@ -29,7 +32,7 @@ export default { /** * Returns the controlbox view. * @method _converse.api.controlbox.get - * @returns { View } View representing the controlbox + * @returns {ControlBox} View representing the controlbox * @example const view = _converse.api.controlbox.get(); */ get () { diff --git a/src/plugins/controlbox/navback.js b/src/plugins/controlbox/navback.js index 73a487c07d..64f7154586 100644 --- a/src/plugins/controlbox/navback.js +++ b/src/plugins/controlbox/navback.js @@ -5,6 +5,11 @@ import { api } from "@converse/headless"; class ControlBoxNavback extends CustomElement { + constructor () { + super(); + this.jid = null; + } + static get properties () { return { 'jid': { type: String } diff --git a/src/plugins/dragresize/index.js b/src/plugins/dragresize/index.js index b1301594d3..ea755edc2f 100644 --- a/src/plugins/dragresize/index.js +++ b/src/plugins/dragresize/index.js @@ -68,25 +68,25 @@ converse.plugins.add('converse-dragresize', { } /** - * This function registers mousedown and mouseup events hadlers to + * This function registers mousedown and mouseup events hadlers to * all iframes in the DOM when converse UI resizing events are called * to prevent mouse drag stutter effect which is bad user experience. * @function dragresizeOverIframeHandler * @param {Object} e - dragging node element. */ function dragresizeOverIframeHandler (e) { - const iframes = document.getElementsByTagName('iframe'); - for (let iframe of iframes) { + const iframes = Array.from(document.getElementsByTagName('iframe')); + for (const iframe of iframes) { e.addEventListener('mousedown', () => { iframe.style.pointerEvents = 'none'; }, { once: true }); - + e.addEventListener('mouseup', () => { iframe.style.pointerEvents = 'initial'; }, { once: true }); } } - + api.listen.on('registeredGlobalEventHandlers', registerGlobalEventHandlers); api.listen.on('unregisteredGlobalEventHandlers', unregisterGlobalEventHandlers); api.listen.on('beforeShowingChatView', view => view.initDragResize().setDimensions()); diff --git a/src/plugins/headlines-view/heading.js b/src/plugins/headlines-view/heading.js index 43c756a563..313d916651 100644 --- a/src/plugins/headlines-view/heading.js +++ b/src/plugins/headlines-view/heading.js @@ -6,6 +6,11 @@ import { _converse, api } from "@converse/headless"; export default class HeadlinesHeading extends CustomElement { + constructor () { + super(); + this.jid = null; + } + static get properties () { return { 'jid': { type: String }, diff --git a/src/plugins/modal/api.js b/src/plugins/modal/api.js index 7fca16ac2e..d31c07bc56 100644 --- a/src/plugins/modal/api.js +++ b/src/plugins/modal/api.js @@ -16,9 +16,9 @@ const modal_api = { * Shows a modal of type `ModalClass` to the user. * Will create a new instance of that class if an existing one isn't * found. - * @param { Class } ModalClass - * @param { Object } [properties] - Optional properties that will be set on a newly created modal instance. - * @param { Event } [event] - The DOM event that causes the modal to be shown. + * @param {string|any} name + * @param {Object} [properties] - Optional properties that will be set on a newly created modal instance. + * @param {Event} [ev] - The DOM event that causes the modal to be shown. */ show (name, properties, ev) { let modal; @@ -45,8 +45,8 @@ const modal_api = { /** * Create a modal of the passed-in type. - * @param { String } name - * @param { Object } [properties] - Optional properties that will be + * @param {String} name + * @param {Object} [properties] - Optional properties that will be * set on the modal instance. */ create (name, properties) { @@ -89,6 +89,14 @@ const modal_api = { modals_map = {}; } }, + /** + * @typedef Field + * @property { String } Field.label - The form label for the input field. + * @property { String } Field.name - The name for the input field. + * @property { String } [Field.challenge] - A challenge value that must be provided by the user. + * @property { String } [Field.placeholder] - The placeholder for the input field. + * @property { Boolean} [Field.required] - Whether the field is required or not + */ /** * Show a confirm modal to the user. @@ -96,11 +104,6 @@ const modal_api = { * @param { String } title - The header text for the confirmation dialog * @param { (Array|String) } messages - The text to show to the user * @param { Array } fields - An object representing a fields presented to the user. - * @property { String } Field.label - The form label for the input field. - * @property { String } Field.name - The name for the input field. - * @property { String } [Field.challenge] - A challenge value that must be provided by the user. - * @property { String } [Field.placeholder] - The placeholder for the input field. - * @property { Boolean} [Field.required] - Whether the field is required or not * @returns { Promise } A promise which resolves with an array of * filled in fields or `false` if the confirm dialog was closed or canceled. */ diff --git a/src/plugins/modal/confirm.js b/src/plugins/modal/confirm.js index b69eb8409b..76aac03af2 100644 --- a/src/plugins/modal/confirm.js +++ b/src/plugins/modal/confirm.js @@ -33,7 +33,7 @@ export default class Confirm extends BaseModal { const form_data = new FormData(ev.target); const fields = (this.model.get('fields') || []) .map(field => { - const value = form_data.get(field.name).trim(); + const value = /** @type {string }*/(form_data.get(field.name)).trim(); field.value = value; if (field.challenge) { field.challenge_failed = (value !== field.challenge); @@ -51,7 +51,7 @@ export default class Confirm extends BaseModal { this.modal.hide(); } - renderModalFooter () { // eslint-disable-line class-methods-use-this + renderModalFooter () { return ''; } } diff --git a/src/plugins/muc-views/affiliation-form.js b/src/plugins/muc-views/affiliation-form.js index b008f342f0..d363282b03 100644 --- a/src/plugins/muc-views/affiliation-form.js +++ b/src/plugins/muc-views/affiliation-form.js @@ -17,6 +17,12 @@ class AffiliationForm extends CustomElement { }; } + constructor () { + super(); + this.jid = null; + this.muc = null; + } + render () { return tplAffiliationForm(this); } @@ -32,10 +38,10 @@ class AffiliationForm extends CustomElement { this.alert(); // clear alert messages const data = new FormData(ev.target); - const affiliation = data.get('affiliation'); + const affiliation = /** @type {string} */(data.get('affiliation')); const attrs = { jid: this.jid, - reason: data.get('reason'), + reason: /** @type {string} */(data.get('reason')), }; const muc_jid = this.muc.get('jid'); try { diff --git a/src/plugins/muc-views/bottom-panel.js b/src/plugins/muc-views/bottom-panel.js index 540fab14b0..a8ad17f851 100644 --- a/src/plugins/muc-views/bottom-panel.js +++ b/src/plugins/muc-views/bottom-panel.js @@ -36,7 +36,8 @@ export default class MUCBottomPanel extends BottomPanel { sendButtonClicked (ev) { if (ev.delegateTarget?.dataset.action === 'sendMessage') { - this.querySelector('converse-muc-message-form')?.onFormSubmitted(ev); + const form = /** @type {HTMLFormElement} */(this.querySelector('converse-muc-message-form')); + form?.onFormSubmitted(ev); } } } diff --git a/src/plugins/muc-views/chatarea.js b/src/plugins/muc-views/chatarea.js index 4e8e05e0b4..d14b842a04 100644 --- a/src/plugins/muc-views/chatarea.js +++ b/src/plugins/muc-views/chatarea.js @@ -17,6 +17,12 @@ export default class MUCChatArea extends CustomElement { } } + constructor () { + super(); + this.jid = null; + this.type = null; + } + async initialize () { this.model = await api.rooms.get(this.jid); this.listenTo(this.model, 'change:show_help_messages', () => this.requestUpdate()); @@ -141,7 +147,7 @@ export default class MUCChatArea extends CustomElement { } resizeSidebarView (delta, current_mouse_position) { - const sidebar_el = this.querySelector('converse-muc-sidebar'); + const sidebar_el = /** @type {HTMLElement} */(this.querySelector('converse-muc-sidebar')); const element_position = sidebar_el.getBoundingClientRect(); if (this.is_minimum) { this.is_minimum = element_position.left < current_mouse_position; diff --git a/src/plugins/muc-views/config-form.js b/src/plugins/muc-views/config-form.js index 802296265d..0420a0701a 100644 --- a/src/plugins/muc-views/config-form.js +++ b/src/plugins/muc-views/config-form.js @@ -9,6 +9,11 @@ const u = converse.env.utils; class MUCConfigForm extends CustomElement { + constructor () { + super(); + this.jid = null; + } + static get properties () { return { 'jid': { type: String } diff --git a/src/plugins/muc-views/destroyed.js b/src/plugins/muc-views/destroyed.js index ef9363b14d..39b0ada666 100644 --- a/src/plugins/muc-views/destroyed.js +++ b/src/plugins/muc-views/destroyed.js @@ -5,6 +5,11 @@ import { _converse, api } from "@converse/headless"; class MUCDestroyed extends CustomElement { + constructor () { + super(); + this.jid = null; + } + static get properties () { return { 'jid': { type: String } diff --git a/src/plugins/muc-views/disconnected.js b/src/plugins/muc-views/disconnected.js index eb95a2997e..f005c04fec 100644 --- a/src/plugins/muc-views/disconnected.js +++ b/src/plugins/muc-views/disconnected.js @@ -6,6 +6,11 @@ import { _converse, api } from "@converse/headless"; class MUCDisconnected extends CustomElement { + constructor () { + super(); + this.jid = null; + } + static get properties () { return { 'jid': { type: String } diff --git a/src/plugins/muc-views/message-form.js b/src/plugins/muc-views/message-form.js index 343d94cecf..0c333d4615 100644 --- a/src/plugins/muc-views/message-form.js +++ b/src/plugins/muc-views/message-form.js @@ -15,8 +15,8 @@ export default class MUCMessageForm extends MessageForm { render () { return tplMUCMessageForm( Object.assign(this.model.toJSON(), { - 'hint_value': this.querySelector('.spoiler-hint')?.value, - 'message_value': this.querySelector('.chat-textarea')?.value, + 'hint_value': /** @type {HTMLInputElement} */(this.querySelector('.spoiler-hint'))?.value, + 'message_value': /** @type {HTMLInputElement} */(this.querySelector('.chat-textarea'))?.value, 'onChange': ev => this.model.set({'draft': ev.target.value}), 'onDrop': ev => this.onDrop(ev), 'onKeyDown': ev => this.onKeyDown(ev), diff --git a/src/plugins/muc-views/modals/add-muc.js b/src/plugins/muc-views/modals/add-muc.js index 0253748112..aae02b7e05 100644 --- a/src/plugins/muc-views/modals/add-muc.js +++ b/src/plugins/muc-views/modals/add-muc.js @@ -1,50 +1,54 @@ -import tplAddMuc from "./templates/add-muc.js"; -import BaseModal from "plugins/modal/modal.js"; +import tplAddMuc from './templates/add-muc.js'; +import BaseModal from 'plugins/modal/modal.js'; import { __ } from 'i18n'; -import { _converse, api, converse } from "@converse/headless"; +import { _converse, api, converse } from '@converse/headless'; import '../styles/add-muc-modal.scss'; const u = converse.env.utils; const { Strophe } = converse.env; - export default class AddMUCModal extends BaseModal { - initialize () { super.initialize(); this.listenTo(this.model, 'change:muc_domain', () => this.render()); this.muc_roomid_policy_error_msg = null; this.render(); - this.addEventListener('shown.bs.modal', () => { - this.querySelector('input[name="chatroom"]').focus(); - }, false); + this.addEventListener( + 'shown.bs.modal', + () => { + /** @type {HTMLInputElement} */ (this.querySelector('input[name="chatroom"]')).focus(); + }, + false + ); } renderModal () { return tplAddMuc(this); } - getModalTitle () { // eslint-disable-line class-methods-use-this + getModalTitle () { + // eslint-disable-line class-methods-use-this return __('Enter a new Groupchat'); } - parseRoomDataFromEvent (form) { // eslint-disable-line class-methods-use-this + parseRoomDataFromEvent (form) { + // eslint-disable-line class-methods-use-this const data = new FormData(form); - const jid = data.get('chatroom')?.trim(); + const jid = /** @type {string} */ (data.get('chatroom'))?.trim(); let nick; if (api.settings.get('locked_muc_nickname')) { nick = _converse.getDefaultMUCNickname(); if (!nick) { - throw new Error("Using locked_muc_nickname but no nickname found!"); + throw new Error('Using locked_muc_nickname but no nickname found!'); } } else { - nick = data.get('nickname').trim(); + nick = /** @type {string} */ (data.get('nickname')).trim(); } return { 'jid': jid, - 'nick': nick - } + 'nick': nick, + }; } openChatRoom (ev) { @@ -52,7 +56,7 @@ export default class AddMUCModal extends BaseModal { if (this.checkRoomidPolicy()) return; const data = this.parseRoomDataFromEvent(ev.target); - if (data.nick === "") { + if (data.nick === '') { // Make sure defaults apply if no nick is provided. data.nick = undefined; } @@ -60,25 +64,24 @@ export default class AddMUCModal extends BaseModal { if (api.settings.get('locked_muc_domain') || (api.settings.get('muc_domain') && !u.isValidJID(data.jid))) { jid = `${Strophe.escapeNode(data.jid)}@${api.settings.get('muc_domain')}`; } else { - jid = data.jid + jid = data.jid; this.model.setDomain(jid); } - api.rooms.open(jid, Object.assign(data, {jid}), true); + api.rooms.open(jid, Object.assign(data, { jid }), true); ev.target.reset(); this.modal.hide(); } checkRoomidPolicy () { if (api.settings.get('muc_roomid_policy') && api.settings.get('muc_domain')) { - let jid = this.querySelector('converse-autocomplete input').value; + let jid = /** @type {HTMLInputElement} */ (this.querySelector('converse-autocomplete input')).value; if (api.settings.get('locked_muc_domain') || !u.isValidJID(jid)) { jid = `${Strophe.escapeNode(jid)}@${api.settings.get('muc_domain')}`; } const roomid = Strophe.getNodeFromJid(jid); const roomdomain = Strophe.getDomainFromJid(jid); - if (api.settings.get('muc_domain') !== roomdomain || - api.settings.get('muc_roomid_policy').test(roomid)) { + if (api.settings.get('muc_domain') !== roomdomain || api.settings.get('muc_roomid_policy').test(roomid)) { this.muc_roomid_policy_error_msg = null; } else { this.muc_roomid_policy_error_msg = __('Groupchat id is invalid.'); diff --git a/src/plugins/muc-views/modals/moderator-tools.js b/src/plugins/muc-views/modals/moderator-tools.js index 639eb1d0d0..82f296f0ab 100644 --- a/src/plugins/muc-views/modals/moderator-tools.js +++ b/src/plugins/muc-views/modals/moderator-tools.js @@ -9,6 +9,8 @@ export default class ModeratorToolsModal extends BaseModal { constructor (options) { super(options); this.id = "converse-modtools-modal"; + this.affiliation = options.affiliation; + this.jid = options.jid; } renderModal () { diff --git a/src/plugins/muc-views/modals/muc-invite.js b/src/plugins/muc-views/modals/muc-invite.js index fd9c5b4bee..588a1fac7b 100644 --- a/src/plugins/muc-views/modals/muc-invite.js +++ b/src/plugins/muc-views/modals/muc-invite.js @@ -1,12 +1,17 @@ import 'shared/autocomplete/index.js'; -import BaseModal from "plugins/modal/modal.js"; -import tplMUCInviteModal from "./templates/muc-invite.js"; +import BaseModal from 'plugins/modal/modal.js'; +import tplMUCInviteModal from './templates/muc-invite.js'; import { __ } from 'i18n'; -import { _converse, api, converse } from "@converse/headless"; +import { _converse, api, converse } from '@converse/headless'; const u = converse.env.utils; export default class MUCInviteModal extends BaseModal { + constructor (options) { + super(options); + this.id = 'converse-modtools-modal'; + this.chatroomview = options.chatroomview; + } initialize () { super.initialize(); @@ -17,26 +22,26 @@ export default class MUCInviteModal extends BaseModal { return tplMUCInviteModal(this); } - getModalTitle () { // eslint-disable-line class-methods-use-this + getModalTitle () { return __('Invite someone to this groupchat'); } - getAutoCompleteList () { // eslint-disable-line class-methods-use-this - return _converse.roster.map(i => ({'label': i.getDisplayName(), 'value': i.get('jid')})); + getAutoCompleteList () { + return _converse.roster.map((i) => ({ 'label': i.getDisplayName(), 'value': i.get('jid') })); } submitInviteForm (ev) { ev.preventDefault(); // TODO: Add support for sending an invite to multiple JIDs const data = new FormData(ev.target); - const jid = data.get('invitee_jids')?.trim(); + const jid = /** @type {string} */ (data.get('invitee_jids'))?.trim(); const reason = data.get('reason'); if (u.isValidJID(jid)) { // TODO: Create and use API here this.chatroomview.model.directInvite(jid, reason); this.modal.hide(); } else { - this.model.set({'invalid_invite_jid': true}); + this.model.set({ 'invalid_invite_jid': true }); } } } diff --git a/src/plugins/muc-views/nickname-form.js b/src/plugins/muc-views/nickname-form.js index fd36e30b65..169882324d 100644 --- a/src/plugins/muc-views/nickname-form.js +++ b/src/plugins/muc-views/nickname-form.js @@ -7,6 +7,11 @@ import './styles/nickname-form.scss'; class MUCNicknameForm extends CustomElement { + constructor () { + super(); + this.jid = null; + } + static get properties () { return { 'jid': { type: String } diff --git a/src/plugins/muc-views/password-form.js b/src/plugins/muc-views/password-form.js index 046b66e144..0e8f14c5f9 100644 --- a/src/plugins/muc-views/password-form.js +++ b/src/plugins/muc-views/password-form.js @@ -1,14 +1,17 @@ -import tplMUCPasswordForm from "./templates/muc-password-form.js"; +import tplMUCPasswordForm from './templates/muc-password-form.js'; import { CustomElement } from 'shared/components/element'; -import { _converse, api } from "@converse/headless"; - +import { _converse, api } from '@converse/headless'; class MUCPasswordForm extends CustomElement { - static get properties () { return { - 'jid': { type: String } - } + 'jid': { type: String }, + }; + } + + constructor () { + super(); + this.jid = null; } connectedCallback () { @@ -21,14 +24,14 @@ class MUCPasswordForm extends CustomElement { render () { return tplMUCPasswordForm({ 'jid': this.model.get('jid'), - 'submitPassword': ev => this.submitPassword(ev), - 'validation_message': this.model.get('password_validation_message') + 'submitPassword': (ev) => this.submitPassword(ev), + 'validation_message': this.model.get('password_validation_message'), }); } submitPassword (ev) { ev.preventDefault(); - const password = this.querySelector('input[type=password]').value; + const password = /** @type {HTMLInputElement} */ (this.querySelector('input[type=password]')).value; this.model.join(this.model.get('nick'), password); this.model.set('password_validation_message', null); } diff --git a/src/plugins/muc-views/role-form.js b/src/plugins/muc-views/role-form.js index 4c0e342174..e0e966c9be 100644 --- a/src/plugins/muc-views/role-form.js +++ b/src/plugins/muc-views/role-form.js @@ -7,6 +7,7 @@ import { isErrorObject } from '@converse/headless/utils/index.js'; const { Strophe, sizzle } = converse.env; class RoleForm extends CustomElement { + static get properties () { return { muc: { type: Object }, @@ -17,6 +18,11 @@ class RoleForm extends CustomElement { }; } + constructor () { + super(); + this.muc = null; + } + render () { return tplRoleForm(this); } diff --git a/src/plugins/muc-views/sidebar.js b/src/plugins/muc-views/sidebar.js index 95ffacdd4f..847386325b 100644 --- a/src/plugins/muc-views/sidebar.js +++ b/src/plugins/muc-views/sidebar.js @@ -12,6 +12,11 @@ const { u } = converse.env; export default class MUCSidebar extends CustomElement { + constructor () { + super(); + this.jid = null; + } + static get properties () { return { jid: { type: String } diff --git a/src/plugins/omemo/api.js b/src/plugins/omemo/api.js index 47bb62f3c1..34c677d558 100644 --- a/src/plugins/omemo/api.js +++ b/src/plugins/omemo/api.js @@ -29,8 +29,8 @@ export default { * Returns the {@link _converse.DeviceList} for a particular JID. * The device list will be created if it doesn't exist already. * @method _converse.api.omemo.devicelists.get - * @param { String } jid - The Jabber ID for which the device list will be returned. - * @param { bool } create=false - Set to `true` if the device list + * @param {String} jid - The Jabber ID for which the device list will be returned. + * @param {boolean} create=false - Set to `true` if the device list * should be created if it cannot be found. */ async get (jid, create=false) { diff --git a/src/plugins/omemo/device.js b/src/plugins/omemo/device.js index fa6ed285b7..5cda5c0a89 100644 --- a/src/plugins/omemo/device.js +++ b/src/plugins/omemo/device.js @@ -58,7 +58,7 @@ class Device extends Model { */ getBundle () { if (this.get('bundle')) { - return Promise.resolve(this.get('bundle'), this); + return Promise.resolve(this.get('bundle')); } else { return this.fetchBundleFromServer(); } diff --git a/src/plugins/omemo/errors.js b/src/plugins/omemo/errors.js index b99c501e94..37f5f85bc9 100644 --- a/src/plugins/omemo/errors.js +++ b/src/plugins/omemo/errors.js @@ -1,6 +1,6 @@ export class IQError extends Error { constructor (message, iq) { - super(message, iq); + super(message); this.name = 'IQError'; this.iq = iq; } diff --git a/src/plugins/omemo/fingerprints.js b/src/plugins/omemo/fingerprints.js index 1fe637c4b0..1adcd1768d 100644 --- a/src/plugins/omemo/fingerprints.js +++ b/src/plugins/omemo/fingerprints.js @@ -4,6 +4,11 @@ import { api } from "@converse/headless"; export class Fingerprints extends CustomElement { + constructor () { + super(); + this.jid = null; + } + static get properties () { return { 'jid': { type: String } diff --git a/src/plugins/profile/modals/chat-status.js b/src/plugins/profile/modals/chat-status.js index 14e67fa2a9..6ada2500c0 100644 --- a/src/plugins/profile/modals/chat-status.js +++ b/src/plugins/profile/modals/chat-status.js @@ -1,26 +1,29 @@ -import BaseModal from "plugins/modal/modal.js"; -import tplChatStatusModal from "../templates/chat-status-modal.js"; +import BaseModal from 'plugins/modal/modal.js'; +import tplChatStatusModal from '../templates/chat-status-modal.js'; import { __ } from 'i18n'; -import { _converse, api, converse } from "@converse/headless"; +import { _converse, api, converse } from '@converse/headless'; const u = converse.env.utils; - export default class ChatStatusModal extends BaseModal { - initialize () { super.initialize(); this.render(); - this.addEventListener('shown.bs.modal', () => { - this.querySelector('input[name="status_message"]').focus(); - }, false); + this.addEventListener( + 'shown.bs.modal', + () => { + /** @type {HTMLInputElement} */ (this.querySelector('input[name="status_message"]')).focus(); + }, + false + ); } renderModal () { return tplChatStatusModal(this); } - getModalTitle () { // eslint-disable-line class-methods-use-this + getModalTitle () { + // eslint-disable-line class-methods-use-this return __('Change chat status'); } @@ -29,7 +32,7 @@ export default class ChatStatusModal extends BaseModal { ev.preventDefault(); u.hideElement(this.querySelector('.clear-input')); } - const roster_filter = this.querySelector('input[name="status_message"]'); + const roster_filter = /** @type {HTMLInputElement} */ (this.querySelector('input[name="status_message"]')); roster_filter.value = ''; } @@ -38,7 +41,7 @@ export default class ChatStatusModal extends BaseModal { const data = new FormData(ev.target); this.model.save({ 'status_message': data.get('status_message'), - 'status': data.get('chat_status') + 'status': data.get('chat_status'), }); this.modal.hide(); } diff --git a/src/plugins/profile/modals/profile.js b/src/plugins/profile/modals/profile.js index 462870258e..1b90bf410d 100644 --- a/src/plugins/profile/modals/profile.js +++ b/src/plugins/profile/modals/profile.js @@ -57,7 +57,7 @@ export default class ProfileModal extends BaseModal { ev.preventDefault(); const reader = new FileReader(); const form_data = new FormData(ev.target); - const image_file = form_data.get('image'); + const image_file = /** @type {File} */(form_data.get('image')); const data = { 'fn': form_data.get('fn'), 'nickname': form_data.get('nickname'), @@ -77,7 +77,7 @@ export default class ProfileModal extends BaseModal { const { photo, } = conversions[0]; reader.onloadend = () => { Object.assign(data, { - 'image': btoa(reader.result), + 'image': btoa(/** @type {string} */(reader.result)), 'image_type': image_file.type }); this.setVCard(data); diff --git a/src/plugins/rosterview/modals/add-contact.js b/src/plugins/rosterview/modals/add-contact.js index 608515c7a2..6aa7c5bfdc 100644 --- a/src/plugins/rosterview/modals/add-contact.js +++ b/src/plugins/rosterview/modals/add-contact.js @@ -1,30 +1,34 @@ import 'shared/autocomplete/index.js'; -import BaseModal from "plugins/modal/modal.js"; -import tplAddContactModal from "./templates/add-contact.js"; +import BaseModal from 'plugins/modal/modal.js'; +import tplAddContactModal from './templates/add-contact.js'; import { Strophe } from 'strophe.js'; import { __ } from 'i18n'; -import { _converse, api } from "@converse/headless"; -import {getNamesAutoCompleteList} from '@converse/headless/plugins/roster/utils.js'; +import { _converse, api } from '@converse/headless'; +import { getNamesAutoCompleteList } from '@converse/headless/plugins/roster/utils.js'; export default class AddContactModal extends BaseModal { - initialize () { super.initialize(); this.listenTo(this.model, 'change', () => this.render()); this.render(); - this.addEventListener('shown.bs.modal', () => this.querySelector('input[name="jid"]')?.focus(), false); + this.addEventListener( + 'shown.bs.modal', + () => /** @type {HTMLInputElement} */ (this.querySelector('input[name="jid"]'))?.focus(), + false + ); } renderModal () { return tplAddContactModal(this); } - getModalTitle () { // eslint-disable-line class-methods-use-this + getModalTitle () { + // eslint-disable-line class-methods-use-this return __('Add a Contact'); } validateSubmission (jid) { - if (!jid || jid.split('@').filter(s => !!s).length < 2) { + if (!jid || jid.split('@').filter((s) => !!s).length < 2) { this.model.set('error', __('Please enter a valid XMPP address')); return false; } else if (_converse.roster.get(Strophe.getBareJidFromJid(jid))) { @@ -47,8 +51,8 @@ export default class AddContactModal extends BaseModal { async addContactFromForm (ev) { ev.preventDefault(); const data = new FormData(ev.target); - let name = (/** @type {string} */(data.get('name')) || '').trim(); - let jid = (/** @type {string} */(data.get('jid')) || '').trim(); + let name = /** @type {string} */ (data.get('name') || '').trim(); + let jid = /** @type {string} */ (data.get('jid') || '').trim(); if (!jid && typeof api.settings.get('xhr_user_search_url') === 'string') { const list = await getNamesAutoCompleteList(name); diff --git a/src/plugins/rosterview/rosterview.js b/src/plugins/rosterview/rosterview.js index fa6d592dc9..dfb67457d0 100644 --- a/src/plugins/rosterview/rosterview.js +++ b/src/plugins/rosterview/rosterview.js @@ -63,7 +63,7 @@ export default class RosterView extends CustomElement { toggleRoster (ev) { ev?.preventDefault?.(); - const list_el = this.querySelector('.list-container.roster-contacts'); + const list_el = /** @type {HTMLElement} */(this.querySelector('.list-container.roster-contacts')); if (this.model.get('toggle_state') === _converse.CLOSED) { slideOut(list_el).then(() => this.model.save({'toggle_state': _converse.OPENED})); } else { diff --git a/src/shared/autocomplete/suggestion.js b/src/shared/autocomplete/suggestion.js index 40bd132eb5..1f59eb94e5 100644 --- a/src/shared/autocomplete/suggestion.js +++ b/src/shared/autocomplete/suggestion.js @@ -3,7 +3,7 @@ */ class Suggestion extends String { /** - * @param { Any } data - The auto-complete data. Ideally an object e.g. { label, value }, + * @param { any } data - The auto-complete data. Ideally an object e.g. { label, value }, * which specifies the value and human-presentable label of the suggestion. * @param { string } query - The query string being auto-completed */ diff --git a/src/shared/avatar/avatar.js b/src/shared/avatar/avatar.js index 6075680c57..43b6c98735 100644 --- a/src/shared/avatar/avatar.js +++ b/src/shared/avatar/avatar.js @@ -18,6 +18,7 @@ export default class Avatar extends CustomElement { constructor () { super(); + this.data = null; this.width = 36; this.height = 36; } diff --git a/src/shared/chat/baseview.js b/src/shared/chat/baseview.js index 63743f7f47..9c6bae1458 100644 --- a/src/shared/chat/baseview.js +++ b/src/shared/chat/baseview.js @@ -48,7 +48,7 @@ export default class BaseChatView extends CustomElement { focus () { const textarea_el = this.getElementsByClassName('chat-textarea')[0]; if (textarea_el && document.activeElement !== textarea_el) { - textarea_el.focus(); + /** @type {HTMLTextAreaElement} */(textarea_el).focus(); } return this; } diff --git a/src/shared/chat/chat-content.js b/src/shared/chat/chat-content.js index 4b2e3776af..64501639f5 100644 --- a/src/shared/chat/chat-content.js +++ b/src/shared/chat/chat-content.js @@ -10,6 +10,11 @@ import './styles/chat-content.scss'; export default class ChatContent extends CustomElement { + constructor () { + super(); + this.jid = null; + } + static get properties () { return { jid: { type: String } diff --git a/src/shared/chat/emoji-dropdown.js b/src/shared/chat/emoji-dropdown.js index b225a4839b..212cee756c 100644 --- a/src/shared/chat/emoji-dropdown.js +++ b/src/shared/chat/emoji-dropdown.js @@ -21,6 +21,7 @@ export default class EmojiDropdown extends DropdownBase { super(); // This is an optimization, we lazily render the emoji picker, otherwise tests slow to a crawl. this.render_emojis = false; + this.chatview = null; } initModel () { @@ -95,7 +96,7 @@ export default class EmojiDropdown extends DropdownBase { await this.updateComplete; } super.showMenu(); - setTimeout(() => this.querySelector('.emoji-search')?.focus()); + setTimeout(() => /** @type {HTMLInputElement} */(this.querySelector('.emoji-search'))?.focus()); } } diff --git a/src/shared/chat/emoji-picker.js b/src/shared/chat/emoji-picker.js index cb4cb81f69..63bd13e652 100644 --- a/src/shared/chat/emoji-picker.js +++ b/src/shared/chat/emoji-picker.js @@ -35,6 +35,7 @@ export default class EmojiPicker extends CustomElement { constructor () { super(); + this.model = null; this.query = ''; this._search_results = []; this.debouncedFilter = debounce(input => this.model.set({'query': input.value}), 250); diff --git a/src/shared/chat/help-messages.js b/src/shared/chat/help-messages.js index a9f373bb74..69773852f6 100644 --- a/src/shared/chat/help-messages.js +++ b/src/shared/chat/help-messages.js @@ -8,6 +8,13 @@ import { unsafeHTML } from 'lit/directives/unsafe-html.js'; export default class ChatHelp extends CustomElement { + constructor () { + super(); + this.messages = []; + this.model = null; + this.type = null; + } + static get properties () { return { chat_type: { type: String }, diff --git a/src/shared/chat/message-history.js b/src/shared/chat/message-history.js index 3db922cfa7..be95a6a1af 100644 --- a/src/shared/chat/message-history.js +++ b/src/shared/chat/message-history.js @@ -9,6 +9,12 @@ import { until } from 'lit/directives/until.js'; export default class MessageHistory extends CustomElement { + constructor () { + super(); + this.model = null; + this.messages = []; + } + static get properties () { return { model: { type: Object }, diff --git a/src/shared/chat/message-limit.js b/src/shared/chat/message-limit.js index 0c5d708762..cb8e1b9389 100644 --- a/src/shared/chat/message-limit.js +++ b/src/shared/chat/message-limit.js @@ -4,6 +4,11 @@ import { api } from '@converse/headless'; export default class MessageLimitIndicator extends CustomElement { + constructor () { + super(); + this.model = null; + } + static get properties () { return { model: { type: Object } diff --git a/src/shared/chat/message.js b/src/shared/chat/message.js index f6a77167d9..4ddcc96bdb 100644 --- a/src/shared/chat/message.js +++ b/src/shared/chat/message.js @@ -23,6 +23,12 @@ const { Strophe, dayjs } = converse.env; export default class Message extends CustomElement { + constructor () { + super(); + this.jid = null; + this.mid = null; + } + static get properties () { return { jid: { type: String }, diff --git a/src/shared/chat/unfurl.js b/src/shared/chat/unfurl.js index 4db15feeea..75e1aecb63 100644 --- a/src/shared/chat/unfurl.js +++ b/src/shared/chat/unfurl.js @@ -18,6 +18,15 @@ export default class MessageUnfurl extends CustomElement { } } + constructor () { + super(); + this.jid = null; + this.url = null; + this.title = null; + this.image = null; + this.description = null; + } + initialize () { const settings = getAppSettings(); this.listenTo(settings, 'change:allowed_image_domains', () => this.requestUpdate()); diff --git a/src/shared/chat/utils.js b/src/shared/chat/utils.js index 9b4a9e9e9d..613bdd7f47 100644 --- a/src/shared/chat/utils.js +++ b/src/shared/chat/utils.js @@ -1,6 +1,9 @@ +/** + * @typedef {import('../../headless/plugins/chat/message.js').default} Message + */ import debounce from 'lodash-es/debounce'; import tplNewDay from "./templates/new-day.js"; -import { _converse, api, converse } from '@converse/headless'; +import { api, converse } from '@converse/headless'; import { html } from 'lit'; import { until } from 'lit/directives/until.js'; import { @@ -118,7 +121,7 @@ export const markScrolled = debounce((ev) => _markScrolled(ev), 50); /** * Given a message object, returns a TemplateResult indicating a new day if * the passed in message is more than a day later than its predecessor. - * @param { _converse.Message } + * @param {Message} message */ export function getDayIndicator (message) { const messages = message.collection?.models; diff --git a/src/shared/components/dropdownbase.js b/src/shared/components/dropdownbase.js index 073dd63425..3a06ba7275 100644 --- a/src/shared/components/dropdownbase.js +++ b/src/shared/components/dropdownbase.js @@ -1,17 +1,15 @@ import { CustomElement } from './element.js'; -import { converse } from "@converse/headless"; +import { converse } from '@converse/headless'; const u = converse.env.utils; - export default class DropdownBase extends CustomElement { - - connectedCallback() { + connectedCallback () { super.connectedCallback(); this.registerEvents(); } - registerEvents() { + registerEvents () { this.clickOutside = (ev) => this._clickOutside(ev); document.addEventListener('click', this.clickOutside); } @@ -20,25 +18,25 @@ export default class DropdownBase extends CustomElement { super.firstUpdated(); this.menu = this.querySelector('.dropdown-menu'); this.button = this.querySelector('button'); - this.addEventListener('click', ev => this.toggleMenu(ev)); - this.addEventListener('keyup', ev => this.handleKeyUp(ev)); + this.addEventListener('click', (ev) => this.toggleMenu(ev)); + this.addEventListener('keyup', (ev) => this.handleKeyUp(ev)); } - _clickOutside(ev) { + _clickOutside (ev) { if (!this.contains(ev.composedPath()[0])) { - this.hideMenu(ev); + this.hideMenu(); } } hideMenu () { u.removeClass('show', this.menu); - this.button?.setAttribute('aria-expanded', false); + this.button?.setAttribute('aria-expanded', 'false'); this.button?.blur(); } showMenu () { u.addClass('show', this.menu); - this.button.setAttribute('aria-expanded', true); + this.button.setAttribute('aria-expanded', 'true'); } toggleMenu (ev) { diff --git a/src/shared/components/gif.js b/src/shared/components/gif.js index 8a289d29d3..89c285142e 100644 --- a/src/shared/components/gif.js +++ b/src/shared/components/gif.js @@ -32,6 +32,7 @@ export default class ConverseGIFElement extends CustomElement { this.autoplay = false; this.noloop = false; this.fallback = 'url'; + this.progress_color = null; } initGIF () { diff --git a/src/shared/components/image-picker.js b/src/shared/components/image-picker.js index 736b6083f7..35e673109d 100644 --- a/src/shared/components/image-picker.js +++ b/src/shared/components/image-picker.js @@ -8,6 +8,12 @@ const i18n_profile_picture = __('Your profile picture'); export default class ImagePicker extends CustomElement { + constructor () { + super(); + this.width = null; + this.height = null; + } + static get properties () { return { 'height': { type: Number }, @@ -27,7 +33,7 @@ export default class ImagePicker extends CustomElement { openFileSelection (ev) { ev.preventDefault(); - this.querySelector('input[type="file"]').click(); + /** @type {HTMLInputElement} */(this.querySelector('input[type="file"]')).click(); } updateFilePreview (ev) { diff --git a/src/shared/components/rich-text.js b/src/shared/components/rich-text.js index 4351a83177..5be940d024 100644 --- a/src/shared/components/rich-text.js +++ b/src/shared/components/rich-text.js @@ -52,6 +52,10 @@ export default class RichText extends CustomElement { constructor () { super(); + this.nick = null; + this.onImgClick = null; + this.onImgLoad = null; + this.text = null; this.embed_audio = false; this.embed_videos = false; this.hide_media_urls = false; diff --git a/src/shared/directives/retraction.js b/src/shared/directives/retraction.js index 9d5d02cd85..430a2bcadd 100644 --- a/src/shared/directives/retraction.js +++ b/src/shared/directives/retraction.js @@ -1,5 +1,6 @@ -import { __ } from '../i18n'; -import { directive, html } from "lit"; +import { __ } from 'i18n'; +import { directive } from "lit/directive"; +import { html } from "lit"; const i18n_retract_message = __('Retract this message'); @@ -14,7 +15,7 @@ const tplRetract = (o) => html` `; -export const renderRetractionLink = directive(o => async part => { +export const renderRetractionLink = directive((o) => async (part) => { const may_be_moderated = o.model.get('type') === 'groupchat' && await o.model.mayBeModerated(); const retractable = !o.is_retracted && (o.model.mayBeRetracted() || may_be_moderated);