diff --git a/CHANGES.md b/CHANGES.md
index 78dde0f71..8b8b2350f 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -13,6 +13,7 @@
- #1306 added option `notification_delay`
- #1305 added value 'all' for 'show_desktop_notifications' to notifiy even if converse.js is open
- #1312 Error `unrecognized expression` in Safari
+- #1319 Implement sending of presences according to XEP-0319: Last User Interaction in Presence
## 4.0.4 (2018-10-29)
diff --git a/dist/converse.js b/dist/converse.js
index fff1db82b..c30903540 100644
--- a/dist/converse.js
+++ b/dist/converse.js
@@ -71780,6 +71780,7 @@ strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].addNamespace('DELAY', 'urn:xm
strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].addNamespace('FORWARD', 'urn:xmpp:forward:0');
strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].addNamespace('HINTS', 'urn:xmpp:hints');
strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].addNamespace('HTTPUPLOAD', 'urn:xmpp:http:upload:0');
+strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].addNamespace('IDLE', 'urn:xmpp:idle:1');
strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].addNamespace('MAM', 'urn:xmpp:mam:2');
strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].addNamespace('NICK', 'http://jabber.org/protocol/nick');
strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].addNamespace('OMEMO', "eu.siacs.conversations.axolotl");
@@ -71931,6 +71932,8 @@ _converse.default_settings = {
expose_rid_and_sid: false,
geouri_regex: /https:\/\/www.openstreetmap.org\/.*#map=[0-9]+\/([\-0-9.]+)\/([\-0-9.]+)\S*/g,
geouri_replacement: 'https://www.openstreetmap.org/?mlat=$1&mlon=$2#map=18/$1/$2',
+ idle_presence_timeout: 300,
+ // Seconds after which an idle presence is sent
jid: undefined,
keepalive: true,
locales_url: 'locale/{{{locale}}}/LC_MESSAGES/converse.json',
@@ -72166,6 +72169,12 @@ _converse.initialize = function (settings, callback) {
_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...
@@ -72192,6 +72201,12 @@ _converse.initialize = function (settings, callback) {
_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;
@@ -72207,7 +72222,7 @@ _converse.initialize = function (settings, callback) {
/* 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) {
+ 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;
}
@@ -72707,7 +72722,17 @@ _converse.initialize = function (settings, callback) {
presence.c('status').t(status_message).up();
}
- presence.c('priority').t(_lodash_noconflict__WEBPACK_IMPORTED_MODULE_4___default.a.isNaN(Number(_converse.priority)) ? 0 : _converse.priority);
+ presence.c('priority').t(_lodash_noconflict__WEBPACK_IMPORTED_MODULE_4___default.a.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_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].NS.IDLE,
+ since: idle_since.toISOString()
+ });
+ }
+
return presence;
},
@@ -73015,6 +73040,12 @@ _converse.initialize = function (settings, callback) {
if (!Backbone.history.started) {
Backbone.history.start();
}
+
+ if (_converse.idle_presence_timeout > 0) {
+ _converse.on('addClientFeatures', () => {
+ _converse.api.disco.own.features.add(strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].NS.IDLE);
+ });
+ }
}
if (!_lodash_noconflict__WEBPACK_IMPORTED_MODULE_4___default.a.isUndefined(_converse.connection) && _converse.connection.service === 'jasmine tests') {
diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst
index 7c19b9be6..ed1b8445c 100644
--- a/docs/source/configuration.rst
+++ b/docs/source/configuration.rst
@@ -757,6 +757,16 @@ then Converse will fall back to trying to determine the browser's language
and fetching those translations, or if that fails the default English texts
will be used.
+idle_presence_timeout
+---------------------
+
+* Default: ``300``
+
+The amount of seconds after which the user is considered to be idle
+and an idle presence according to XEP-0319 is sent.
+
+If the given value is negative or ``0``, this feature is disabled.
+
jid
---
diff --git a/spec/presence.js b/spec/presence.js
index b06815c7d..b24cd73be 100644
--- a/spec/presence.js
+++ b/spec/presence.js
@@ -47,7 +47,7 @@
``+
`Hello world`+
`0`+
- ``+
+ ``+
``
);
_converse.priority = 2;
@@ -57,7 +57,7 @@
`away`+
`Going jogging`+
`2`+
- ``+
+ ``+
``
);
@@ -68,7 +68,7 @@
`dnd`+
`Doing taxes`+
`0`+
- ``+
+ ``+
``
);
}));
@@ -97,7 +97,7 @@
.toBe(``+
`My custom status`+
`0`+
- ``+
+ ``+
``)
return test_utils.waitUntil(() => modal.el.getAttribute('aria-hidden') === "true");
@@ -109,7 +109,7 @@
modal.el.querySelector('[type="submit"]').click();
expect(_converse.connection.send.calls.mostRecent().args[0].toLocaleString())
.toBe(`dndMy custom status0`+
- ``+
+ ``+
``)
done();
});
diff --git a/src/headless/converse-core.js b/src/headless/converse-core.js
index d571b0644..4a9f43473 100644
--- a/src/headless/converse-core.js
+++ b/src/headless/converse-core.js
@@ -32,6 +32,7 @@ Strophe.addNamespace('DELAY', 'urn:xmpp:delay');
Strophe.addNamespace('FORWARD', 'urn:xmpp:forward:0');
Strophe.addNamespace('HINTS', 'urn:xmpp:hints');
Strophe.addNamespace('HTTPUPLOAD', 'urn:xmpp:http:upload:0');
+Strophe.addNamespace('IDLE', 'urn:xmpp:idle:1');
Strophe.addNamespace('MAM', 'urn:xmpp:mam:2');
Strophe.addNamespace('NICK', 'http://jabber.org/protocol/nick');
Strophe.addNamespace('OMEMO', "eu.siacs.conversations.axolotl");
@@ -197,6 +198,7 @@ _converse.default_settings = {
expose_rid_and_sid: false,
geouri_regex: /https:\/\/www.openstreetmap.org\/.*#map=[0-9]+\/([\-0-9.]+)\/([\-0-9.]+)\S*/g,
geouri_replacement: 'https://www.openstreetmap.org/?mlat=$1&mlon=$2#map=18/$1/$2',
+ idle_presence_timeout: 300, // Seconds after which an idle presence is sent
jid: undefined,
keepalive: true,
locales_url: 'locale/{{{locale}}}/LC_MESSAGES/converse.json',
@@ -416,6 +418,10 @@ _converse.initialize = function (settings, callback) {
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
@@ -440,6 +446,12 @@ _converse.initialize = function (settings, callback) {
!_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') {
@@ -457,7 +469,7 @@ _converse.initialize = function (settings, callback) {
/* 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) {
+ 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;
}
@@ -888,7 +900,12 @@ _converse.initialize = function (settings, callback) {
}
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;
},
@@ -1187,6 +1204,12 @@ _converse.initialize = function (settings, callback) {
if (!Backbone.history.started) {
Backbone.history.start();
}
+
+ if (_converse.idle_presence_timeout > 0) {
+ _converse.on('addClientFeatures', () => {
+ _converse.api.disco.own.features.add(Strophe.NS.IDLE);
+ });
+ }
}
if (!_.isUndefined(_converse.connection) &&