Refactor _converse.XMPPStatus
out into headless/converse-status.js
Also move some other methods out of `converse-core` into the plugins that use them.
This commit is contained in:
parent
1a7f58b578
commit
b6d5077d04
|
@ -10,6 +10,7 @@
|
|||
- Initial support for sending custom emojis. Currently only between Converse
|
||||
instances. Still working out a wire protocol for compatibility with other clients.
|
||||
To add custom emojis, edit the `emojis.json` file.
|
||||
- Refactor some presence and status handling code from `converse-core` into `@converse/headless/converse-status`.
|
||||
|
||||
### Breaking changes
|
||||
|
||||
|
@ -26,6 +27,7 @@
|
|||
* `_converse.api.rooms.create`
|
||||
|
||||
- The `show_only_online_users` setting has been removed.
|
||||
- The order of certain events have now changed: `statusInitialized` is now triggered after `initialized` and `connected` and `reconnected`.
|
||||
|
||||
## 5.0.4 (2019-10-08)
|
||||
- New config option [allow_message_corrections](https://conversejs.org/docs/html/configuration.html#allow-message-corrections)
|
||||
|
|
|
@ -1138,15 +1138,14 @@
|
|||
describe("A Message Counter", function () {
|
||||
|
||||
it("is incremented when the message is received and the window is not focused",
|
||||
mock.initConverse(
|
||||
['rosterGroupsFetched'], {},
|
||||
async function (done, _converse) {
|
||||
|
||||
mock.initConverse(
|
||||
['rosterGroupsFetched'], {},
|
||||
async function (done, _converse) {
|
||||
|
||||
await test_utils.waitForRoster(_converse, 'current');
|
||||
test_utils.openControlBox();
|
||||
|
||||
expect(_converse.msg_counter).toBe(0);
|
||||
expect(document.title).toBe('Converse Tests');
|
||||
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
const view = await test_utils.openChatBoxFor(_converse, sender_jid)
|
||||
|
@ -1170,7 +1169,7 @@
|
|||
await new Promise(resolve => view.once('messageInserted', resolve));
|
||||
expect(_converse.incrementMsgCounter).toHaveBeenCalled();
|
||||
expect(_converse.clearMsgCounter).not.toHaveBeenCalled();
|
||||
expect(_converse.msg_counter).toBe(1);
|
||||
expect(document.title).toBe('Messages (1) Converse Tests');
|
||||
expect(_converse.api.trigger).toHaveBeenCalledWith('message', jasmine.any(Object));
|
||||
_converse.windowSate = previous_state;
|
||||
done();
|
||||
|
@ -1199,7 +1198,7 @@
|
|||
await test_utils.waitForRoster(_converse, 'current');
|
||||
test_utils.openControlBox();
|
||||
|
||||
expect(_converse.msg_counter).toBe(0);
|
||||
expect(document.title).toBe('Converse Tests');
|
||||
spyOn(_converse, 'incrementMsgCounter').and.callThrough();
|
||||
_converse.saveWindowState(null, 'focus');
|
||||
const message = 'This message will not increment the message counter';
|
||||
|
@ -1213,7 +1212,7 @@
|
|||
.c('active', {'xmlns': Strophe.NS.CHATSTATES}).tree();
|
||||
await _converse.chatboxes.onMessage(msg);
|
||||
expect(_converse.incrementMsgCounter).not.toHaveBeenCalled();
|
||||
expect(_converse.msg_counter).toBe(0);
|
||||
expect(document.title).toBe('Converse Tests');
|
||||
done();
|
||||
}));
|
||||
|
||||
|
@ -1224,7 +1223,7 @@
|
|||
|
||||
await test_utils.waitForRoster(_converse, 'current');
|
||||
// initial state
|
||||
expect(_converse.msg_counter).toBe(0);
|
||||
expect(document.title).toBe('Converse Tests');
|
||||
const message = 'This message will always increment the message counter from zero',
|
||||
sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit',
|
||||
msgFactory = function () {
|
||||
|
@ -1244,12 +1243,12 @@
|
|||
_converse.chatboxes.onMessage(msgFactory());
|
||||
await u.waitUntil(() => _converse.api.chats.get().length === 2)
|
||||
let view = _converse.chatboxviews.get(sender_jid);
|
||||
expect(_converse.msg_counter).toBe(1);
|
||||
expect(document.title).toBe('Messages (1) Converse Tests');
|
||||
|
||||
// come back to converse-chat page
|
||||
_converse.saveWindowState(null, 'focus');
|
||||
expect(u.isVisible(view.el)).toBeTruthy();
|
||||
expect(_converse.msg_counter).toBe(0);
|
||||
expect(document.title).toBe('Converse Tests');
|
||||
|
||||
// close chatbox and leave converse-chat page again
|
||||
view.close();
|
||||
|
@ -1260,7 +1259,7 @@
|
|||
await u.waitUntil(() => _converse.api.chats.get().length === 2)
|
||||
view = _converse.chatboxviews.get(sender_jid);
|
||||
expect(u.isVisible(view.el)).toBeTruthy();
|
||||
expect(_converse.msg_counter).toBe(1);
|
||||
expect(document.title).toBe('Messages (1) Converse Tests');
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -50,16 +50,17 @@
|
|||
|
||||
await test_utils.waitForRoster(_converse, 'current', 1);
|
||||
|
||||
iq = IQ_stanzas.pop();
|
||||
IQ_stanzas.pop();
|
||||
const disco_iq = IQ_stanzas.pop();
|
||||
expect(Strophe.serialize(disco_iq)).toBe(
|
||||
`<iq from="romeo@montague.lit" id="${disco_iq.getAttribute('id')}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+
|
||||
`<pubsub xmlns="http://jabber.org/protocol/pubsub"><items node="eu.siacs.conversations.axolotl.devicelist"/></pubsub></iq>`);
|
||||
|
||||
iq = IQ_stanzas.pop();
|
||||
expect(Strophe.serialize(iq)).toBe(
|
||||
`<iq from="romeo@montague.lit/orchard" id="${iq.getAttribute('id')}" to="montague.lit" type="get" xmlns="jabber:client">`+
|
||||
`<query xmlns="http://jabber.org/protocol/disco#info"/></iq>`);
|
||||
|
||||
const disco_iq = IQ_stanzas.pop();
|
||||
expect(Strophe.serialize(disco_iq)).toBe(
|
||||
`<iq from="romeo@montague.lit" id="${disco_iq.getAttribute('id')}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+
|
||||
`<pubsub xmlns="http://jabber.org/protocol/pubsub"><items node="eu.siacs.conversations.axolotl.devicelist"/></pubsub></iq>`);
|
||||
|
||||
expect(sent_stanzas.filter(s => (s.nodeName === 'r')).length).toBe(2);
|
||||
expect(_converse.session.get('unacked_stanzas').length).toBe(5);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
/**
|
||||
* @module converse-profile
|
||||
*/
|
||||
import "@converse/headless/converse-status";
|
||||
import "@converse/headless/converse-vcard";
|
||||
import "converse-modal";
|
||||
import "formdata-polyfill";
|
||||
|
@ -23,7 +24,7 @@ const u = converse.env.utils;
|
|||
|
||||
converse.plugins.add('converse-profile', {
|
||||
|
||||
dependencies: ["converse-modal", "converse-vcard", "converse-chatboxviews"],
|
||||
dependencies: ["converse-status", "converse-modal", "converse-vcard", "converse-chatboxviews"],
|
||||
|
||||
initialize () {
|
||||
/* The initialize function gets called as soon as the plugin is
|
||||
|
|
|
@ -49,6 +49,33 @@ converse.plugins.add('converse-chatboxes', {
|
|||
'privateChatsAutoJoined'
|
||||
]);
|
||||
|
||||
let msg_counter = 0;
|
||||
|
||||
_converse.incrementMsgCounter = function () {
|
||||
msg_counter += 1;
|
||||
const title = document.title;
|
||||
if (!title) {
|
||||
return;
|
||||
}
|
||||
if (title.search(/^Messages \(\d+\) /) === -1) {
|
||||
document.title = `Messages (${msg_counter}) ${title}`;
|
||||
} else {
|
||||
document.title = title.replace(/^Messages \(\d+\) /, `Messages (${msg_counter})`);
|
||||
}
|
||||
};
|
||||
|
||||
_converse.clearMsgCounter = function () {
|
||||
msg_counter = 0;
|
||||
const title = document.title;
|
||||
if (!title) {
|
||||
return;
|
||||
}
|
||||
if (title.search(/^Messages \(\d+\) /) !== -1) {
|
||||
document.title = title.replace(/^Messages \(\d+\) /, "");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function openChat (jid) {
|
||||
if (!utils.isValidJID(jid)) {
|
||||
return _converse.log(
|
||||
|
@ -1405,6 +1432,7 @@ converse.plugins.add('converse-chatboxes', {
|
|||
|
||||
_converse.api.listen.on('presencesInitialized', (reconnecting) => _converse.chatboxes.onConnected(reconnecting));
|
||||
_converse.api.listen.on('reconnected', () => _converse.chatboxes.forEach(m => m.onReconnection()));
|
||||
_converse.api.listen.on('windowStateChanged', d => (d.state === 'visible') && _converse.clearMsgCounter());
|
||||
/************************ END Event Handlers ************************/
|
||||
|
||||
|
||||
|
|
|
@ -86,6 +86,7 @@ const CORE_PLUGINS = [
|
|||
'converse-roster',
|
||||
'converse-rsm',
|
||||
'converse-smacks',
|
||||
'converse-status',
|
||||
'converse-vcard'
|
||||
];
|
||||
|
||||
|
@ -560,11 +561,6 @@ function clearSession () {
|
|||
_converse.session.browserStorage._clear();
|
||||
delete _converse.session;
|
||||
}
|
||||
if (_converse.shouldClearCache() && _converse.xmppstatus) {
|
||||
_converse.xmppstatus.destroy();
|
||||
_converse.xmppstatus.browserStorage._clear();
|
||||
delete _converse.xmppstatus;
|
||||
}
|
||||
/**
|
||||
* Triggered once the user session has been cleared,
|
||||
* for example when the user has logged out or when Converse has
|
||||
|
@ -739,6 +735,33 @@ _converse.setUserJID = async function (jid) {
|
|||
}
|
||||
|
||||
|
||||
function enableCarbons () {
|
||||
/* Ask the XMPP server to enable Message Carbons
|
||||
* See XEP-0280 https://xmpp.org/extensions/xep-0280.html#enabling
|
||||
*/
|
||||
if (!_converse.message_carbons || !_converse.session || _converse.session.get('carbons_enabled')) {
|
||||
return;
|
||||
}
|
||||
const carbons_iq = new Strophe.Builder('iq', {
|
||||
'from': _converse.connection.jid,
|
||||
'id': 'enablecarbons',
|
||||
'type': 'set'
|
||||
})
|
||||
.c('enable', {xmlns: Strophe.NS.CARBONS});
|
||||
_converse.connection.addHandler((iq) => {
|
||||
if (iq.querySelectorAll('error').length > 0) {
|
||||
_converse.log(
|
||||
'An error occurred while trying to enable message carbons.',
|
||||
Strophe.LogLevel.WARN);
|
||||
} else {
|
||||
_converse.session.save({'carbons_enabled': true});
|
||||
_converse.log('Message carbons have been enabled.');
|
||||
}
|
||||
}, null, "iq", null, "enablecarbons");
|
||||
_converse.connection.send(carbons_iq);
|
||||
}
|
||||
|
||||
|
||||
async function onConnected (reconnecting) {
|
||||
/* Called as soon as a new connection has been established, either
|
||||
* by logging in or by attaching to an existing BOSH session.
|
||||
|
@ -751,9 +774,33 @@ async function onConnected (reconnecting) {
|
|||
* user's JID resource for this session.
|
||||
* @event _converse#afterResourceBinding
|
||||
*/
|
||||
await _converse.api.trigger('afterResourceBinding', {'synchronous': true});
|
||||
_converse.enableCarbons();
|
||||
_converse.initStatus(reconnecting)
|
||||
await _converse.api.trigger('afterResourceBinding', reconnecting, {'synchronous': true});
|
||||
enableCarbons();
|
||||
|
||||
if (reconnecting) {
|
||||
/**
|
||||
* After the connection has dropped and converse.js has reconnected.
|
||||
* Any Strophe stanza handlers (as registered via `converse.listen.stanza`) will
|
||||
* have to be registered anew.
|
||||
* @event _converse#reconnected
|
||||
* @example _converse.api.listen.on('reconnected', () => { ... });
|
||||
*/
|
||||
_converse.api.trigger('reconnected');
|
||||
} else {
|
||||
/**
|
||||
* Triggered once converse.js has been initialized.
|
||||
* See also {@link _converse#event:pluginsInitialized}.
|
||||
* @event _converse#initialized
|
||||
*/
|
||||
_converse.api.trigger('initialized');
|
||||
/**
|
||||
* Triggered after the connection has been established and Converse
|
||||
* has got all its ducks in a row.
|
||||
* @event _converse#initialized
|
||||
*/
|
||||
_converse.api.trigger('connected');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -895,7 +942,6 @@ _converse.initialize = async function (settings, callback) {
|
|||
cleanup();
|
||||
|
||||
settings = settings !== undefined ? settings : {};
|
||||
const init_promise = u.getResolveablePromise();
|
||||
PROMISES.forEach(addPromise);
|
||||
|
||||
if ('onpagehide' in window) {
|
||||
|
@ -955,7 +1001,6 @@ _converse.initialize = async function (settings, callback) {
|
|||
* https://github.com/jcbrand/converse.js/issues/521
|
||||
*/
|
||||
this.send_initial_presence = true;
|
||||
this.msg_counter = 0;
|
||||
this.user_settings = settings; // Save the user settings so that they can be used by plugins
|
||||
|
||||
// Module-level functions
|
||||
|
@ -963,107 +1008,6 @@ _converse.initialize = async function (settings, callback) {
|
|||
|
||||
this.generateResource = () => `/converse.js-${Math.floor(Math.random()*139749528).toString()}`;
|
||||
|
||||
/**
|
||||
* Send out a Client State Indication (XEP-0352)
|
||||
* @private
|
||||
* @method sendCSI
|
||||
* @memberOf _converse
|
||||
* @param { String } stat - The user's chat status
|
||||
*/
|
||||
this.sendCSI = function (stat) {
|
||||
_converse.api.send($build(stat, {xmlns: Strophe.NS.CSI}));
|
||||
_converse.inactive = (stat === _converse.INACTIVE) ? true : false;
|
||||
};
|
||||
|
||||
this.onUserActivity = function () {
|
||||
/* Resets counters and flags relating to CSI and auto_away/auto_xa */
|
||||
if (_converse.idle_seconds > 0) {
|
||||
_converse.idle_seconds = 0;
|
||||
}
|
||||
if (!_.get(_converse.connection, 'authenticated')) {
|
||||
// We can't send out any stanzas when there's no authenticated connection.
|
||||
// This can happen when the connection reconnects.
|
||||
return;
|
||||
}
|
||||
if (_converse.inactive) {
|
||||
_converse.sendCSI(_converse.ACTIVE);
|
||||
}
|
||||
if (_converse.idle) {
|
||||
_converse.idle = false;
|
||||
_converse.xmppstatus.sendPresence();
|
||||
}
|
||||
if (_converse.auto_changed_status === true) {
|
||||
_converse.auto_changed_status = false;
|
||||
// XXX: we should really remember the original state here, and
|
||||
// then set it back to that...
|
||||
_converse.xmppstatus.set('status', _converse.default_state);
|
||||
}
|
||||
};
|
||||
|
||||
this.onEverySecond = function () {
|
||||
/* An interval handler running every second.
|
||||
* Used for CSI and the auto_away and auto_xa features.
|
||||
*/
|
||||
if (!_.get(_converse.connection, 'authenticated')) {
|
||||
// We can't send out any stanzas when there's no authenticated connection.
|
||||
// This can happen when the connection reconnects.
|
||||
return;
|
||||
}
|
||||
const stat = _converse.xmppstatus.get('status');
|
||||
_converse.idle_seconds++;
|
||||
if (_converse.csi_waiting_time > 0 &&
|
||||
_converse.idle_seconds > _converse.csi_waiting_time &&
|
||||
!_converse.inactive) {
|
||||
_converse.sendCSI(_converse.INACTIVE);
|
||||
}
|
||||
if (_converse.idle_presence_timeout > 0 &&
|
||||
_converse.idle_seconds > _converse.idle_presence_timeout &&
|
||||
!_converse.idle) {
|
||||
_converse.idle = true;
|
||||
_converse.xmppstatus.sendPresence();
|
||||
}
|
||||
if (_converse.auto_away > 0 &&
|
||||
_converse.idle_seconds > _converse.auto_away &&
|
||||
stat !== 'away' && stat !== 'xa' && stat !== 'dnd') {
|
||||
_converse.auto_changed_status = true;
|
||||
_converse.xmppstatus.set('status', 'away');
|
||||
} else if (_converse.auto_xa > 0 &&
|
||||
_converse.idle_seconds > _converse.auto_xa &&
|
||||
stat !== 'xa' && stat !== 'dnd') {
|
||||
_converse.auto_changed_status = true;
|
||||
_converse.xmppstatus.set('status', 'xa');
|
||||
}
|
||||
};
|
||||
|
||||
this.registerIntervalHandler = function () {
|
||||
/* Set an interval of one second and register a handler for it.
|
||||
* Required for the auto_away, auto_xa and csi_waiting_time features.
|
||||
*/
|
||||
if (
|
||||
_converse.auto_away < 1 &&
|
||||
_converse.auto_xa < 1 &&
|
||||
_converse.csi_waiting_time < 1 &&
|
||||
_converse.idle_presence_timeout < 1
|
||||
) {
|
||||
// Waiting time of less then one second means features aren't used.
|
||||
return;
|
||||
}
|
||||
_converse.idle_seconds = 0;
|
||||
_converse.auto_changed_status = false; // Was the user's status changed by Converse?
|
||||
window.addEventListener('click', _converse.onUserActivity);
|
||||
window.addEventListener('focus', _converse.onUserActivity);
|
||||
window.addEventListener('keypress', _converse.onUserActivity);
|
||||
window.addEventListener('mousemove', _converse.onUserActivity);
|
||||
const options = {'once': true, 'passive': true};
|
||||
window.addEventListener(_converse.unloadevent, _converse.onUserActivity, options);
|
||||
window.addEventListener(_converse.unloadevent, () => {
|
||||
if (_converse.session) {
|
||||
_converse.session.save('active', false);
|
||||
}
|
||||
});
|
||||
_converse.everySecondTrigger = window.setInterval(_converse.onEverySecond, 1000);
|
||||
};
|
||||
|
||||
this.setConnectionStatus = function (connection_status, message) {
|
||||
_converse.connfeedback.set({
|
||||
'connection_status': connection_status,
|
||||
|
@ -1071,21 +1015,6 @@ _converse.initialize = async function (settings, callback) {
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Reject or cancel another user's subscription to our presence updates.
|
||||
* @method rejectPresenceSubscription
|
||||
* @private
|
||||
* @memberOf _converse
|
||||
* @param { String } jid - The Jabber ID of the user whose subscription is being canceled
|
||||
* @param { String } message - An optional message to the user
|
||||
*/
|
||||
this.rejectPresenceSubscription = function (jid, message) {
|
||||
const pres = $pres({to: jid, type: "unsubscribed"});
|
||||
if (message && message !== "") { pres.c("status").t(message); }
|
||||
_converse.api.send(pres);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets called once strophe's status reaches Strophe.Status.DISCONNECTED.
|
||||
* Will either start a teardown process for converse.js or attempt
|
||||
|
@ -1134,11 +1063,15 @@ _converse.initialize = async function (settings, callback) {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback method called by Strophe as the Strophe.Connection goes
|
||||
* through various states while establishing or tearing down a
|
||||
* connection.
|
||||
* @method _converse#onConnectStatusChanged
|
||||
* @private
|
||||
* @memberOf _converse
|
||||
*/
|
||||
this.onConnectStatusChanged = function (status, message) {
|
||||
/* Callback method called by Strophe as the Strophe.Connection goes
|
||||
* through various states while establishing or tearing down a
|
||||
* connection.
|
||||
*/
|
||||
_converse.log(`Status changed to: ${_converse.CONNECTION_STATUS[status]}`);
|
||||
if (status === Strophe.Status.CONNECTED || status === Strophe.Status.ATTACHED) {
|
||||
_converse.setConnectionStatus(status);
|
||||
|
@ -1193,49 +1126,6 @@ _converse.initialize = async function (settings, callback) {
|
|||
}
|
||||
};
|
||||
|
||||
this.incrementMsgCounter = function () {
|
||||
this.msg_counter += 1;
|
||||
const unreadMsgCount = this.msg_counter;
|
||||
let title = document.title;
|
||||
if (!title) {
|
||||
return;
|
||||
}
|
||||
if (title.search(/^Messages \(\d+\) /) === -1) {
|
||||
title = `Messages (${unreadMsgCount}) ${title}`;
|
||||
} else {
|
||||
title = title.replace(/^Messages \(\d+\) /, `Messages (${unreadMsgCount})`);
|
||||
}
|
||||
};
|
||||
|
||||
this.clearMsgCounter = function () {
|
||||
this.msg_counter = 0;
|
||||
let title = document.title;
|
||||
if (!title) {
|
||||
return;
|
||||
}
|
||||
if (title.search(/^Messages \(\d+\) /) !== -1) {
|
||||
title = title.replace(/^Messages \(\d+\) /, "");
|
||||
}
|
||||
};
|
||||
|
||||
this.initStatus = (reconnecting) => {
|
||||
// If there's no xmppstatus obj, then we were never connected to
|
||||
// begin with, so we set reconnecting to false.
|
||||
reconnecting = _converse.xmppstatus === undefined ? false : reconnecting;
|
||||
if (reconnecting) {
|
||||
_converse.onStatusInitialized(reconnecting);
|
||||
} else {
|
||||
const id = `converse.xmppstatus-${_converse.bare_jid}`;
|
||||
_converse.xmppstatus = new this.XMPPStatus({'id': id});
|
||||
_converse.xmppstatus.browserStorage = _converse.createStore(id, "session");
|
||||
_converse.xmppstatus.fetch({
|
||||
'success': () => _converse.onStatusInitialized(reconnecting),
|
||||
'error': () => _converse.onStatusInitialized(reconnecting),
|
||||
'silent': true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.saveWindowState = function (ev) {
|
||||
// XXX: eventually we should be able to just use
|
||||
// document.visibilityState (when we drop support for older
|
||||
|
@ -1255,9 +1145,6 @@ _converse.initialize = async function (settings, callback) {
|
|||
} else {
|
||||
state = document.hidden ? "hidden" : "visible";
|
||||
}
|
||||
if (state === 'visible') {
|
||||
_converse.clearMsgCounter();
|
||||
}
|
||||
_converse.windowState = state;
|
||||
/**
|
||||
* Triggered when window state has changed.
|
||||
|
@ -1284,73 +1171,6 @@ _converse.initialize = async function (settings, callback) {
|
|||
_converse.api.trigger('registeredGlobalEventHandlers');
|
||||
};
|
||||
|
||||
this.enableCarbons = function () {
|
||||
/* Ask the XMPP server to enable Message Carbons
|
||||
* See XEP-0280 https://xmpp.org/extensions/xep-0280.html#enabling
|
||||
*/
|
||||
if (!this.message_carbons || !this.session || this.session.get('carbons_enabled')) {
|
||||
return;
|
||||
}
|
||||
const carbons_iq = new Strophe.Builder('iq', {
|
||||
'from': this.connection.jid,
|
||||
'id': 'enablecarbons',
|
||||
'type': 'set'
|
||||
})
|
||||
.c('enable', {xmlns: Strophe.NS.CARBONS});
|
||||
this.connection.addHandler((iq) => {
|
||||
if (iq.querySelectorAll('error').length > 0) {
|
||||
_converse.log(
|
||||
'An error occurred while trying to enable message carbons.',
|
||||
Strophe.LogLevel.WARN);
|
||||
} else {
|
||||
this.session.save({'carbons_enabled': true});
|
||||
_converse.log('Message carbons have been enabled.');
|
||||
}
|
||||
}, null, "iq", null, "enablecarbons");
|
||||
this.connection.send(carbons_iq);
|
||||
};
|
||||
|
||||
|
||||
this.sendInitialPresence = function () {
|
||||
if (_converse.send_initial_presence) {
|
||||
_converse.xmppstatus.sendPresence();
|
||||
}
|
||||
};
|
||||
|
||||
this.onStatusInitialized = function (reconnecting) {
|
||||
/**
|
||||
* Triggered when the user's own chat status has been initialized.
|
||||
* @event _converse#statusInitialized
|
||||
* @example _converse.api.listen.on('statusInitialized', status => { ... });
|
||||
* @example _converse.api.waitUntil('statusInitialized').then(() => { ... });
|
||||
*/
|
||||
_converse.api.trigger('statusInitialized', reconnecting);
|
||||
if (reconnecting) {
|
||||
/**
|
||||
* After the connection has dropped and converse.js has reconnected.
|
||||
* Any Strophe stanza handlers (as registered via `converse.listen.stanza`) will
|
||||
* have to be registered anew.
|
||||
* @event _converse#reconnected
|
||||
* @example _converse.api.listen.on('reconnected', () => { ... });
|
||||
*/
|
||||
_converse.api.trigger('reconnected');
|
||||
} else {
|
||||
init_promise.resolve();
|
||||
/**
|
||||
* Triggered once converse.js has been initialized.
|
||||
* See also {@link _converse#event:pluginsInitialized}.
|
||||
* @event _converse#initialized
|
||||
*/
|
||||
_converse.api.trigger('initialized');
|
||||
/**
|
||||
* Triggered after the connection has been established and Converse
|
||||
* has got all its ducks in a row.
|
||||
* @event _converse#initialized
|
||||
*/
|
||||
_converse.api.trigger('connected');
|
||||
}
|
||||
};
|
||||
|
||||
this.bindResource = async function () {
|
||||
/**
|
||||
* Synchronous event triggered before we send an IQ to bind the user's
|
||||
|
@ -1373,79 +1193,11 @@ _converse.initialize = async function (settings, callback) {
|
|||
});
|
||||
this.connfeedback = new this.ConnectionFeedback();
|
||||
|
||||
|
||||
this.XMPPStatus = Backbone.Model.extend({
|
||||
defaults: {
|
||||
"status": _converse.default_state
|
||||
},
|
||||
|
||||
initialize () {
|
||||
this.on('change', item => {
|
||||
if (!_.isObject(item.changed)) {
|
||||
return;
|
||||
}
|
||||
if ('status' in item.changed || 'status_message' in item.changed) {
|
||||
this.sendPresence(this.get('status'), this.get('status_message'));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
getNickname () {
|
||||
return _converse.nickname;
|
||||
},
|
||||
|
||||
getFullname () {
|
||||
// Gets overridden in converse-vcard
|
||||
return '';
|
||||
},
|
||||
|
||||
constructPresence (type, status_message) {
|
||||
let presence;
|
||||
type = _.isString(type) ? type : (this.get('status') || _converse.default_state);
|
||||
status_message = _.isString(status_message) ? status_message : this.get('status_message');
|
||||
// Most of these presence types are actually not explicitly sent,
|
||||
// but I add all of them here for reference and future proofing.
|
||||
if ((type === 'unavailable') ||
|
||||
(type === 'probe') ||
|
||||
(type === 'error') ||
|
||||
(type === 'unsubscribe') ||
|
||||
(type === 'unsubscribed') ||
|
||||
(type === 'subscribe') ||
|
||||
(type === 'subscribed')) {
|
||||
presence = $pres({'type': type});
|
||||
} else if (type === 'offline') {
|
||||
presence = $pres({'type': 'unavailable'});
|
||||
} else if (type === 'online') {
|
||||
presence = $pres();
|
||||
} else {
|
||||
presence = $pres().c('show').t(type).up();
|
||||
}
|
||||
if (status_message) {
|
||||
presence.c('status').t(status_message).up();
|
||||
}
|
||||
presence.c('priority').t(
|
||||
_.isNaN(Number(_converse.priority)) ? 0 : _converse.priority
|
||||
).up();
|
||||
if (_converse.idle) {
|
||||
const idle_since = new Date();
|
||||
idle_since.setSeconds(idle_since.getSeconds() - _converse.idle_seconds);
|
||||
presence.c('idle', {xmlns: Strophe.NS.IDLE, since: idle_since.toISOString()});
|
||||
}
|
||||
return presence;
|
||||
},
|
||||
|
||||
sendPresence (type, status_message) {
|
||||
_converse.api.send(this.constructPresence(type, status_message));
|
||||
}
|
||||
});
|
||||
|
||||
// Initialization
|
||||
// --------------
|
||||
await finishInitialization();
|
||||
if (_converse.isTestEnv()) {
|
||||
return _converse;
|
||||
} else {
|
||||
return init_promise;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1671,70 +1423,6 @@ _converse.api = {
|
|||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set and get the user's chat status, also called their *availability*.
|
||||
*
|
||||
* @namespace _converse.api.user.status
|
||||
* @memberOf _converse.api.user
|
||||
*/
|
||||
status: {
|
||||
/** Return the current user's availability status.
|
||||
*
|
||||
* @method _converse.api.user.status.get
|
||||
* @example _converse.api.user.status.get();
|
||||
*/
|
||||
get () {
|
||||
return _converse.xmppstatus.get('status');
|
||||
},
|
||||
/**
|
||||
* The user's status can be set to one of the following values:
|
||||
*
|
||||
* @method _converse.api.user.status.set
|
||||
* @param {string} value The user's chat status (e.g. 'away', 'dnd', 'offline', 'online', 'unavailable' or 'xa')
|
||||
* @param {string} [message] A custom status message
|
||||
*
|
||||
* @example this._converse.api.user.status.set('dnd');
|
||||
* @example this._converse.api.user.status.set('dnd', 'In a meeting');
|
||||
*/
|
||||
set (value, message) {
|
||||
const data = {'status': value};
|
||||
if (!_.includes(Object.keys(_converse.STATUS_WEIGHTS), value)) {
|
||||
throw new Error(
|
||||
'Invalid availability value. See https://xmpp.org/rfcs/rfc3921.html#rfc.section.2.2.2.1'
|
||||
);
|
||||
}
|
||||
if (_.isString(message)) {
|
||||
data.status_message = message;
|
||||
}
|
||||
_converse.xmppstatus.sendPresence(value);
|
||||
_converse.xmppstatus.save(data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Set and retrieve the user's custom status message.
|
||||
*
|
||||
* @namespace _converse.api.user.status.message
|
||||
* @memberOf _converse.api.user.status
|
||||
*/
|
||||
message: {
|
||||
/**
|
||||
* @method _converse.api.user.status.message.get
|
||||
* @returns {string} The status message
|
||||
* @example const message = _converse.api.user.status.message.get()
|
||||
*/
|
||||
get () {
|
||||
return _converse.xmppstatus.get('status_message');
|
||||
},
|
||||
/**
|
||||
* @method _converse.api.user.status.message.set
|
||||
* @param {string} status The status message
|
||||
* @example _converse.api.user.status.message.set('In a meeting');
|
||||
*/
|
||||
set (status) {
|
||||
_converse.xmppstatus.save({ status_message: status });
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
/**
|
||||
* @module converse-roster
|
||||
*/
|
||||
import "@converse/headless/converse-status";
|
||||
import converse from "@converse/headless/converse-core";
|
||||
|
||||
const { Backbone, Strophe, $iq, $pres, dayjs, sizzle, _ } = converse.env;
|
||||
|
@ -14,7 +15,7 @@ const u = converse.env.utils;
|
|||
|
||||
converse.plugins.add('converse-roster', {
|
||||
|
||||
dependencies: [],
|
||||
dependencies: ['converse-status'],
|
||||
|
||||
initialize () {
|
||||
/* The initialize function gets called as soon as the plugin is
|
||||
|
@ -58,6 +59,21 @@ converse.plugins.add('converse-roster', {
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* Reject or cancel another user's subscription to our presence updates.
|
||||
* @method rejectPresenceSubscription
|
||||
* @private
|
||||
* @memberOf _converse
|
||||
* @param { String } jid - The Jabber ID of the user whose subscription is being canceled
|
||||
* @param { String } message - An optional message to the user
|
||||
*/
|
||||
_converse.rejectPresenceSubscription = function (jid, message) {
|
||||
const pres = $pres({to: jid, type: "unsubscribed"});
|
||||
if (message && message !== "") { pres.c("status").t(message); }
|
||||
_converse.api.send(pres);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the Bakcbone collections that represent the contats
|
||||
* roster and the roster groups.
|
||||
|
@ -91,6 +107,13 @@ converse.plugins.add('converse-roster', {
|
|||
};
|
||||
|
||||
|
||||
_converse.sendInitialPresence = function () {
|
||||
if (_converse.send_initial_presence) {
|
||||
_converse.xmppstatus.sendPresence();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Fetch all the roster groups, and then the roster contacts.
|
||||
* Emit an event after fetching is done in each case.
|
||||
|
@ -714,6 +737,7 @@ converse.plugins.add('converse-roster', {
|
|||
_converse.api.trigger('contactRequest', this.create(user_data));
|
||||
},
|
||||
|
||||
|
||||
handleIncomingSubscription (presence) {
|
||||
const jid = presence.getAttribute('from'),
|
||||
bare_jid = Strophe.getBareJidFromJid(jid),
|
||||
|
@ -972,6 +996,7 @@ converse.plugins.add('converse-roster', {
|
|||
_converse.api.trigger('presencesInitialized', reconnecting);
|
||||
});
|
||||
|
||||
|
||||
_converse.api.listen.on('presencesInitialized', (reconnecting) => {
|
||||
if (reconnecting) {
|
||||
/**
|
||||
|
@ -983,7 +1008,6 @@ converse.plugins.add('converse-roster', {
|
|||
*/
|
||||
_converse.api.trigger('rosterReadyAfterReconnection');
|
||||
} else {
|
||||
_converse.registerIntervalHandler();
|
||||
_converse.initRoster();
|
||||
}
|
||||
_converse.roster.onConnected();
|
||||
|
|
306
src/headless/converse-status.js
Normal file
306
src/headless/converse-status.js
Normal file
|
@ -0,0 +1,306 @@
|
|||
// Converse.js
|
||||
// https://conversejs.org
|
||||
//
|
||||
// Copyright (c) 2013-2019, the Converse.js developers
|
||||
// Licensed under the Mozilla Public License (MPLv2)
|
||||
/**
|
||||
* @module converse-status
|
||||
*/
|
||||
import { get, isNaN, isObject, isString } from "lodash";
|
||||
import converse from "@converse/headless/converse-core";
|
||||
|
||||
const { Backbone, Strophe, $build, $pres } = converse.env;
|
||||
|
||||
|
||||
converse.plugins.add('converse-status', {
|
||||
|
||||
initialize () {
|
||||
const { _converse } = this;
|
||||
|
||||
_converse.XMPPStatus = Backbone.Model.extend({
|
||||
defaults () {
|
||||
return {"status": _converse.default_state}
|
||||
},
|
||||
|
||||
initialize () {
|
||||
this.on('change', item => {
|
||||
if (!isObject(item.changed)) {
|
||||
return;
|
||||
}
|
||||
if ('status' in item.changed || 'status_message' in item.changed) {
|
||||
this.sendPresence(this.get('status'), this.get('status_message'));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
getNickname () {
|
||||
return _converse.nickname;
|
||||
},
|
||||
|
||||
getFullname () {
|
||||
// Gets overridden in converse-vcard
|
||||
return '';
|
||||
},
|
||||
|
||||
constructPresence (type, status_message) {
|
||||
let presence;
|
||||
type = isString(type) ? type : (this.get('status') || _converse.default_state);
|
||||
status_message = isString(status_message) ? status_message : this.get('status_message');
|
||||
// Most of these presence types are actually not explicitly sent,
|
||||
// but I add all of them here for reference and future proofing.
|
||||
if ((type === 'unavailable') ||
|
||||
(type === 'probe') ||
|
||||
(type === 'error') ||
|
||||
(type === 'unsubscribe') ||
|
||||
(type === 'unsubscribed') ||
|
||||
(type === 'subscribe') ||
|
||||
(type === 'subscribed')) {
|
||||
presence = $pres({'type': type});
|
||||
} else if (type === 'offline') {
|
||||
presence = $pres({'type': 'unavailable'});
|
||||
} else if (type === 'online') {
|
||||
presence = $pres();
|
||||
} else {
|
||||
presence = $pres().c('show').t(type).up();
|
||||
}
|
||||
if (status_message) {
|
||||
presence.c('status').t(status_message).up();
|
||||
}
|
||||
presence.c('priority').t(isNaN(Number(_converse.priority)) ? 0 : _converse.priority).up();
|
||||
if (_converse.idle) {
|
||||
const idle_since = new Date();
|
||||
idle_since.setSeconds(idle_since.getSeconds() - _converse.idle_seconds);
|
||||
presence.c('idle', {xmlns: Strophe.NS.IDLE, since: idle_since.toISOString()});
|
||||
}
|
||||
return presence;
|
||||
},
|
||||
|
||||
sendPresence (type, status_message) {
|
||||
_converse.api.send(this.constructPresence(type, status_message));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Send out a Client State Indication (XEP-0352)
|
||||
* @private
|
||||
* @method sendCSI
|
||||
* @memberOf _converse
|
||||
* @param { String } stat - The user's chat status
|
||||
*/
|
||||
_converse.sendCSI = function (stat) {
|
||||
_converse.api.send($build(stat, {xmlns: Strophe.NS.CSI}));
|
||||
_converse.inactive = (stat === _converse.INACTIVE) ? true : false;
|
||||
};
|
||||
|
||||
|
||||
_converse.onUserActivity = function () {
|
||||
/* Resets counters and flags relating to CSI and auto_away/auto_xa */
|
||||
if (_converse.idle_seconds > 0) {
|
||||
_converse.idle_seconds = 0;
|
||||
}
|
||||
if (!get(_converse.connection, 'authenticated')) {
|
||||
// We can't send out any stanzas when there's no authenticated connection.
|
||||
// This can happen when the connection reconnects.
|
||||
return;
|
||||
}
|
||||
if (_converse.inactive) {
|
||||
_converse.sendCSI(_converse.ACTIVE);
|
||||
}
|
||||
if (_converse.idle) {
|
||||
_converse.idle = false;
|
||||
_converse.xmppstatus.sendPresence();
|
||||
}
|
||||
if (_converse.auto_changed_status === true) {
|
||||
_converse.auto_changed_status = false;
|
||||
// XXX: we should really remember the original state here, and
|
||||
// then set it back to that...
|
||||
_converse.xmppstatus.set('status', _converse.default_state);
|
||||
}
|
||||
};
|
||||
|
||||
_converse.onEverySecond = function () {
|
||||
/* An interval handler running every second.
|
||||
* Used for CSI and the auto_away and auto_xa features.
|
||||
*/
|
||||
if (!get(_converse.connection, 'authenticated')) {
|
||||
// We can't send out any stanzas when there's no authenticated connection.
|
||||
// This can happen when the connection reconnects.
|
||||
return;
|
||||
}
|
||||
const stat = _converse.xmppstatus.get('status');
|
||||
_converse.idle_seconds++;
|
||||
if (_converse.csi_waiting_time > 0 &&
|
||||
_converse.idle_seconds > _converse.csi_waiting_time &&
|
||||
!_converse.inactive) {
|
||||
_converse.sendCSI(_converse.INACTIVE);
|
||||
}
|
||||
if (_converse.idle_presence_timeout > 0 &&
|
||||
_converse.idle_seconds > _converse.idle_presence_timeout &&
|
||||
!_converse.idle) {
|
||||
_converse.idle = true;
|
||||
_converse.xmppstatus.sendPresence();
|
||||
}
|
||||
if (_converse.auto_away > 0 &&
|
||||
_converse.idle_seconds > _converse.auto_away &&
|
||||
stat !== 'away' && stat !== 'xa' && stat !== 'dnd') {
|
||||
_converse.auto_changed_status = true;
|
||||
_converse.xmppstatus.set('status', 'away');
|
||||
} else if (_converse.auto_xa > 0 &&
|
||||
_converse.idle_seconds > _converse.auto_xa &&
|
||||
stat !== 'xa' && stat !== 'dnd') {
|
||||
_converse.auto_changed_status = true;
|
||||
_converse.xmppstatus.set('status', 'xa');
|
||||
}
|
||||
};
|
||||
|
||||
_converse.registerIntervalHandler = function () {
|
||||
/* Set an interval of one second and register a handler for it.
|
||||
* Required for the auto_away, auto_xa and csi_waiting_time features.
|
||||
*/
|
||||
if (
|
||||
_converse.auto_away < 1 &&
|
||||
_converse.auto_xa < 1 &&
|
||||
_converse.csi_waiting_time < 1 &&
|
||||
_converse.idle_presence_timeout < 1
|
||||
) {
|
||||
// Waiting time of less then one second means features aren't used.
|
||||
return;
|
||||
}
|
||||
_converse.idle_seconds = 0;
|
||||
_converse.auto_changed_status = false; // Was the user's status changed by Converse?
|
||||
window.addEventListener('click', _converse.onUserActivity);
|
||||
window.addEventListener('focus', _converse.onUserActivity);
|
||||
window.addEventListener('keypress', _converse.onUserActivity);
|
||||
window.addEventListener('mousemove', _converse.onUserActivity);
|
||||
const options = {'once': true, 'passive': true};
|
||||
window.addEventListener(_converse.unloadevent, _converse.onUserActivity, options);
|
||||
window.addEventListener(_converse.unloadevent, () => {
|
||||
if (_converse.session) {
|
||||
_converse.session.save('active', false);
|
||||
}
|
||||
});
|
||||
_converse.everySecondTrigger = window.setInterval(_converse.onEverySecond, 1000);
|
||||
};
|
||||
|
||||
|
||||
_converse.api.listen.on('presencesInitialized', (reconnecting) => {
|
||||
if (!reconnecting) {
|
||||
_converse.registerIntervalHandler();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function onStatusInitialized (reconnecting) {
|
||||
/**
|
||||
* Triggered when the user's own chat status has been initialized.
|
||||
* @event _converse#statusInitialized
|
||||
* @example _converse.api.listen.on('statusInitialized', status => { ... });
|
||||
* @example _converse.api.waitUntil('statusInitialized').then(() => { ... });
|
||||
*/
|
||||
_converse.api.trigger('statusInitialized', reconnecting);
|
||||
}
|
||||
|
||||
|
||||
function initStatus (reconnecting) {
|
||||
// If there's no xmppstatus obj, then we were never connected to
|
||||
// begin with, so we set reconnecting to false.
|
||||
reconnecting = _converse.xmppstatus === undefined ? false : reconnecting;
|
||||
if (reconnecting) {
|
||||
onStatusInitialized(reconnecting);
|
||||
} else {
|
||||
const id = `converse.xmppstatus-${_converse.bare_jid}`;
|
||||
_converse.xmppstatus = new _converse.XMPPStatus({'id': id});
|
||||
_converse.xmppstatus.browserStorage = _converse.createStore(id, "session");
|
||||
_converse.xmppstatus.fetch({
|
||||
'success': () => onStatusInitialized(reconnecting),
|
||||
'error': () => onStatusInitialized(reconnecting),
|
||||
'silent': true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/************************ BEGIN Event Handlers ************************/
|
||||
_converse.api.listen.on('clearSession', () => {
|
||||
if (_converse.shouldClearCache() && _converse.xmppstatus) {
|
||||
_converse.xmppstatus.destroy();
|
||||
_converse.xmppstatus.browserStorage._clear();
|
||||
delete _converse.xmppstatus;
|
||||
}
|
||||
});
|
||||
|
||||
_converse.api.listen.on('connected', () => initStatus(false));
|
||||
_converse.api.listen.on('reconnected', () => initStatus(true));
|
||||
/************************ END Event Handlers ************************/
|
||||
|
||||
|
||||
/************************ BEGIN API ************************/
|
||||
Object.assign(_converse.api.user, {
|
||||
/**
|
||||
* Set and get the user's chat status, also called their *availability*.
|
||||
*
|
||||
* @namespace _converse.api.user.status
|
||||
* @memberOf _converse.api.user
|
||||
*/
|
||||
status: {
|
||||
/** Return the current user's availability status.
|
||||
*
|
||||
* @method _converse.api.user.status.get
|
||||
* @example _converse.api.user.status.get();
|
||||
*/
|
||||
get () {
|
||||
return _converse.xmppstatus.get('status');
|
||||
},
|
||||
/**
|
||||
* The user's status can be set to one of the following values:
|
||||
*
|
||||
* @method _converse.api.user.status.set
|
||||
* @param {string} value The user's chat status (e.g. 'away', 'dnd', 'offline', 'online', 'unavailable' or 'xa')
|
||||
* @param {string} [message] A custom status message
|
||||
*
|
||||
* @example this._converse.api.user.status.set('dnd');
|
||||
* @example this._converse.api.user.status.set('dnd', 'In a meeting');
|
||||
*/
|
||||
set (value, message) {
|
||||
const data = {'status': value};
|
||||
if (!Object.keys(_converse.STATUS_WEIGHTS).includes(value)) {
|
||||
throw new Error(
|
||||
'Invalid availability value. See https://xmpp.org/rfcs/rfc3921.html#rfc.section.2.2.2.1'
|
||||
);
|
||||
}
|
||||
if (isString(message)) {
|
||||
data.status_message = message;
|
||||
}
|
||||
_converse.xmppstatus.sendPresence(value);
|
||||
_converse.xmppstatus.save(data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Set and retrieve the user's custom status message.
|
||||
*
|
||||
* @namespace _converse.api.user.status.message
|
||||
* @memberOf _converse.api.user.status
|
||||
*/
|
||||
message: {
|
||||
/**
|
||||
* @method _converse.api.user.status.message.get
|
||||
* @returns {string} The status message
|
||||
* @example const message = _converse.api.user.status.message.get()
|
||||
*/
|
||||
get () {
|
||||
return _converse.xmppstatus.get('status_message');
|
||||
},
|
||||
/**
|
||||
* @method _converse.api.user.status.message.set
|
||||
* @param {string} status The status message
|
||||
* @example _converse.api.user.status.message.set('In a meeting');
|
||||
*/
|
||||
set (status) {
|
||||
_converse.xmppstatus.save({ status_message: status });
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
|
@ -6,6 +6,7 @@
|
|||
/**
|
||||
* @module converse-vcard
|
||||
*/
|
||||
import "./converse-status";
|
||||
import converse from "./converse-core";
|
||||
import tpl_vcard from "./templates/vcard.html";
|
||||
|
||||
|
@ -15,7 +16,7 @@ const u = converse.env.utils;
|
|||
|
||||
converse.plugins.add('converse-vcard', {
|
||||
|
||||
dependencies: ["converse-roster"],
|
||||
dependencies: ["converse-status", "converse-roster"],
|
||||
|
||||
overrides: {
|
||||
XMPPStatus: {
|
||||
|
@ -162,10 +163,9 @@ converse.plugins.add('converse-vcard', {
|
|||
_converse.vcards.browserStorage = _converse.createStore(id, _converse.config.get('storage'));
|
||||
_converse.vcards.fetch();
|
||||
}
|
||||
_converse.api.listen.on('afterResourceBinding', _converse.initVCardCollection);
|
||||
|
||||
|
||||
_converse.api.listen.on('statusInitialized', () => {
|
||||
_converse.initVCardCollection();
|
||||
const vcards = _converse.vcards;
|
||||
if (_converse.session) {
|
||||
const jid = _converse.session.get('bare_jid');
|
||||
|
|
|
@ -14,6 +14,7 @@ import "./converse-pubsub"; // XEP-0060 Pubsub
|
|||
import "./converse-roster"; // Contacts Roster
|
||||
import "./converse-rsm"; // XEP-0059 Result Set management
|
||||
import "./converse-smacks"; // XEP-0198 Stream Management
|
||||
import "./converse-status"; // XEP-0199 XMPP Ping
|
||||
import "./converse-vcard"; // XEP-0054 VCard-temp
|
||||
/* END: Removable components */
|
||||
|
||||
|
|
|
@ -256,6 +256,7 @@
|
|||
if (el) {
|
||||
el.parentElement.removeChild(el);
|
||||
}
|
||||
document.title = "Converse Tests";
|
||||
done();
|
||||
}
|
||||
await Promise.all((promise_names || []).map(_converse.api.waitUntil));
|
||||
|
|
Loading…
Reference in New Issue
Block a user