ソースを参照

Decouple automatic away and XEP-0352 support.

- Add new config option csi_waiting_time for CSI support.
- The auto_away and auto_xa options won't send out CSI stanzas if csi_waiting_time is 0
- Update docs and add tests for both features.
JC Brand 10 年 前
コミット
868435173f
4 ファイル変更149 行追加59 行削除
  1. 53 52
      converse.js
  2. 1 0
      docs/CHANGES.rst
  3. 22 7
      docs/source/configuration.rst
  4. 73 0
      spec/converse.js

+ 53 - 52
converse.js

@@ -287,8 +287,8 @@
             allow_logout: true,
             allow_muc: true,
             allow_otr: true,
-            auto_away: 0, //in seconds
-            auto_xa: 0, //in seconds
+            auto_away: 0, // Seconds after which user status is set to 'away'
+            auto_xa: 0, // Seconds after which user status is set to 'xa'
             allow_registration: true,
             animate: true,
             auto_list_rooms: false,
@@ -297,6 +297,7 @@
             auto_subscribe: false,
             bosh_service_url: undefined, // The BOSH connection manager URL.
             cache_otr_key: false,
+            csi_waiting_time: 0, // Support for XEP-0352. Seconds before client is considered idle and CSI is sent out.
             debug: false,
             domain_placeholder: __(" e.g. conversejs.org"),  // Placeholder text shown in the domain input on the registration form
             default_box_height: 400, // The default height, in pixels, for the control box, chat boxes and chatrooms.
@@ -306,7 +307,7 @@
             hide_offline_users: false,
             jid: undefined,
             keepalive: false,
-            message_carbons: false,
+            message_carbons: false, // Support for XEP-280
             no_trimming: false, // Set to true for phantomjs tests (where browser apparently has no width)
             ping_interval: 180, //in seconds
             play_sounds: false,
@@ -337,6 +338,8 @@
             xhr_user_search: false,
             xhr_user_search_url: ''
         };
+
+
         _.extend(this, this.default_settings);
         // Allow only whitelisted configuration attributes to be overwritten
         _.extend(this, _.pick(settings, Object.keys(this.default_settings)));
