Просмотр исходного кода

Run prettier, fix eslint and type errors.

Declare more types.
Rename variable.
Refine_converse type definition.
JC Brand 1 год назад
Родитель
Сommit
4847461571
76 измененных файлов с 925 добавлено и 285 удалено
  1. 12 0
      .eslintrc.json
  2. 4 3
      package-lock.json
  3. 1 0
      package.json
  4. 2 1
      src/headless/package.json
  5. 23 13
      src/headless/plugins/disco/api.js
  6. 6 0
      src/headless/plugins/disco/index.js
  7. 7 4
      src/headless/plugins/muc/muc.js
  8. 6 1
      src/headless/plugins/muc/occupant.js
  9. 1 1
      src/headless/plugins/muc/parsers.js
  10. 13 13
      src/headless/plugins/roster/contact.js
  11. 18 9
      src/headless/plugins/roster/utils.js
  12. 8 3
      src/headless/plugins/status/status.js
  13. 15 0
      src/headless/plugins/vcard/utils.js
  14. 16 8
      src/headless/shared/_converse.js
  15. 6 5
      src/headless/shared/api/presence.js
  16. 1 1
      src/headless/shared/connection/feedback.js
  17. 3 1
      src/headless/shared/i18n.js
  18. 12 12
      src/headless/shared/parsers.js
  19. 12 3
      src/headless/types/plugins/disco/api.d.ts
  20. 4 1
      src/headless/types/plugins/disco/index.d.ts
  21. 1 0
      src/headless/types/plugins/muc/muc.d.ts
  22. 2 0
      src/headless/types/plugins/muc/occupant.d.ts
  23. 11 11
      src/headless/types/plugins/roster/contact.d.ts
  24. 1 0
      src/headless/types/plugins/roster/utils.d.ts
  25. 5 3
      src/headless/types/plugins/status/status.d.ts
  26. 16 4
      src/headless/types/plugins/vcard/utils.d.ts
  27. 15 3
      src/headless/types/shared/_converse.d.ts
  28. 3 3
      src/headless/types/shared/api/index.d.ts
  29. 5 4
      src/headless/types/shared/api/presence.d.ts
  30. 2 2
      src/headless/types/shared/i18n.d.ts
  31. 3 3
      src/headless/types/utils/session.d.ts
  32. 12 11
      src/i18n/index.js
  33. 4 3
      src/plugins/muc-views/bottom-panel.js
  34. 36 22
      src/plugins/muc-views/modals/templates/add-muc.js
  35. 27 25
      src/plugins/muc-views/templates/muc-bottom-panel.js
  36. 3 3
      src/plugins/muc-views/templates/muc-sidebar.js
  37. 9 1
      src/plugins/notifications/utils.js
  38. 51 33
      src/plugins/rosterview/templates/roster.js
  39. 2 2
      src/plugins/rosterview/utils.js
  40. 25 15
      src/shared/dom-navigator.js
  41. 9 3
      src/shared/rich-text.js
  42. 2 0
      src/shared/styling.js
  43. 7 1
      src/templates/audio.js
  44. 1 0
      src/templates/background_logo.js
  45. 8 2
      src/templates/gif.js
  46. 10 5
      src/templates/spinner.js
  47. 8 2
      src/templates/video.js
  48. 261 0
      src/types/headless/plugins/disco/api.d.ts
  49. 9 0
      src/types/headless/plugins/disco/entities.d.ts
  50. 49 0
      src/types/headless/plugins/disco/entity.d.ts
  51. 5 0
      src/types/headless/plugins/disco/index.d.ts
  52. 6 0
      src/types/headless/plugins/disco/utils.d.ts
  53. 1 0
      src/types/headless/plugins/muc/muc.d.ts
  54. 2 0
      src/types/headless/plugins/muc/occupant.d.ts
  55. 18 0
      src/types/headless/plugins/status/status.d.ts
  56. 22 0
      src/types/headless/plugins/status/utils.d.ts
  57. 16 0
      src/types/headless/plugins/vcard/vcard.d.ts
  58. 15 3
      src/types/headless/shared/_converse.d.ts
  59. 3 3
      src/types/headless/shared/api/index.d.ts
  60. 5 4
      src/types/headless/shared/api/presence.d.ts
  61. 2 2
      src/types/headless/shared/i18n.d.ts
  62. 3 3
      src/types/headless/utils/session.d.ts
  63. 11 2
      src/types/i18n/index.d.ts
  64. 1 0
      src/types/index.d.ts
  65. 6 2
      src/types/plugins/muc-views/sidebar.d.ts
  66. 5 0
      src/types/plugins/notifications/utils.d.ts
  67. 1 1
      src/types/plugins/omemo/devicelist.d.ts
  68. 1 1
      src/types/plugins/omemo/utils.d.ts
  69. 1 1
      src/types/plugins/profile/modals/profile.d.ts
  70. 2 1
      src/types/plugins/rosterview/templates/roster.d.ts
  71. 8 2
      src/types/shared/chat/utils.d.ts
  72. 15 12
      src/types/shared/dom-navigator.d.ts
  73. 1 1
      src/types/templates/audio.d.ts
  74. 1 1
      src/types/templates/gif.d.ts
  75. 1 1
      src/types/templates/video.d.ts
  76. 6 5
      src/utils/html.js

+ 12 - 0
.eslintrc.json

@@ -185,6 +185,18 @@
         "no-unmodified-loop-condition": "error",
         "no-unmodified-loop-condition": "error",
         "no-unneeded-ternary": "off",
         "no-unneeded-ternary": "off",
         "no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
         "no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
+        "@typescript-eslint/no-unused-vars": [
+            "error",
+            {
+                "args": "all",
+                "argsIgnorePattern": "^_",
+                "caughtErrors": "all",
+                "caughtErrorsIgnorePattern": "^_",
+                "destructuredArrayIgnorePattern": "^_",
+                "varsIgnorePattern": "^_",
+                "ignoreRestSiblings": true
+            }
+        ],
         "no-unused-expressions": "off",
         "no-unused-expressions": "off",
         "no-use-before-define": "off",
         "no-use-before-define": "off",
         "no-useless-call": "error",
         "no-useless-call": "error",

+ 4 - 3
package-lock.json

