浏览代码

Merge pull request #1319 from ChaosKid42/implement_last_user_interaction_in_presence

Send presences according to XEP-0319: Last User Interaction in Presence
JC Brand 6 年之前
父节点
当前提交
5a556dd544
共有 5 个文件被更改,包括 74 次插入9 次删除
  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

@@ -14,6 +14,7 @@
 - #1305 added value 'all' for 'show_desktop_notifications' to notifiy even if converse.js is open
 - #1305 added value 'all' for 'show_desktop_notifications' to notifiy even if converse.js is open
 - #1312 Error `unrecognized expression` in Safari
 - #1312 Error `unrecognized expression` in Safari
 - #1318 added values 'on' and 'off' for 'trusted' option which removes the "This is a trusted device" checkbox from the login form
 - #1318 added values 'on' and 'off' for 'trusted' option which removes the "This is a trusted device" checkbox from the login form
+- #1319 Implement sending of presences according to XEP-0319: Last User Interaction in Presence
 
 
 ## 4.0.4 (2018-10-29)
 ## 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('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('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('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('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('NICK', 'http://jabber.org/protocol/nick');
 strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].addNamespace('OMEMO', "eu.siacs.conversations.axolotl");
 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,
   expose_rid_and_sid: false,
   geouri_regex: /https:\/\/www.openstreetmap.org\/.*#map=[0-9]+\/([\-0-9.]+)\/([\-0-9.]+)\S*/g,
   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',
   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,
   jid: undefined,
   keepalive: true,
   keepalive: true,
   locales_url: 'locale/{{{locale}}}/LC_MESSAGES/converse.json',
   locales_url: 'locale/{{{locale}}}/LC_MESSAGES/converse.json',
@@ -72166,6 +72169,12 @@ _converse.initialize = function (settings, callback) {
       _converse.sendCSI(_converse.ACTIVE);
       _converse.sendCSI(_converse.ACTIVE);
     }
     }
 
 
