Bläddra i källkod

Move presence/message status code parsing to parsers file and add types

JC Brand 7 månader sedan
förälder
incheckning
69c2fb8a08

+ 28 - 2
src/headless/plugins/muc/constants.js

@@ -1,11 +1,37 @@
-export const ACTION_INFO_CODES = ['301', '303', '333', '307', '321', '322'];
+export const ACTION_INFO_CODES = ['301', '333', '307', '321', '322'];
+export const NEW_NICK_CODES = ['210', '303'];
 export const ADMIN_COMMANDS = ['admin', 'ban', 'deop', 'destroy', 'member', 'op', 'revoke'];
 export const AFFILIATIONS = ['owner', 'admin', 'member', 'outcast', 'none'];
+export const DISCONNECT_CODES = ['301', '333', '307', '321', '322', '332'];
 export const MODERATOR_COMMANDS = ['kick', 'mute', 'voice', 'modtools'];
 export const OWNER_COMMANDS = ['owner'];
 export const ROLES = ['moderator', 'participant', 'visitor'];
 export const VISITOR_COMMANDS = ['nick'];
 
+// Which types of stanzas a status code might appear in.
+export const STATUS_CODE_STANZAS = {
+    '100': ['message', 'presence'],
+    '101': ['message'],
+    '102': ['message'],
+    '103': ['message'],
+    '104': ['message'],
+    '110': ['presence'],
+    '170': ['message', 'presence'],
+    '171': ['message'],
+    '172': ['message'],
+    '173': ['message'],
+    '174': ['message'],
+    '201': ['presence'],
+    '210': ['presence'],
+    '301': ['presence'],
+    '303': ['presence'],
+    '307': ['presence'],
+    '321': ['presence'],
+    '322': ['presence'],
+    '332': ['presence'],
+    '333': ['presence'],
+};
+
 export const MUC_ROLE_WEIGHTS = {
     'moderator': 1,
     'participant': 2,
@@ -67,7 +93,7 @@ export const ROOM_FEATURES = [
     'moderated',
     'unmoderated',
     'mam_enabled',
-    'vcard-temp'
+    'vcard-temp',
 ];
 
 export const MUC_NICK_CHANGED_CODE = '303';

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 192 - 181
src/headless/plugins/muc/muc.js


+ 79 - 39
src/headless/plugins/muc/parsers.js

@@ -27,6 +27,7 @@ import {
     isValidReceiptRequest,
     throwErrorIfInvalidForward,
 } from '../../shared/parsers';
+import { ACTION_INFO_CODES, NEW_NICK_CODES, STATUS_CODE_STANZAS } from './constants.js';
 
 const { Strophe, sizzle, u } = converse.env;
 const { NS } = Strophe;
@@ -114,6 +115,32 @@ function getModerationAttributes (stanza) {
     return {};
 }
 
+/**
+ * @param {Element} stanza
+ * @param {'presence'|'message'} type
+ * @returns {{codes: Array<import('./types').MUCStatusCode>, is_self: boolean}}
+ */
+function getStatusCodes(stanza, type) {
+    /**
+     * @typedef {import('./types').MUCStatusCode} MUCStatusCode
+     */
+    const codes = sizzle(`x[xmlns="${Strophe.NS.MUC_USER}"] status`, stanza)
+        .map(/** @param {Element} s */ (s) => s.getAttribute('code'))
+        .filter(
+            /** @param {MUCStatusCode} c */
+            (c) => STATUS_CODE_STANZAS[c]?.includes(type));
+
+    if (type === 'presence' && codes.includes('333') && codes.includes('307')) {
+        // See: https://github.com/xsf/xeps/pull/969/files#diff-ac5113766e59219806793c1f7d967f1bR4966
+        codes.splice(codes.indexOf('307'), 1);
+    }
+
+    return {
+        codes,
+        is_self: codes.includes('110')
+    };
+}
+
 /**
  * @param {Element} stanza
  * @param {MUC} chatbox
@@ -206,6 +233,7 @@ export async function parseMUCMessage (stanza, chatbox) {
         getRetractionAttributes(stanza, original_stanza),
         getModerationAttributes(stanza),
         getEncryptionAttributes(stanza),
+        getStatusCodes(stanza, 'message'),
     ));
 
     attrs.from_real_jid = attrs.is_archived && getJIDFromMUCUserData(stanza) ||
@@ -280,6 +308,37 @@ export function parseMemberListIQ (iq) {
     );
 }
 
+/**
+ * @param {Element} stanza - The presence stanza
+ * @param {string} nick
+ * @returns {import('./types').MUCPresenceItemAttributes}
+ */
+function parsePresenceUserItem(stanza, nick) {
+    /**
+     * @typedef {import('./types').MUCAffiliation} MUCAffiliation
+     * @typedef {import('./types').MUCRole} MUCRole
+     */
+    const item = sizzle(`x[xmlns="${Strophe.NS.MUC_USER}"] item`, stanza).pop();
+    if (item) {
+        const actor = item.querySelector('actor');
+        return {
+            affiliation: /** @type {MUCAffiliation} */ (item.getAttribute('affiliation')),
+            role: /** @type {MUCRole} */ (item.getAttribute('role')),
+            jid: item.getAttribute('jid'),
+            nick: item.getAttribute('nick') || nick,
+            ...(actor
+                ? {
+                      actor: {
+                          nick: actor?.getAttribute('nick') ?? null,
+                          jid: actor?.getAttribute('jid') ?? null,
+                      },
+                  }
+                : {}),
+            reason: item.querySelector('reason')?.textContent ?? null,
+        };
+    }
+}
+
 /**
  * Parses a passed in MUC presence stanza and returns an object of attributes.
  * @param {Element} stanza - The presence stanza
@@ -287,46 +346,27 @@ export function parseMemberListIQ (iq) {
  * @returns {import('./types').MUCPresenceAttributes}
  */
 export function parseMUCPresence (stanza, chatbox) {
+    /**
+     * @typedef {import('./types').MUCPresenceAttributes} MUCPresenceAttributes
+     */
     const from = stanza.getAttribute('from');
     const type = stanza.getAttribute('type');
-    const data = {
-        'is_me': !!stanza.querySelector("status[code='110']"),
-        'from': from,
-        'occupant_id': getOccupantID(stanza, chatbox),
-        'nick': Strophe.getResourceFromJid(from),
-        'type': type,
-        'states': [],
-        'hats': [],
-        'show': type !== 'unavailable' ? 'online' : 'offline'
-    };
-
-    Array.from(stanza.children).forEach(child => {
-        if (child.matches('status')) {
-            data.status = child.textContent || null;
-        } else if (child.matches('show')) {
-            data.show = child.textContent || 'online';
-        } else if (child.matches('x') && child.getAttribute('xmlns') === Strophe.NS.MUC_USER) {
-            Array.from(child.children).forEach(item => {
-                if (item.nodeName === 'item') {
-                    data.affiliation = item.getAttribute('affiliation');
-                    data.role = item.getAttribute('role');
-                    data.jid = item.getAttribute('jid');
-                    data.nick = item.getAttribute('nick') || data.nick;
-                } else if (item.nodeName == 'status' && item.getAttribute('code')) {
-                    data.states.push(item.getAttribute('code'));
-                }
-            });
-        } else if (child.matches('x') && child.getAttribute('xmlns') === Strophe.NS.VCARDUPDATE) {
-            data.image_hash = child.querySelector('photo')?.textContent;
-        } else if (child.matches('hats') && child.getAttribute('xmlns') === Strophe.NS.MUC_HATS) {
-            data['hats'] = Array.from(child.children).map(
-                c =>
-                    c.matches('hat') && {
-                        'title': c.getAttribute('title'),
-                        'uri': c.getAttribute('uri')
-                    }
-            );
-        }
+    const nick = Strophe.getResourceFromJid(from);
+    const attrs = /** @type {MUCPresenceAttributes} */({
+        from,
+        is_me: !!stanza.querySelector("status[code='110']"),
+        nick,
+        occupant_id: getOccupantID(stanza, chatbox),
+        type,
+        status: stanza.querySelector(':scope > status')?.textContent ?? undefined,
+        show: stanza.querySelector(':scope > show')?.textContent ?? (type !== 'unavailable' ? 'online' : 'offline'),
+        image_hash: sizzle(`:scope > x[xmlns="${Strophe.NS.VCARDUPDATE}"] photo`, stanza).pop()?.textContent,
+        hats: sizzle(`:scope x[xmlns="${Strophe.NS.MUC_HATS}"] hat`, stanza).map(/** @param {Element} h */(h) => ({
+            title: h.getAttribute('title'),
+            uri: h.getAttribute('uri')
+        })),
+        ...getStatusCodes(stanza, 'presence'),
+        ...parsePresenceUserItem(stanza, nick),
     });
-    return data;
+    return attrs;
 }

+ 25 - 53
src/headless/plugins/muc/plugin.js

@@ -125,61 +125,33 @@ converse.plugins.add('converse-muc', {
         Object.assign(api, muc_api);
         Object.assign(api.rooms, affiliations_api);
 
-        /* https://xmpp.org/extensions/xep-0045.html
-         * ----------------------------------------
-         * 100 message      Entering a groupchat         Inform user that any occupant is allowed to see the user's full JID
-         * 101 message (out of band)                     Affiliation change  Inform user that his or her affiliation changed while not in the groupchat
-         * 102 message      Configuration change         Inform occupants that groupchat now shows unavailable members
-         * 103 message      Configuration change         Inform occupants that groupchat now does not show unavailable members
-         * 104 message      Configuration change         Inform occupants that a non-privacy-related groupchat configuration change has occurred
-         * 110 presence     Any groupchat presence       Inform user that presence refers to one of its own groupchat occupants
-         * 170 message or initial presence               Configuration change    Inform occupants that groupchat logging is now enabled
-         * 171 message      Configuration change         Inform occupants that groupchat logging is now disabled
-         * 172 message      Configuration change         Inform occupants that the groupchat is now non-anonymous
-         * 173 message      Configuration change         Inform occupants that the groupchat is now semi-anonymous
-         * 174 message      Configuration change         Inform occupants that the groupchat is now fully-anonymous
-         * 201 presence     Entering a groupchat         Inform user that a new groupchat has been created
-         * 210 presence     Entering a groupchat         Inform user that the service has assigned or modified the occupant's roomnick
-         * 301 presence     Removal from groupchat       Inform user that he or she has been banned from the groupchat
-         * 303 presence     Exiting a groupchat          Inform all occupants of new groupchat nickname
-         * 307 presence     Removal from groupchat       Inform user that he or she has been kicked from the groupchat
-         * 321 presence     Removal from groupchat       Inform user that he or she is being removed from the groupchat because of an affiliation change
-         * 322 presence     Removal from groupchat       Inform user that he or she is being removed from the groupchat because the groupchat has been changed to members-only and the user is not a member
-         * 332 presence     Removal from groupchat       Inform user that he or she is being removed from the groupchat because of a system shutdown
+        /**
+         * https://xmpp.org/extensions/xep-0045.html
+         * -----------------------------------------
          */
-        const MUC_FEEDBACK_MESSAGES = {
-            info_messages: {
-                100: __('This groupchat is not anonymous'),
-                102: __('This groupchat now shows unavailable members'),
-                103: __('This groupchat does not show unavailable members'),
-                104: __('The groupchat configuration has changed'),
-                170: __('Groupchat logging is now enabled'),
-                171: __('Groupchat logging is now disabled'),
-                172: __('This groupchat is now no longer anonymous'),
-                173: __('This groupchat is now semi-anonymous'),
-                174: __('This groupchat is now fully-anonymous'),
-                201: __('A new groupchat has been created'),
-            },
-
-            new_nickname_messages: {
-                // XXX: Note the triple underscore function and not double underscore.
-                210: ___('Your nickname has been automatically set to %1$s'),
-                303: ___('Your nickname has been changed to %1$s'),
-            },
-
-            disconnect_messages: {
-                301: __('You have been banned from this groupchat'),
-                333: __('You have exited this groupchat due to a technical problem'),
-                307: __('You have been kicked from this groupchat'),
-                321: __('You have been removed from this groupchat because of an affiliation change'),
-                322: __(
-                    "You have been removed from this groupchat because the groupchat has changed to members-only and you're not a member"
-                ),
-                332: __('You have been removed from this groupchat because the service hosting it is being shut down'),
-            },
+        const STATUS_CODE_MESSAGES = {
+            '100': __('This groupchat is not anonymous'),
+            '102': __('This groupchat now shows unavailable members'),
+            '103': __('This groupchat does not show unavailable members'),
+            '104': __('The groupchat configuration has changed'),
+            '170': __('Groupchat logging is now enabled'),
+            '171': __('Groupchat logging is now disabled'),
+            '172': __('This groupchat is now no longer anonymous'),
+            '173': __('This groupchat is now semi-anonymous'),
+            '174': __('This groupchat is now fully-anonymous'),
+            '201': __('A new groupchat has been created'),
+            // XXX: Note the triple underscore function and not double underscore.
+            '210': ___('Your nickname has been automatically set to %1$s'),
+            '301': __('You have been banned from this groupchat'),
+            // XXX: Note the triple underscore function and not double underscore.
+            '303': ___('Your nickname has been changed to %1$s'),
+            '307': __('You have been kicked from this groupchat'),
+            '321': __('You have been removed from this groupchat because of an affiliation change'),
+            '322': __("You have been removed from this groupchat because it has changed to members-only and you're not a member"),
+            '332': __('You have been removed from this groupchat because the service hosting it is being shut down'),
+            '333': __('You have exited this groupchat due to a technical problem'),
         };
-
-        const labels = { muc: MUC_FEEDBACK_MESSAGES };
+        const labels = { muc: { STATUS_CODE_MESSAGES }};
         Object.assign(_converse.labels, labels);
         Object.assign(_converse, labels); // XXX DEPRECATED
 

+ 57 - 8
src/headless/plugins/muc/types.ts

@@ -1,5 +1,28 @@
 import { CHAT_STATES } from '../../shared/constants';
 import { MessageAttributes } from '../chat/types';
+import MUC from './muc';
+
+export type MUCStatusCode =
+    | '100'
+    | '101'
+    | '102'
+    | '103'
+    | '104'
+    | '110'
+    | '170'
+    | '171'
+    | '172'
+    | '173'
+    | '174'
+    | '201'
+    | '210'
+    | '301'
+    | '303'
+    | '307'
+    | '321'
+    | '322'
+    | '332'
+    | '333';
 
 export type DefaultMUCAttributes = {
     bookmarked: boolean;
@@ -17,6 +40,13 @@ export type DefaultMUCAttributes = {
     type: 'chatroom';
 };
 
+// An object containing the parsed {@link MUCMessageAttributes} and current {@link MUC}.
+export type MUCMessageEventData = {
+    stanza: Element;
+    attrs: MUCMessageAttributes;
+    chatbox: MUC;
+}
+
 export type MUCAttributes = DefaultMUCAttributes & {
     jid: string;
     nick: string;
@@ -32,16 +62,20 @@ type ExtraMUCAttributes = {
     moderated_id: string; // The  XEP-0359 Stanza ID of the message that this one moderates
     moderation_reason: string; // The reason provided why this message moderates another
     occupant_id: string; // The XEP-0421 occupant ID
+    codes: MUCStatusCode[];
 };
 
 export type MUCMessageAttributes = MessageAttributes & ExtraMUCAttributes;
 
+export type MUCAffiliation = 'owner'|'admin'|'member'|'outcast'|'none';
+export type MUCRole = 'moderator'|'participant'|'visitor'|'none';
+
 /**
  * Either the JID or the nickname (or both) will be available.
  */
 export type MemberListItem = {
-    affiliation: string;
-    role?: string;
+    affiliation: MUCAffiliation;
+    role?: MUCRole;
     jid?: string;
     nick?: string;
 };
@@ -54,14 +88,29 @@ export type MUCHat = {
     uri: string;
 };
 
-export type MUCPresenceAttributes = {
-    show: string;
-    hats: Array<MUCHat>; // An array of XEP-0317 hats
-    states: Array<string>;
+export type MUCPresenceItemAttributes = {
+    actor?: {
+        nick?: string;
+        jid?: string;
+    };
+    affiliation?: MUCAffiliation;
+    jid?: string;
+    nick: string;
+    reason?: string;
+    role?: MUCRole;
+}
+
+export type MUCPresenceAttributes = MUCPresenceItemAttributes & {
+    codes: MUCStatusCode[];
     from: string; // The sender JID (${muc_jid}/${nick})
+    hats: Array<MUCHat>; // An array of XEP-0317 hats
+    image_hash?: string;
+    is_me?: boolean;
+    is_self: boolean;
     nick: string; // The nickname of the sender
     occupant_id: string; // The XEP-0421 occupant ID
+    show: string;
+    states: Array<string>;
+    status?: string;
     type: string; // The type of presence
-    jid?: string;
-    is_me?: boolean;
 };

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

@@ -159,7 +159,7 @@ export function getDefaultMUCNickname () {
 /**
  * Determines info message visibility based on
  * muc_show_info_messages configuration setting
- * @param {*} code
+ * @param {import('./types').MUCStatusCode} code
  * @memberOf _converse
  */
 export function isInfoVisible (code) {

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

@@ -1,10 +1,34 @@
 export const ACTION_INFO_CODES: string[];
+export const NEW_NICK_CODES: string[];
 export const ADMIN_COMMANDS: string[];
 export const AFFILIATIONS: string[];
+export const DISCONNECT_CODES: string[];
 export const MODERATOR_COMMANDS: string[];
 export const OWNER_COMMANDS: string[];
 export const ROLES: string[];
 export const VISITOR_COMMANDS: string[];
+export const STATUS_CODE_STANZAS: {
+    '100': string[];
+    '101': string[];
+    '102': string[];
+    '103': string[];
+    '104': string[];
+    '110': string[];
+    '170': string[];
+    '171': string[];
+    '172': string[];
+    '173': string[];
+    '174': string[];
+    '201': string[];
+    '210': string[];
+    '301': string[];
+    '303': string[];
+    '307': string[];
+    '321': string[];
+    '322': string[];
+    '332': string[];
+    '333': string[];
+};
 export namespace MUC_ROLE_WEIGHTS {
     let moderator: number;
     let participant: number;

+ 29 - 18
src/headless/types/plugins/muc/muc.d.ts

@@ -209,6 +209,7 @@ declare class MUC extends MUC_base {
      * @typedef {import('./types').MemberListItem} MemberListItem
      * @typedef {import('../chat/types').MessageAttributes} MessageAttributes
      * @typedef {import('./types').MUCMessageAttributes} MUCMessageAttributes
+     * @typedef {import('./types').MUCPresenceAttributes} MUCPresenceAttributes
      * @typedef {module:shared.converse.UserMessage} UserMessage
      * @typedef {import('strophe.js').Builder} Builder
      * @typedef {import('../../shared/errors').StanzaParseError} StanzaParseError
@@ -621,10 +622,13 @@ declare class MUC extends MUC_base {
     sendUnregistrationIQ(): any;
     /**
      * Given a presence stanza, update the occupant model based on its contents.
-     * @param {Element} pres - The presence stanza
+     * @param {MUCPresenceAttributes} attrs - The presence stanza
      */
-    updateOccupantsOnPresence(pres: Element): boolean;
-    fetchFeaturesIfConfigurationChanged(stanza: any): void;
+    updateOccupantsOnPresence(attrs: import("./types").MUCPresenceAttributes): boolean;
+    /**
+     * @param {MUCMessageAttributes} attrs
+     */
+    fetchFeaturesIfConfigurationChanged(attrs: import("./types").MUCMessageAttributes): void;
     /**
      * Given two JIDs, which can be either user JIDs or MUC occupant JIDs,
      * determine whether they belong to the same user.
@@ -761,24 +765,31 @@ declare class MUC extends MUC_base {
     handleModifyError(pres: Element): void;
     /**
      * Handle a presence stanza that disconnects the user from the MUC
-     * @param { Element } stanza
+     * @param {MUCPresenceAttributes} attrs - The stanza
+     */
+    handleDisconnection(attrs: import("./types").MUCPresenceAttributes): void;
+    /**
+     * @param {import('./types').MUCStatusCode} code
+     * @param {MUCPresenceAttributes} attrs
      */
-    handleDisconnection(stanza: Element): void;
-    getActionInfoMessage(code: any, nick: any, actor: any): any;
-    createAffiliationChangeMessage(occupant: any): void;
+    getActionInfoMessage(code: import("./types").MUCStatusCode, attrs: import("./types").MUCPresenceAttributes): any;
+    /**
+     * @param {MUCOccupant} occupant
+     */
+    createAffiliationChangeMessage(occupant: import("./occupant.js").default): void;
     createRoleChangeMessage(occupant: any, changed: any): void;
     /**
-     * Create an info message based on a received MUC status code
-     * @param {string} code - The MUC status code
-     * @param {Element} stanza - The original stanza that contains the code
-     * @param {Boolean} is_self - Whether this stanza refers to our own presence
+     * Create an info message based on a received MUC status code in a
+     * <presence> stanza.
+     * @param {import('./types').MUCStatusCode} code
+     * @param {MUCPresenceAttributes} attrs - The original stanza
      */
-    createInfoMessage(code: string, stanza: Element, is_self: boolean): void;
+    createInfoMessageFromPresence(code: import("./types").MUCStatusCode, attrs: import("./types").MUCPresenceAttributes): void;
     /**
-     * Create info messages based on a received presence or message stanza
-     * @param {Element} stanza
+     * Create an info message based on a received MUC status code in a <message> stanza.
+     * @param {import('./types').MUCStatusCode} code
      */
-    createInfoMessages(stanza: Element): void;
+    createInfoMessage(code: import("./types").MUCStatusCode): void;
     /**
      * Set parameters regarding disconnection from this room. This helps to
      * communicate to the user why they were disconnected.
@@ -797,7 +808,7 @@ declare class MUC extends MUC_base {
      * Parses a <presence> stanza with type "error" and sets the proper
      * `connection_status` value for this {@link MUC} as
      * well as any additional output that can be shown to the user.
-     * @param { Element } stanza - The presence stanza
+     * @param {Element} stanza - The presence stanza
      */
     onErrorPresence(stanza: Element): void;
     /**
@@ -821,9 +832,9 @@ declare class MUC extends MUC_base {
      * If the groupchat is not locked, then the groupchat will be
      * auto-configured only if applicable and if the current
      * user is the groupchat's owner.
-     * @param {Element} stanza - The stanza
+     * @param {MUCPresenceAttributes} attrs
      */
-    onOwnPresence(stanza: Element): Promise<void>;
+    onOwnPresence(attrs: import("./types").MUCPresenceAttributes): Promise<void>;
     /**
      * Returns a boolean to indicate whether the current user
      * was mentioned in a message.

+ 32 - 8
src/headless/types/plugins/muc/types.d.ts

@@ -1,5 +1,7 @@
 import { CHAT_STATES } from '../../shared/constants';
 import { MessageAttributes } from '../chat/types';
+import MUC from './muc';
+export type MUCStatusCode = '100' | '101' | '102' | '103' | '104' | '110' | '170' | '171' | '172' | '173' | '174' | '201' | '210' | '301' | '303' | '307' | '321' | '322' | '332' | '333';
 export type DefaultMUCAttributes = {
     bookmarked: boolean;
     chat_state: typeof CHAT_STATES;
@@ -15,6 +17,11 @@ export type DefaultMUCAttributes = {
     time_sent: string;
     type: 'chatroom';
 };
+export type MUCMessageEventData = {
+    stanza: Element;
+    attrs: MUCMessageAttributes;
+    chatbox: MUC;
+};
 export type MUCAttributes = DefaultMUCAttributes & {
     jid: string;
     nick: string;
@@ -29,14 +36,17 @@ type ExtraMUCAttributes = {
     moderated_id: string;
     moderation_reason: string;
     occupant_id: string;
+    codes: MUCStatusCode[];
 };
 export type MUCMessageAttributes = MessageAttributes & ExtraMUCAttributes;
+export type MUCAffiliation = 'owner' | 'admin' | 'member' | 'outcast' | 'none';
+export type MUCRole = 'moderator' | 'participant' | 'visitor' | 'none';
 /**
  * Either the JID or the nickname (or both) will be available.
  */
 export type MemberListItem = {
-    affiliation: string;
-    role?: string;
+    affiliation: MUCAffiliation;
+    role?: MUCRole;
     jid?: string;
     nick?: string;
 };
@@ -47,16 +57,30 @@ export type MUCHat = {
     title: string;
     uri: string;
 };
-export type MUCPresenceAttributes = {
-    show: string;
-    hats: Array<MUCHat>;
-    states: Array<string>;
+export type MUCPresenceItemAttributes = {
+    actor?: {
+        nick?: string;
+        jid?: string;
+    };
+    affiliation?: MUCAffiliation;
+    jid?: string;
+    nick: string;
+    reason?: string;
+    role?: MUCRole;
+};
+export type MUCPresenceAttributes = MUCPresenceItemAttributes & {
+    codes: MUCStatusCode[];
     from: string;
+    hats: Array<MUCHat>;
+    image_hash?: string;
+    is_me?: boolean;
+    is_self: boolean;
     nick: string;
     occupant_id: string;
+    show: string;
+    states: Array<string>;
+    status?: string;
     type: string;
-    jid?: string;
-    is_me?: boolean;
 };
 export {};
 //# sourceMappingURL=types.d.ts.map

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

@@ -28,10 +28,10 @@ export function getDefaultMUCNickname(): any;
 /**
  * Determines info message visibility based on
  * muc_show_info_messages configuration setting
- * @param {*} code
+ * @param {import('./types').MUCStatusCode} code
  * @memberOf _converse
  */
-export function isInfoVisible(code: any): boolean;
+export function isInfoVisible(code: import("./types").MUCStatusCode): boolean;
 /**
  * Automatically join groupchats, based on the
  * "auto_join_rooms" configuration setting, which is an array

+ 7 - 10
src/plugins/muc-views/tests/info-messages.js

@@ -1,5 +1,4 @@
 /*global mock, converse */
-
 const { u, stx } = converse.env;
 
 describe("an info message", function () {
@@ -52,18 +51,16 @@ describe("an info message", function () {
                     <status code="110"/>
                 </x>
             </presence>`;
-        // XXX: We wait for createInfoMessages to complete, if we don't
-        // we still get two info messages due to messages
-        // created from presences not being queued and run
-        // sequentially (i.e. by waiting for promises to resolve)
-        // like we do with message stanzas.
-        spyOn(view.model, 'createInfoMessages').and.callThrough();
         _converse.api.connection.get()._dataRecv(mock.createRequest(presence));
-        await u.waitUntil(() => view.model.createInfoMessages.calls.count());
         await u.waitUntil(() => view.querySelectorAll('.chat-info').length === 1);
 
         _converse.api.connection.get()._dataRecv(mock.createRequest(presence));
-        await u.waitUntil(() => view.model.createInfoMessages.calls.count() === 2);
-        expect(view.querySelectorAll('.chat-info').length).toBe(1);
+
+        const promise = u.getOpenPromise();
+        setTimeout(() => {
+            expect(view.querySelectorAll('.chat-info').length).toBe(1);
+            promise.resolve();
+        }, 250);
+        return promise;
     }));
 });

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

@@ -149,7 +149,7 @@ describe("A MUC", function () {
         await u.waitUntil(() => view.querySelectorAll('.chat-info').length);
 
         expect(sizzle('div.chat-info:last').pop().textContent.trim()).toBe(
-            __(_converse.muc.new_nickname_messages["303"], "newnick")
+            __(_converse.labels.muc.STATUS_CODE_MESSAGES["303"], "newnick")
         );
         expect(view.model.session.get('connection_status')).toBe(converse.ROOMSTATUS.ENTERED);
 
@@ -174,7 +174,7 @@ describe("A MUC", function () {
         expect(view.model.session.get('connection_status')).toBe(converse.ROOMSTATUS.ENTERED);
         expect(view.querySelectorAll('div.chat-info').length).toBe(1);
         expect(sizzle('div.chat-info', view)[0].textContent.trim()).toBe(
-            __(_converse.muc.new_nickname_messages["303"], "newnick")
+            __(_converse.labels.muc.STATUS_CODE_MESSAGES["303"], "newnick")
         );
         occupants = view.querySelector('.occupant-list');
         await u.waitUntil(() => sizzle('.occupant-nick:first', occupants).pop().textContent.trim() === "newnick");

Vissa filer visades inte eftersom för många filer har ändrats