Browse Source

Implement sending presences according to XEP-0319: Last User Interaction in Presence

Christoph Scholz 6 years ago
parent
commit
12b7687a89
5 changed files with 74 additions and 9 deletions
  1. 1 0
      CHANGES.md
  2. 33 2
      dist/converse.js
  3. 10 0
      docs/source/configuration.rst
  4. 5 5
      spec/presence.js
  5. 25 2
      src/headless/converse-core.js

+ 1 - 0
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)
 

+ 33 - 2
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') {

+ 10 - 0
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
 ---
 

+ 5 - 5
spec/presence.js

@@ -47,7 +47,7 @@
                 `<presence xmlns="jabber:client">`+
                     `<status>Hello world</status>`+
                     `<priority>0</priority>`+
-                    `<c hash="sha-1" node="https://conversejs.org" ver="K7kn/M6VtmdMyo61pgn/jkZlax8=" xmlns="http://jabber.org/protocol/caps"/>`+
+                    `<c hash="sha-1" node="https://conversejs.org" ver="Hxbsr5fazs62i+O0GxIXf2OEDNs=" xmlns="http://jabber.org/protocol/caps"/>`+
                 `</presence>`
             );
             _converse.priority = 2;
@@ -57,7 +57,7 @@
                     `<show>away</show>`+
                     `<status>Going jogging</status>`+
                     `<priority>2</priority>`+
-                    `<c hash="sha-1" node="https://conversejs.org" ver="K7kn/M6VtmdMyo61pgn/jkZlax8=" xmlns="http://jabber.org/protocol/caps"/>`+
+                    `<c hash="sha-1" node="https://conversejs.org" ver="Hxbsr5fazs62i+O0GxIXf2OEDNs=" xmlns="http://jabber.org/protocol/caps"/>`+
                 `</presence>`
             );
 
@@ -68,7 +68,7 @@
                     `<show>dnd</show>`+
                     `<status>Doing taxes</status>`+
                     `<priority>0</priority>`+
-                    `<c hash="sha-1" node="https://conversejs.org" ver="K7kn/M6VtmdMyo61pgn/jkZlax8=" xmlns="http://jabber.org/protocol/caps"/>`+
+                    `<c hash="sha-1" node="https://conversejs.org" ver="Hxbsr5fazs62i+O0GxIXf2OEDNs=" xmlns="http://jabber.org/protocol/caps"/>`+
                 `</presence>`
             );
         }));
@@ -97,7 +97,7 @@
                     .toBe(`<presence xmlns="jabber:client">`+
                           `<status>My custom status</status>`+
                           `<priority>0</priority>`+
-                          `<c hash="sha-1" node="https://conversejs.org" ver="K7kn/M6VtmdMyo61pgn/jkZlax8=" xmlns="http://jabber.org/protocol/caps"/>`+
+                          `<c hash="sha-1" node="https://conversejs.org" ver="Hxbsr5fazs62i+O0GxIXf2OEDNs=" xmlns="http://jabber.org/protocol/caps"/>`+
                           `</presence>`)
 
                 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(`<presence xmlns="jabber:client"><show>dnd</show><status>My custom status</status><priority>0</priority>`+
-                          `<c hash="sha-1" node="https://conversejs.org" ver="K7kn/M6VtmdMyo61pgn/jkZlax8=" xmlns="http://jabber.org/protocol/caps"/>`+
+                          `<c hash="sha-1" node="https://conversejs.org" ver="Hxbsr5fazs62i+O0GxIXf2OEDNs=" xmlns="http://jabber.org/protocol/caps"/>`+
                           `</presence>`)
                 done();
             });

+ 25 - 2
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) &&