From 12b7687a895e7eafdabce7e699d3be5f28ee413c Mon Sep 17 00:00:00 2001 From: Christoph Scholz Date: Mon, 12 Nov 2018 18:46:03 +0100 Subject: [PATCH] Implement sending presences according to XEP-0319: Last User Interaction in Presence --- CHANGES.md | 1 + dist/converse.js | 35 +++++++++++++++++++++++++++++++++-- docs/source/configuration.rst | 10 ++++++++++ spec/presence.js | 10 +++++----- src/headless/converse-core.js | 27 +++++++++++++++++++++++++-- 5 files changed, 74 insertions(+), 9 deletions(-) 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) &&