Переглянути джерело

Add types and run prettier

JC Brand 3 місяців тому
батько
коміт
5ebd9ac0b6

+ 101 - 95
src/plugins/chatview/message-form.js

@@ -2,70 +2,68 @@
  * @typedef {import('shared/chat/emoji-dropdown.js').default} EmojiDropdown
  */
 import { _converse, api, converse, constants, u } from "@converse/headless";
-import { __ } from 'i18n';
-import { CustomElement } from 'shared/components/element.js';
-import tplMessageForm from './templates/message-form.js';
-import { parseMessageForCommands } from './utils.js';
+import { __ } from "i18n";
+import { CustomElement } from "shared/components/element.js";
+import tplMessageForm from "./templates/message-form.js";
+import { parseMessageForCommands } from "./utils.js";
 
 const { ACTIVE, COMPOSING } = constants;
 
-
 export default class MessageForm extends CustomElement {
-
-    static get properties () {
+    static get properties() {
         return {
             model: { type: Object },
-        }
+        };
     }
 
-    constructor () {
+    constructor() {
         super();
         this.model = null;
     }
 
-    async initialize () {
+    async initialize() {
         await this.model.initialized;
-        this.listenTo(this.model.messages, 'change:correcting', this.onMessageCorrecting);
-        this.listenTo(this.model, 'change:composing_spoiler', () => this.requestUpdate());
+        this.listenTo(this.model.messages, "change:correcting", this.onMessageCorrecting);
+        this.listenTo(this.model, "change:composing_spoiler", () => this.requestUpdate());
 
-        this.handleEmojiSelection = (/** @type { CustomEvent } */{ detail }) => {
-            if (this.model.get('jid') === detail.jid) {
+        this.handleEmojiSelection = (/** @type { CustomEvent } */ { detail }) => {
+            if (this.model.get("jid") === detail.jid) {
                 this.insertIntoTextArea(detail.value, detail.autocompleting, false, detail.ac_position);
             }
-        }
+        };
         document.addEventListener("emojiSelected", this.handleEmojiSelection);
         this.requestUpdate();
     }
 
-    disconnectedCallback () {
+    disconnectedCallback() {
         super.disconnectedCallback();
         document.removeEventListener("emojiSelected", this.handleEmojiSelection);
     }
 
-    render () {
+    render() {
         return tplMessageForm(this);
     }
 
     /**
      * Insert a particular string value into the textarea of this chat box.
-     * @param { string } value - The value to be inserted.
+     * @param {string} value - The value to be inserted.
      * @param {(boolean|string)} [replace] - Whether an existing value
      *  should be replaced. If set to `true`, the entire textarea will
      *  be replaced with the new value. If set to a string, then only
      *  that string will be replaced *if* a position is also specified.
-     * @param { number } [position] - The end index of the string to be
+     * @param {number} [position] - The end index of the string to be
      *  replaced with the new value.
      */
-    insertIntoTextArea (value, replace = false, correcting = false, position, separator = ' ') {
-        const textarea = /** @type {HTMLTextAreaElement} */(this.querySelector('.chat-textarea'));
+    insertIntoTextArea(value, replace = false, correcting = false, position, separator = " ") {
+        const textarea = /** @type {HTMLTextAreaElement} */ (this.querySelector(".chat-textarea"));
         if (correcting) {
-            u.addClass('correcting', textarea);
+            u.addClass("correcting", textarea);
         } else {
-            u.removeClass('correcting', textarea);
+            u.removeClass("correcting", textarea);
         }
         if (replace) {
-            if (position && typeof replace == 'string') {
-                textarea.value = textarea.value.replace(new RegExp(replace, 'g'), (match, offset) =>
+            if (position && typeof replace == "string") {
+                textarea.value = textarea.value.replace(new RegExp(replace, "g"), (match, offset) =>
                     offset == position - replace.length ? value + separator : match
                 );
             } else {
@@ -78,83 +76,91 @@ export default class MessageForm extends CustomElement {
             }
             textarea.value = existing + value + separator;
         }
-        const ev = document.createEvent('HTMLEvents');
-        ev.initEvent('change', false, true);
+        const ev = new Event("change", { bubbles: false, cancelable: true });
         textarea.dispatchEvent(ev);
         u.placeCaretAtEnd(textarea);
     }
 
-    onMessageCorrecting (message) {
-        if (message.get('correcting')) {
+    /**
+     * @param {import('@converse/headless').BaseMessage} message
+     */
+    onMessageCorrecting(message) {
+        if (message.get("correcting")) {
             this.insertIntoTextArea(u.prefixMentions(message), true, true);
         } else {
-            const currently_correcting = this.model.messages.findWhere('correcting');
+            const currently_correcting = this.model.messages.findWhere("correcting");
             if (currently_correcting && currently_correcting !== message) {
                 this.insertIntoTextArea(u.prefixMentions(message), true, true);
             } else {
-                this.insertIntoTextArea('', true, false);
+                this.insertIntoTextArea("", true, false);
             }
         }
     }
 
-    onEscapePressed (ev) {
-        const idx = this.model.messages.findLastIndex('correcting');
+    /**
+     * Handles the escape key press event to stop correcting a message.
+     * @param {KeyboardEvent} ev
+     */
+    onEscapePressed(ev) {
+        const idx = this.model.messages.findLastIndex("correcting");
         const message = idx >= 0 ? this.model.messages.at(idx) : null;
         if (message) {
             ev.preventDefault();
-            message.save('correcting', false);
-            this.insertIntoTextArea('', true, false);
+            message.save("correcting", false);
+            this.insertIntoTextArea("", true, false);
         }
     }
 
-    onPaste (ev) {
+    /**
+     * Handles the paste event to insert text or files into the chat.
+     * @param {ClipboardEvent} ev
+     */
+    onPaste(ev) {
         ev.stopPropagation();
         if (ev.clipboardData.files.length !== 0) {
             ev.preventDefault();
-            // Workaround for quirk in at least Firefox 60.7 ESR:
-            // It seems that pasted files disappear from the event payload after
-            // the event has finished, which apparently happens during async
-            // processing in sendFiles(). So we copy the array here.
             this.model.sendFiles(Array.from(ev.clipboardData.files));
             return;
         }
-        this.model.set({'draft': ev.clipboardData.getData('text/plain')});
+        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.
+    /**
+     * Handles the drop event to send files dragged-and-dropped into the chat.
+     * @param {DragEvent} ev
+     */
+    onDrop(ev) {
+        if (ev.dataTransfer.files.length == 0) {
             return;
         }
-        evt.preventDefault();
-        this.model.sendFiles(evt.dataTransfer.files);
+        ev.preventDefault();
+        this.model.sendFiles(ev.dataTransfer.files);
     }
 
-    onKeyUp (ev) {
-        this.model.set({'draft': ev.target.value});
+    /**
+     * @param {KeyboardEvent} ev
+     */
+    onKeyUp(ev) {
+        this.model.set({ draft: /** @type {HTMLTextAreaElement} */ (ev.target).value });
     }
 
     /**
      * @param {KeyboardEvent} [ev]
      */
-    onKeyDown (ev) {
+    onKeyDown(ev) {
         if (ev.ctrlKey) {
             // When ctrl is pressed, no chars are entered into the textarea.
             return;
         }
         if (!ev.shiftKey && !ev.altKey && !ev.metaKey) {
-            const target = /** @type {HTMLInputElement|HTMLTextAreaElement} */(ev.target);
+            const target = /** @type {HTMLInputElement|HTMLTextAreaElement} */ (ev.target);
 
             if (ev.key === converse.keycodes.TAB) {
                 const value = u.getCurrentWord(target, null, /(:.*?:)/g);
-                if (value.startsWith(':')) {
+                if (value.startsWith(":")) {
                     ev.preventDefault();
                     ev.stopPropagation();
-                    this.model.trigger(
-                        'emoji-picker-autocomplete',
-                        { target, value },
-                    );
+                    this.model.trigger("emoji-picker-autocomplete", { target, value });
                 }
             } else if (ev.key === converse.keycodes.FORWARD_SLASH) {
                 // Forward slash is used to run commands. Nothing to do here.
@@ -164,92 +170,92 @@ export default class MessageForm extends CustomElement {
             } else if (ev.key === converse.keycodes.ENTER) {
                 return this.onFormSubmitted(ev);
             } else if (ev.key === converse.keycodes.UP_ARROW && !target.selectionEnd) {
-                const textarea = /** @type {HTMLTextAreaElement} */(this.querySelector('.chat-textarea'));
-                if (!textarea.value || u.hasClass('correcting', textarea)) {
+                const textarea = /** @type {HTMLTextAreaElement} */ (this.querySelector(".chat-textarea"));
+                if (!textarea.value || u.hasClass("correcting", textarea)) {
                     return this.model.editEarlierMessage();
                 }
             } else if (
                 ev.key === converse.keycodes.DOWN_ARROW &&
                 target.selectionEnd === target.value.length &&
-                u.hasClass('correcting', this.querySelector('.chat-textarea'))
+                u.hasClass("correcting", this.querySelector(".chat-textarea"))
             ) {
                 return this.model.editLaterMessage();
             }
         }
         if (
-            [
-                converse.keycodes.SHIFT,
-                converse.keycodes.META,
-                converse.keycodes.ESCAPE,
-                converse.keycodes.ALT
-            ].includes(ev.key)
+            [converse.keycodes.SHIFT, converse.keycodes.META, converse.keycodes.ESCAPE, converse.keycodes.ALT].includes(
+                ev.key
+            )
         ) {
             return;
         }
-        if (this.model.get('chat_state') !== COMPOSING) {
+        if (this.model.get("chat_state") !== COMPOSING) {
             // Set chat state to composing if key is not a forward-slash
             // (which would imply an internal command and not a message).
             this.model.setChatState(COMPOSING);
         }
     }
 
-    async onFormSubmitted (ev) {
+    /**
+     * @param {SubmitEvent|KeyboardEvent} ev
+     */
+    async onFormSubmitted(ev) {
         ev?.preventDefault?.();
         const { chatboxviews } = _converse.state;
-        const textarea = /** @type {HTMLTextAreaElement} */(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')) ||
-            !message_text.replace(/\s/g, '').length
+            (api.settings.get("message_limit") && message_text.length > api.settings.get("message_limit")) ||
+            !message_text.replace(/\s/g, "").length
         ) {
             return;
         }
         if (!api.connection.get().authenticated) {
-            const err_msg = __('Sorry, the connection has been lost, and your message could not be sent');
-            api.alert('error', __('Error'), err_msg);
+            const err_msg = __("Sorry, the connection has been lost, and your message could not be sent");
+            api.alert("error", __("Error"), err_msg);
             api.connection.reconnect();
             return;
         }
         let spoiler_hint,
             hint_el = {};
-        if (this.model.get('composing_spoiler')) {
-            hint_el = /** @type {HTMLInputElement} */(this.querySelector('form.chat-message-form input.spoiler-hint'));
+        if (this.model.get("composing_spoiler")) {
+            hint_el = /** @type {HTMLInputElement} */ (this.querySelector("form.chat-message-form input.spoiler-hint"));
             spoiler_hint = hint_el.value;
         }
-        u.addClass('disabled', textarea);
-        textarea.setAttribute('disabled', 'disabled');
-        /** @type {EmojiDropdown} */(this.querySelector('converse-emoji-dropdown'))?.dropdown.hide();
+        u.addClass("disabled", textarea);
+        textarea.setAttribute("disabled", "disabled");
+        /** @type {EmojiDropdown} */ (this.querySelector("converse-emoji-dropdown"))?.dropdown.hide();
 
         const is_command = await parseMessageForCommands(this.model, message_text);
-        const message = is_command ? null : await this.model.sendMessage({'body': message_text, spoiler_hint});
+        const message = is_command ? null : await this.model.sendMessage({ "body": message_text, spoiler_hint });
         if (is_command || message) {
-            hint_el.value = '';
-            textarea.value = '';
-            u.removeClass('correcting', textarea);
-            textarea.style.height = 'auto';
-            this.model.set({'draft': ''});
+            hint_el.value = "";
+            textarea.value = "";
+            u.removeClass("correcting", textarea);
+            textarea.style.height = "auto";
+            this.model.set({ "draft": "" });
         }
-        if (api.settings.get('view_mode') === 'overlayed') {
+        if (api.settings.get("view_mode") === "overlayed") {
             // XXX: Chrome flexbug workaround. The .chat-content area
             // doesn't resize when the textarea is resized to its original size.
-            const chatview = chatboxviews.get(this.model.get('jid'));
-            const msgs_container = chatview.querySelector('.chat-content__messages');
-            msgs_container.parentElement.style.display = 'none';
+            const chatview = chatboxviews.get(this.model.get("jid"));
+            const msgs_container = chatview.querySelector(".chat-content__messages");
+            msgs_container.parentElement.style.display = "none";
         }
-        textarea.removeAttribute('disabled');
-        u.removeClass('disabled', textarea);
+        textarea.removeAttribute("disabled");
+        u.removeClass("disabled", textarea);
 
-        if (api.settings.get('view_mode') === 'overlayed') {
+        if (api.settings.get("view_mode") === "overlayed") {
             // XXX: Chrome flexbug workaround.
-            const chatview = chatboxviews.get(this.model.get('jid'));
-            const msgs_container = chatview.querySelector('.chat-content__messages');
-            msgs_container.parentElement.style.display = '';
+            const chatview = chatboxviews.get(this.model.get("jid"));
+            const msgs_container = chatview.querySelector(".chat-content__messages");
+            msgs_container.parentElement.style.display = "";
         }
         // Suppress events, otherwise superfluous CSN gets set
         // immediately after the message, causing rate-limiting issues.
-        this.model.setChatState(ACTIVE, { 'silent': true });
+        this.model.setChatState(ACTIVE, { "silent": true });
         textarea.focus();
     }
 }
 
-api.elements.define('converse-message-form', MessageForm);
+api.elements.define("converse-message-form", MessageForm);

+ 55 - 49
src/plugins/chatview/templates/message-form.js

@@ -1,58 +1,64 @@
-import { __ } from 'i18n';
+import { __ } from "i18n";
 import { api } from "@converse/headless";
 import { html } from "lit";
-import { resetElementHeight } from '../utils.js';
-
+import { resetElementHeight } from "../utils.js";
 
 /**
  * @param {import('../message-form').default} el
  */
 export default (el) => {
-    const composing_spoiler = el.model.get('composing_spoiler');
-    const label_message = composing_spoiler ? __('Hidden message') : __('Message');
-    const label_spoiler_hint = __('Optional hint');
-    const message_limit = api.settings.get('message_limit');
-    const show_call_button = api.settings.get('visible_toolbar_buttons').call;
-    const show_emoji_button = api.settings.get('visible_toolbar_buttons').emoji;
-    const show_send_button = api.settings.get('show_send_button');
-    const show_spoiler_button = api.settings.get('visible_toolbar_buttons').spoiler;
-    const show_toolbar = api.settings.get('show_toolbar');
-    const hint_value = /** @type {HTMLInputElement} */(el.querySelector('.spoiler-hint'))?.value;
-    const message_value = /** @type {HTMLTextAreaElement} */(el.querySelector('.chat-textarea'))?.value;
+    const composing_spoiler = el.model.get("composing_spoiler");
+    const label_message = composing_spoiler ? __("Hidden message") : __("Message");
+    const label_spoiler_hint = __("Optional hint");
+    const message_limit = api.settings.get("message_limit");
+    const show_call_button = api.settings.get("visible_toolbar_buttons").call;
+    const show_emoji_button = api.settings.get("visible_toolbar_buttons").emoji;
+    const show_send_button = api.settings.get("show_send_button");
+    const show_spoiler_button = api.settings.get("visible_toolbar_buttons").spoiler;
+    const show_toolbar = api.settings.get("show_toolbar");
+    const hint_value = /** @type {HTMLInputElement} */ (el.querySelector(".spoiler-hint"))?.value;
+    const message_value = /** @type {HTMLTextAreaElement} */ (el.querySelector(".chat-textarea"))?.value;
 
-    return html`
-        <form class="chat-message-form" @submit=${(ev) => el.onFormSubmitted(ev)}>
-            ${show_toolbar ? html`
-                <converse-chat-toolbar
-                    class="btn-toolbar chat-toolbar no-text-select"
-                    .model=${el.model}
-                    ?composing_spoiler="${composing_spoiler}"
-                    ?show_call_button="${show_call_button}"
-                    ?show_emoji_button="${show_emoji_button}"
-                    ?show_send_button="${show_send_button}"
-                    ?show_spoiler_button="${show_spoiler_button}"
-                    ?show_toolbar="${show_toolbar}"
-                    message_limit="${message_limit}"></converse-chat-toolbar>` : '' }
+    return html` <form class="chat-message-form" @submit="${/** @param {SubmitEvent} ev */ (ev) => el.onFormSubmitted(ev)}">
+        ${show_toolbar
+            ? html` <converse-chat-toolbar
+                  class="btn-toolbar chat-toolbar no-text-select"
+                  .model=${el.model}
+                  ?composing_spoiler="${composing_spoiler}"
+                  ?show_call_button="${show_call_button}"
+                  ?show_emoji_button="${show_emoji_button}"
+                  ?show_send_button="${show_send_button}"
+                  ?show_spoiler_button="${show_spoiler_button}"
+                  ?show_toolbar="${show_toolbar}"
+                  message_limit="${message_limit}"
+              ></converse-chat-toolbar>`
+            : ""}
 
-                <input type="text"
-                    enterkeyhint="send"
-                    placeholder="${label_spoiler_hint || ''}"i
-                    value="${hint_value || ''}"
-                    class="${composing_spoiler ? '' : 'hidden'} spoiler-hint"/>
-                <textarea
-                    autofocus
-                    type="text"
-                    enterkeyhint="send"
-                    .value=${ message_value || ''}
-                    @drop=${ev => el.onDrop(ev)}
-                    @input=${resetElementHeight}
-                    @keydown=${ev => el.onKeyDown(ev)}
-                    @keyup=${ev => el.onKeyUp(ev)}
-                    @paste=${ev => el.onPaste(ev)}
-                    @change=${ev => el.model.set({'draft': ev.target.value})}
-                    class="chat-textarea
-                        ${ show_send_button ? 'chat-textarea-send-button' : '' }
-                        ${ composing_spoiler ? 'spoiler' : '' }"
-                    placeholder="${label_message}"></textarea>
-        </form>`;
-}
+        <input
+            type="text"
+            enterkeyhint="send"
+            placeholder="${label_spoiler_hint || ""}"
+            value="${hint_value || ""}"
+            class="${composing_spoiler ? "" : "hidden"} spoiler-hint"
+        />
+        <textarea
+            autofocus
+            type="text"
+            enterkeyhint="send"
+            .value="${message_value || ""}"
+            @drop="${/** @param {DragEvent} ev */ (ev) => el.onDrop(ev)}"
+            @input="${resetElementHeight}"
+            @keydown="${/** @param {KeyboardEvent} ev */ (ev) => el.onKeyDown(ev)}"
+            @keyup="${/** @param {KeyboardEvent} ev */ (ev) => el.onKeyUp(ev)}"
+            @paste="${/** @param {ClipboardEvent} ev */ (ev) => el.onPaste(ev)}"
+            @change="${
+                /** @param {Event} ev */ (ev) =>
+                    el.model.set({ draft: /** @type {HTMLTextAreaElement} */ (ev.target).value })
+            }"
+            class="chat-textarea
+                        ${show_send_button ? "chat-textarea-send-button" : ""}
+                        ${composing_spoiler ? "spoiler" : ""}"
+            placeholder="${label_message}"
+        ></textarea>
+    </form>`;
+};

+ 62 - 65
src/plugins/rosterview/modals/templates/add-contact.js

@@ -1,75 +1,72 @@
-import { __ } from 'i18n';
-import { api } from '@converse/headless';
-import {
-    getGroupsAutoCompleteList,
-    getJIDsAutoCompleteList,
-    getNamesAutoCompleteList
-} from '../../utils.js';
+import { __ } from "i18n";
+import { api } from "@converse/headless";
+import { getGroupsAutoCompleteList, getJIDsAutoCompleteList, getNamesAutoCompleteList } from "../../utils.js";
 import { html } from "lit";
-import { FILTER_STARTSWITH } from 'shared/autocomplete/utils';
-
+import { FILTER_STARTSWITH } from "shared/autocomplete/utils";
 
 /**
  * @param {import('../add-contact.js').default} el
  */
 export default (el) => {
-    const i18n_add = __('Add');
-    const i18n_contact_placeholder = __('name@example.org');
-    const i18n_groups = __('Groups');
+    const i18n_add = __("Add");
+    const i18n_contact_placeholder = __("name@example.org");
+    const i18n_groups = __("Groups");
     const i18n_groups_help = __("Use commas to separate multiple values");
-    const i18n_nickname = __('Name');
-    const i18n_xmpp_address = __('XMPP Address');
-    const error = el.model.get('error');
-
-    return html`
-        <div class="modal-body">
-            ${error ? html`<div class="alert alert-danger" role="alert">${error}</div>` : ''}
-            <form class="converse-form add-xmpp-contact" @submit=${ev => el.addContactFromForm(ev)}>
-                <div class="mb-3">
-                    <label class="form-label clearfix" for="jid">${i18n_xmpp_address}:</label>
-                    ${api.settings.get('autocomplete_add_contact') ?
-                        html`<converse-autocomplete
-                            .list=${getJIDsAutoCompleteList()}
-                            .data=${(text, input) => `${input.slice(0, input.indexOf("@"))}@${text}`}
-                            position="below"
-                            filter=${FILTER_STARTSWITH}
-                            ?required=${(!api.settings.get('xhr_user_search_url'))}
-                            value="${el.model.get('jid') || ''}"
-                            placeholder="${i18n_contact_placeholder}"
-                            name="jid"></converse-autocomplete>` :
+    const i18n_nickname = __("Name");
+    const i18n_xmpp_address = __("XMPP Address");
+    const error = el.model.get("error");
 
-                        html`<input type="text" name="jid"
-                            ?required=${(!api.settings.get('xhr_user_search_url'))}
-                            value="${el.model.get('jid') || ''}"
-                            class="form-control"
-                            placeholder="${i18n_contact_placeholder}"/>`
-                    }
-                </div>
+    return html` <div class="modal-body">
+        ${error ? html`<div class="alert alert-danger" role="alert">${error}</div>` : ""}
+        <form class="converse-form add-xmpp-contact" @submit=${(ev) => el.addContactFromForm(ev)}>
+            <div class="mb-3">
+                <label class="form-label clearfix" for="jid">${i18n_xmpp_address}:</label>
+                ${api.settings.get("autocomplete_add_contact")
+                    ? html`<converse-autocomplete
+                          .list=${getJIDsAutoCompleteList()}
+                          .data=${(text, input) => `${input.slice(0, input.indexOf("@"))}@${text}`}
+                          position="below"
+                          filter=${FILTER_STARTSWITH}
+                          ?required=${!api.settings.get("xhr_user_search_url")}
+                          value="${el.model.get("jid") || ""}"
+                          placeholder="${i18n_contact_placeholder}"
+                          name="jid"
+                      ></converse-autocomplete>`
+                    : html`<input
+                          type="text"
+                          name="jid"
+                          ?required=${!api.settings.get("xhr_user_search_url")}
+                          value="${el.model.get("jid") || ""}"
+                          class="form-control"
+                          placeholder="${i18n_contact_placeholder}"
+                      />`}
+            </div>
 
-                <div class="mb-3">
-                    <label class="form-label clearfix" for="name">${i18n_nickname}:</label>
-                    ${api.settings.get('autocomplete_add_contact') && typeof api.settings.get('xhr_user_search_url') === 'string' ?
-                        html`<converse-autocomplete
-                            .getAutoCompleteList=${(query) => getNamesAutoCompleteList(query, 'fullname')}
-                            filter=${FILTER_STARTSWITH}
-                            value="${el.model.get('nickname') || ''}"
-                            name="name"></converse-autocomplete>` :
-
-                        html`<input type="text" name="name"
-                            value="${el.model.get('nickname') || ''}"
-                            class="form-control" />`
-                    }
-                </div>
-                <div class="mb-3">
-                    <label class="form-label clearfix" for="name">${i18n_groups}:</label>
-                    <div class="mb-1">
-                        <small class="form-text text-muted">${i18n_groups_help}</small>
-                    </div>
-                    <converse-autocomplete
-                        .list=${getGroupsAutoCompleteList()}
-                        name="groups"></converse-autocomplete>
+            <div class="mb-3">
+                <label class="form-label clearfix" for="name">${i18n_nickname}:</label>
+                ${api.settings.get("autocomplete_add_contact") &&
+                typeof api.settings.get("xhr_user_search_url") === "string"
+                    ? html`<converse-autocomplete
+                          .getAutoCompleteList=${(query) => getNamesAutoCompleteList(query, "fullname")}
+                          filter=${FILTER_STARTSWITH}
+                          value="${el.model.get("nickname") || ""}"
+                          name="name"
+                      ></converse-autocomplete>`
+                    : html`<input
+                          type="text"
+                          name="name"
+                          value="${el.model.get("nickname") || ""}"
+                          class="form-control"
+                      />`}
+            </div>
+            <div class="mb-3">
+                <label class="form-label clearfix" for="name">${i18n_groups}:</label>
+                <div class="mb-1">
+                    <small class="form-text text-muted">${i18n_groups_help}</small>
                 </div>
-                <button type="submit" class="btn btn-primary">${i18n_add}</button>
-            </form>
-        </div>`;
-}
+                <converse-autocomplete .list=${getGroupsAutoCompleteList()} name="groups"></converse-autocomplete>
+            </div>
+            <button type="submit" class="btn btn-primary">${i18n_add}</button>
+        </form>
+    </div>`;
+};

+ 30 - 9
src/types/plugins/chatview/message-form.d.ts

@@ -10,26 +10,47 @@ export default class MessageForm extends CustomElement {
     render(): import("lit").TemplateResult<1>;
     /**
      * Insert a particular string value into the textarea of this chat box.
-     * @param { string } value - The value to be inserted.
+     * @param {string} value - The value to be inserted.
      * @param {(boolean|string)} [replace] - Whether an existing value
      *  should be replaced. If set to `true`, the entire textarea will
      *  be replaced with the new value. If set to a string, then only
      *  that string will be replaced *if* a position is also specified.
-     * @param { number } [position] - The end index of the string to be
+     * @param {number} [position] - The end index of the string to be
      *  replaced with the new value.
      */
     insertIntoTextArea(value: string, replace?: (boolean | string), correcting?: boolean, position?: number, separator?: string): void;
-    onMessageCorrecting(message: any): void;
-    onEscapePressed(ev: any): void;
-    onPaste(ev: any): void;
-    onDrop(evt: any): void;
-    onKeyUp(ev: any): void;
+    /**
+     * @param {import('@converse/headless').BaseMessage} message
+     */
+    onMessageCorrecting(message: import("@converse/headless").BaseMessage<any>): void;
+    /**
+     * Handles the escape key press event to stop correcting a message.
+     * @param {KeyboardEvent} ev
+     */
+    onEscapePressed(ev: KeyboardEvent): void;
+    /**
+     * Handles the paste event to insert text or files into the chat.
+     * @param {ClipboardEvent} ev
+     */
+    onPaste(ev: ClipboardEvent): void;
+    /**
+     * Handles the drop event to send files dragged-and-dropped into the chat.
+     * @param {DragEvent} ev
+     */
+    onDrop(ev: DragEvent): void;
+    /**
+     * @param {KeyboardEvent} ev
+     */
+    onKeyUp(ev: KeyboardEvent): void;
     /**
      * @param {KeyboardEvent} [ev]
      */
     onKeyDown(ev?: KeyboardEvent): any;
-    onFormSubmitted(ev: any): Promise<void>;
+    /**
+     * @param {SubmitEvent|KeyboardEvent} ev
+     */
+    onFormSubmitted(ev: SubmitEvent | KeyboardEvent): Promise<void>;
 }
 export type EmojiDropdown = import("shared/chat/emoji-dropdown.js").default;
-import { CustomElement } from 'shared/components/element.js';
+import { CustomElement } from "shared/components/element.js";
 //# sourceMappingURL=message-form.d.ts.map

+ 0 - 4
src/types/plugins/muc-views/message-form.d.ts

@@ -11,10 +11,6 @@ export default class MUCMessageForm extends MessageForm {
      * @param {KeyboardEvent} ev
      */
     onKeyDown(ev: KeyboardEvent): void;
-    /**
-     * @param {KeyboardEvent} ev
-     */
-    onKeyUp(ev: KeyboardEvent): void;
 }
 import MessageForm from 'plugins/chatview/message-form.js';
 import AutoComplete from 'shared/autocomplete/autocomplete.js';