+    if (_converse.idle) {
+      _converse.idle = false;
+
+      _converse.xmppstatus.sendPresence();
+    }
+
     if (_converse.auto_changed_status === true) {
     if (_converse.auto_changed_status === true) {
       _converse.auto_changed_status = false; // XXX: we should really remember the original state here, and
       _converse.auto_changed_status = false; // XXX: we should really remember the original state here, and
       // then set it back to that...
       // then set it back to that...
@@ -72192,6 +72201,12 @@ _converse.initialize = function (settings, callback) {
       _converse.sendCSI(_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') {
     if (_converse.auto_away > 0 && _converse.idle_seconds > _converse.auto_away && stat !== 'away' && stat !== 'xa' && stat !== 'dnd') {
       _converse.auto_changed_status = true;
       _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.
     /* Set an interval of one second and register a handler for it.
      * Required for the auto_away, auto_xa and csi_waiting_time features.
      * 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.
       // Waiting time of less then one second means features aren't used.
       return;
       return;
     }
     }
@@ -72707,7 +72722,17 @@ _converse.initialize = function (settings, callback) {
         presence.c('status').t(status_message).up();
         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;
       return presence;
     },
     },
 
 
@@ -73015,6 +73040,12 @@ _converse.initialize = function (settings, callback) {
     if (!Backbone.history.started) {
     if (!Backbone.history.started) {
       Backbone.history.start();
       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') {
   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
 and fetching those translations, or if that fails the default English texts
 will be used.
 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
 jid
 ---
 ---
 
 

+ 5 - 5
spec/presence.js

@@ -47,7 +47,7 @@
                 `<presence xmlns="jabber:client">`+
                 `<presence xmlns="jabber:client">`+
                     `<status>Hello world</status>`+
                     `<status>Hello world</status>`+
                     `<priority>0</priority>`+
                     `<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>`
                 `</presence>`
             );
             );
             _converse.priority = 2;
             _converse.priority = 2;
@@ -57,7 +57,7 @@
                     `<show>away</show>`+
                     `<show>away</show>`+
                     `<status>Going jogging</status>`+
                     `<status>Going jogging</status>`+
                     `<priority>2</priority>`+
                     `<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>`
                 `</presence>`
             );
             );
 
 
@@ -68,7 +68,7 @@
                     `<show>dnd</show>`+
                     `<show>dnd</show>`+
                     `<status>Doing taxes</status>`+
                     `<status>Doing taxes</status>`+
                     `<priority>0</priority>`+
                     `<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>`
                 `</presence>`
             );
             );
         }));
         }));
@@ -97,7 +97,7 @@
                     .toBe(`<presence xmlns="jabber:client">`+
                     .toBe(`<presence xmlns="jabber:client">`+
                           `<status>My custom status</status>`+
                           `<status>My custom status</status>`+
                           `<priority>0</priority>`+
                           `<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>`)
                           `</presence>`)
 
 
                 return test_utils.waitUntil(() => modal.el.getAttribute('aria-hidden') === "true");
                 return test_utils.waitUntil(() => modal.el.getAttribute('aria-hidden') === "true");
@@ -109,7 +109,7 @@
                 modal.el.querySelector('[type="submit"]').click();
                 modal.el.querySelector('[type="submit"]').click();
                 expect(_converse.connection.send.calls.mostRecent().args[0].toLocaleString())
                 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>`+
                     .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>`)
                           `</presence>`)
                 done();
                 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('FORWARD', 'urn:xmpp:forward:0');
 Strophe.addNamespace('HINTS', 'urn:xmpp:hints');
 Strophe.addNamespace('HINTS', 'urn:xmpp:hints');
 Strophe.addNamespace('HTTPUPLOAD', 'urn:xmpp:http:upload:0');
 Strophe.addNamespace('HTTPUPLOAD', 'urn:xmpp:http:upload:0');
+Strophe.addNamespace('IDLE', 'urn:xmpp:idle:1');
 Strophe.addNamespace('MAM', 'urn:xmpp:mam:2');
 Strophe.addNamespace('MAM', 'urn:xmpp:mam:2');
 Strophe.addNamespace('NICK', 'http://jabber.org/protocol/nick');
 Strophe.addNamespace('NICK', 'http://jabber.org/protocol/nick');
 Strophe.addNamespace('OMEMO', "eu.siacs.conversations.axolotl");
 Strophe.addNamespace('OMEMO', "eu.siacs.conversations.axolotl");
@@ -197,6 +198,7 @@ _converse.default_settings = {
     expose_rid_and_sid: false,
     expose_rid_and_sid: false,
     geouri_regex: /https:\/\/www.openstreetmap.org\/.*#map=[0-9]+\/([\-0-9.]+)\/([\-0-9.]+)\S*/g,
     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',
     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,
     jid: undefined,
     keepalive: true,
     keepalive: true,
     locales_url: 'locale/{{{locale}}}/LC_MESSAGES/converse.json',
     locales_url: 'locale/{{{locale}}}/LC_MESSAGES/converse.json',
@@ -416,6 +418,10 @@ _converse.initialize = function (settings, callback) {
         if (_converse.inactive) {
         if (_converse.inactive) {
             _converse.sendCSI(_converse.ACTIVE);
             _converse.sendCSI(_converse.ACTIVE);
         }
         }
+        if (_converse.idle) {
+            _converse.idle = false;
+            _converse.xmppstatus.sendPresence();
+        }
         if (_converse.auto_changed_status === true) {
         if (_converse.auto_changed_status === true) {
             _converse.auto_changed_status = false;
             _converse.auto_changed_status = false;
             // XXX: we should really remember the original state here, and
             // XXX: we should really remember the original state here, and
@@ -440,6 +446,12 @@ _converse.initialize = function (settings, callback) {
                 !_converse.inactive) {
                 !_converse.inactive) {
             _converse.sendCSI(_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 &&
         if (_converse.auto_away > 0 &&
                 _converse.idle_seconds > _converse.auto_away &&
                 _converse.idle_seconds > _converse.auto_away &&
                 stat !== 'away' && stat !== 'xa' && stat !== 'dnd') {
                 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.
         /* Set an interval of one second and register a handler for it.
          * Required for the auto_away, auto_xa and csi_waiting_time features.
          * 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.
             // Waiting time of less then one second means features aren't used.
             return;
             return;
         }
         }
@@ -888,7 +900,12 @@ _converse.initialize = function (settings, callback) {
             }
             }
             presence.c('priority').t(
             presence.c('priority').t(
                 _.isNaN(Number(_converse.priority)) ? 0 : _converse.priority
                 _.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;
             return presence;
         },
         },
 
 
@@ -1187,6 +1204,12 @@ _converse.initialize = function (settings, callback) {
         if (!Backbone.history.started) {
         if (!Backbone.history.started) {
             Backbone.history.start();
             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) &&
     if (!_.isUndefined(_converse.connection) &&