import isObject from 'lodash-es/isObject';
import log from "@converse/headless/log.js";
import { ROLES } from './constants.js';
import { _converse, api, converse } from '@converse/headless/core.js';
import { safeSave } from '@converse/headless/utils/core.js';
const { Strophe, sizzle, u } = converse.env;
export function getAutoFetchedAffiliationLists () {
const affs = api.settings.get('muc_fetch_members');
return Array.isArray(affs) ? affs : affs ? ['member', 'admin', 'owner'] : [];
}
/**
* Given an occupant model, see which roles may be assigned to that user.
* @param { Model } occupant
* @returns { Array<('moderator'|'participant'|'visitor')> } - An array of assignable roles
*/
export function getAssignableRoles (occupant) {
let disabled = api.settings.get('modtools_disable_assign');
if (!Array.isArray(disabled)) {
disabled = disabled ? ROLES : [];
}
if (occupant.get('role') === 'moderator') {
return ROLES.filter(r => !disabled.includes(r));
} else {
return [];
}
}
export function registerDirectInvitationHandler () {
_converse.connection.addHandler(
message => {
_converse.onDirectMUCInvitation(message);
return true;
},
'jabber:x:conference',
'message'
);
}
export function disconnectChatRooms () {
/* When disconnecting, mark all groupchats as
* disconnected, so that they will be properly entered again
* when fetched from session storage.
*/
return _converse.chatboxes
.filter(m => m.get('type') === _converse.CHATROOMS_TYPE)
.forEach(m => m.session.save({ 'connection_status': converse.ROOMSTATUS.DISCONNECTED }));
}
export async function onWindowStateChanged (data) {
if (data.state === 'visible' && api.connection.connected()) {
const rooms = await api.rooms.get();
rooms.forEach(room => room.rejoinIfNecessary());
}
}
export async function routeToRoom (jid) {
if (!u.isValidMUCJID(jid)) {
return log.warn(`invalid jid "${jid}" provided in url fragment`);
}
await api.waitUntil('roomsAutoJoined');
if (api.settings.get('allow_bookmarks')) {
await api.waitUntil('bookmarksInitialized');
}
api.rooms.open(jid);
}
/* Opens a groupchat, making sure that certain attributes
* are correct, for example that the "type" is set to
* "chatroom".
*/
export async function openChatRoom (jid, settings) {
settings.type = _converse.CHATROOMS_TYPE;
settings.id = jid;
const chatbox = await api.rooms.get(jid, settings, true);
chatbox.maybeShow(true);
return chatbox;
}
/**
* A direct MUC invitation to join a groupchat has been received
* See XEP-0249: Direct MUC invitations.
* @private
* @method _converse.ChatRoom#onDirectMUCInvitation
* @param { Element } message - The message stanza containing the invitation.
*/
export async function onDirectMUCInvitation (message) {
const x_el = sizzle('x[xmlns="jabber:x:conference"]', message).pop(),
from = Strophe.getBareJidFromJid(message.getAttribute('from')),
room_jid = x_el.getAttribute('jid'),
reason = x_el.getAttribute('reason');
let result;
if (api.settings.get('auto_join_on_invite')) {
result = true;
} else {
// Invite request might come from someone not your roster list
const contact = _converse.roster.get(from)?.getDisplayName() ?? from;
/**
* *Hook* which is used to gather confirmation whether a direct MUC
* invitation should be accepted or not.
*
* It's meant for consumers of `@converse/headless` to subscribe to
* this hook and then ask the user to confirm.
*
* @event _converse#confirmDirectMUCInvitation
*/
result = await api.hook('confirmDirectMUCInvitation', { contact, reason, jid: room_jid }, false);
}
if (result) {
const chatroom = await openChatRoom(room_jid, { 'password': x_el.getAttribute('password') });
if (chatroom.session.get('connection_status') === converse.ROOMSTATUS.DISCONNECTED) {
_converse.chatboxes.get(room_jid).rejoin();
}
}
}
export function getDefaultMUCNickname () {
// XXX: if anything changes here, update the docs for the
// locked_muc_nickname setting.
if (!_converse.xmppstatus) {
throw new Error(
"Can't call _converse.getDefaultMUCNickname before the statusInitialized has been fired."
);
}
const nick = _converse.xmppstatus.getNickname();
if (nick) {
return nick;
} else if (api.settings.get('muc_nickname_from_jid')) {
return Strophe.unescapeNode(Strophe.getNodeFromJid(_converse.bare_jid));
}
}
/**
* Determines info message visibility based on
* muc_show_info_messages configuration setting
* @param {*} code
* @memberOf _converse
*/
export function isInfoVisible (code) {
const info_messages = api.settings.get('muc_show_info_messages');
if (info_messages.includes(code)) {
return true;
}
return false;
}
/**
* Automatically join groupchats, based on the
* "auto_join_rooms" configuration setting, which is an array
* of strings (groupchat JIDs) or objects (with groupchat JID and other settings).
*/
export async function autoJoinRooms () {
await Promise.all(
api.settings.get('auto_join_rooms').map(muc => {
if (typeof muc === 'string') {
if (_converse.chatboxes.where({ 'jid': muc }).length) {
return Promise.resolve();
}
return api.rooms.open(muc);
} else if (isObject(muc)) {
return api.rooms.open(muc.jid, { ...muc });
} else {
log.error('Invalid muc criteria specified for "auto_join_rooms"');
return Promise.resolve();
}
})
);
/**
* Triggered once any rooms that have been configured to be automatically joined,
* specified via the _`auto_join_rooms` setting, have been entered.
* @event _converse#roomsAutoJoined
* @example _converse.api.listen.on('roomsAutoJoined', () => { ... });
* @example _converse.api.waitUntil('roomsAutoJoined').then(() => { ... });
*/
api.trigger('roomsAutoJoined');
}
export function onAddClientFeatures () {
api.disco.own.features.add(Strophe.NS.MUC);
if (api.settings.get('allow_muc_invitations')) {
api.disco.own.features.add('jabber:x:conference'); // Invites
}
}
export function onBeforeTearDown () {
_converse.chatboxes
.where({ 'type': _converse.CHATROOMS_TYPE })
.forEach(muc => safeSave(muc.session, { 'connection_status': converse.ROOMSTATUS.DISCONNECTED }));
}
export function onStatusInitialized () {
window.addEventListener(_converse.unloadevent, () => {
const using_websocket = api.connection.isType('websocket');
if (
using_websocket &&
(!api.settings.get('enable_smacks') || !_converse.session.get('smacks_stream_id'))
) {
// For non-SMACKS websocket connections, or non-resumeable
// connections, we disconnect all chatrooms when the page unloads.
// See issue #1111
disconnectChatRooms();
}
});
}
export function onBeforeResourceBinding () {
_converse.connection.addHandler(
stanza => {
const muc_jid = Strophe.getBareJidFromJid(stanza.getAttribute('from'));
if (!_converse.chatboxes.get(muc_jid)) {
api.waitUntil('chatBoxesFetched').then(async () => {
const muc = _converse.chatboxes.get(muc_jid);
if (muc) {
await muc.initialized;
muc.message_handler.run(stanza);
}
});
}
return true;
},
null,
'message',
'groupchat'
);
}
Object.assign(_converse, { getAssignableRoles });