Add an external API for sending presences

This commit is contained in:
JC Brand 2020-04-19 09:23:52 +02:00
parent edf7f6b8d3
commit c1efb0d2b4
8 changed files with 68 additions and 55 deletions

View File

@ -140,8 +140,9 @@
const view = _converse.xmppstatusview;
modal.el.querySelector('label[for="radio-busy"]').click(); // Change status to "dnd"
modal.el.querySelector('[type="submit"]').click();
const last_stanza = _converse.connection.sent_stanzas.pop();
expect(Strophe.serialize(last_stanza)).toBe(
const sent_stanzas = _converse.connection.sent_stanzas;
const sent_presence = await u.waitUntil(() => sent_stanzas.filter(s => Strophe.serialize(s).match('presence')).pop());
expect(Strophe.serialize(sent_presence)).toBe(
`<presence xmlns="jabber:client">`+
`<show>dnd</show>`+
`<priority>0</priority>`+
@ -169,8 +170,9 @@
const msg = 'I am happy';
modal.el.querySelector('input[name="status_message"]').value = msg;
modal.el.querySelector('[type="submit"]').click();
const last_stanza = _converse.connection.sent_stanzas.pop();
expect(Strophe.serialize(last_stanza)).toBe(
const sent_stanzas = _converse.connection.sent_stanzas;
const sent_presence = await u.waitUntil(() => sent_stanzas.filter(s => Strophe.serialize(s).match('presence')).pop());
expect(Strophe.serialize(sent_presence)).toBe(
`<presence xmlns="jabber:client">`+
`<status>I am happy</status>`+
`<priority>0</priority>`+

View File

@ -8,6 +8,7 @@
], factory);
} (this, function (jasmine, mock, test_utils) {
"use strict";
const Strophe = converse.env.Strophe;
const u = converse.env.utils;
// See: https://xmpp.org/rfcs/rfc3921.html
@ -37,7 +38,7 @@
}));
it("has a given priority", mock.initConverse((done, _converse) => {
let pres = _converse.xmppstatus.constructPresence('online', 'Hello world');
let pres = _converse.xmppstatus.constructPresence('online', null, 'Hello world');
expect(pres.toLocaleString()).toBe(
`<presence xmlns="jabber:client">`+
`<status>Hello world</status>`+
@ -46,7 +47,7 @@
`</presence>`
);
_converse.priority = 2;
pres = _converse.xmppstatus.constructPresence('away', 'Going jogging');
pres = _converse.xmppstatus.constructPresence('away', null, 'Going jogging');
expect(pres.toLocaleString()).toBe(
`<presence xmlns="jabber:client">`+
`<show>away</show>`+
@ -57,7 +58,7 @@
);
delete _converse.priority;
pres = _converse.xmppstatus.constructPresence('dnd', 'Doing taxes');
pres = _converse.xmppstatus.constructPresence('dnd', null, 'Doing taxes');
expect(pres.toLocaleString()).toBe(
`<presence xmlns="jabber:client">`+
`<show>dnd</show>`+
@ -75,8 +76,6 @@
async (done, _converse) => {
test_utils.openControlBox(_converse);
const view = _converse.xmppstatusview;
spyOn(view.model, 'sendPresence').and.callThrough();
spyOn(_converse.connection, 'send').and.callThrough();
const cbview = _converse.chatboxviews.get('controlbox');
@ -86,8 +85,10 @@
const msg = 'My custom status';
modal.el.querySelector('input[name="status_message"]').value = msg;
modal.el.querySelector('[type="submit"]').click();
expect(view.model.sendPresence).toHaveBeenCalled();
expect(_converse.connection.send.calls.mostRecent().args[0].toLocaleString())
const sent_stanzas = _converse.connection.sent_stanzas;
let sent_presence = await u.waitUntil(() => sent_stanzas.filter(s => Strophe.serialize(s).match('presence')).pop());
expect(Strophe.serialize(sent_presence))
.toBe(`<presence xmlns="jabber:client">`+
`<status>My custom status</status>`+
`<priority>0</priority>`+
@ -100,10 +101,16 @@
await u.waitUntil(() => modal.el.getAttribute('aria-hidden') === "false", 1000);
modal.el.querySelector('label[for="radio-busy"]').click(); // Change status to "dnd"
modal.el.querySelector('[type="submit"]').click();
expect(_converse.connection.send.calls.mostRecent().args[0].toLocaleString())
.toBe(`<presence xmlns="jabber:client"><show>dnd</show><status>My custom status</status><priority>0</priority>`+
await u.waitUntil(() => sent_stanzas.filter(s => Strophe.serialize(s).match('presence')).length === 2);
sent_presence = sent_stanzas.filter(s => Strophe.serialize(s).match('presence')).pop();
expect(Strophe.serialize(sent_presence))
.toBe(
`<presence xmlns="jabber:client">`+
`<show>dnd</show>`+
`<status>My custom status</status>`+
`<priority>0</priority>`+
`<c hash="sha-1" node="https://conversejs.org" ver="Hxbsr5fazs62i+O0GxIXf2OEDNs=" xmlns="http://jabber.org/protocol/caps"/>`+
`</presence>`)
`</presence>`)
done();
}));
});

View File

@ -1,14 +1,16 @@
(function (root, factory) {
define(["jasmine", "mock"], factory);
} (this, function (jasmine, mock) {
const u = converse.env.utils;
return describe("The XMPPStatus model", function () {
it("won't send <show>online</show> when setting a custom status message", mock.initConverse((done, _converse) => {
it("won't send <show>online</show> when setting a custom status message",
mock.initConverse(async (done, _converse) => {
_converse.xmppstatus.save({'status': 'online'});
spyOn(_converse.connection, 'send');
_converse.api.user.status.message.set("I'm also happy!");
expect(_converse.connection.send).toHaveBeenCalled();
await u.waitUntil(() => _converse.connection.send.calls.count());
const stanza = _converse.connection.send.calls.argsFor(0)[0].tree();
expect(stanza.childNodes.length).toBe(3);
expect(stanza.querySelectorAll('status').length).toBe(1);

View File

@ -834,7 +834,7 @@ converse.plugins.add('converse-rosterview', {
u.addClass('fa-spin', ev.target);
_converse.roster.data.save('version', null);
await _converse.roster.fetchFromServer();
_converse.xmppstatus.sendPresence();
api.user.presence.send();
u.removeClass('fa-spin', ev.target);
},

View File

@ -90,7 +90,6 @@ const PROMISES = [
'connectionInitialized',
'initialized',
'pluginsInitialized',
'statusInitialized'
];
// Core plugins are whitelisted automatically
@ -712,7 +711,6 @@ export const api = _converse.api = {
* * [rosterContactsFetched](/docs/html/events.html#rosterContactsFetched)
* * [rosterGroupsFetched](/docs/html/events.html#rosterGroupsFetched)
* * [rosterInitialized](/docs/html/events.html#rosterInitialized)
* * [statusInitialized](/docs/html/events.html#statusInitialized)
* * [roomsPanelRendered](/docs/html/events.html#roomsPanelRendered)
*
* The various plugins might also provide promises, and they do this by using the

View File

@ -852,7 +852,7 @@ converse.plugins.add('converse-muc', {
}
}
if (api.connection.connected()) {
this.sendUnavailablePresence(exit_msg);
api.user.presence.send('unavailable', this.getRoomJIDAndNick(), exit_msg);
}
u.safeSave(this.session, {'connection_status': converse.ROOMSTATUS.DISCONNECTED});
this.removeHandlers();
@ -877,18 +877,6 @@ converse.plugins.add('converse-muc', {
return self && self.isModerator() && api.disco.supports(Strophe.NS.MODERATE, this.get('jid'));
},
sendUnavailablePresence (exit_msg) {
const presence = $pres({
type: "unavailable",
from: _converse.connection.jid,
to: this.getRoomJIDAndNick()
});
if (exit_msg !== null) {
presence.c("status", exit_msg);
}
_converse.connection.sendPresence(presence);
},
/**
* Return an array of unique nicknames based on all occupants and messages in this MUC.
* @private

View File

@ -80,7 +80,7 @@ converse.plugins.add('converse-roster', {
_converse.sendInitialPresence = function () {
if (_converse.send_initial_presence) {
_converse.xmppstatus.sendPresence();
api.user.presence.send();
}
};
@ -771,7 +771,7 @@ converse.plugins.add('converse-roster', {
//
// As a workaround for now we simply send our presence again,
// otherwise we're treated as offline.
_converse.xmppstatus.sendPresence();
api.user.presence.send();
}
},

View File

@ -23,6 +23,7 @@ converse.plugins.add('converse-status', {
default_state: 'online',
priority: 0,
});
api.promises.add(['statusInitialized']);
_converse.XMPPStatus = Model.extend({
defaults () {
@ -35,7 +36,7 @@ converse.plugins.add('converse-status', {
return;
}
if ('status' in item.changed || 'status_message' in item.changed) {
this.sendPresence(this.get('status'), this.get('status_message'));
api.user.presence.send(this.get('status'), null, this.get('status_message'));
}
});
},
@ -49,12 +50,11 @@ converse.plugins.add('converse-status', {
return '';
},
constructPresence (type, status_message) {
let presence;
constructPresence (type, to=null, status_message) {
type = isString(type) ? type : (this.get('status') || api.settings.get("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.
let presence;
const attrs = {to};
if ((type === 'unavailable') ||
(type === 'probe') ||
(type === 'error') ||
@ -62,14 +62,17 @@ converse.plugins.add('converse-status', {
(type === 'unsubscribed') ||
(type === 'subscribe') ||
(type === 'subscribed')) {
presence = $pres({'type': type});
attrs['type'] = type;
presence = $pres(attrs);
} else if (type === 'offline') {
presence = $pres({'type': 'unavailable'});
attrs['type'] = 'unavailable';
presence = $pres(attrs);
} else if (type === 'online') {
presence = $pres();
presence = $pres(attrs);
} else {
presence = $pres().c('show').t(type).up();
presence = $pres(attrs).c('show').t(type).up();
}
if (status_message) {
presence.c('status').t(status_message).up();
}
@ -82,10 +85,6 @@ converse.plugins.add('converse-status', {
presence.c('idle', {xmlns: Strophe.NS.IDLE, since: idle_since.toISOString()});
}
return presence;
},
sendPresence (type, status_message) {
api.send(this.constructPresence(type, status_message));
}
});
@ -118,7 +117,7 @@ converse.plugins.add('converse-status', {
}
if (_converse.idle) {
_converse.idle = false;
_converse.xmppstatus.sendPresence();
api.user.presence.send();
}
if (_converse.auto_changed_status === true) {
_converse.auto_changed_status = false;
@ -148,7 +147,7 @@ converse.plugins.add('converse-status', {
_converse.idle_seconds > api.settings.get("idle_presence_timeout") &&
!_converse.idle) {
_converse.idle = true;
_converse.xmppstatus.sendPresence();
api.user.presence.send();
}
if (api.settings.get("auto_away") > 0 &&
_converse.idle_seconds > api.settings.get("auto_away") &&
@ -245,21 +244,39 @@ converse.plugins.add('converse-status', {
/************************ BEGIN API ************************/
Object.assign(_converse.api.user, {
/**
* @namespace _converse.api.user.presence
* @memberOf _converse.api.user
*/
presence: {
/**
* Send out a presence stanza
* @method _converse.api.user.presence.send
* @param { String } type
* @param { String } to
* @param { String } [status] - An optional status message
*/
async send (type, to, status) {
await api.waitUntil('statusInitialized');
api.send(_converse.xmppstatus.constructPresence(type, to, status));
}
},
/**
* 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.
*
/**
* 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:
*
@ -267,8 +284,8 @@ converse.plugins.add('converse-status', {
* @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');
* @example _converse.api.user.status.set('dnd');
* @example _converse.api.user.status.set('dnd', 'In a meeting');
*/
set (value, message) {
const data = {'status': value};
@ -280,7 +297,6 @@ converse.plugins.add('converse-status', {
if (isString(message)) {
data.status_message = message;
}
_converse.xmppstatus.sendPresence(value);
_converse.xmppstatus.save(data);
},