@@ -1815,8 +1815,8 @@
     },
     },
     "node_modules/@converse/skeletor": {
     "node_modules/@converse/skeletor": {
       "version": "0.0.8",
       "version": "0.0.8",
-      "resolved": "git+ssh://git@github.com/conversejs/skeletor.git#4ff728207fa30721686021d11fb8b5245c54a6b4",
-      "integrity": "sha512-1IcP9VyfT75DzIcH4AG5csnSUGvOZdwHeSKXPs54E0IYUzJrMlExSsBKP24au9zyk/AJyhj+pBSMD2SY5zfppg==",
+      "resolved": "git+ssh://git@github.com/conversejs/skeletor.git#2d33edf584cb37b7a54c8b140b85210f080769b1",
+      "integrity": "sha512-6wRYreBOB/e5CcWDwzU8l5Wnt2ruU36mXEj/V0cC8M396kCYxH1xngD3AytHG3zbRpkc6qKayNbhhppncRdPTQ==",
       "license": "MIT",
       "license": "MIT",
       "dependencies": {
       "dependencies": {
         "@converse/localforage-getitems": "1.4.3",
         "@converse/localforage-getitems": "1.4.3",
@@ -10001,11 +10001,12 @@
       }
       }
     },
     },
     "src/headless": {
     "src/headless": {
+      "name": "@converse/headless",
       "version": "10.1.7",
       "version": "10.1.7",
       "license": "MPL-2.0",
       "license": "MPL-2.0",
       "dependencies": {
       "dependencies": {
         "@converse/openpromise": "^0.0.1",
         "@converse/openpromise": "^0.0.1",
-        "@converse/skeletor": "conversejs/skeletor#4ff728207fa30721686021d11fb8b5245c54a6b4",
+        "@converse/skeletor": "conversejs/skeletor#2d33edf584cb37b7a54c8b140b85210f080769b1 ",
         "dayjs": "^1.11.8",
         "dayjs": "^1.11.8",
         "dompurify": "^3.0.8",
         "dompurify": "^3.0.8",
         "filesize": "^10.0.7",
         "filesize": "^10.0.7",

+ 1 - 0
package.json

@@ -3,6 +3,7 @@
   "version": "10.1.7",
   "version": "10.1.7",
   "description": "Browser based XMPP chat client",
   "description": "Browser based XMPP chat client",
   "main": "src/index.js",
   "main": "src/index.js",
+  "types": "src/types/index.d.ts",
   "browser": "dist/converse.js",
   "browser": "dist/converse.js",
   "module": "src/index.js",
   "module": "src/index.js",
   "workspaces": [
   "workspaces": [

+ 2 - 1
src/headless/package.json

@@ -9,6 +9,7 @@
   "homepage": "https://conversejs.org",
   "homepage": "https://conversejs.org",
   "license": "MPL-2.0",
   "license": "MPL-2.0",
   "main": "dist/converse-headless.js",
   "main": "dist/converse-headless.js",
+  "types": "types/index.d.ts",
   "module": "index.js",
   "module": "index.js",
   "keywords": [
   "keywords": [
     "converse.js",
     "converse.js",
@@ -32,7 +33,7 @@
   },
   },
   "dependencies": {
   "dependencies": {
     "@converse/openpromise": "^0.0.1",
     "@converse/openpromise": "^0.0.1",
-    "@converse/skeletor": "conversejs/skeletor#4ff728207fa30721686021d11fb8b5245c54a6b4",
+    "@converse/skeletor": "conversejs/skeletor#2d33edf584cb37b7a54c8b140b85210f080769b1 ",
     "dayjs": "^1.11.8",
     "dayjs": "^1.11.8",
     "dompurify": "^3.0.8",
     "dompurify": "^3.0.8",
     "filesize": "^10.0.7",
     "filesize": "^10.0.7",

+ 23 - 13
src/headless/plugins/disco/api.js

@@ -1,3 +1,8 @@
+/**
+ * @typedef {import('./index').DiscoState} DiscoState
+ * @typedef {import('./entities').default} DiscoEntities
+ * @typedef {import('@converse/skeletor').Collection} Collection
+ */
 import _converse from '../../shared/_converse.js';
 import _converse from '../../shared/_converse.js';
 import api, { converse } from '../../shared/api/index.js';
 import api, { converse } from '../../shared/api/index.js';
 import log from "../../log.js";
 import log from "../../log.js";
@@ -31,7 +36,7 @@ export default {
             async getFeature (name, xmlns) {
             async getFeature (name, xmlns) {
                 await api.waitUntil('streamFeaturesAdded');
                 await api.waitUntil('streamFeaturesAdded');
 
 
-                const { stream_features } = _converse.state;
+                const stream_features = /** @type {Collection} */(_converse.state.stream_features);
                 if (!name || !xmlns) {
                 if (!name || !xmlns) {
                     throw new Error("name and xmlns need to be provided when calling disco.stream.getFeature");
                     throw new Error("name and xmlns need to be provided when calling disco.stream.getFeature");
                 }
                 }
@@ -67,7 +72,7 @@ export default {
                  * @example _converse.api.disco.own.identities.clear();
                  * @example _converse.api.disco.own.identities.clear();
                  */
                  */
                 add (category, type, name, lang) {
                 add (category, type, name, lang) {
-                    const { disco } = _converse.state;
+                    const disco = /** @type {DiscoState} */(_converse.state.disco);
                     for (var i=0; i<disco._identities.length; i++) {
                     for (var i=0; i<disco._identities.length; i++) {
                         if (disco._identities[i].category == category &&
                         if (disco._identities[i].category == category &&
                                 disco._identities[i].type == type &&
                                 disco._identities[i].type == type &&
@@ -84,7 +89,7 @@ export default {
                  * @example _converse.api.disco.own.identities.clear();
                  * @example _converse.api.disco.own.identities.clear();
                  */
                  */
                 clear () {
                 clear () {
-                    _converse.state.disco._identities = []
+                    /** @type {DiscoState} */(_converse.state.disco)._identities = []
                 },
                 },
                 /**
                 /**
                  * Returns all of the identities registered for this client
                  * Returns all of the identities registered for this client
@@ -93,7 +98,7 @@ export default {
                  * @example const identities = api.disco.own.identities.get();
                  * @example const identities = api.disco.own.identities.get();
                  */
                  */
                 get () {
                 get () {
-                    return _converse.state.disco._identities;
+                    return /** @type {DiscoState} */(_converse.state.disco)._identities;
                 }
                 }
             },
             },
 
 
@@ -109,8 +114,8 @@ export default {
                  * @example _converse.api.disco.own.features.add("http://jabber.org/protocol/caps");
                  * @example _converse.api.disco.own.features.add("http://jabber.org/protocol/caps");
                  */
                  */
                 add (name) {
                 add (name) {
-                    const { disco } = _converse.state;
-                    for (var i=0; i<disco._features.length; i++) {
+                    const disco = /** @type {DiscoState} */(_converse.state.disco);
+                    for (let i=0; i<disco._features.length; i++) {
                         if (disco._features[i] == name) { return false; }
                         if (disco._features[i] == name) { return false; }
                     }
                     }
                     disco._features.push(name);
                     disco._features.push(name);
@@ -121,7 +126,8 @@ export default {
                  * @example _converse.api.disco.own.features.clear();
                  * @example _converse.api.disco.own.features.clear();
                  */
                  */
                 clear () {
                 clear () {
-                    _converse.state.disco._features = []
+                    const disco = /** @type {DiscoState} */(_converse.state.disco);
+                    disco._features = []
                 },
                 },
                 /**
                 /**
                  * Returns all of the features registered for this client (i.e. instance of Converse).
                  * Returns all of the features registered for this client (i.e. instance of Converse).
@@ -129,7 +135,7 @@ export default {
                  * @example const features = api.disco.own.features.get();
                  * @example const features = api.disco.own.features.get();
                  */
                  */
                 get () {
                 get () {
-                    return _converse.state.disco._features;
+                    return /** @type {DiscoState} */(_converse.state.disco)._features;
                 }
                 }
             }
             }
         },
         },
@@ -194,7 +200,7 @@ export default {
              */
              */
             async get (jid, create=false) {
             async get (jid, create=false) {
                 await api.waitUntil('discoInitialized');
                 await api.waitUntil('discoInitialized');
-                const { disco_entities } = _converse.state;
+                const disco_entities = /** @type {DiscoEntities} */(_converse.state.disco_entities);
                 if (!jid) {
                 if (!jid) {
                     return disco_entities;
                     return disco_entities;
                 }
                 }
@@ -218,7 +224,8 @@ export default {
              * @example api.disco.entities.items(jid);
              * @example api.disco.entities.items(jid);
              */
              */
             items (jid) {
             items (jid) {
-                return _converse.state.disco_entities.filter(e => e.get('parent_jids')?.includes(jid));
+                const disco_entities = /** @type {DiscoEntities} */(_converse.state.disco_entities);
+                return disco_entities.filter(e => e.get('parent_jids')?.includes(jid));
             },
             },
 
 
             /**
             /**
@@ -240,7 +247,8 @@ export default {
              * @example _converse.api.disco.entities.create({ jid }, {'ignore_cache': true});
              * @example _converse.api.disco.entities.create({ jid }, {'ignore_cache': true});
              */
              */
             create (data, options) {
             create (data, options) {
-                return _converse.state.disco_entities.create(data, options);
+                const disco_entities = /** @type {DiscoEntities} */(_converse.state.disco_entities);
+                return disco_entities.create(data, options);
             }
             }
         },
         },
 
 
@@ -273,7 +281,8 @@ export default {
 
 
                 if (_converse.state.disco_entities === undefined && !api.connection.connected()) {
                 if (_converse.state.disco_entities === undefined && !api.connection.connected()) {
                     // Happens during tests when disco lookups happen asynchronously after teardown.
                     // Happens during tests when disco lookups happen asynchronously after teardown.
-                    log.warn(`Tried to get feature ${feature} for ${jid} but _converse.disco_entities has been torn down`);
+                    log.warn(`Tried to get feature ${feature} for ${jid} but `+
+                        `_converse.disco_entities has been torn down`);
                     return [];
                     return [];
                 }
                 }
 
 
@@ -455,7 +464,8 @@ export default {
             const e = await api.disco.entities.get(jid, true);
             const e = await api.disco.entities.get(jid, true);
             if (e === undefined && !api.connection.connected()) {
             if (e === undefined && !api.connection.connected()) {
                 // Happens during tests when disco lookups happen asynchronously after teardown.
                 // Happens during tests when disco lookups happen asynchronously after teardown.
-                const msg = `Tried to look up category ${category} for ${jid} but _converse.disco_entities has been torn down`;
+                const msg = `Tried to look up category ${category} for ${jid} `+
+                    `but _converse.disco_entities has been torn down`;
                 log.warn(msg);
                 log.warn(msg);
                 return;
                 return;
             }
             }

+ 6 - 0
src/headless/plugins/disco/index.js

@@ -18,6 +18,12 @@ import {
 
 
 const { Strophe } = converse.env;
 const { Strophe } = converse.env;
 
 
+/**
+ * @typedef {Object} DiscoState
+ * @property {Array} _identities
+ * @property {Array} _features
+ */
+
 converse.plugins.add('converse-disco', {
 converse.plugins.add('converse-disco', {
     initialize () {
     initialize () {
         Object.assign(api, disco_api);
         Object.assign(api, disco_api);

+ 7 - 4
src/headless/plugins/muc/muc.js

@@ -6,6 +6,7 @@
  * @typedef {module:plugin-muc-parsers.MemberListItem} MemberListItem
  * @typedef {module:plugin-muc-parsers.MemberListItem} MemberListItem
  * @typedef {module:plugin-chat-parsers.MessageAttributes} MessageAttributes
  * @typedef {module:plugin-chat-parsers.MessageAttributes} MessageAttributes
  * @typedef {module:plugin-muc-parsers.MUCMessageAttributes} MUCMessageAttributes
  * @typedef {module:plugin-muc-parsers.MUCMessageAttributes} MUCMessageAttributes
+ * @typedef {module:shared.converse.UserMessage} UserMessage
  * @typedef {import('strophe.js/src/builder.js').Builder} Strophe.Builder
  * @typedef {import('strophe.js/src/builder.js').Builder} Strophe.Builder
  */
  */
 import _converse from '../../shared/_converse.js';
 import _converse from '../../shared/_converse.js';
@@ -2390,7 +2391,8 @@ class MUC extends ChatBox {
         if (!x) {
         if (!x) {
             return;
             return;
         }
         }
-        const disconnection_codes = Object.keys(_converse.labels.muc.disconnect_messages);
+        const muc = /** @type {UserMessage} */(_converse.labels.muc);
+        const disconnection_codes = Object.keys(muc.disconnect_messages);
         const codes = sizzle('status', x)
         const codes = sizzle('status', x)
             .map(s => s.getAttribute('code'))
             .map(s => s.getAttribute('code'))
             .filter(c => disconnection_codes.includes(c));
             .filter(c => disconnection_codes.includes(c));
@@ -2405,7 +2407,7 @@ class MUC extends ChatBox {
         const item = x.querySelector('item');
         const item = x.querySelector('item');
         const reason = item ? item.querySelector('reason')?.textContent : undefined;
         const reason = item ? item.querySelector('reason')?.textContent : undefined;
         const actor = item ? item.querySelector('actor')?.getAttribute('nick') : undefined;
         const actor = item ? item.querySelector('actor')?.getAttribute('nick') : undefined;
-        const message = _converse.labels.muc.disconnect_messages[codes[0]];
+        const message = muc.disconnect_messages[codes[0]];
         const status = codes.includes('301') ? ROOMSTATUS.BANNED : ROOMSTATUS.DISCONNECTED;
         const status = codes.includes('301') ? ROOMSTATUS.BANNED : ROOMSTATUS.DISCONNECTED;
         this.setDisconnectionState(message, reason, actor, status);
         this.setDisconnectionState(message, reason, actor, status);
     }
     }
@@ -2523,7 +2525,7 @@ class MUC extends ChatBox {
     createInfoMessage (code, stanza, is_self) {
     createInfoMessage (code, stanza, is_self) {
         const __ = _converse.__;
         const __ = _converse.__;
         const data = { 'type': 'info', 'is_ephemeral': true };
         const data = { 'type': 'info', 'is_ephemeral': true };
-        const { info_messages, new_nickname_messages } = _converse.labels.muc;
+        const { info_messages, new_nickname_messages } = /** @type {UserMessage} */(_converse.labels.muc);
 
 
         if (!isInfoVisible(code)) {
         if (!isInfoVisible(code)) {
             return;
             return;
@@ -2625,6 +2627,7 @@ class MUC extends ChatBox {
      */
      */
     onErrorPresence (stanza) {
     onErrorPresence (stanza) {
         const __ = _converse.__;
         const __ = _converse.__;
+        const muc = /** @type {UserMessage} */(_converse.labels.muc);
         const error = stanza.querySelector('error');
         const error = stanza.querySelector('error');
         const error_type = error.getAttribute('type');
         const error_type = error.getAttribute('type');
         const reason = sizzle(`text[xmlns="${Strophe.NS.STANZAS}"]`, error).pop()?.textContent;
         const reason = sizzle(`text[xmlns="${Strophe.NS.STANZAS}"]`, error).pop()?.textContent;
@@ -2641,7 +2644,7 @@ class MUC extends ChatBox {
                 this.setDisconnectionState(message, reason);
                 this.setDisconnectionState(message, reason);
             } else if (error.querySelector('forbidden')) {
             } else if (error.querySelector('forbidden')) {
                 this.setDisconnectionState(
                 this.setDisconnectionState(
-                    _converse.labels.muc.disconnect_messages[301],
+                    muc.disconnect_messages[301],
                     reason,
                     reason,
                     null,
                     null,
                     ROOMSTATUS.BANNED
                     ROOMSTATUS.BANNED

+ 6 - 1
src/headless/plugins/muc/occupant.js

@@ -8,7 +8,12 @@ import { Model } from '@converse/skeletor';
  */
  */
 class ChatRoomOccupant extends Model {
 class ChatRoomOccupant extends Model {
 
 
-    defaults () { // eslint-disable-line class-methods-use-this
+    constructor (attributes, options) {
+        super(attributes, options);
+        this.vcard = null;
+    }
+
+    defaults () {
         return {
         return {
             hats: [],
             hats: [],
             show: 'offline',
             show: 'offline',

+ 1 - 1
src/headless/plugins/muc/parsers.js

@@ -168,7 +168,7 @@ export async function parseMUCMessage (stanza, chatbox) {
     const marker = getChatMarker(stanza);
     const marker = getChatMarker(stanza);
 
 
     /**
     /**
-     * @typedef { Object } MUCMessageAttributes
+     * @typedef {Object} MUCMessageAttributes
      * The object which {@link parseMUCMessage} returns
      * The object which {@link parseMUCMessage} returns
      * @property { ('me'|'them') } sender - Whether the message was sent by the current user or someone else
      * @property { ('me'|'them') } sender - Whether the message was sent by the current user or someone else
      * @property { Array<Object> } activities - A list of objects representing XEP-0316 MEP notification data
      * @property { Array<Object> } activities - A list of objects representing XEP-0316 MEP notification data

+ 13 - 13
src/headless/plugins/roster/contact.js

@@ -8,11 +8,11 @@ import { rejectPresenceSubscription } from './utils.js';
 const { Strophe, $iq, $pres } = converse.env;
 const { Strophe, $iq, $pres } = converse.env;
 
 
 class RosterContact extends Model {
 class RosterContact extends Model {
-    get idAttribute () { // eslint-disable-line class-methods-use-this
+    get idAttribute () {
         return 'jid';
         return 'jid';
     }
     }
 
 
-    defaults () { // eslint-disable-line class-methods-use-this
+    defaults () {
         return {
         return {
             'chat_state': undefined,
             'chat_state': undefined,
             'groups': [],
             'groups': [],
@@ -69,7 +69,7 @@ class RosterContact extends Model {
      *
      *
      * The goal is to be able to filter against the VCard fullname,
      * The goal is to be able to filter against the VCard fullname,
      * roster nickname and JID.
      * roster nickname and JID.
-     * @returns { String } Lower-cased, tab-separated values
+     * @returns {string} Lower-cased, tab-separated values
      */
      */
     getFilterCriteria () {
     getFilterCriteria () {
         const nick = this.get('nickname');
         const nick = this.get('nickname');
@@ -96,8 +96,8 @@ class RosterContact extends Model {
 
 
     /**
     /**
      * Send a presence subscription request to this roster contact
      * Send a presence subscription request to this roster contact
-     * @method _converse.RosterContacts#subscribe
-     * @param { String } message - An optional message to explain the
+     * @method RosterContacts#subscribe
+     * @param {string} message - An optional message to explain the
      *      reason for the subscription request.
      *      reason for the subscription request.
      */
      */
     subscribe (message) {
     subscribe (message) {
@@ -111,7 +111,7 @@ class RosterContact extends Model {
      * the user SHOULD acknowledge receipt of that subscription
      * the user SHOULD acknowledge receipt of that subscription
      * state notification by sending a presence stanza of type
      * state notification by sending a presence stanza of type
      * "subscribe" to the contact
      * "subscribe" to the contact
-     * @method _converse.RosterContacts#ackSubscribe
+     * @method RosterContacts#ackSubscribe
      */
      */
     ackSubscribe () {
     ackSubscribe () {
         api.send($pres({
         api.send($pres({
@@ -126,7 +126,7 @@ class RosterContact extends Model {
      * notification by sending a presence stanza of type "unsubscribe"
      * notification by sending a presence stanza of type "unsubscribe"
      * this step lets the user's server know that it MUST no longer
      * this step lets the user's server know that it MUST no longer
      * send notification of the subscription state change to the user.
      * send notification of the subscription state change to the user.
-     * @method _converse.RosterContacts#ackUnsubscribe
+     * @method RosterContacts#ackUnsubscribe
      */
      */
     ackUnsubscribe () {
     ackUnsubscribe () {
         api.send($pres({'type': 'unsubscribe', 'to': this.get('jid')}));
         api.send($pres({'type': 'unsubscribe', 'to': this.get('jid')}));
@@ -136,8 +136,8 @@ class RosterContact extends Model {
 
 
     /**
     /**
      * Unauthorize this contact's presence subscription
      * Unauthorize this contact's presence subscription
-     * @method _converse.RosterContacts#unauthorize
-     * @param { String } message - Optional message to send to the person being unauthorized
+     * @method RosterContacts#unauthorize
+     * @param {string} message - Optional message to send to the person being unauthorized
      */
      */
     unauthorize (message) {
     unauthorize (message) {
         rejectPresenceSubscription(this.get('jid'), message);
         rejectPresenceSubscription(this.get('jid'), message);
@@ -146,8 +146,8 @@ class RosterContact extends Model {
 
 
     /**
     /**
      * Authorize presence subscription
      * Authorize presence subscription
-     * @method _converse.RosterContacts#authorize
-     * @param { String } message - Optional message to send to the person being authorized
+     * @method RosterContacts#authorize
+     * @param {string} message - Optional message to send to the person being authorized
      */
      */
     authorize (message) {
     authorize (message) {
         const pres = $pres({'to': this.get('jid'), 'type': "subscribed"});
         const pres = $pres({'to': this.get('jid'), 'type': "subscribed"});
@@ -160,8 +160,8 @@ class RosterContact extends Model {
 
 
     /**
     /**
      * Instruct the XMPP server to remove this contact from our roster
      * Instruct the XMPP server to remove this contact from our roster
-     * @method _converse.RosterContacts#removeFromRoster
-     * @returns { Promise }
+     * @method RosterContacts#removeFromRoster
+     * @returns {Promise}
      */
      */
     removeFromRoster () {
     removeFromRoster () {
         const iq = $iq({type: 'set'})
         const iq = $iq({type: 'set'})

+ 18 - 9
src/headless/plugins/roster/utils.js

@@ -1,3 +1,6 @@
+/**
+ * @typedef {import('./contacts').default} RosterContacts
+ */
 import _converse from '../../shared/_converse.js';
 import _converse from '../../shared/_converse.js';
 import api, { converse } from '../../shared/api/index.js';
 import api, { converse } from '../../shared/api/index.js';
 import log from "../../log.js";
 import log from "../../log.js";
@@ -35,7 +38,7 @@ function initRoster () {
     initStorage(roster.data, id);
     initStorage(roster.data, id);
     roster.data.fetch();
     roster.data.fetch();
     /**
     /**
-     * Triggered once the `_converse.RosterContacts`
+     * Triggered once the `RosterContacts`
      * been created, but not yet populated with data.
      * been created, but not yet populated with data.
      * This event is useful when you want to create views for these collections.
      * This event is useful when you want to create views for these collections.
      * @event _converse#chatBoxMaximized
      * @event _converse#chatBoxMaximized
@@ -58,8 +61,9 @@ async function populateRoster (ignore_cache=false) {
     if (ignore_cache) {
     if (ignore_cache) {
         connection.send_initial_presence = true;
         connection.send_initial_presence = true;
     }
     }
+    const roster = /** @type {RosterContacts} */(_converse.state.roster);
     try {
     try {
-        await _converse.state.roster.fetchRosterContacts();
+        await roster.fetchRosterContacts();
         api.trigger('rosterContactsFetched');
         api.trigger('rosterContactsFetched');
     } catch (reason) {
     } catch (reason) {
         log.error(reason);
         log.error(reason);
@@ -70,7 +74,8 @@ async function populateRoster (ignore_cache=false) {
 
 
 
 
 function updateUnreadCounter (chatbox) {
 function updateUnreadCounter (chatbox) {
-    const contact = _converse.state.roster?.get(chatbox.get('jid'));
+    const roster = /** @type {RosterContacts} */(_converse.state.roster);
+    const contact = roster?.get(chatbox.get('jid'));
     contact?.save({'num_unread': chatbox.get('num_unread')});
     contact?.save({'num_unread': chatbox.get('num_unread')});
 }
 }
 
 
@@ -80,7 +85,8 @@ function registerPresenceHandler () {
     unregisterPresenceHandler();
     unregisterPresenceHandler();
     const connection = api.connection.get();
     const connection = api.connection.get();
     presence_ref = connection.addHandler(presence => {
     presence_ref = connection.addHandler(presence => {
-            _converse.state.roster.presenceHandler(presence);
+            const roster = /** @type {RosterContacts} */(_converse.state.roster);
+            roster.presenceHandler(presence);
             return true;
             return true;
         }, null, 'presence', null);
         }, null, 'presence', null);
 }
 }
@@ -104,7 +110,7 @@ async function clearPresences () {
 export async function onClearSession () {
 export async function onClearSession () {
     await clearPresences();
     await clearPresences();
     if (shouldClearCache()) {
     if (shouldClearCache()) {
-        const { roster } = _converse.state;
+        const roster = /** @type {RosterContacts} */(_converse.state.roster);
         if (roster) {
         if (roster) {
             roster.data?.destroy();
             roster.data?.destroy();
             await roster.clearStore();
             await roster.clearStore();
@@ -132,7 +138,8 @@ export function onPresencesInitialized (reconnecting) {
     } else {
     } else {
         initRoster();
         initRoster();
     }
     }
-    _converse.state.roster.onConnected();
+    const roster = /** @type {RosterContacts} */(_converse.state.roster);
+    roster.onConnected();
     registerPresenceHandler();
     registerPresenceHandler();
     populateRoster(!api.connection.get().restored);
     populateRoster(!api.connection.get().restored);
 }
 }
@@ -193,7 +200,8 @@ export function onChatBoxesInitialized () {
  * Roster specific handler for the rosterContactsFetched promise
  * Roster specific handler for the rosterContactsFetched promise
  */
  */
 export function onRosterContactsFetched () {
 export function onRosterContactsFetched () {
-    _converse.state.roster.on('add', contact => {
+    const roster = /** @type {RosterContacts} */(_converse.state.roster);
+    roster.on('add', contact => {
         // When a new contact is added, check if we already have a
         // When a new contact is added, check if we already have a
         // chatbox open for it, and if so attach it to the chatbox.
         // chatbox open for it, and if so attach it to the chatbox.
         const chatbox = _converse.state.chatboxes.findWhere({ 'jid': contact.get('jid') });
         const chatbox = _converse.state.chatboxes.findWhere({ 'jid': contact.get('jid') });
@@ -259,13 +267,14 @@ export function groupsComparator (a, b) {
 }
 }
 
 
 export function getGroupsAutoCompleteList () {
 export function getGroupsAutoCompleteList () {
-    const { roster } = _converse.state;
+    const roster = /** @type {RosterContacts} */(_converse.state.roster);
     const groups = roster.reduce((groups, contact) => groups.concat(contact.get('groups')), []);
     const groups = roster.reduce((groups, contact) => groups.concat(contact.get('groups')), []);
     return [...new Set(groups.filter(i => i))];
     return [...new Set(groups.filter(i => i))];
 }
 }
 
 
 export function getJIDsAutoCompleteList () {
 export function getJIDsAutoCompleteList () {
-    return [...new Set(_converse.state.roster.map(item => Strophe.getDomainFromJid(item.get('jid'))))];
+    const roster = /** @type {RosterContacts} */(_converse.state.roster);
+    return [...new Set(roster.map(item => Strophe.getDomainFromJid(item.get('jid'))))];
 }
 }
 
 
 
 

+ 8 - 3
src/headless/plugins/status/status.js

@@ -7,6 +7,11 @@ const { Strophe, $pres } = converse.env;
 
 
 export default class XMPPStatus extends Model {
 export default class XMPPStatus extends Model {
 
 
+  constructor(attributes, options) {
+        super(attributes, options);
+        this.vcard = null;
+    }
+
     defaults () {
     defaults () {
         return { "status":  api.settings.get("default_state") }
         return { "status":  api.settings.get("default_state") }
     }
     }
@@ -35,9 +40,9 @@ export default class XMPPStatus extends Model {
     }
     }
 
 
     /** Constructs a presence stanza
     /** Constructs a presence stanza
-     * @param { string } [type]
-     * @param { string } [to] - The JID to which this presence should be sent
-     * @param { string } [status_message]
+     * @param {string} [type]
+     * @param {string} [to] - The JID to which this presence should be sent
+     * @param {string} [status_message]
      */
      */
     async constructPresence (type, to=null, status_message) {
     async constructPresence (type, to=null, status_message) {
         type = typeof type === 'string' ? type : (this.get('status') || api.settings.get("default_state"));
         type = typeof type === 'string' ? type : (this.get('status') || api.settings.get("default_state"));

+ 15 - 0
src/headless/plugins/vcard/utils.js

@@ -1,6 +1,9 @@
 /**
 /**
  * @typedef {import('../muc/occupant.js').default} ChatRoomOccupant
  * @typedef {import('../muc/occupant.js').default} ChatRoomOccupant
  * @typedef {import('../chat/model-with-contact.js').default} ModelWithContact
  * @typedef {import('../chat/model-with-contact.js').default} ModelWithContact
+ * @typedef {import('../../plugins/status/status').default} XMPPStatus
+ * @typedef {import('../../plugins/vcard/vcards').default} VCards
+ * @typedef {import('../../plugins/muc/message').default} MUCMessage
  */
  */
 import _converse from '../../shared/_converse.js';
 import _converse from '../../shared/_converse.js';
 import api, { converse } from '../../shared/api/index.js';
 import api, { converse } from '../../shared/api/index.js';
@@ -111,6 +114,9 @@ function getVCardForOccupant (occupant) {
     }
     }
 }
 }
 
 
+/**
+ * @param {ChatRoomOccupant} occupant
+ */
 export async function setVCardOnOccupant (occupant) {
 export async function setVCardOnOccupant (occupant) {
     await api.waitUntil('VCardsInitialized');
     await api.waitUntil('VCardsInitialized');
     occupant.vcard = getVCardForOccupant(occupant);
     occupant.vcard = getVCardForOccupant(occupant);
@@ -121,6 +127,9 @@ export async function setVCardOnOccupant (occupant) {
 }
 }
 
 
 
 
+/**
+ * @param {MUCMessage} message
+ */
 function getVCardForMUCMessage (message) {
 function getVCardForMUCMessage (message) {
     const { vcards, xmppstatus } = _converse.state;
     const { vcards, xmppstatus } = _converse.state;
     const muc = message?.collection?.chatbox;
     const muc = message?.collection?.chatbox;
@@ -139,6 +148,9 @@ function getVCardForMUCMessage (message) {
     }
     }
 }
 }
 
 
+/**
+ * @param {MUCMessage} message
+ */
 export async function setVCardOnMUCMessage (message) {
 export async function setVCardOnMUCMessage (message) {
     if (['error', 'info'].includes(message.get('type'))) {
     if (['error', 'info'].includes(message.get('type'))) {
         return;
         return;
@@ -192,6 +204,9 @@ export function clearVCardsSession () {
     }
     }
 }
 }
 
 
+/**
+ * @param {string} jid
+ */
 export async function getVCard (jid) {
 export async function getVCard (jid) {
     const bare_jid = _converse.session.get('bare_jid');
     const bare_jid = _converse.session.get('bare_jid');
     const to = Strophe.getBareJidFromJid(jid) === bare_jid ? null : jid;
     const to = Strophe.getBareJidFromJid(jid) === bare_jid ? null : jid;

+ 16 - 8
src/headless/shared/_converse.js

@@ -1,6 +1,10 @@
 /**
 /**
  * @module:shared.converse
  * @module:shared.converse
- * @typedef {import('@converse/skeletor/src/storage.js').Storage} Storage
+ * @typedef {import('@converse/skeletor/src/storage').Storage} Storage
+ * @typedef {import('@converse/skeletor').Collection} Collection
+ * @typedef {import('../plugins/disco/index').DiscoState} DiscoState
+ * @typedef {import('../plugins/status/status').default} XMPPStatus
+ * @typedef {import('../plugins/vcard/vcard').default} VCards
  */
  */
 import log from '../log.js';
 import log from '../log.js';
 import i18n from './i18n.js';
 import i18n from './i18n.js';
@@ -108,12 +112,11 @@ class ConversePrivateGlobal extends EventEmitter(Object) {
 
 
         /**
         /**
          * Namespace for storing translated strings.
          * Namespace for storing translated strings.
+         *
+         * @typedef {Record<string, string>} UserMessage
+         * @typedef {Record<string, string|UserMessage>} UserMessages
          */
          */
-        this.labels =
-            /**
-             * @typedef {Record<string, string>} UserMessage
-             * @typedef {Record<string, string|UserMessage>} UserMessage
-             * @type {UserMessages} */{};
+        this.labels = /** @type {UserMessages} */({});
 
 
         /**
         /**
          * Namespace for storing code that might be useful to 3rd party
          * Namespace for storing code that might be useful to 3rd party
@@ -121,13 +124,18 @@ class ConversePrivateGlobal extends EventEmitter(Object) {
          * access to code (e.g. classes) from converse.js without having to add
          * access to code (e.g. classes) from converse.js without having to add
          * converse.js as a dependency.
          * converse.js as a dependency.
          */
          */
-        this.exports = /** @type {Record<string, Object>} */{};
+        this.exports = /** @type {Record<string, Object>} */({});
 
 
         /**
         /**
          * Namespace for storing the state, as represented by instances of
          * Namespace for storing the state, as represented by instances of
          * Models and Collections.
          * Models and Collections.
+         *
+         * @typedef {Object & Record<string, Collection|Model|VCards|XMPPStatus|DiscoState>} ConverseState
+         * @property {VCards} [vcards]
+         * @property {XMPPStatus} xmppstatus
+         * @property {DiscoState} disco
          */
          */
-        this.state = /** @type {Record<string, Model|Collection>} */{};
+        this.state = /** @type {ConverseState} */({});
 
 
         this.initSession();
         this.initSession();
     }
     }

+ 6 - 5
src/headless/shared/api/presence.js

@@ -1,5 +1,6 @@
 /**
 /**
  * @typedef {import('strophe.js/src/builder.js').Builder} Strophe.Builder
  * @typedef {import('strophe.js/src/builder.js').Builder} Strophe.Builder
+ * @typedef {import('../../plugins/status/status').default} XMPPStatus
  */
  */
 import _converse from '../_converse.js';
 import _converse from '../_converse.js';
 import api from '../../shared/api/index.js';
 import api from '../../shared/api/index.js';
@@ -13,10 +14,10 @@ export default {
         /**
         /**
          * Send out a presence stanza
          * Send out a presence stanza
          * @method _converse.api.user.presence.send
          * @method _converse.api.user.presence.send
-         * @param { String } [type]
-         * @param { String } [to]
-         * @param { String } [status] - An optional status message
-         * @param { Array<Element>|Array<Strophe.Builder>|Element|Strophe.Builder } [child_nodes]
+         * @param {String} [type]
+         * @param {String} [to]
+         * @param {String} [status] - An optional status message
+         * @param {Array<Element>|Array<Strophe.Builder>|Element|Strophe.Builder} [child_nodes]
          *  Nodes(s) to be added as child nodes of the `presence` XML element.
          *  Nodes(s) to be added as child nodes of the `presence` XML element.
          */
          */
         async send (type, to, status, child_nodes) {
         async send (type, to, status, child_nodes) {
@@ -24,7 +25,7 @@ export default {
             if (child_nodes && !Array.isArray(child_nodes)) {
             if (child_nodes && !Array.isArray(child_nodes)) {
                 child_nodes = [child_nodes];
                 child_nodes = [child_nodes];
             }
             }
-            const model = _converse.state.xmppstatus
+            const model = /** @type {XMPPStatus} */(_converse.state.xmppstatus);
             const presence = await model.constructPresence(type, to, status);
             const presence = await model.constructPresence(type, to, status);
             child_nodes?.map(c => c?.tree() ?? c).forEach(c => presence.cnode(c).up());
             child_nodes?.map(c => c?.tree() ?? c).forEach(c => presence.cnode(c).up());
             api.send(presence);
             api.send(presence);

+ 1 - 1
src/headless/shared/connection/feedback.js

@@ -5,7 +5,7 @@ import { Strophe } from 'strophe.js';
 
 
 class Feedback extends Model {
 class Feedback extends Model {
 
 
-    defaults () { // eslint-disable-line class-methods-use-this
+    defaults () {
         return {
         return {
             'connection_status': Strophe.Status.DISCONNECTED,
             'connection_status': Strophe.Status.DISCONNECTED,
             'message': '',
             'message': '',

+ 3 - 1
src/headless/shared/i18n.js

@@ -3,7 +3,7 @@ import { sprintf } from 'sprintf-js';
 /**
 /**
  * @namespace i18n
  * @namespace i18n
  */
  */
-export default {
+const i18nStub = {
     // eslint-disable-next-line @typescript-eslint/no-empty-function
     // eslint-disable-next-line @typescript-eslint/no-empty-function
     initialize () {},
     initialize () {},
 
 
@@ -24,3 +24,5 @@ export default {
         return sprintf(...args);
         return sprintf(...args);
     }
     }
 };
 };
+
+export default i18nStub;

+ 12 - 12
src/headless/shared/parsers.js

@@ -196,14 +196,14 @@ export function getMediaURLsMetadata (text, offset=0) {
     }
     }
 
 
     /**
     /**
-     * @typedef { Object } MediaURLMetadata
+     * @typedef {Object} MediaURLMetadata
      * An object representing the metadata of a URL found in a chat message
      * An object representing the metadata of a URL found in a chat message
      * The actual URL is not saved, it can be extracted via the `start` and `end` indexes.
      * The actual URL is not saved, it can be extracted via the `start` and `end` indexes.
-     * @property { Boolean } is_audio
-     * @property { Boolean } is_image
-     * @property { Boolean } is_video
-     * @property { String } end
-     * @property { String } start
+     * @property {Boolean} is_audio
+     * @property {Boolean} is_image
+     * @property {Boolean} is_video
+     * @property {String} end
+     * @property {String} start
      */
      */
     const media_urls = objs
     const media_urls = objs
         .map(o => ({
         .map(o => ({
@@ -273,13 +273,13 @@ export function getReferences (stanza) {
         const begin = ref.getAttribute('begin');
         const begin = ref.getAttribute('begin');
         const end = ref.getAttribute('end');
         const end = ref.getAttribute('end');
         /**
         /**
-         * @typedef { Object } Reference
+         * @typedef {Object} Reference
          * An object representing XEP-0372 reference data
          * An object representing XEP-0372 reference data
-         * @property { string } begin
-         * @property { string } end
-         * @property { string } type
-         * @property { String } value
-         * @property { String } uri
+         * @property {string} begin
+         * @property {string} end
+         * @property {string} type
+         * @property {String} value
+         * @property {String} uri
          */
          */
         return {
         return {
             begin, end,
             begin, end,

+ 12 - 3
src/headless/types/plugins/disco/api.d.ts

@@ -35,7 +35,7 @@ declare namespace _default {
                  * @method api.disco.identities.get
                  * @method api.disco.identities.get
                  * @example const identities = api.disco.own.identities.get();
                  * @example const identities = api.disco.own.identities.get();
                  */
                  */
-                function get(): any;
+                function get(): any[];
             }
             }
             namespace features {
             namespace features {
                 /**
                 /**
@@ -56,7 +56,7 @@ declare namespace _default {
                  * @method api.disco.own.features.get
                  * @method api.disco.own.features.get
                  * @example const features = api.disco.own.features.get();
                  * @example const features = api.disco.own.features.get();
                  */
                  */
-                function get(): any;
+                function get(): any[];
             }
             }
         }
         }
         /**
         /**
@@ -119,7 +119,13 @@ declare namespace _default {
                 name: string;
                 name: string;
             }, options?: {
             }, options?: {
                 ignore_cache?: boolean;
                 ignore_cache?: boolean;
-            }): any;
+            }): false | import("@converse/skeletor").Model | import("@converse/skeletor/src/types/collection.js").Attributes | (Promise<any> & {
+                isResolved: boolean;
+                isPending: boolean;
+                isRejected: boolean;
+                resolve: Function;
+                reject: Function;
+            });
         }
         }
         export namespace features_1 {
         export namespace features_1 {
             /**
             /**
@@ -249,4 +255,7 @@ declare namespace _default {
     }
     }
 }
 }
 export default _default;
 export default _default;
+export type DiscoState = import('./index').DiscoState;
+export type DiscoEntities = import('./entities').default;
+export type Collection = import('@converse/skeletor').Collection;
 //# sourceMappingURL=api.d.ts.map
 //# sourceMappingURL=api.d.ts.map

+ 4 - 1
src/headless/types/plugins/disco/index.d.ts

@@ -1,2 +1,5 @@
-export {};
+export type DiscoState = {
+    _identities: any[];
+    _features: any[];
+};
 //# sourceMappingURL=index.d.ts.map
 //# sourceMappingURL=index.d.ts.map

+ 1 - 0
src/headless/types/plugins/muc/muc.d.ts

@@ -5,6 +5,7 @@ export type NonOutcastAffiliation = import('./affiliations/utils.js').NonOutcast
 export type MemberListItem = any;
 export type MemberListItem = any;
 export type MessageAttributes = any;
 export type MessageAttributes = any;
 export type MUCMessageAttributes = any;
 export type MUCMessageAttributes = any;
+export type UserMessage = any;
 export namespace Strophe {
 export namespace Strophe {
     type Builder = any;
     type Builder = any;
 }
 }

+ 2 - 0
src/headless/types/plugins/muc/occupant.d.ts

@@ -6,6 +6,8 @@ export default ChatRoomOccupant;
  * @memberOf _converse
  * @memberOf _converse
  */
  */
 declare class ChatRoomOccupant extends Model {
 declare class ChatRoomOccupant extends Model {
+    constructor(attributes: any, options: any);
+    vcard: any;
     defaults(): {
     defaults(): {
         hats: any[];
         hats: any[];
         show: string;
         show: string;

+ 11 - 11
src/headless/types/plugins/roster/contact.d.ts

@@ -19,15 +19,15 @@ declare class RosterContact extends Model {
      *
      *
      * The goal is to be able to filter against the VCard fullname,
      * The goal is to be able to filter against the VCard fullname,
      * roster nickname and JID.
      * roster nickname and JID.
-     * @returns { String } Lower-cased, tab-separated values
+     * @returns {string} Lower-cased, tab-separated values
      */
      */
     getFilterCriteria(): string;
     getFilterCriteria(): string;
     getDisplayName(): any;
     getDisplayName(): any;
     getFullname(): any;
     getFullname(): any;
     /**
     /**
      * Send a presence subscription request to this roster contact
      * Send a presence subscription request to this roster contact
-     * @method _converse.RosterContacts#subscribe
-     * @param { String } message - An optional message to explain the
+     * @method RosterContacts#subscribe
+     * @param {string} message - An optional message to explain the
      *      reason for the subscription request.
      *      reason for the subscription request.
      */
      */
     subscribe(message: string): RosterContact;
     subscribe(message: string): RosterContact;
@@ -36,7 +36,7 @@ declare class RosterContact extends Model {
      * the user SHOULD acknowledge receipt of that subscription
      * the user SHOULD acknowledge receipt of that subscription
      * state notification by sending a presence stanza of type
      * state notification by sending a presence stanza of type
      * "subscribe" to the contact
      * "subscribe" to the contact
-     * @method _converse.RosterContacts#ackSubscribe
+     * @method RosterContacts#ackSubscribe
      */
      */
     ackSubscribe(): void;
     ackSubscribe(): void;
     /**
     /**
@@ -45,25 +45,25 @@ declare class RosterContact extends Model {
      * notification by sending a presence stanza of type "unsubscribe"
      * notification by sending a presence stanza of type "unsubscribe"
      * this step lets the user's server know that it MUST no longer
      * this step lets the user's server know that it MUST no longer
      * send notification of the subscription state change to the user.
      * send notification of the subscription state change to the user.
-     * @method _converse.RosterContacts#ackUnsubscribe
+     * @method RosterContacts#ackUnsubscribe
      */
      */
     ackUnsubscribe(): void;
     ackUnsubscribe(): void;
     /**
     /**
      * Unauthorize this contact's presence subscription
      * Unauthorize this contact's presence subscription
-     * @method _converse.RosterContacts#unauthorize
-     * @param { String } message - Optional message to send to the person being unauthorized
+     * @method RosterContacts#unauthorize
+     * @param {string} message - Optional message to send to the person being unauthorized
      */
      */
     unauthorize(message: string): RosterContact;
     unauthorize(message: string): RosterContact;
     /**
     /**
      * Authorize presence subscription
      * Authorize presence subscription
-     * @method _converse.RosterContacts#authorize
-     * @param { String } message - Optional message to send to the person being authorized
+     * @method RosterContacts#authorize
+     * @param {string} message - Optional message to send to the person being authorized
      */
      */
     authorize(message: string): RosterContact;
     authorize(message: string): RosterContact;
     /**
     /**
      * Instruct the XMPP server to remove this contact from our roster
      * Instruct the XMPP server to remove this contact from our roster
-     * @method _converse.RosterContacts#removeFromRoster
-     * @returns { Promise }
+     * @method RosterContacts#removeFromRoster
+     * @returns {Promise}
      */
      */
     removeFromRoster(): Promise<any>;
     removeFromRoster(): Promise<any>;
 }
 }

+ 1 - 0
src/headless/types/plugins/roster/utils.d.ts

@@ -39,4 +39,5 @@ export function getNamesAutoCompleteList(query: string): Promise<{
     label: any;
     label: any;
     value: any;
     value: any;
 }[]>;
 }[]>;
+export type RosterContacts = import('./contacts').default;
 //# sourceMappingURL=utils.d.ts.map
 //# sourceMappingURL=utils.d.ts.map

+ 5 - 3
src/headless/types/plugins/status/status.d.ts

@@ -1,4 +1,6 @@
 export default class XMPPStatus extends Model {
 export default class XMPPStatus extends Model {
+    constructor(attributes: any, options: any);
+    vcard: any;
     defaults(): {
     defaults(): {
         status: any;
         status: any;
     };
     };
@@ -6,9 +8,9 @@ export default class XMPPStatus extends Model {
     getNickname(): any;
     getNickname(): any;
     getFullname(): string;
     getFullname(): string;
     /** Constructs a presence stanza
     /** Constructs a presence stanza
-     * @param { string } [type]
-     * @param { string } [to] - The JID to which this presence should be sent
-     * @param { string } [status_message]
+     * @param {string} [type]
+     * @param {string} [to] - The JID to which this presence should be sent
+     * @param {string} [status_message]
      */
      */
     constructPresence(type?: string, to?: string, status_message?: string): Promise<any>;
     constructPresence(type?: string, to?: string, status_message?: string): Promise<any>;
 }
 }

+ 16 - 4
src/headless/types/plugins/vcard/utils.d.ts

@@ -7,17 +7,29 @@ export function onOccupantAvatarChanged(occupant: ChatRoomOccupant): void;
  * @param {ModelWithContact} model
  * @param {ModelWithContact} model
  */
  */
 export function setVCardOnModel(model: ModelWithContact): Promise<void>;
 export function setVCardOnModel(model: ModelWithContact): Promise<void>;
-export function setVCardOnOccupant(occupant: any): Promise<void>;
-export function setVCardOnMUCMessage(message: any): Promise<void>;
+/**
+ * @param {ChatRoomOccupant} occupant
+ */
+export function setVCardOnOccupant(occupant: ChatRoomOccupant): Promise<void>;
+/**
+ * @param {MUCMessage} message
+ */
+export function setVCardOnMUCMessage(message: MUCMessage): Promise<void>;
 export function initVCardCollection(): Promise<void>;
 export function initVCardCollection(): Promise<void>;
 export function clearVCardsSession(): void;
 export function clearVCardsSession(): void;
-export function getVCard(jid: any): Promise<{
+/**
+ * @param {string} jid
+ */
+export function getVCard(jid: string): Promise<{
     image_hash: any;
     image_hash: any;
 } | {
 } | {
-    jid: any;
+    jid: string;
     stanza: any;
     stanza: any;
     vcard_error: string;
     vcard_error: string;
 }>;
 }>;
 export type ChatRoomOccupant = import('../muc/occupant.js').default;
 export type ChatRoomOccupant = import('../muc/occupant.js').default;
 export type ModelWithContact = import('../chat/model-with-contact.js').default;
 export type ModelWithContact = import('../chat/model-with-contact.js').default;
+export type XMPPStatus = import('../../plugins/status/status').default;
+export type VCards = import('../../plugins/vcard/vcards').default;
+export type MUCMessage = import('../../plugins/muc/message').default;
 //# sourceMappingURL=utils.d.ts.map
 //# sourceMappingURL=utils.d.ts.map

+ 15 - 3
src/headless/types/shared/_converse.d.ts

@@ -1,5 +1,9 @@
 export default _converse;
 export default _converse;
 export type Storage = any;
 export type Storage = any;
+export type Collection = import('@converse/skeletor').Collection;
+export type DiscoState = import('../plugins/disco/index').DiscoState;
+export type XMPPStatus = import('../plugins/status/status').default;
+export type VCards = import('../plugins/vcard/vcard').default;
 declare const _converse: ConversePrivateGlobal;
 declare const _converse: ConversePrivateGlobal;
 declare const ConversePrivateGlobal_base: (new (...args: any[]) => {
 declare const ConversePrivateGlobal_base: (new (...args: any[]) => {
     on(name: string, callback: (event: any, model: Model, collection: import("@converse/skeletor").Collection, options?: Record<string, any>) => any, context: any): any;
     on(name: string, callback: (event: any, model: Model, collection: import("@converse/skeletor").Collection, options?: Record<string, any>) => any, context: any): any;
@@ -41,20 +45,28 @@ declare class ConversePrivateGlobal extends ConversePrivateGlobal_base {
     api: any;
     api: any;
     /**
     /**
      * Namespace for storing translated strings.
      * Namespace for storing translated strings.
+     *
+     * @typedef {Record<string, string>} UserMessage
+     * @typedef {Record<string, string|UserMessage>} UserMessages
      */
      */
-    labels: {};
+    labels: Record<string, string | Record<string, string>>;
     /**
     /**
      * Namespace for storing code that might be useful to 3rd party
      * Namespace for storing code that might be useful to 3rd party
      * plugins. We want to make it possible for 3rd party plugins to have
      * plugins. We want to make it possible for 3rd party plugins to have
      * access to code (e.g. classes) from converse.js without having to add
      * access to code (e.g. classes) from converse.js without having to add
      * converse.js as a dependency.
      * converse.js as a dependency.
      */
      */
-    exports: {};
+    exports: Record<string, any>;
     /**
     /**
      * Namespace for storing the state, as represented by instances of
      * Namespace for storing the state, as represented by instances of
      * Models and Collections.
      * Models and Collections.
+     *
+     * @typedef {Object & Record<string, Collection|Model|VCards|XMPPStatus|DiscoState>} ConverseState
+     * @property {VCards} [vcards]
+     * @property {XMPPStatus} xmppstatus
+     * @property {DiscoState} disco
      */
      */
-    state: {};
+    state: any;
     initSession(): void;
     initSession(): void;
     session: Model;
     session: Model;
     /**
     /**

+ 3 - 3
src/headless/types/shared/api/index.d.ts

@@ -18,9 +18,9 @@ export type _converse = {
         INACTIVE: number;
         INACTIVE: number;
     };
     };
     api: any;
     api: any;
-    labels: {};
-    exports: {};
-    state: {};
+    labels: Record<string, string | Record<string, string>>;
+    exports: Record<string, any>;
+    state: any;
     initSession(): void;
     initSession(): void;
     session: import("@converse/skeletor").Model;
     session: import("@converse/skeletor").Model;
     __(...args: string[]): any;
     __(...args: string[]): any;

+ 5 - 4
src/headless/types/shared/api/presence.d.ts

@@ -3,10 +3,10 @@ declare namespace _default {
         /**
         /**
          * Send out a presence stanza
          * Send out a presence stanza
          * @method _converse.api.user.presence.send
          * @method _converse.api.user.presence.send
-         * @param { String } [type]
-         * @param { String } [to]
-         * @param { String } [status] - An optional status message
-         * @param { Array<Element>|Array<Strophe.Builder>|Element|Strophe.Builder } [child_nodes]
+         * @param {String} [type]
+         * @param {String} [to]
+         * @param {String} [status] - An optional status message
+         * @param {Array<Element>|Array<Strophe.Builder>|Element|Strophe.Builder} [child_nodes]
          *  Nodes(s) to be added as child nodes of the `presence` XML element.
          *  Nodes(s) to be added as child nodes of the `presence` XML element.
          */
          */
         function send(type?: string, to?: string, status?: string, child_nodes?: any): Promise<void>;
         function send(type?: string, to?: string, status?: string, child_nodes?: any): Promise<void>;
@@ -16,4 +16,5 @@ export default _default;
 export namespace Strophe {
 export namespace Strophe {
     type Builder = any;
     type Builder = any;
 }
 }
+export type XMPPStatus = import('../../plugins/status/status').default;
 //# sourceMappingURL=presence.d.ts.map
 //# sourceMappingURL=presence.d.ts.map

+ 2 - 2
src/headless/types/shared/i18n.d.ts

@@ -1,4 +1,5 @@
-declare namespace _default {
+export default i18nStub;
+declare namespace i18nStub {
     function initialize(): void;
     function initialize(): void;
     /**
     /**
      * Overridable string wrapper method which can be used to provide i18n
      * Overridable string wrapper method which can be used to provide i18n
@@ -15,5 +16,4 @@ declare namespace _default {
      */
      */
     function __(...args: any[]): any;
     function __(...args: any[]): any;
 }
 }
-export default _default;
 //# sourceMappingURL=i18n.d.ts.map
 //# sourceMappingURL=i18n.d.ts.map

+ 3 - 3
src/headless/types/utils/session.d.ts

@@ -27,9 +27,9 @@ export function tearDown(): Promise<{
         INACTIVE: number;
         INACTIVE: number;
     };
     };
     api: any;
     api: any;
-    labels: {};
-    exports: {};
-    state: {};
+    labels: Record<string, string | Record<string, string>>;
+    exports: Record<string, any>;
+    state: any;
     initSession(): void;
     initSession(): void;
     session: import("@converse/skeletor").Model;
     session: import("@converse/skeletor").Model;
     __(...args: string[]): any;
     __(...args: string[]): any;

+ 12 - 11
src/i18n/index.js

@@ -5,7 +5,7 @@
  * @description This is the internationalization module
  * @description This is the internationalization module
  */
  */
 import Jed from 'jed';
 import Jed from 'jed';
-import { api, converse, log, i18n } from '@converse/headless';
+import { api, converse, log } from '@converse/headless';
 import { isTestEnv } from '@converse/headless/utils/session';
 import { isTestEnv } from '@converse/headless/utils/session';
 
 
 const { dayjs } = converse.env;
 const { dayjs } = converse.env;
@@ -90,35 +90,36 @@ async function fetchTranslations () {
 /**
 /**
  * @namespace i18n
  * @namespace i18n
  */
  */
-Object.assign(i18n, {
-
-    getLocale () {
+const i18n = {
+    getLocale() {
         return locale;
         return locale;
     },
     },
 
 
     /**
     /**
      * @param {string} str - The string to be translated
      * @param {string} str - The string to be translated
+     * @param {Array<any>} args
      */
      */
-    translate (str) {
+    translate(str, args) {
         if (!jed_instance) {
         if (!jed_instance) {
             return Jed.sprintf.apply(Jed, arguments);
             return Jed.sprintf.apply(Jed, arguments);
         }
         }
         const t = jed_instance.translate(str);
         const t = jed_instance.translate(str);
         if (arguments.length > 1) {
         if (arguments.length > 1) {
-            return t.fetch.apply(t, [].slice.call(arguments, 1));
+            return t.fetch.apply(t, args);
         } else {
         } else {
             return t.fetch();
             return t.fetch();
         }
         }
     },
     },
 
 
-    async initialize () {
+    async initialize() {
         if (isTestEnv()) {
         if (isTestEnv()) {
             locale = 'en';
             locale = 'en';
         } else {
         } else {
             try {
             try {
                 const preferred_locale = api.settings.get('i18n');
                 const preferred_locale = api.settings.get('i18n');
                 const available_locales = api.settings.get('locales');
                 const available_locales = api.settings.get('locales');
-                const isSupportedByLibrary = /** @param {string} pref */(pref) => isConverseLocale(pref, available_locales);
+                const isSupportedByLibrary = /** @param {string} pref */ (pref) =>
+                    isConverseLocale(pref, available_locales);
                 locale = determineLocale(preferred_locale, isSupportedByLibrary);
                 locale = determineLocale(preferred_locale, isSupportedByLibrary);
                 jed_instance = await fetchTranslations();
                 jed_instance = await fetchTranslations();
             } catch (e) {
             } catch (e) {
@@ -128,10 +129,10 @@ Object.assign(i18n, {
         }
         }
     },
     },
 
 
-    __ (...args) {
-        return i18n.translate(...args);
+    __(str, ...args) {
+        return i18n.translate(str, args);
     },
     },
-});
+};
 
 
 export { i18n };
 export { i18n };
 export const __ = i18n.__;
 export const __ = i18n.__;

+ 4 - 3
src/plugins/muc-views/bottom-panel.js

@@ -1,7 +1,7 @@
 import 'shared/autocomplete/index.js';
 import 'shared/autocomplete/index.js';
 import BottomPanel from 'plugins/chatview/bottom-panel.js';
 import BottomPanel from 'plugins/chatview/bottom-panel.js';
 import tplMUCBottomPanel from './templates/muc-bottom-panel.js';
 import tplMUCBottomPanel from './templates/muc-bottom-panel.js';
-import { _converse, api, converse } from "@converse/headless";
+import { _converse, api } from "@converse/headless";
 
 
 import './styles/muc-bottom-panel.scss';
 import './styles/muc-bottom-panel.scss';
 
 
@@ -21,9 +21,10 @@ export default class MUCBottomPanel extends BottomPanel {
     render () {
     render () {
         if (!this.model) return '';
         if (!this.model) return '';
         const entered = this.model.isEntered();
         const entered = this.model.isEntered();
-        const can_edit = this.model.canPostMessages();
+        const can_post = this.model.canPostMessages();
         return tplMUCBottomPanel({
         return tplMUCBottomPanel({
-            can_edit, entered,
+            can_post,
+            entered,
             'model': this.model,
             'model': this.model,
             'is_groupchat': true,
             'is_groupchat': true,
             'viewUnreadMessages': ev => this.viewUnreadMessages(ev)
             'viewUnreadMessages': ev => this.viewUnreadMessages(ev)

+ 36 - 22
src/plugins/muc-views/modals/templates/add-muc.js

@@ -1,26 +1,27 @@
 import DOMPurify from 'dompurify';
 import DOMPurify from 'dompurify';
 import { __ } from 'i18n';
 import { __ } from 'i18n';
 import { api } from '@converse/headless';
 import { api } from '@converse/headless';
-import { html } from "lit";
-import { unsafeHTML } from "lit/directives/unsafe-html.js";
-import { getAutoCompleteList } from "../../search.js";
-
+import { html } from 'lit';
+import { unsafeHTML } from 'lit/directives/unsafe-html.js';
+import { getAutoCompleteList } from '../../search.js';
 
 
 const nickname_input = (el) => {
 const nickname_input = (el) => {
     const i18n_nickname = __('Nickname');
     const i18n_nickname = __('Nickname');
     const i18n_required_field = __('This field is required');
     const i18n_required_field = __('This field is required');
-        return html`
-            <div class="form-group" >
-                <label for="nickname">${i18n_nickname}:</label>
-                <input type="text"
-                    title="${i18n_required_field}"
-                    required="required"
-                    name="nickname"
-                    value="${el.model.get('nick') || ''}"
-                    class="form-control"/>
-            </div>
+    return html`
+        <div class="form-group">
+            <label for="nickname">${i18n_nickname}:</label>
+            <input
+                type="text"
+                title="${i18n_required_field}"
+                required="required"
+                name="nickname"
+                value="${el.model.get('nick') || ''}"
+                class="form-control"
+            />
+        </div>
     `;
     `;
-}
+};
 
 
 export default (el) => {
 export default (el) => {
     const i18n_join = __('Join');
     const i18n_join = __('Join');
@@ -31,14 +32,16 @@ export default (el) => {
         placeholder = muc_domain ? `name@${muc_domain}` : __('name@conference.example.org');
         placeholder = muc_domain ? `name@${muc_domain}` : __('name@conference.example.org');
     }
     }
 
 
-    const label_room_address = muc_domain ? __('Groupchat name') :  __('Groupchat address');
+    const label_room_address = muc_domain ? __('Groupchat name') : __('Groupchat address');
     const muc_roomid_policy_error_msg = el.muc_roomid_policy_error_msg;
     const muc_roomid_policy_error_msg = el.muc_roomid_policy_error_msg;
     const muc_roomid_policy_hint = api.settings.get('muc_roomid_policy_hint');
     const muc_roomid_policy_hint = api.settings.get('muc_roomid_policy_hint');
     return html`
     return html`
         <form class="converse-form add-chatroom" @submit=${(ev) => el.openChatRoom(ev)}>
         <form class="converse-form add-chatroom" @submit=${(ev) => el.openChatRoom(ev)}>
             <div class="form-group">
             <div class="form-group">
                 <label for="chatroom">${label_room_address}:</label>
                 <label for="chatroom">${label_room_address}:</label>
-                ${ (muc_roomid_policy_error_msg) ? html`<label class="roomid-policy-error">${muc_roomid_policy_error_msg}</label>` : '' }
+                ${muc_roomid_policy_error_msg
+                    ? html`<label class="roomid-policy-error">${muc_roomid_policy_error_msg}</label>`
+                    : ''}
                 <converse-autocomplete
                 <converse-autocomplete
                     .getAutoCompleteList=${getAutoCompleteList}
                     .getAutoCompleteList=${getAutoCompleteList}
                     ?autofocus=${true}
                     ?autofocus=${true}
@@ -46,12 +49,23 @@ export default (el) => {
                     position="below"
                     position="below"
                     placeholder="${placeholder}"
                     placeholder="${placeholder}"
                     class="add-muc-autocomplete"
                     class="add-muc-autocomplete"
-                    name="chatroom">
+                    name="chatroom"
+                >
                 </converse-autocomplete>
                 </converse-autocomplete>
             </div>
             </div>
-            ${ muc_roomid_policy_hint ?  html`<div class="form-group">${unsafeHTML(DOMPurify.sanitize(muc_roomid_policy_hint, {'ALLOWED_TAGS': ['b', 'br', 'em']}))}</div>` : '' }
-            ${ !api.settings.get('locked_muc_nickname') ? nickname_input(el) : '' }
-            <input type="submit" class="btn btn-primary" name="join" value="${i18n_join || ''}" ?disabled=${muc_roomid_policy_error_msg}/>
+            ${muc_roomid_policy_hint
+                ? html`<div class="form-group">
+                      ${unsafeHTML(DOMPurify.sanitize(muc_roomid_policy_hint, { 'ALLOWED_TAGS': ['b', 'br', 'em'] }))}
+                  </div>`
+                : ''}
+            ${!api.settings.get('locked_muc_nickname') ? nickname_input(el) : ''}
+            <input
+                type="submit"
+                class="btn btn-primary"
+                name="join"
+                value="${i18n_join || ''}"
+                ?disabled=${muc_roomid_policy_error_msg}
+            />
         </form>
         </form>
     `;
     `;
-}
+};

+ 27 - 25
src/plugins/muc-views/templates/muc-bottom-panel.js

@@ -2,9 +2,8 @@ import '../message-form.js';
 import '../nickname-form.js';
 import '../nickname-form.js';
 import 'shared/chat/toolbar.js';
 import 'shared/chat/toolbar.js';
 import { __ } from 'i18n';
 import { __ } from 'i18n';
-import { api, converse } from "@converse/headless";
-import { html } from "lit";
-
+import { api, converse } from '@converse/headless';
+import { html } from 'lit';
 
 
 const tplCanEdit = (o) => {
 const tplCanEdit = (o) => {
     const unread_msgs = __('You have unread messages');
     const unread_msgs = __('You have unread messages');
@@ -14,34 +13,37 @@ const tplCanEdit = (o) => {
     const show_send_button = api.settings.get('show_send_button');
     const show_send_button = api.settings.get('show_send_button');
     const show_spoiler_button = api.settings.get('visible_toolbar_buttons').spoiler;
     const show_spoiler_button = api.settings.get('visible_toolbar_buttons').spoiler;
     const show_toolbar = api.settings.get('show_toolbar');
     const show_toolbar = api.settings.get('show_toolbar');
-    return html`
-        ${ (o.model.ui.get('scrolled') && o.model.get('num_unread')) ?
-                html`<div class="new-msgs-indicator" @click=${ev => o.viewUnreadMessages(ev)}>▼ ${ unread_msgs } ▼</div>` : '' }
-        ${show_toolbar ? html`
-            <converse-chat-toolbar
-                class="chat-toolbar no-text-select"
-                .model=${o.model}
-                ?hidden_occupants="${o.model.get('hidden_occupants')}"
-                ?is_groupchat="${o.is_groupchat}"
-                ?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` ${o.model.ui.get('scrolled') && o.model.get('num_unread')
+            ? html`<div class="new-msgs-indicator" @click=${(ev) => o.viewUnreadMessages(ev)}>▼ ${unread_msgs} ▼</div>`
+            : ''}
+        ${show_toolbar
+            ? html` <converse-chat-toolbar
+                  class="chat-toolbar no-text-select"
+                  .model=${o.model}
+                  ?hidden_occupants="${o.model.get('hidden_occupants')}"
+                  ?is_groupchat="${o.is_groupchat}"
+                  ?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>`
+            : ''}
         <converse-muc-message-form jid=${o.model.get('jid')}></converse-muc-message-form>`;
         <converse-muc-message-form jid=${o.model.get('jid')}></converse-muc-message-form>`;
-}
-
+};
 
 
 export default (o) => {
 export default (o) => {
     const unread_msgs = __('You have unread messages');
     const unread_msgs = __('You have unread messages');
     const conn_status = o.model.session.get('connection_status');
     const conn_status = o.model.session.get('connection_status');
     const i18n_not_allowed = __("You're not allowed to send messages in this room");
     const i18n_not_allowed = __("You're not allowed to send messages in this room");
     if (conn_status === converse.ROOMSTATUS.ENTERED) {
     if (conn_status === converse.ROOMSTATUS.ENTERED) {
-        return html`
-            ${ o.model.ui.get('scrolled') && o.model.get('num_unread_general') ?
-                    html`<div class="new-msgs-indicator" @click=${ev => o.viewUnreadMessages(ev)}>▼ ${ unread_msgs } ▼</div>` : '' }
-            ${(o.can_edit) ? tplCanEdit(o) : html`<span class="muc-bottom-panel muc-bottom-panel--muted">${i18n_not_allowed}</span>`}`;
+        return html` ${o.model.ui.get('scrolled') && o.model.get('num_unread_general')
+            ? html`<div class="new-msgs-indicator" @click=${(ev) => o.viewUnreadMessages(ev)}>▼ ${unread_msgs} ▼</div>`
+            : ''}
+        ${o.can_post
+            ? tplCanEdit(o)
+            : html`<span class="muc-bottom-panel muc-bottom-panel--muted">${i18n_not_allowed}</span>`}`;
     } else if (conn_status == converse.ROOMSTATUS.NICKNAME_REQUIRED) {
     } else if (conn_status == converse.ROOMSTATUS.NICKNAME_REQUIRED) {
         if (api.settings.get('muc_show_logs_before_join')) {
         if (api.settings.get('muc_show_logs_before_join')) {
             return html`<span class="muc-bottom-panel muc-bottom-panel--nickname">
             return html`<span class="muc-bottom-panel muc-bottom-panel--nickname">
@@ -51,4 +53,4 @@ export default (o) => {
     } else {
     } else {
         return '';
         return '';
     }
     }
-}
+};

+ 3 - 3
src/plugins/muc-views/templates/muc-sidebar.js

@@ -53,19 +53,19 @@ export default (el, o) => {
     if (el.model.occupants < 6) {
     if (el.model.occupants < 6) {
         // We don't show the filter
         // We don't show the filter
         btns.push(
         btns.push(
-            html` <i class="hide-occupants" @click=${(ev) => el.closeSidebar(ev)}>
+            html` <i class="hide-occupants" @click=${(/** @type {MouseEvent} */ev) => el.closeSidebar(ev)}>
                 <converse-icon class="fa fa-times" size="1em"></converse-icon>
                 <converse-icon class="fa fa-times" size="1em"></converse-icon>
             </i>`
             </i>`
         );
         );
     } else {
     } else {
         btns.push(html`
         btns.push(html`
-            <a href="#" class="dropdown-item" @click=${(ev) => el.closeSidebar(ev)}>
+            <a href="#" class="dropdown-item" @click=${(/** @type {MouseEvent} */ev) => el.closeSidebar(ev)}>
                 <converse-icon size="1em" class="fa fa-times"></converse-icon>
                 <converse-icon size="1em" class="fa fa-times"></converse-icon>
                 ${i18n_close}
                 ${i18n_close}
             </a>
             </a>
         `);
         `);
         btns.push(html`
         btns.push(html`
-            <a href="#" class="dropdown-item" @click=${(ev) => el.toggleFilter(ev)}>
+            <a href="#" class="dropdown-item" @click=${(/** @type {MouseEvent} */ev) => el.toggleFilter(ev)}>
                 <converse-icon size="1em" class="fa fa-filter"></converse-icon>
                 <converse-icon size="1em" class="fa fa-filter"></converse-icon>
                 ${is_filter_visible ? i18n_hide_filter : i18n_show_filter}
                 ${is_filter_visible ? i18n_hide_filter : i18n_show_filter}
             </a>
             </a>

+ 9 - 1
src/plugins/notifications/utils.js

@@ -2,6 +2,7 @@
  * @typedef {module:headless-plugins-muc-muc.MUCMessageAttributes} MUCMessageAttributes
  * @typedef {module:headless-plugins-muc-muc.MUCMessageAttributes} MUCMessageAttributes
  * @typedef {module:headless-plugins-muc-muc.MUCMessageData} MUCMessageData
  * @typedef {module:headless-plugins-muc-muc.MUCMessageData} MUCMessageData
  * @typedef {module:headless-plugins-chat-utils.MessageData} MessageData
  * @typedef {module:headless-plugins-chat-utils.MessageData} MessageData
+ * @typedef {import('@converse/headless/plugins/roster/contact').default} RosterContact
  */
  */
 import Favico from 'favico.js-slevomat';
 import Favico from 'favico.js-slevomat';
 import { __, i18n } from 'i18n';
 import { __, i18n } from 'i18n';
@@ -122,7 +123,6 @@ async function shouldNotifyOfInfoMessage (attrs) {
 }
 }
 
 
 /**
 /**
- * @private
  * @async
  * @async
  * @method shouldNotifyOfMessage
  * @method shouldNotifyOfMessage
  * @param {MessageData|MUCMessageData} data
  * @param {MessageData|MUCMessageData} data
@@ -164,6 +164,7 @@ export function showFeedbackNotification (data) {
 /**
 /**
  * Creates an HTML5 Notification to inform of a change in a
  * Creates an HTML5 Notification to inform of a change in a
  * contact's chat state.
  * contact's chat state.
+ * @param {RosterContact} contact
  */
  */
 function showChatStateNotification (contact) {
 function showChatStateNotification (contact) {
     if (api.settings.get('chatstate_notification_blacklist')?.includes(contact.jid)) {
     if (api.settings.get('chatstate_notification_blacklist')?.includes(contact.jid)) {
@@ -315,6 +316,7 @@ export function handleFeedback (data) {
 /**
 /**
  * Event handler for on('contactPresenceChanged').
  * Event handler for on('contactPresenceChanged').
  * Will show an HTML5 notification to indicate that the chat status has changed.
  * Will show an HTML5 notification to indicate that the chat status has changed.
+ * @param {RosterContact} contact
  */
  */
 export function handleChatStateNotification (contact) {
 export function handleChatStateNotification (contact) {
     if (areDesktopNotificationsEnabled() && api.settings.get('show_chat_state_notifications')) {
     if (areDesktopNotificationsEnabled() && api.settings.get('show_chat_state_notifications')) {
@@ -322,6 +324,9 @@ export function handleChatStateNotification (contact) {
     }
     }
 }
 }
 
 
+/**
+ * @param {RosterContact} contact
+ */
 function showContactRequestNotification (contact) {
 function showContactRequestNotification (contact) {
     const n = new Notification(contact.getDisplayName(), {
     const n = new Notification(contact.getDisplayName(), {
         body: __('wants to be your contact'),
         body: __('wants to be your contact'),
@@ -331,6 +336,9 @@ function showContactRequestNotification (contact) {
     setTimeout(() => n.close(), 5000);
     setTimeout(() => n.close(), 5000);
 }
 }
 
 
+/**
+ * @param {RosterContact} contact
+ */
 export function handleContactRequestNotification (contact) {
 export function handleContactRequestNotification (contact) {
     if (areDesktopNotificationsEnabled()) {
     if (areDesktopNotificationsEnabled()) {
         showContactRequestNotification(contact);
         showContactRequestNotification(contact);

+ 51 - 33
src/plugins/rosterview/templates/roster.js

@@ -1,14 +1,19 @@
-import tplGroup from "./group.js";
-import tplRosterFilter from "./roster_filter.js";
-import { CLOSED } from "@converse/headless/shared/constants.js";
+/**
+ * @typedef {import('../rosterview').default} RosterView
+ */
+import tplGroup from './group.js';
+import tplRosterFilter from './roster_filter.js';
+import { CLOSED } from '@converse/headless/shared/constants.js';
 import { __ } from 'i18n';
 import { __ } from 'i18n';
-import { _converse, api } from "@converse/headless";
+import { _converse, api } from '@converse/headless';
 import { contactsComparator, groupsComparator } from '@converse/headless/plugins/roster/utils.js';
 import { contactsComparator, groupsComparator } from '@converse/headless/plugins/roster/utils.js';
-import { html } from "lit";
+import { html } from 'lit';
 import { repeat } from 'lit/directives/repeat.js';
 import { repeat } from 'lit/directives/repeat.js';
 import { shouldShowContact, shouldShowGroup, populateContactsMap } from '../utils.js';
 import { shouldShowContact, shouldShowGroup, populateContactsMap } from '../utils.js';
 
 
-
+/**
+ * @param {RosterView} el
+ */
 export default (el) => {
 export default (el) => {
     const i18n_heading_contacts = __('Contacts');
     const i18n_heading_contacts = __('Contacts');
     const i18n_toggle_contacts = __('Click to toggle contacts');
     const i18n_toggle_contacts = __('Click to toggle contacts');
@@ -25,41 +30,54 @@ export default (el) => {
             <span class="w-100 controlbox-heading controlbox-heading--contacts">
             <span class="w-100 controlbox-heading controlbox-heading--contacts">
                 <a class="list-toggle open-contacts-toggle" title="${i18n_toggle_contacts}" @click=${el.toggleRoster}>
                 <a class="list-toggle open-contacts-toggle" title="${i18n_toggle_contacts}" @click=${el.toggleRoster}>
                     <converse-icon
                     <converse-icon
-                        class="fa ${ is_closed ? 'fa-caret-right' : 'fa-caret-down' }"
+                        class="fa ${is_closed ? 'fa-caret-right' : 'fa-caret-down'}"
                         size="1em"
                         size="1em"
-                        color="var(--chat-color)"></converse-icon>
+                        color="var(--chat-color)"
+                    ></converse-icon>
                     ${i18n_heading_contacts}
                     ${i18n_heading_contacts}
                 </a>
                 </a>
             </span>
             </span>
-            <a class="controlbox-heading__btn sync-contacts"
-               @click=${ev => el.syncContacts(ev)}
-               title="${i18n_title_sync_contacts}">
-
-                <converse-icon class="fa fa-sync right ${el.syncing_contacts ? 'fa-spin' : ''}" size="1em"></converse-icon>
+            <a
+                class="controlbox-heading__btn sync-contacts"
+                @click=${(ev) => el.syncContacts(ev)}
+                title="${i18n_title_sync_contacts}"
+            >
+                <converse-icon
+                    class="fa fa-sync right ${el.syncing_contacts ? 'fa-spin' : ''}"
+                    size="1em"
+                ></converse-icon>
             </a>
             </a>
-            ${ api.settings.get('allow_contact_requests') ? html`
-                <a class="controlbox-heading__btn add-contact"
-                    @click=${ev => el.showAddContactModal(ev)}
-                    title="${i18n_title_add_contact}"
-                    data-toggle="modal"
-                    data-target="#add-contact-modal">
-                    <converse-icon class="fa fa-user-plus right" size="1.25em"></converse-icon>
-                </a>` : '' }
+            ${api.settings.get('allow_contact_requests')
+                ? html` <a
+                      class="controlbox-heading__btn add-contact"
+                      @click=${(ev) => el.showAddContactModal(ev)}
+                      title="${i18n_title_add_contact}"
+                      data-toggle="modal"
+                      data-target="#add-contact-modal"
+                  >
+                      <converse-icon class="fa fa-user-plus right" size="1.25em"></converse-icon>
+                  </a>`
+                : ''}
         </div>
         </div>
 
 
-        <div class="list-container roster-contacts ${ is_closed ? 'hidden' : '' }">
+        <div class="list-container roster-contacts ${is_closed ? 'hidden' : ''}">
             <converse-list-filter
             <converse-list-filter
-                    @update=${() => el.requestUpdate()}
-                    .promise=${api.waitUntil('rosterInitialized')}
-                    .items=${_converse.state.roster}
-                    .template=${tplRosterFilter}
-                    .model=${_converse.state.roster_filter}></converse-list-filter>
+                @update=${() => el.requestUpdate()}
+                .promise=${api.waitUntil('rosterInitialized')}
+                .items=${_converse.state.roster}
+                .template=${tplRosterFilter}
+                .model=${_converse.state.roster_filter}
+            ></converse-list-filter>
 
 
-            ${ repeat(groupnames, (n) => n, (name) => {
-                const contacts = contacts_map[name].filter(c => shouldShowContact(c, name));
-                contacts.sort(contactsComparator);
-                return contacts.length ? tplGroup({ contacts, name }) : '';
-            }) }
+            ${repeat(
+                groupnames,
+                (n) => n,
+                (name) => {
+                    const contacts = contacts_map[name].filter((c) => shouldShowContact(c, name));
+                    contacts.sort(contactsComparator);
+                    return contacts.length ? tplGroup({ contacts, name }) : '';
+                }
+            )}
         </div>
         </div>
     `;
     `;
-}
+};

+ 2 - 2
src/plugins/rosterview/utils.js

@@ -88,7 +88,7 @@ export function shouldShowGroup (group) {
 
 
 export function populateContactsMap (contacts_map, contact) {
 export function populateContactsMap (contacts_map, contact) {
     if (contact.get('requesting')) {
     if (contact.get('requesting')) {
-        const name = _converse.labels.HEADER_REQUESTING_CONTACTS;
+        const name = /** @type {string} */(_converse.labels.HEADER_REQUESTING_CONTACTS);
         contacts_map[name] ? contacts_map[name].push(contact) : (contacts_map[name] = [contact]);
         contacts_map[name] ? contacts_map[name].push(contact) : (contacts_map[name] = [contact]);
     } else {
     } else {
         let contact_groups;
         let contact_groups;
@@ -107,7 +107,7 @@ export function populateContactsMap (contacts_map, contact) {
         }
         }
     }
     }
     if (contact.get('num_unread')) {
     if (contact.get('num_unread')) {
-        const name = _converse.labels.HEADER_UNREAD;
+        const name = /** @type {string} */(_converse.labels.HEADER_UNREAD);
         contacts_map[name] ? contacts_map[name].push(contact) : (contacts_map[name] = [contact]);
         contacts_map[name] ? contacts_map[name].push(contact) : (contacts_map[name] = [contact]);
     }
     }
     return contacts_map;
     return contacts_map;

+ 25 - 15
src/shared/dom-navigator.js

@@ -151,20 +151,21 @@ class DOMNavigator {
      * @property {number[]} [DOMNavigatorOptions.right] - The keycode for navigating right
      * @property {number[]} [DOMNavigatorOptions.right] - The keycode for navigating right
      * @property {number[]} [DOMNavigatorOptions.up] - The keycode for navigating up
      * @property {number[]} [DOMNavigatorOptions.up] - The keycode for navigating up
      * @property {String} [DOMNavigatorOptions.selector]
      * @property {String} [DOMNavigatorOptions.selector]
-     * @property {String} [DOMNavigatorOptions.selected] - The class that should be added to the currently selected DOM element.
+     * @property {String} [DOMNavigatorOptions.selected] - The class that should be added
+     *  to the currently selected DOM element
      * @property {String} [DOMNavigatorOptions.jump_to_picked] - A selector, which if
      * @property {String} [DOMNavigatorOptions.jump_to_picked] - A selector, which if
-     * matched by the next element being navigated to, based on the direction
-     * given by `jump_to_picked_direction`, will cause navigation
-     * to jump to the element that matches the `jump_to_picked_selector`.
-     * For example, this is useful when navigating to tabs. You want to
-     * immediately navigate to the currently active tab instead of just
-     * navigating to the first tab.
+     *  matched by the next element being navigated to, based on the direction
+     *  given by `jump_to_picked_direction`, will cause navigation
+     *  to jump to the element that matches the `jump_to_picked_selector`.
+     *  For example, this is useful when navigating to tabs. You want to
+     *  immediately navigate to the currently active tab instead of just
+     *  navigating to the first tab.
      * @property {String} [DOMNavigatorOptions.jump_to_picked_selector=picked] - The selector
      * @property {String} [DOMNavigatorOptions.jump_to_picked_selector=picked] - The selector
-     * indicating the currently picked element to jump to.
+     *  indicating the currently picked element to jump to.
      * @property {String} [DOMNavigatorOptions.jump_to_picked_direction] - The direction for
      * @property {String} [DOMNavigatorOptions.jump_to_picked_direction] - The direction for
-     * which jumping to the picked element should be enabled.
+     *  which jumping to the picked element should be enabled.
      * @property {Function} [DOMNavigatorOptions.onSelected] - The callback function which
      * @property {Function} [DOMNavigatorOptions.onSelected] - The callback function which
-     * should be called when en element gets selected.
+     *  should be called when en element gets selected.
      * @property {HTMLElement} [DOMNavigatorOptions.scroll_container]
      * @property {HTMLElement} [DOMNavigatorOptions.scroll_container]
      */
      */
 
 
@@ -331,11 +332,14 @@ class DOMNavigator {
                     container.scrollTop = el.offsetTop - container.offsetTop;
                     container.scrollTop = el.offsetTop - container.offsetTop;
                     break;
                     break;
                 case DOMNavigator.DIRECTION.right:
                 case DOMNavigator.DIRECTION.right:
-                    container.scrollLeft = el.offsetLeft - container.offsetLeft - (container.offsetWidth - el.offsetWidth);
-                    container.scrollTop = el.offsetTop - container.offsetTop - (container.offsetHeight - el.offsetHeight);
+                    container.scrollLeft =
+                        el.offsetLeft - container.offsetLeft - (container.offsetWidth - el.offsetWidth);
+                    container.scrollTop =
+                        el.offsetTop - container.offsetTop - (container.offsetHeight - el.offsetHeight);
                     break;
                     break;
                 case DOMNavigator.DIRECTION.down:
                 case DOMNavigator.DIRECTION.down:
-                    container.scrollTop = el.offsetTop - container.offsetTop - (container.offsetHeight - el.offsetHeight);
+                    container.scrollTop =
+                        el.offsetTop - container.offsetTop - (container.offsetHeight - el.offsetHeight);
                     break;
                     break;
             }
             }
         } else if (!inViewport(el)) {
         } else if (!inViewport(el)) {
@@ -347,10 +351,16 @@ class DOMNavigator {
                     document.body.scrollTop = absoluteOffsetTop(el) - document.body.offsetTop;
                     document.body.scrollTop = absoluteOffsetTop(el) - document.body.offsetTop;
                     break;
                     break;
                 case DOMNavigator.DIRECTION.right:
                 case DOMNavigator.DIRECTION.right:
-                    document.body.scrollLeft = absoluteOffsetLeft(el) - document.body.offsetLeft - (document.documentElement.clientWidth - el.offsetWidth);
+                    document.body.scrollLeft =
+                        absoluteOffsetLeft(el) -
+                        document.body.offsetLeft -
+                        (document.documentElement.clientWidth - el.offsetWidth);
                     break;
                     break;
                 case DOMNavigator.DIRECTION.down:
                 case DOMNavigator.DIRECTION.down:
-                    document.body.scrollTop = absoluteOffsetTop(el) - document.body.offsetTop - (document.documentElement.clientHeight - el.offsetHeight);
+                    document.body.scrollTop =
+                        absoluteOffsetTop(el) -
+                        document.body.offsetTop -
+                        (document.documentElement.clientHeight - el.offsetHeight);
                     break;
                     break;
             }
             }
         }
         }

+ 9 - 3
src/shared/rich-text.js

@@ -34,8 +34,10 @@ const isString = s => typeof s === 'string';
 // multi-line quotes.
 // multi-line quotes.
 const collapseLineBreaks = text => text.replace(/\n(\u200B*\n)+/g, m => `\n${'\u200B'.repeat(m.length - 2)}\n`);
 const collapseLineBreaks = text => text.replace(/\n(\u200B*\n)+/g, m => `\n${'\u200B'.repeat(m.length - 2)}\n`);
 
 
-const tplMentionWithNick = o => html`<span class="mention mention--self badge badge-info" data-uri="${o.uri}">${o.mention}</span>`;
-const tplMention = o => html`<span class="mention" data-uri="${o.uri}">${o.mention}</span>`;
+const tplMentionWithNick = (o) =>
+    html`<span class="mention mention--self badge badge-info" data-uri="${o.uri}">${o.mention}</span>`;
+
+const tplMention = (o) => html`<span class="mention" data-uri="${o.uri}">${o.mention}</span>`;
 
 
 /**
 /**
  * @class RichText
  * @class RichText
@@ -209,7 +211,11 @@ export class RichText extends String {
             const end = Number(ref.end) - full_offset;
             const end = Number(ref.end) - full_offset;
             const mention = text.slice(begin, end);
             const mention = text.slice(begin, end);
             if (mention === this.nick) {
             if (mention === this.nick) {
-                this.addTemplateResult(begin + local_offset, end + local_offset, tplMentionWithNick({...ref, mention }));
+                this.addTemplateResult(
+                    begin + local_offset,
+                    end + local_offset,
+                    tplMentionWithNick({...ref, mention })
+                );
             } else {
             } else {
                 this.addTemplateResult(begin + local_offset, end + local_offset, tplMention({...ref, mention }));
                 this.addTemplateResult(begin + local_offset, end + local_offset, tplMention({...ref, mention }));
             }
             }

+ 2 - 0
src/shared/styling.js

@@ -21,6 +21,8 @@ const styling_map = {
 
 
 const dont_escape = ['_', '>', '`', '~'];
 const dont_escape = ['_', '>', '`', '~'];
 
 
+// prettier-ignore
+/* eslint-disable max-len */
 const styling_templates = {
 const styling_templates = {
     // m is the chatbox model
     // m is the chatbox model
     // i is the offset of this directive relative to the start of the original message
     // i is the offset of this directive relative to the start of the original message

+ 7 - 1
src/templates/audio.js

@@ -1,4 +1,10 @@
 import { html } from 'lit';
 import { html } from 'lit';
 
 
+/**
+ * @param {string} url
+ * @param {boolean} [hide_url]
+ */
 export default (url, hide_url) =>
 export default (url, hide_url) =>
-    html`<audio controls src="${url}"></audio>${ hide_url ? '' : html`<a target="_blank" rel="noopener" href="${url}">${url}</a>` }`;
+    html`<audio controls src="${url}"></audio>${hide_url
+            ? ''
+            : html`<a target="_blank" rel="noopener" href="${url}">${url}</a>`}`;

+ 1 - 0
src/templates/background_logo.js

@@ -1,3 +1,4 @@
+/* eslint-disable max-len */
 import { html } from "lit";
 import { html } from "lit";
 import { api } from '@converse/headless';
 import { api } from '@converse/headless';
 
 

+ 8 - 2
src/templates/gif.js

@@ -1,5 +1,11 @@
-import { html } from "lit";
+import { html } from 'lit';
 import 'shared/components/gif.js';
 import 'shared/components/gif.js';
 
 
+/**
+ * @param {string} url
+ * @param {boolean} hide_url
+ */
 export default (url, hide_url) =>
 export default (url, hide_url) =>
-    html`<converse-gif autoplay noloop fallback='empty' src=${url}></converse-gif>${ hide_url ? '' : html`<a target="_blank" rel="noopener" href="${url}">${url}</a>` }`;
+    html`<converse-gif autoplay noloop fallback="empty" src=${url}></converse-gif>${hide_url
+            ? ''
+            : html`<a target="_blank" rel="noopener" href="${url}">${url}</a>`}`;

+ 10 - 5
src/templates/spinner.js

@@ -1,9 +1,14 @@
-import { html } from "lit";
+import { html } from 'lit';
 
 
-export default (o={}) => {
+export default (o = {}) => {
     if (o.classes?.includes('hor_centered')) {
     if (o.classes?.includes('hor_centered')) {
-        return html`<div class="spinner__container"><converse-icon size="1em" class="fa fa-spinner spinner centered ${o.classes || ''}"></converse-icon></div>`
+        return html`<div class="spinner__container">
+            <converse-icon size="1em" class="fa fa-spinner spinner centered ${o.classes || ''}"></converse-icon>
+        </div>`;
     } else {
     } else {
-        return html`<converse-icon size="1em" class="fa fa-spinner spinner centered ${o.classes || ''}"></converse-icon>`
+        return html`<converse-icon
+            size="1em"
+            class="fa fa-spinner spinner centered ${o.classes || ''}"
+        ></converse-icon>`;
     }
     }
-}
+};

+ 8 - 2
src/templates/video.js

@@ -1,4 +1,10 @@
-import { html } from "lit";
+import { html } from 'lit';
 
 
+/**
+ * @param {string} url
+ * @param {boolean} [hide_url]
+ */
 export default (url, hide_url) =>
 export default (url, hide_url) =>
-    html`<video controls preload="metadata" src="${url}"></video>${ hide_url ? '' : html`<a target="_blank" rel="noopener" href="${url}">${url}</a>` }`;
+    html`<video controls preload="metadata" src="${url}"></video>${hide_url
+            ? ''
+            : html`<a target="_blank" rel="noopener" href="${url}">${url}</a>`}`;

+ 261 - 0
src/types/headless/plugins/disco/api.d.ts

@@ -0,0 +1,261 @@
+declare namespace _default {
+    namespace disco {
+        export namespace stream {
+            /**
+             * @method api.disco.stream.getFeature
+             * @param { String } name The feature name
+             * @param { String } xmlns The XML namespace
+             * @example _converse.api.disco.stream.getFeature('ver', 'urn:xmpp:features:rosterver')
+             */
+            function getFeature(name: string, xmlns: string): Promise<any>;
+        }
+        export namespace own {
+            namespace identities {
+                /**
+                 * Lets you add new identities for this client (i.e. instance of Converse)
+                 * @method api.disco.own.identities.add
+                 *
+                 * @param { String } category - server, client, gateway, directory, etc.
+                 * @param { String } type - phone, pc, web, etc.
+                 * @param { String } name - "Converse"
+                 * @param { String } lang - en, el, de, etc.
+                 *
+                 * @example _converse.api.disco.own.identities.clear();
+                 */
+                function add(category: string, type: string, name: string, lang: string): boolean;
+                /**
+                 * Clears all previously registered identities.
+                 * @method api.disco.own.identities.clear
+                 * @example _converse.api.disco.own.identities.clear();
+                 */
+                function clear(): void;
+                /**
+                 * Returns all of the identities registered for this client
+                 * (i.e. instance of Converse).
+                 * @method api.disco.identities.get
+                 * @example const identities = api.disco.own.identities.get();
+                 */
+                function get(): any[];
+            }
+            namespace features {
+                /**
+                 * Lets you register new disco features for this client (i.e. instance of Converse)
+                 * @method api.disco.own.features.add
+                 * @param { String } name - e.g. http://jabber.org/protocol/caps
+                 * @example _converse.api.disco.own.features.add("http://jabber.org/protocol/caps");
+                 */
+                function add(name: string): boolean;
+                /**
+                 * Clears all previously registered features.
+                 * @method api.disco.own.features.clear
+                 * @example _converse.api.disco.own.features.clear();
+                 */
+                function clear(): void;
+                /**
+                 * Returns all of the features registered for this client (i.e. instance of Converse).
+                 * @method api.disco.own.features.get
+                 * @example const features = api.disco.own.features.get();
+                 */
+                function get(): any[];
+            }
+        }
+        /**
+         * Query for information about an XMPP entity
+         *
+         * @method api.disco.info
+         * @param { string } jid The Jabber ID of the entity to query
+         * @param { string } [node] A specific node identifier associated with the JID
+         * @returns {promise} Promise which resolves once we have a result from the server.
+         */
+        export function info(jid: string, node?: string): Promise<any>;
+        /**
+         * Query for items associated with an XMPP entity
+         *
+         * @method api.disco.items
+         * @param { string } jid The Jabber ID of the entity to query for items
+         * @param { string } [node] A specific node identifier associated with the JID
+         * @returns {promise} Promise which resolves once we have a result from the server.
+         */
+        export function items(jid: string, node?: string): Promise<any>;
+        export namespace entities {
+            /**
+             * Get the corresponding `DiscoEntity` instance.
+             *
+             * @method api.disco.entities.get
+             * @param { string } jid The Jabber ID of the entity
+             * @param { boolean } [create] Whether the entity should be created if it doesn't exist.
+             * @example _converse.api.disco.entities.get(jid);
+             */
+            function get(jid: string, create?: boolean): Promise<any>;
+            /**
+             * Return any disco items advertised on this entity
+             *
+             * @method api.disco.entities.items
+             * @param { string } jid - The Jabber ID of the entity for which we want to fetch items
+             * @example api.disco.entities.items(jid);
+             */
+            function items(jid: string): any;
+            /**
+             * Create a new  disco entity. It's identity and features
+             * will automatically be fetched from cache or from the
+             * XMPP server.
+             *
+             * Fetching from cache can be disabled by passing in
+             * `ignore_cache: true` in the options parameter.
+             *
+             * @method api.disco.entities.create
+             * @param { object } data
+             * @param { string } data.jid - The Jabber ID of the entity
+             * @param { string } data.parent_jid - The Jabber ID of the parent entity
+             * @param { string } data.name
+             * @param { object } [options] - Additional options
+             * @param { boolean } [options.ignore_cache]
+             *     If true, fetch all features from the XMPP server instead of restoring them from cache
+             * @example _converse.api.disco.entities.create({ jid }, {'ignore_cache': true});
+             */
+            function create(data: {
+                jid: string;
+                parent_jid: string;
+                name: string;
+            }, options?: {
+                ignore_cache?: boolean;
+            }): false | import("@converse/skeletor").Model | import("@converse/skeletor/src/types/collection.js").Attributes | (Promise<any> & {
+                isResolved: boolean;
+                isPending: boolean;
+                isRejected: boolean;
+                resolve: Function;
+                reject: Function;
+            });
+        }
+        export namespace features_1 {
+            /**
+             * Return a given feature of a disco entity
+             *
+             * @method api.disco.features.get
+             * @param { string } feature The feature that might be
+             *     supported. In the XML stanza, this is the `var`
+             *     attribute of the `<feature>` element. For
+             *     example: `http://jabber.org/protocol/muc`
+             * @param { string } jid The JID of the entity
+             *     (and its associated items) which should be queried
+             * @returns {promise} A promise which resolves with a list containing
+             *     _converse.Entity instances representing the entity
+             *     itself or those items associated with the entity if
+             *     they support the given feature.
+             * @example
+             * api.disco.features.get(Strophe.NS.MAM, _converse.bare_jid);
+             */
+            function get(feature: string, jid: string): Promise<any>;
+            /**
+             * Returns true if an entity with the given JID, or if one of its
+             * associated items, supports a given feature.
+             *
+             * @method api.disco.features.has
+             * @param { string } feature The feature that might be
+             *     supported. In the XML stanza, this is the `var`
+             *     attribute of the `<feature>` element. For
+             *     example: `http://jabber.org/protocol/muc`
+             * @param { string } jid The JID of the entity
+             *     (and its associated items) which should be queried
+             * @returns {Promise} A promise which resolves with a boolean
+             * @example
+             *      api.disco.features.has(Strophe.NS.MAM, _converse.bare_jid);
+             */
+            function has(feature: string, jid: string): Promise<any>;
+        }
+        export { features_1 as features };
+        /**
+         * Used to determine whether an entity supports a given feature.
+         *
+         * @method api.disco.supports
+         * @param { string } feature The feature that might be
+         *     supported. In the XML stanza, this is the `var`
+         *     attribute of the `<feature>` element. For
+         *     example: `http://jabber.org/protocol/muc`
+         * @param { string } jid The JID of the entity
+         *     (and its associated items) which should be queried
+         * @returns {promise} A promise which resolves with `true` or `false`.
+         * @example
+         * if (await api.disco.supports(Strophe.NS.MAM, _converse.bare_jid)) {
+         *     // The feature is supported
+         * } else {
+         *     // The feature is not supported
+         * }
+         */
+        export function supports(feature: string, jid: string): Promise<any>;
+        /**
+         * Refresh the features, fields and identities associated with a
+         * disco entity by refetching them from the server
+         * @method api.disco.refresh
+         * @param { string } jid The JID of the entity whose features are refreshed.
+         * @returns {promise} A promise which resolves once the features have been refreshed
+         * @example
+         * await api.disco.refresh('room@conference.example.org');
+         */
+        export function refresh(jid: string): Promise<any>;
+        /**
+         * @deprecated Use {@link api.disco.refresh} instead.
+         * @method api.disco.refreshFeatures
+         */
+        export function refreshFeatures(jid: any): any;
+        /**
+         * Return all the features associated with a disco entity
+         *
+         * @method api.disco.getFeatures
+         * @param { string } jid The JID of the entity whose features are returned.
+         * @returns {promise} A promise which resolves with the returned features
+         * @example
+         * const features = await api.disco.getFeatures('room@conference.example.org');
+         */
+        export function getFeatures(jid: string): Promise<any>;
+        /**
+         * Return all the service discovery extensions fields
+         * associated with an entity.
+         *
+         * See [XEP-0129: Service Discovery Extensions](https://xmpp.org/extensions/xep-0128.html)
+         *
+         * @method api.disco.getFields
+         * @param { string } jid The JID of the entity whose fields are returned.
+         * @example
+         * const fields = await api.disco.getFields('room@conference.example.org');
+         */
+        export function getFields(jid: string): Promise<any>;
+        /**
+         * Get the identity (with the given category and type) for a given disco entity.
+         *
+         * For example, when determining support for PEP (personal eventing protocol), you
+         * want to know whether the user's own JID has an identity with
+         * `category='pubsub'` and `type='pep'` as explained in this section of
+         * XEP-0163: https://xmpp.org/extensions/xep-0163.html#support
+         *
+         * @method api.disco.getIdentity
+         * @param {string} category -The identity category.
+         *     In the XML stanza, this is the `category`
+         *     attribute of the `<identity>` element.
+         *     For example: 'pubsub'
+         * @param {string} type - The identity type.
+         *     In the XML stanza, this is the `type`
+         *     attribute of the `<identity>` element.
+         *     For example: 'pep'
+         * @param {string} jid - The JID of the entity which might have the identity
+         * @returns {promise} A promise which resolves with a map indicating
+         *     whether an identity with a given type is provided by the entity.
+         * @example
+         * api.disco.getIdentity('pubsub', 'pep', _converse.bare_jid).then(
+         *     function (identity) {
+         *         if (identity) {
+         *             // The entity DOES have this identity
+         *         } else {
+         *             // The entity DOES NOT have this identity
+         *         }
+         *     }
+         * ).catch(e => log.error(e));
+         */
+        export function getIdentity(category: string, type: string, jid: string): Promise<any>;
+    }
+}
+export default _default;
+export type DiscoState = import('./index').DiscoState;
+export type DiscoEntities = import('./entities').default;
+export type Collection = import('@converse/skeletor').Collection;
+//# sourceMappingURL=api.d.ts.map

+ 9 - 0
src/types/headless/plugins/disco/entities.d.ts

@@ -0,0 +1,9 @@
+export default DiscoEntities;
+declare class DiscoEntities extends Collection {
+    constructor();
+    model: typeof DiscoEntity;
+    fetchEntities(): Promise<any>;
+}
+import { Collection } from "@converse/skeletor";
+import DiscoEntity from "./entity.js";
+//# sourceMappingURL=entities.d.ts.map

+ 49 - 0
src/types/headless/plugins/disco/entity.d.ts

@@ -0,0 +1,49 @@
+export default DiscoEntity;
+/**
+ * @class
+ * @namespace _converse.DiscoEntity
+ * @memberOf _converse
+ *
+ * A Disco Entity is a JID addressable entity that can be queried for features.
+ *
+ * See XEP-0030: https://xmpp.org/extensions/xep-0030.html
+ */
+declare class DiscoEntity extends Model {
+    initialize(_: any, options: any): void;
+    waitUntilFeaturesDiscovered: any;
+    dataforms: Collection;
+    features: Collection;
+    fields: Collection;
+    identities: Collection;
+    /**
+     * Returns a Promise which resolves with a map indicating
+     * whether a given identity is provided by this entity.
+     * @method _converse.DiscoEntity#getIdentity
+     * @param { String } category - The identity category
+     * @param { String } type - The identity type
+     */
+    getIdentity(category: string, type: string): Promise<any>;
+    /**
+     * Returns a Promise which resolves with a map indicating
+     * whether a given feature is supported.
+     * @method _converse.DiscoEntity#getFeature
+     * @param { String } feature - The feature that might be supported.
+     */
+    getFeature(feature: string): Promise<DiscoEntity>;
+    onFeatureAdded(feature: any): void;
+    onFieldAdded(field: any): void;
+    fetchFeatures(options: any): Promise<void>;
+    queryInfo(): Promise<void>;
+    /**
+     * @param {Element} stanza
+     */
+    onDiscoItems(stanza: Element): void;
+    queryForItems(): Promise<void>;
+    /**
+     * @param {Element} stanza
+     */
+    onInfo(stanza: Element): Promise<void>;
+}
+import { Model } from "@converse/skeletor";
+import { Collection } from "@converse/skeletor";
+//# sourceMappingURL=entity.d.ts.map

+ 5 - 0
src/types/headless/plugins/disco/index.d.ts

@@ -0,0 +1,5 @@
+export type DiscoState = {
+    _identities: any[];
+    _features: any[];
+};
+//# sourceMappingURL=index.d.ts.map

+ 6 - 0
src/types/headless/plugins/disco/utils.d.ts

@@ -0,0 +1,6 @@
+export function initializeDisco(): Promise<void>;
+export function initStreamFeatures(): void;
+export function notifyStreamFeaturesAdded(): void;
+export function populateStreamFeatures(): void;
+export function clearSession(): void;
+//# sourceMappingURL=utils.d.ts.map

+ 1 - 0
src/types/headless/plugins/muc/muc.d.ts

@@ -5,6 +5,7 @@ export type NonOutcastAffiliation = import('./affiliations/utils.js').NonOutcast
 export type MemberListItem = any;
 export type MemberListItem = any;
 export type MessageAttributes = any;
 export type MessageAttributes = any;
 export type MUCMessageAttributes = any;
 export type MUCMessageAttributes = any;
+export type UserMessage = any;
 export namespace Strophe {
 export namespace Strophe {
     type Builder = any;
     type Builder = any;
 }
 }

+ 2 - 0
src/types/headless/plugins/muc/occupant.d.ts

@@ -6,6 +6,8 @@ export default ChatRoomOccupant;
  * @memberOf _converse
  * @memberOf _converse
  */
  */
 declare class ChatRoomOccupant extends Model {
 declare class ChatRoomOccupant extends Model {
+    constructor(attributes: any, options: any);
+    vcard: any;
     defaults(): {
     defaults(): {
         hats: any[];
         hats: any[];
         show: string;
         show: string;

+ 18 - 0
src/types/headless/plugins/status/status.d.ts

@@ -0,0 +1,18 @@
+export default class XMPPStatus extends Model {
+    constructor(attributes: any, options: any);
+    vcard: any;
+    defaults(): {
+        status: any;
+    };
+    getDisplayName(): any;
+    getNickname(): any;
+    getFullname(): string;
+    /** Constructs a presence stanza
+     * @param {string} [type]
+     * @param {string} [to] - The JID to which this presence should be sent
+     * @param {string} [status_message]
+     */
+    constructPresence(type?: string, to?: string, status_message?: string): Promise<any>;
+}
+import { Model } from "@converse/skeletor";
+//# sourceMappingURL=status.d.ts.map

+ 22 - 0
src/types/headless/plugins/status/utils.d.ts

@@ -0,0 +1,22 @@
+export function initStatus(reconnecting: any): void;
+export function isIdle(): boolean;
+export function getIdleSeconds(): number;
+/**
+ * Resets counters and flags relating to CSI and auto_away/auto_xa
+ */
+export function onUserActivity(): void;
+export function onEverySecond(): void;
+/**
+ * Send out a Client State Indication (XEP-0352)
+ * @function sendCSI
+ * @param { String } stat - The user's chat status
+ */
+export function sendCSI(stat: string): void;
+/**
+ * Set an interval of one second and register a handler for it.
+ * Required for the auto_away, auto_xa and csi_waiting_time features.
+ */
+export function registerIntervalHandler(): void;
+export function tearDown(): void;
+export function addStatusToMUCJoinPresence(_: any, stanza: any): any;
+//# sourceMappingURL=utils.d.ts.map

+ 16 - 0
src/types/headless/plugins/vcard/vcard.d.ts

@@ -0,0 +1,16 @@
+export default VCard;
+/**
+ * Represents a VCard
+ * @namespace _converse.VCard
+ * @memberOf _converse
+ */
+declare class VCard extends Model {
+    defaults(): {
+        image: string;
+        image_type: string;
+    };
+    set(key: any, val: any, options: any, ...args: any[]): any;
+    getDisplayName(): any;
+}
+import { Model } from "@converse/skeletor";
+//# sourceMappingURL=vcard.d.ts.map

+ 15 - 3
src/types/headless/shared/_converse.d.ts

@@ -1,5 +1,9 @@
 export default _converse;
 export default _converse;
 export type Storage = any;
 export type Storage = any;
+export type Collection = import('@converse/skeletor').Collection;
+export type DiscoState = import('../plugins/disco/index').DiscoState;
+export type XMPPStatus = import('../plugins/status/status').default;
+export type VCards = import('../plugins/vcard/vcard').default;
 declare const _converse: ConversePrivateGlobal;
 declare const _converse: ConversePrivateGlobal;
 declare const ConversePrivateGlobal_base: (new (...args: any[]) => {
 declare const ConversePrivateGlobal_base: (new (...args: any[]) => {
     on(name: string, callback: (event: any, model: Model, collection: import("@converse/skeletor").Collection, options?: Record<string, any>) => any, context: any): any;
     on(name: string, callback: (event: any, model: Model, collection: import("@converse/skeletor").Collection, options?: Record<string, any>) => any, context: any): any;
@@ -41,20 +45,28 @@ declare class ConversePrivateGlobal extends ConversePrivateGlobal_base {
     api: any;
     api: any;
     /**
     /**
      * Namespace for storing translated strings.
      * Namespace for storing translated strings.
+     *
+     * @typedef {Record<string, string>} UserMessage
+     * @typedef {Record<string, string|UserMessage>} UserMessages
      */
      */
-    labels: {};
+    labels: Record<string, string | Record<string, string>>;
     /**
     /**
      * Namespace for storing code that might be useful to 3rd party
      * Namespace for storing code that might be useful to 3rd party
      * plugins. We want to make it possible for 3rd party plugins to have
      * plugins. We want to make it possible for 3rd party plugins to have
      * access to code (e.g. classes) from converse.js without having to add
      * access to code (e.g. classes) from converse.js without having to add
      * converse.js as a dependency.
      * converse.js as a dependency.
      */
      */
-    exports: {};
+    exports: Record<string, any>;
     /**
     /**
      * Namespace for storing the state, as represented by instances of
      * Namespace for storing the state, as represented by instances of
      * Models and Collections.
      * Models and Collections.
+     *
+     * @typedef {Object & Record<string, Collection|Model|VCards|XMPPStatus|DiscoState>} ConverseState
+     * @property {VCards} [vcards]
+     * @property {XMPPStatus} xmppstatus
+     * @property {DiscoState} disco
      */
      */
-    state: {};
+    state: any;
     initSession(): void;
     initSession(): void;
     session: Model;
     session: Model;
     /**
     /**

+ 3 - 3
src/types/headless/shared/api/index.d.ts

@@ -18,9 +18,9 @@ export type _converse = {
         INACTIVE: number;
         INACTIVE: number;
     };
     };
     api: any;
     api: any;
-    labels: {};
-    exports: {};
-    state: {};
+    labels: Record<string, string | Record<string, string>>;
+    exports: Record<string, any>;
+    state: any;
     initSession(): void;
     initSession(): void;
     session: import("@converse/skeletor").Model;
     session: import("@converse/skeletor").Model;
     __(...args: string[]): any;
     __(...args: string[]): any;

+ 5 - 4
src/types/headless/shared/api/presence.d.ts

@@ -3,10 +3,10 @@ declare namespace _default {
         /**
         /**
          * Send out a presence stanza
          * Send out a presence stanza
          * @method _converse.api.user.presence.send
          * @method _converse.api.user.presence.send
-         * @param { String } [type]
-         * @param { String } [to]
-         * @param { String } [status] - An optional status message
-         * @param { Array<Element>|Array<Strophe.Builder>|Element|Strophe.Builder } [child_nodes]
+         * @param {String} [type]
+         * @param {String} [to]
+         * @param {String} [status] - An optional status message
+         * @param {Array<Element>|Array<Strophe.Builder>|Element|Strophe.Builder} [child_nodes]
          *  Nodes(s) to be added as child nodes of the `presence` XML element.
          *  Nodes(s) to be added as child nodes of the `presence` XML element.
          */
          */
         function send(type?: string, to?: string, status?: string, child_nodes?: any): Promise<void>;
         function send(type?: string, to?: string, status?: string, child_nodes?: any): Promise<void>;
@@ -16,4 +16,5 @@ export default _default;
 export namespace Strophe {
 export namespace Strophe {
     type Builder = any;
     type Builder = any;
 }
 }
+export type XMPPStatus = import('../../plugins/status/status').default;
 //# sourceMappingURL=presence.d.ts.map
 //# sourceMappingURL=presence.d.ts.map

+ 2 - 2
src/types/headless/shared/i18n.d.ts

@@ -1,4 +1,5 @@
-declare namespace _default {
+export default i18nStub;
+declare namespace i18nStub {
     function initialize(): void;
     function initialize(): void;
     /**
     /**
      * Overridable string wrapper method which can be used to provide i18n
      * Overridable string wrapper method which can be used to provide i18n
@@ -15,5 +16,4 @@ declare namespace _default {
      */
      */
     function __(...args: any[]): any;
     function __(...args: any[]): any;
 }
 }
-export default _default;
 //# sourceMappingURL=i18n.d.ts.map
 //# sourceMappingURL=i18n.d.ts.map

+ 3 - 3
src/types/headless/utils/session.d.ts

@@ -27,9 +27,9 @@ export function tearDown(): Promise<{
         INACTIVE: number;
         INACTIVE: number;
     };
     };
     api: any;
     api: any;
-    labels: {};
-    exports: {};
-    state: {};
+    labels: Record<string, string | Record<string, string>>;
+    exports: Record<string, any>;
+    state: any;
     initSession(): void;
     initSession(): void;
     session: import("@converse/skeletor").Model;
     session: import("@converse/skeletor").Model;
     __(...args: string[]): any;
     __(...args: string[]): any;

+ 11 - 2
src/types/i18n/index.d.ts

@@ -1,3 +1,12 @@
-export { i18n };
-export const __: any;
+export function __(str: any, ...args: any[]): any;
+export namespace i18n {
+    function getLocale(): string;
+    /**
+     * @param {string} str - The string to be translated
+     * @param {Array<any>} args
+     */
+    function translate(str: string, args: any[], ...args: any[]): any;
+    function initialize(): Promise<void>;
+    function __(str: any, ...args: any[]): any;
+}
 //# sourceMappingURL=index.d.ts.map
 //# sourceMappingURL=index.d.ts.map

+ 1 - 0
src/types/index.d.ts

@@ -1,2 +1,3 @@
 export default converse;
 export default converse;
+import { converse } from "@converse/headless";
 //# sourceMappingURL=index.d.ts.map
 //# sourceMappingURL=index.d.ts.map

+ 6 - 2
src/types/plugins/muc-views/sidebar.d.ts

@@ -9,8 +9,12 @@ export default class MUCSidebar extends CustomElement {
     filter: any;
     filter: any;
     model: any;
     model: any;
     render(): import("lit-html").TemplateResult<1>;
     render(): import("lit-html").TemplateResult<1>;
-    closeSidebar(ev: any): void;
-    onOccupantClicked(ev: any): void;
+    /** @param {MouseEvent} ev */
+    toggleFilter(ev: MouseEvent): void;
+    /** @param {MouseEvent} ev */
+    closeSidebar(ev: MouseEvent): void;
+    /** @param {MouseEvent} ev */
+    onOccupantClicked(ev: MouseEvent): void;
 }
 }
 import { CustomElement } from "shared/components/element.js";
 import { CustomElement } from "shared/components/element.js";
 //# sourceMappingURL=sidebar.d.ts.map
 //# sourceMappingURL=sidebar.d.ts.map

+ 5 - 0
src/types/plugins/notifications/utils.d.ts

@@ -20,8 +20,12 @@ export function handleFeedback(data: any): void;
 /**
 /**
  * Event handler for on('contactPresenceChanged').
  * Event handler for on('contactPresenceChanged').
  * Will show an HTML5 notification to indicate that the chat status has changed.
  * Will show an HTML5 notification to indicate that the chat status has changed.
+ * @param {RosterContact} contact
  */
  */
 export function handleChatStateNotification(contact: any): void;
 export function handleChatStateNotification(contact: any): void;
+/**
+ * @param {RosterContact} contact
+ */
 export function handleContactRequestNotification(contact: any): void;
 export function handleContactRequestNotification(contact: any): void;
 export function requestPermission(): void;
 export function requestPermission(): void;
 export type navigator = Navigator & {
 export type navigator = Navigator & {
@@ -31,4 +35,5 @@ export type navigator = Navigator & {
 export type MUCMessageAttributes = any;
 export type MUCMessageAttributes = any;
 export type MUCMessageData = any;
 export type MUCMessageData = any;
 export type MessageData = any;
 export type MessageData = any;
+export type RosterContact = any;
 //# sourceMappingURL=utils.d.ts.map
 //# sourceMappingURL=utils.d.ts.map

+ 1 - 1
src/types/plugins/omemo/devicelist.d.ts

@@ -13,7 +13,7 @@ declare class DeviceList extends Model {
     _devices_promise: Promise<any>;
     _devices_promise: Promise<any>;
     getOwnDeviceId(): Promise<any>;
     getOwnDeviceId(): Promise<any>;
     publishCurrentDevice(device_ids: any): Promise<any>;
     publishCurrentDevice(device_ids: any): Promise<any>;
-    fetchDevicesFromServer(): Promise<any>;
+    fetchDevicesFromServer(): Promise<any[]>;
     /**
     /**
      * Send an IQ stanza to the current user's "devices" PEP node to
      * Send an IQ stanza to the current user's "devices" PEP node to
      * ensure that all devices are published for potential chat partners to see.
      * ensure that all devices are published for potential chat partners to see.

+ 1 - 1
src/types/plugins/omemo/utils.d.ts

@@ -39,7 +39,7 @@ export function parseBundle(bundle_el: any): {
     };
     };
     prekeys: any;
     prekeys: any;
 };
 };
-export function generateFingerprints(jid: any): Promise<any>;
+export function generateFingerprints(jid: any): Promise<any[]>;
 export function generateFingerprint(device: any): Promise<void>;
 export function generateFingerprint(device: any): Promise<void>;
 export function getDevicesForContact(jid: any): Promise<any>;
 export function getDevicesForContact(jid: any): Promise<any>;
 export function getDeviceForContact(jid: any, device_id: any): Promise<any>;
 export function getDeviceForContact(jid: any, device_id: any): Promise<any>;

+ 1 - 1
src/types/plugins/profile/modals/profile.d.ts

@@ -5,6 +5,6 @@ export default class ProfileModal extends BaseModal {
     setVCard(data: any): Promise<void>;
     setVCard(data: any): Promise<void>;
     onFormSubmitted(ev: any): void;
     onFormSubmitted(ev: any): void;
 }
 }
-export type XMPPStatus = any;
+export type XMPPStatus = import("@converse/headless/plugins/status/status").default;
 import BaseModal from "plugins/modal/modal.js";
 import BaseModal from "plugins/modal/modal.js";
 //# sourceMappingURL=profile.d.ts.map
 //# sourceMappingURL=profile.d.ts.map

+ 2 - 1
src/types/plugins/rosterview/templates/roster.d.ts

@@ -1,3 +1,4 @@
-declare function _default(el: any): import("lit-html").TemplateResult<1>;
+declare function _default(el: RosterView): import("lit-html").TemplateResult<1>;
 export default _default;
 export default _default;
+export type RosterView = import('../rosterview').default;
 //# sourceMappingURL=roster.d.ts.map
 //# sourceMappingURL=roster.d.ts.map

+ 8 - 2
src/types/shared/chat/utils.d.ts

@@ -1,7 +1,13 @@
 export function getHeadingDropdownItem(promise_or_data: any): Promise<import("lit-html").TemplateResult<1> | "">;
 export function getHeadingDropdownItem(promise_or_data: any): Promise<import("lit-html").TemplateResult<1> | "">;
 export function getHeadingStandaloneButton(promise_or_data: any): Promise<import("lit-html").TemplateResult<1>>;
 export function getHeadingStandaloneButton(promise_or_data: any): Promise<import("lit-html").TemplateResult<1>>;
-export function getStandaloneButtons(promise: any): any;
-export function getDropdownButtons(promise: any): any;
+/**
+ * @param {Promise} promise
+ */
+export function getStandaloneButtons(promise: Promise<any>): Promise<any>;
+/**
+ * @param {Promise} promise
+ */
+export function getDropdownButtons(promise: Promise<any>): Promise<import("lit-html").TemplateResult<1> | "">;
 export function onScrolledDown(model: any): void;
 export function onScrolledDown(model: any): void;
 /**
 /**
  * Given a message object, returns a TemplateResult indicating a new day if
  * Given a message object, returns a TemplateResult indicating a new day if

+ 15 - 12
src/types/shared/dom-navigator.d.ts

@@ -65,20 +65,21 @@ declare class DOMNavigator {
      * @property {number[]} [DOMNavigatorOptions.right] - The keycode for navigating right
      * @property {number[]} [DOMNavigatorOptions.right] - The keycode for navigating right
      * @property {number[]} [DOMNavigatorOptions.up] - The keycode for navigating up
      * @property {number[]} [DOMNavigatorOptions.up] - The keycode for navigating up
      * @property {String} [DOMNavigatorOptions.selector]
      * @property {String} [DOMNavigatorOptions.selector]
-     * @property {String} [DOMNavigatorOptions.selected] - The class that should be added to the currently selected DOM element.
+     * @property {String} [DOMNavigatorOptions.selected] - The class that should be added
+     *  to the currently selected DOM element
      * @property {String} [DOMNavigatorOptions.jump_to_picked] - A selector, which if
      * @property {String} [DOMNavigatorOptions.jump_to_picked] - A selector, which if
-     * matched by the next element being navigated to, based on the direction
-     * given by `jump_to_picked_direction`, will cause navigation
-     * to jump to the element that matches the `jump_to_picked_selector`.
-     * For example, this is useful when navigating to tabs. You want to
-     * immediately navigate to the currently active tab instead of just
-     * navigating to the first tab.
+     *  matched by the next element being navigated to, based on the direction
+     *  given by `jump_to_picked_direction`, will cause navigation
+     *  to jump to the element that matches the `jump_to_picked_selector`.
+     *  For example, this is useful when navigating to tabs. You want to
+     *  immediately navigate to the currently active tab instead of just
+     *  navigating to the first tab.
      * @property {String} [DOMNavigatorOptions.jump_to_picked_selector=picked] - The selector
      * @property {String} [DOMNavigatorOptions.jump_to_picked_selector=picked] - The selector
-     * indicating the currently picked element to jump to.
+     *  indicating the currently picked element to jump to.
      * @property {String} [DOMNavigatorOptions.jump_to_picked_direction] - The direction for
      * @property {String} [DOMNavigatorOptions.jump_to_picked_direction] - The direction for
-     * which jumping to the picked element should be enabled.
+     *  which jumping to the picked element should be enabled.
      * @property {Function} [DOMNavigatorOptions.onSelected] - The callback function which
      * @property {Function} [DOMNavigatorOptions.onSelected] - The callback function which
-     * should be called when en element gets selected.
+     *  should be called when en element gets selected.
      * @property {HTMLElement} [DOMNavigatorOptions.scroll_container]
      * @property {HTMLElement} [DOMNavigatorOptions.scroll_container]
      */
      */
     /**
     /**
@@ -108,7 +109,8 @@ declare class DOMNavigator {
         up?: number[];
         up?: number[];
         selector?: string;
         selector?: string;
         /**
         /**
-         * - The class that should be added to the currently selected DOM element.
+         * - The class that should be added
+         * to the currently selected DOM element
          */
          */
         selected?: string;
         selected?: string;
         /**
         /**
@@ -164,7 +166,8 @@ declare class DOMNavigator {
         up?: number[];
         up?: number[];
         selector?: string;
         selector?: string;
         /**
         /**
-         * - The class that should be added to the currently selected DOM element.
+         * - The class that should be added
+         * to the currently selected DOM element
          */
          */
         selected?: string;
         selected?: string;
         /**
         /**

+ 1 - 1
src/types/templates/audio.d.ts

@@ -1,3 +1,3 @@
-declare function _default(url: any, hide_url: any): import("lit-html").TemplateResult<1>;
+declare function _default(url: string, hide_url?: boolean): import("lit-html").TemplateResult<1>;
 export default _default;
 export default _default;
 //# sourceMappingURL=audio.d.ts.map
 //# sourceMappingURL=audio.d.ts.map

+ 1 - 1
src/types/templates/gif.d.ts

@@ -1,3 +1,3 @@
-declare function _default(url: any, hide_url: any): import("lit-html").TemplateResult<1>;
+declare function _default(url: string, hide_url: boolean): import("lit-html").TemplateResult<1>;
 export default _default;
 export default _default;
 //# sourceMappingURL=gif.d.ts.map
 //# sourceMappingURL=gif.d.ts.map

+ 1 - 1
src/types/templates/video.d.ts

@@ -1,3 +1,3 @@
-declare function _default(url: any, hide_url: any): import("lit-html").TemplateResult<1>;
+declare function _default(url: string, hide_url?: boolean): import("lit-html").TemplateResult<1>;
 export default _default;
 export default _default;
 //# sourceMappingURL=video.d.ts.map
 //# sourceMappingURL=video.d.ts.map

+ 6 - 5
src/utils/html.js

@@ -82,12 +82,12 @@ const serializer = new XMLSerializer();
 
 
 /**
 /**
  * Given two XML or HTML elements, determine if they're equal
  * Given two XML or HTML elements, determine if they're equal
- * @param { Element } actual
- * @param { Element } expected
- * @returns { Boolean }
+ * @param {Element} actual
+ * @param {Element} expected
+ * @returns {Boolean}
  */
  */
 function isEqualNode (actual, expected) {
 function isEqualNode (actual, expected) {
-    if (!u.isElement(actual)) throw new Error("Element being compared must be an Element!");
+    if (!u.isElement(actual)) throw new Error('Element being compared must be an Element!');
 
 
     actual = stripEmptyTextNodes(actual);
     actual = stripEmptyTextNodes(actual);
     expected = stripEmptyTextNodes(expected);
     expected = stripEmptyTextNodes(expected);
@@ -115,7 +115,8 @@ function isEqualNode (actual, expected) {
         const { xmlHtmlNode } = Strophe;
         const { xmlHtmlNode } = Strophe;
         const actual_string = serializer.serializeToString(actual);
         const actual_string = serializer.serializeToString(actual);
         const expected_string = serializer.serializeToString(expected);
         const expected_string = serializer.serializeToString(expected);
-        isEqual = actual_string === expected_string || xmlHtmlNode(actual_string).isEqualNode(xmlHtmlNode(expected_string));
+        isEqual =
+            actual_string === expected_string || xmlHtmlNode(actual_string).isEqualNode(xmlHtmlNode(expected_string));
     }
     }
 
 
     return isEqual;
     return isEqual;