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.
This commit is contained in:
JC Brand 2015-06-22 00:01:31 +02:00
parent 7551c629bd
commit 868435173f
4 changed files with 153 additions and 63 deletions

View File

@ -287,8 +287,8 @@
allow_logout: true, allow_logout: true,
allow_muc: true, allow_muc: true,
allow_otr: true, allow_otr: true,
auto_away: 0, //in seconds auto_away: 0, // Seconds after which user status is set to 'away'
auto_xa: 0, //in seconds auto_xa: 0, // Seconds after which user status is set to 'xa'
allow_registration: true, allow_registration: true,
animate: true, animate: true,
auto_list_rooms: false, auto_list_rooms: false,
@ -297,6 +297,7 @@
auto_subscribe: false, auto_subscribe: false,
bosh_service_url: undefined, // The BOSH connection manager URL. bosh_service_url: undefined, // The BOSH connection manager URL.
cache_otr_key: false, 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, debug: false,
domain_placeholder: __(" e.g. conversejs.org"), // Placeholder text shown in the domain input on the registration form 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. 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, hide_offline_users: false,
jid: undefined, jid: undefined,
keepalive: false, 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) no_trimming: false, // Set to true for phantomjs tests (where browser apparently has no width)
ping_interval: 180, //in seconds ping_interval: 180, //in seconds
play_sounds: false, play_sounds: false,
@ -337,6 +338,8 @@
xhr_user_search: false, xhr_user_search: false,
xhr_user_search_url: '' xhr_user_search_url: ''
}; };
_.extend(this, this.default_settings); _.extend(this, this.default_settings);
// Allow only whitelisted configuration attributes to be overwritten // Allow only whitelisted configuration attributes to be overwritten
_.extend(this, _.pick(settings, Object.keys(this.default_settings))); _.extend(this, _.pick(settings, Object.keys(this.default_settings)));
@ -415,54 +418,56 @@
// ---------------------- // ----------------------
this.sendCSI = function (stat) { 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})); 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.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.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.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 () { this.playNotification = function () {
var audio; var audio;
@ -842,7 +847,7 @@
this.enableCarbons(); this.enableCarbons();
this.initStatus($.proxy(function () { this.initStatus($.proxy(function () {
this.registerPingHandler(); this.registerPingHandler();
this.registerAutoAwayHandler(); this.registerIntervalHandler();
this.chatboxes.onConnected(); this.chatboxes.onConnected();
this.giveFeedback(__('Contacts')); this.giveFeedback(__('Contacts'));
if (this.callback) { if (this.callback) {
@ -1206,9 +1211,7 @@
); );
this.renderToolbar().renderAvatar(); this.renderToolbar().renderAvatar();
converse.emit('chatBoxOpened', this); converse.emit('chatBoxOpened', this);
setTimeout(function () { setTimeout(converse.refreshWebkit, 50);
converse.refreshWebkit();
}, 50);
return this.showStatusMessage(); return this.showStatusMessage();
}, },
@ -2543,9 +2546,7 @@
this.$el.attr('id', this.model.get('box_id')) this.$el.attr('id', this.model.get('box_id'))
.html(converse.templates.chatroom(this.model.toJSON())); .html(converse.templates.chatroom(this.model.toJSON()));
this.renderChatArea(); this.renderChatArea();
setTimeout(function () { setTimeout(converse.refreshWebkit, 50);
converse.refreshWebkit();
}, 50);
return this; return this;
}, },
@ -5720,7 +5721,7 @@
*/ */
if (this.keepalive) { if (this.keepalive) {
try { try {
return this.connection.restore(null, this.onConnectStatusChanged); return this.connection.restore(undefined, this.onConnectStatusChanged);
} catch (e) { } catch (e) {
converse.log("Could not restore sessions. Error message: "+e.message); 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."); 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) { 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) { } else if (this.bosh_service_url) {
this.connection = new Strophe.Connection(this.bosh_service_url, {'keepalive': this.keepalive}); this.connection = new Strophe.Connection(this.bosh_service_url, {'keepalive': this.keepalive});
} else { } else {

View File

@ -23,6 +23,7 @@ Changelog
* Refactored in order to remove the strophe.roster.js dependency. [jcbrand] * Refactored in order to remove the strophe.roster.js dependency. [jcbrand]
* Refactored the plugin architecture. Add `overrides` convention for * Refactored the plugin architecture. Add `overrides` convention for
automatically overriding converse.js's methods and Backbone views and models. [jcbrand] 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) 0.9.3 (2015-05-01)
------------------ ------------------

View File

@ -180,21 +180,22 @@ auto_away
* Default: ``0`` * 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 user's status is ``extended away``, it won't be changed to ``away``.
If the value if negative or ``0``, the function is disabled.
If the given value is negative or ``0``, this option is disabled.
auto_xa auto_xa
------- -------
* Default: ``0`` * 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) If the value is negative or ``0``, the function is disabled.
The value must be greater than ``auto_away``
If the value if negative or ``0``, the function is disabled.
auto_reconnect 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 to retrieve your private key and read your all the chat messages in your
current session. Previous sessions however cannot be decrypted. 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 debug
----- -----

View File

@ -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 () { describe("The \"tokens\" API", $.proxy(function () {
beforeEach(function () { beforeEach(function () {
test_utils.closeAllChatBoxes(); test_utils.closeAllChatBoxes();