@@ -415,54 +418,56 @@
         // ----------------------
 
         this.sendCSI = function (stat) {
-            if (converse.features[Strophe.NS.CSI]) {
+            /* Send out a Chat Status Notification (XEP-0352) */
+            if (converse.features[Strophe.NS.CSI] || true) {
                 converse.connection.send($build(stat, {xmlns: Strophe.NS.CSI}));
+                this.inactive = (stat === INACTIVE) ? true : false;
             }
         };
-        this.autoAwayReset = function () {
-            if (converse._idleCounter > 0) {
-                converse._idleCounter = 0;
-                if (converse._autoAway > 0) {
-                    converse._autoAway = 0;
-                    converse.sendCSI(ACTIVE);
-                    converse.xmppstatus.setStatus('online');
-                }
+
+        this.onUserActivity = function () {
+            /* Reset counters and flags relating to user activity. */
+            if (this.idle_seconds > 0) {
+                this.idle_seconds = 0;
+            }
+            if (this.inactive) {
+                this.sendCSI(ACTIVE);
+            }
+            if (this.auto_changed_status === true) {
+                this.auto_changed_status = false;
+                this.xmppstatus.setStatus('online');
             }
         };
-        this.registerAutoAwayHandler = function () {
-            // TODO: we should probably come up with a way to decouple CSI and auto-away
-            if (this.auto_away > 0 || this.auto_xa > 0) {
-                if (this.auto_xa > 0 && this.auto_xa < this.auto_away) {
-                    this.auto_xa = this.auto_away;
-                }
-                this._idleCounter = 0;
-                this._autoAway = 0;
-                $(window).on('click' , function () { converse.autoAwayReset(); });
-                $(window).on('mousemove' , function () { converse.autoAwayReset(); });
-                $(window).on('keypress' , function () { converse.autoAwayReset(); });
-                $(window).on('focus' , function () { converse.autoAwayReset(); });
-                $(window).on(unloadevent , function () { converse.autoAwayReset(); });
-
-                window.setInterval(function () {
-                    if ((this._idleCounter <= this.auto_away || (this.auto_xa > 0 && this._idleCounter <= this.auto_xa)) &&
-                        (this.xmppstatus.get('status') == 'online' && this._autoAway === 0) || (this.xmppstatus.get('status') == 'away' && this._autoAway == 1) ){
-                        this._idleCounter++;
-                    }
-                    if (this.auto_away > 0 && this._autoAway != 1 && this._idleCounter > this.auto_away && this._idleCounter <= this.auto_xa){
-                        this.sendCSI(INACTIVE);
-                        this._autoAway = 1;
-                        this.xmppstatus.setStatus('away');
-                    }
-                    else if (this.auto_xa > 0 && this._autoAway != 2 && this._idleCounter > this.auto_xa){
-                        this.sendCSI(INACTIVE);
-                        this._autoAway = 2;
-                        this.xmppstatus.setStatus('xa');
-                    }
-                }.bind(this), 1000); //every seconds
-                return true;
+
+        this.onEverySecond = function () {
+            /* An interval handler running every second */
+            var stat = this.xmppstatus.getStatus();
+            this.idle_seconds++;
+            if (this.idle_seconds > this.csi_waiting_time && !this.inactive) {
+                this.sendCSI(INACTIVE);
+            }
+            if (this.auto_away > 0 && this.idle_seconds > this.auto_away && stat !== 'away' && stat !== 'xa') {
+                this.auto_changed_status = true;
+                this.xmppstatus.setStatus('away');
+            } else if (this.auto_xa > 0 && this.idle_seconds > this.auto_xa && stat !== 'xa') {
+                this.auto_changed_status = true;
+                this.xmppstatus.setStatus('xa');
             }
         };
 
+        this.registerIntervalHandler = function () {
+            /* 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 (this.auto_away < 1 && this.auto_xa < 1 && this.csi_waiting_time < 1) {
+                // Waiting time of less then one second means features aren't used.
+                return;
+            }
+            this.idle_seconds = 0;
+            this.auto_changed_status = false; // Was the user's status changed by converse.js?
+            $(window).on('click mousemove keypress focus'+unloadevent , this.onUserActivity.bind(this));
+            window.setInterval(this.onEverySecond.bind(this), 1000);
+        };
 		
         this.playNotification = function () {
             var audio;
@@ -842,7 +847,7 @@
             this.enableCarbons();
             this.initStatus($.proxy(function () {
                 this.registerPingHandler();
-                this.registerAutoAwayHandler();				
+                this.registerIntervalHandler();				
                 this.chatboxes.onConnected();
                 this.giveFeedback(__('Contacts'));
                 if (this.callback) {
@@ -1206,9 +1211,7 @@
                     );
                 this.renderToolbar().renderAvatar();
                 converse.emit('chatBoxOpened', this);
-                setTimeout(function () {
-                    converse.refreshWebkit();
-                }, 50);
+                setTimeout(converse.refreshWebkit, 50);
                 return this.showStatusMessage();
             },
 
@@ -2543,9 +2546,7 @@
                 this.$el.attr('id', this.model.get('box_id'))
                         .html(converse.templates.chatroom(this.model.toJSON()));
                 this.renderChatArea();
-                setTimeout(function () {
-                    converse.refreshWebkit();
-                }, 50);
+                setTimeout(converse.refreshWebkit, 50);
                 return this;
             },
 
@@ -5720,7 +5721,7 @@
              */
             if (this.keepalive) {
                 try {
-                    return this.connection.restore(null, this.onConnectStatusChanged);
+                    return this.connection.restore(undefined, this.onConnectStatusChanged);
                 } catch (e) {
                     converse.log("Could not restore sessions. Error message: "+e.message);
                 }
@@ -5749,7 +5750,7 @@
                     throw new Error("initConnection: you must supply a value for either the bosh_service_url or websocket_url or both.");
                 }
                 if (('WebSocket' in window || 'MozWebSocket' in window) && this.websocket_url) {
-                    this.connection = new Strophe.Connection(this.websocket_url, {'keepalive': this.keepalive});
+                    this.connection = new Strophe.Connection(this.websocket_url);
                 } else if (this.bosh_service_url) {
                     this.connection = new Strophe.Connection(this.bosh_service_url, {'keepalive': this.keepalive});
                 } else {

+ 1 - 0
docs/CHANGES.rst

@@ -23,6 +23,7 @@ Changelog
 * Refactored in order to remove the strophe.roster.js dependency. [jcbrand]
 * Refactored the plugin architecture. Add `overrides` convention for
   automatically overriding converse.js's methods and Backbone views and models. [jcbrand]
+* Decouple automatic away and XEP-0352 support. [jcbrand]
 
 0.9.3 (2015-05-01)
 ------------------

+ 22 - 7
docs/source/configuration.rst

@@ -180,21 +180,22 @@ auto_away
 
 * Default:  ``0``
 
-This option can be used to let converse.js automatically change user presence
+The amount of seconds after which the user's presence status should
+automatically become ``away``.
 
-This set the number a seconds before user presence become ``away``
-If the value if negative or ``0``, the function is disabled.
+If the user's status is ``extended away``, it won't be changed to ``away``.
+
+If the given value is negative or ``0``, this option is disabled.
 
 auto_xa
 -------
 
 * Default:  ``0``
 
-This option can be used to let converse.js automatically change user presence
+The amount of seconds after which the user's presence status should
+automatically become ``extended away``.
 
-This set the number a seconds before user presence become ``xa`` (eXtended Away)
-The value must be greater than ``auto_away``
-If the value if negative or ``0``, the function is disabled.
+If the value is negative or ``0``, the function is disabled.
 
 auto_reconnect
 --------------
@@ -253,6 +254,20 @@ This setting can only be used together with ``allow_otr = true``.
     to retrieve your private key and read your all the chat messages in your
     current session. Previous sessions however cannot be decrypted.
 
+csi_waiting_time
+----------------
+
+* Default: ``0``
+
+This option adds support for **XEP-0085 Chat State Indication**.
+
+If converse.js is idle for the configured amount of seconds, a chat state
+indication of ``inactive`` will be sent out to the XMPP server (if the server
+supports CSI).
+
+Afterwards, ss soon as there is any activity (for example, the mouse moves),
+a chat state indication of ``active`` will be sent out.
+
 debug
 -----
 

+ 73 - 0
spec/converse.js

@@ -57,6 +57,79 @@
             });
         });
 
+        describe("A chat state indication", function () {
+
+            it("are sent out when the client becomes or stops being idle", function () {
+                spyOn(converse, 'sendCSI').andCallThrough();
+                var sent_stanza;
+                spyOn(converse.connection, 'send').andCallFake(function (stanza) {
+                    sent_stanza = stanza;
+                });
+                var i = 0;
+                converse.idle_seconds = 0; // Usually initialized by registerIntervalHandler
+                converse.features['urn:xmpp:csi:0'] = true; // Mock that the server supports CSI
+
+                converse.csi_waiting_time = 3; // The relevant config option
+                while (i <= converse.csi_waiting_time) {
+                    expect(converse.sendCSI).not.toHaveBeenCalled();
+                    converse.onEverySecond();
+                    i++;
+                }
+                expect(converse.sendCSI).toHaveBeenCalledWith('inactive');
+                expect(sent_stanza.toLocaleString()).toBe(
+                    "<inactive xmlns='urn:xmpp:csi:0'/>"
+                );
+                converse.onUserActivity();
+                expect(converse.sendCSI).toHaveBeenCalledWith('active');
+                expect(sent_stanza.toLocaleString()).toBe(
+                    "<active xmlns='urn:xmpp:csi:0'/>"
+                );
+
+                // Reset values
+                converse.csi_waiting_time = 0;
+                converse.features['urn:xmpp:csi:0'] = false;
+            });
+        });
+
+        describe("Automatic status change", function () {
+
+            it("happens when the client is idle for long enough", function () {
+                var i = 0;
+                // Usually initialized by registerIntervalHandler
+                converse.idle_seconds = 0; 
+                converse.auto_changed_status = false;
+
+                // The relevant config options
+                converse.auto_away = 3;
+                converse.auto_xa = 6;
+
+                expect(converse.xmppstatus.getStatus()).toBe('online');
+
+                while (i <= converse.auto_away) {
+                    converse.onEverySecond();
+                    i++;
+                }
+                expect(converse.auto_changed_status).toBe(true);
+
+                while (i <= converse.auto_xa) {
+                    expect(converse.xmppstatus.getStatus()).toBe('away');
+                    converse.onEverySecond();
+                    i++;
+                }
+                expect(converse.xmppstatus.getStatus()).toBe('xa');
+                expect(converse.auto_changed_status).toBe(true);
+
+                converse.onUserActivity();
+                expect(converse.xmppstatus.getStatus()).toBe('online');
+                expect(converse.auto_changed_status).toBe(false);
+
+                // Reset values
+                converse.auto_away = 0;
+                converse.auto_xa = 0;
+                converse.auto_changed_status = false;
+            });
+        });
+
         describe("The \"tokens\" API", $.proxy(function () {
             beforeEach(function () {
                 test_utils.closeAllChatBoxes();