Show join/leave messages in chat rooms. Updates #365

This commit is contained in:
JC Brand 2017-02-24 10:54:54 +00:00
parent e140eb84c8
commit 0d48929bb3
5 changed files with 173 additions and 67 deletions

View File

@ -50,6 +50,9 @@
- Bugfix. `TypeError: this.sendConfiguration(...).then is not a function` when - Bugfix. `TypeError: this.sendConfiguration(...).then is not a function` when
an instant room is created. [jcbrand] an instant room is created. [jcbrand]
- Ensure consistent behavior from `show_controlbox_by_default` [jcbrand] - Ensure consistent behavior from `show_controlbox_by_default` [jcbrand]
- #365 Show join/leave messages for chat rooms.
New configuration setting:
[muc_show_join_leave](https://conversejs.org/docs/html/configuration.html#muc-show-join-leave)
- #366 Show the chat room occupant's JID in the tooltip (if you're allowed to see it). [jcbrand] - #366 Show the chat room occupant's JID in the tooltip (if you're allowed to see it). [jcbrand]
- #610, #785 Add presence priority handling [w3host, jcbrand] - #610, #785 Add presence priority handling [w3host, jcbrand]
- #694 The `notification_option` wasn't being used consistently. [jcbrand] - #694 The `notification_option` wasn't being used consistently. [jcbrand]

View File

@ -791,6 +791,14 @@ automatically be "john". If now john@differentdomain.com tries to join the
room, his nickname will be "john-2", and if john@somethingelse.com joins, then room, his nickname will be "john-2", and if john@somethingelse.com joins, then
his nickname will be "john-3", and so forth. his nickname will be "john-3", and so forth.
muc_show_join_leave
-------------------
* Default; ``true``
Determines whether Converse.js will show info messages inside a chat room
whenever a user joins or leaves it.
notify_all_room_messages notify_all_room_messages
------------------------ ------------------------

View File

@ -345,6 +345,76 @@
describe("A Chat Room", function () { describe("A Chat Room", function () {
it("shows join/leave messages when users enter or exit a room", mock.initConverse(function (_converse) {
test_utils.openChatRoom(_converse, "coven", 'chat.shakespeare.lit', 'some1');
var view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
var $chat_content = view.$el.find('.chat-content');
/* We don't show join/leave messages for existing occupants. We
* know about them because we receive their presences before we
* receive our own.
*/
presence = $pres({
to: 'dummy@localhost/_converse.js-29092160',
from: 'coven@chat.shakespeare.lit/oldguy'
}).c('x', {xmlns: Strophe.NS.MUC_USER})
.c('item', {
'affiliation': 'none',
'jid': 'oldguy@localhost/_converse.js-290929789',
'role': 'participant'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($chat_content.find('div.chat-info').length).toBe(0);
/* <presence to="dummy@localhost/_converse.js-29092160"
* from="coven@chat.shakespeare.lit/some1">
* <x xmlns="http://jabber.org/protocol/muc#user">
* <item affiliation="owner" jid="dummy@localhost/_converse.js-29092160" role="moderator"/>
* <status code="110"/>
* </x>
* </presence></body>
*/
var presence = $pres({
to: 'dummy@localhost/_converse.js-29092160',
from: 'coven@chat.shakespeare.lit/some1'
}).c('x', {xmlns: Strophe.NS.MUC_USER})
.c('item', {
'affiliation': 'owner',
'jid': 'dummy@localhost/_converse.js-29092160',
'role': 'moderator'
}).up()
.c('status', {code: '110'});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($chat_content.find('div.chat-info:first').html()).toBe("some1 has joined the room");
presence = $pres({
to: 'dummy@localhost/_converse.js-29092160',
from: 'coven@chat.shakespeare.lit/newguy'
}).c('x', {xmlns: Strophe.NS.MUC_USER})
.c('item', {
'affiliation': 'none',
'jid': 'newguy@localhost/_converse.js-290929789',
'role': 'participant'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($chat_content.find('div.chat-info').length).toBe(2);
expect($chat_content.find('div.chat-info:last').html()).toBe("newguy has joined the room");
presence = $pres({
to: 'dummy@localhost/_converse.js-29092160',
type: 'unavailable',
from: 'coven@chat.shakespeare.lit/newguy'
}).c('x', {xmlns: Strophe.NS.MUC_USER})
.c('item', {
'affiliation': 'none',
'jid': 'newguy@localhost/_converse.js-290929789',
'role': 'participant'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($chat_content.find('div.chat-info').length).toBe(3);
expect($chat_content.find('div.chat-info:last').html()).toBe("newguy has left the room");
}));
it("shows its description in the chat heading", mock.initConverse(function (_converse) { it("shows its description in the chat heading", mock.initConverse(function (_converse) {
var sent_IQ, IQ_id; var sent_IQ, IQ_id;
var sendIQ = _converse.connection.sendIQ; var sendIQ = _converse.connection.sendIQ;
@ -1036,8 +1106,7 @@
_converse.connection._dataRecv(test_utils.createRequest(stanza)); _converse.connection._dataRecv(test_utils.createRequest(stanza));
var view = _converse.chatboxviews.get('jdev@conference.jabber.org'); var view = _converse.chatboxviews.get('jdev@conference.jabber.org');
var $chat_content = view.$el.find('.chat-content'); var $chat_content = view.$el.find('.chat-content');
expect($chat_content.find('.chat-info').length).toBe(1); expect($chat_content.find('.chat-info:last').text()).toBe('Topic set by ralphm to: '+text);
expect($chat_content.find('.chat-info').text()).toBe('Topic set by ralphm to: '+text);
})); }));
it("escapes the subject before rendering it, to avoid JS-injection attacks", mock.initConverse(function (_converse) { it("escapes the subject before rendering it, to avoid JS-injection attacks", mock.initConverse(function (_converse) {
@ -1047,8 +1116,7 @@
var view = _converse.chatboxviews.get('jdev@conference.jabber.org'); var view = _converse.chatboxviews.get('jdev@conference.jabber.org');
view.setChatRoomSubject('ralphm', subject); view.setChatRoomSubject('ralphm', subject);
var $chat_content = view.$el.find('.chat-content'); var $chat_content = view.$el.find('.chat-content');
expect($chat_content.find('.chat-info').length).toBe(1); expect($chat_content.find('.chat-info:last').text()).toBe('Topic set by ralphm to: '+subject);
expect($chat_content.find('.chat-info').text()).toBe('Topic set by ralphm to: '+subject);
})); }));
it("informs users if their nicknames has been changed.", mock.initConverse(function (_converse) { it("informs users if their nicknames has been changed.", mock.initConverse(function (_converse) {
@ -1114,8 +1182,9 @@
expect($occupants.children().length).toBe(1); expect($occupants.children().length).toBe(1);
expect($occupants.children().first(0).text()).toBe("oldnick"); expect($occupants.children().first(0).text()).toBe("oldnick");
expect($chat_content.find('div.chat-info').length).toBe(1); expect($chat_content.find('div.chat-info').length).toBe(2);
expect($chat_content.find('div.chat-info').html()).toBe(__(_converse.muc.new_nickname_messages["210"], "oldnick")); expect($chat_content.find('div.chat-info:first').html()).toBe("oldnick has joined the room");
expect($chat_content.find('div.chat-info:last').html()).toBe(__(_converse.muc.new_nickname_messages["210"], "oldnick"));
presence = $pres().attrs({ presence = $pres().attrs({
from:'lounge@localhost/oldnick', from:'lounge@localhost/oldnick',
@ -1134,7 +1203,7 @@
.c('status').attrs({code:'110'}).nodeTree; .c('status').attrs({code:'110'}).nodeTree;
_converse.connection._dataRecv(test_utils.createRequest(presence)); _converse.connection._dataRecv(test_utils.createRequest(presence));
expect($chat_content.find('div.chat-info').length).toBe(2); expect($chat_content.find('div.chat-info').length).toBe(3);
expect($chat_content.find('div.chat-info').last().html()).toBe(__(_converse.muc.new_nickname_messages["303"], "newnick")); expect($chat_content.find('div.chat-info').last().html()).toBe(__(_converse.muc.new_nickname_messages["303"], "newnick"));
$occupants = view.$('.occupant-list'); $occupants = view.$('.occupant-list');
@ -1154,8 +1223,9 @@
.c('status').attrs({code:'110'}).nodeTree; .c('status').attrs({code:'110'}).nodeTree;
_converse.connection._dataRecv(test_utils.createRequest(presence)); _converse.connection._dataRecv(test_utils.createRequest(presence));
expect($chat_content.find('div.chat-info').length).toBe(2); expect($chat_content.find('div.chat-info').length).toBe(4);
expect($chat_content.find('div.chat-info').last().html()).toBe(__(_converse.muc.new_nickname_messages["303"], "newnick")); expect($chat_content.find('div.chat-info').get(2).textContent).toBe(__(_converse.muc.new_nickname_messages["303"], "newnick"));
expect($chat_content.find('div.chat-info').last().html()).toBe("newnick has joined the room");
$occupants = view.$('.occupant-list'); $occupants = view.$('.occupant-list');
expect($occupants.children().length).toBe(1); expect($occupants.children().length).toBe(1);
expect($occupants.children().first(0).text()).toBe("newnick"); expect($occupants.children().first(0).text()).toBe("newnick");
@ -1400,7 +1470,7 @@
describe("Each chat room can take special commands", function () { describe("Each chat room can take special commands", function () {
it("to set the room subject", mock.initConverse(function (_converse) { it("to set the room topic", mock.initConverse(function (_converse) {
var sent_stanza; var sent_stanza;
test_utils.openChatRoom(_converse, 'lounge', 'localhost', 'dummy'); test_utils.openChatRoom(_converse, 'lounge', 'localhost', 'dummy');
var view = _converse.chatboxviews.get('lounge@localhost'); var view = _converse.chatboxviews.get('lounge@localhost');

View File

@ -50,10 +50,10 @@
it("can be used to replay conversations", mock.initConverse(function (_converse) { it("can be used to replay conversations", mock.initConverse(function (_converse) {
/* /*
test_utils.openChatRoom("discuss", 'conference.conversejs.org', 'jc'); test_utils.openChatRoom(_converse, "dummy", 'rooms.localhost', 'jc');
test_utils.openChatRoom("dummy", 'rooms.localhost', 'jc'); test_utils.openChatRoom(_converse, "prosody", 'conference.prosody.im', 'jc');
test_utils.openChatRoom("prosody", 'conference.prosody.im', 'jc');
*/ */
test_utils.openChatRoom(_converse, "discuss", 'conference.conversejs.org', 'ee');
spyOn(_converse, 'areDesktopNotificationsEnabled').andReturn(true); spyOn(_converse, 'areDesktopNotificationsEnabled').andReturn(true);
_.each(transcripts, function (transcript) { _.each(transcripts, function (transcript) {
var text = transcript(); var text = transcript();

View File

@ -73,6 +73,13 @@
Strophe.addNamespace('MUC_ROOMCONF', Strophe.NS.MUC + "#roomconfig"); Strophe.addNamespace('MUC_ROOMCONF', Strophe.NS.MUC + "#roomconfig");
Strophe.addNamespace('MUC_USER', Strophe.NS.MUC + "#user"); Strophe.addNamespace('MUC_USER', Strophe.NS.MUC + "#user");
var ROOMSTATUS = {
CONNECTED: 0,
CONNECTING: 1,
DISCONNECTED: 2,
ENTERED: 3
};
converse.plugins.add('converse-muc', { converse.plugins.add('converse-muc', {
/* Optional dependencies are other plugins which might be /* Optional dependencies are other plugins which might be
* overridden or relied upon, if they exist, otherwise they're ignored. * overridden or relied upon, if they exist, otherwise they're ignored.
@ -274,6 +281,7 @@
muc_history_max_stanzas: undefined, muc_history_max_stanzas: undefined,
muc_instant_rooms: true, muc_instant_rooms: true,
muc_nickname_from_jid: false, muc_nickname_from_jid: false,
muc_show_join_leave: true,
visible_toolbar_buttons: { visible_toolbar_buttons: {
'toggle_occupants': true 'toggle_occupants': true
}, },
@ -287,7 +295,7 @@
return _converse.chatboxviews.showChat( return _converse.chatboxviews.showChat(
_.extend({ _.extend({
'affiliation': null, 'affiliation': null,
'connection_status': Strophe.Status.DISCONNECTED, 'connection_status': ROOMSTATUS.DISCONNECTED,
'description': '', 'description': '',
'features_fetched': false, 'features_fetched': false,
'hidden': false, 'hidden': false,
@ -351,7 +359,7 @@
this.$el.find('.chat-content').on('scroll', this.markScrolled.bind(this)); this.$el.find('.chat-content').on('scroll', this.markScrolled.bind(this));
this.registerHandlers(); this.registerHandlers();
if (this.model.get('connection_status') !== Strophe.Status.CONNECTED) { if (this.model.get('connection_status') !== ROOMSTATUS.ENTERED) {
this.getRoomFeatures().always(function () { this.getRoomFeatures().always(function () {
that.join(); that.join();
that.fetchMessages(); that.fetchMessages();
@ -443,7 +451,7 @@
}, },
afterConnected: function () { afterConnected: function () {
if (this.model.get('connection_status') === Strophe.Status.CONNECTED) { if (this.model.get('connection_status') === ROOMSTATUS.ENTERED) {
this.setChatState(_converse.ACTIVE); this.setChatState(_converse.ACTIVE);
this.scrollDown(); this.scrollDown();
this.focus(); this.focus();
@ -807,7 +815,7 @@
* as taken from the 'chat_state' attribute of the chat box. * as taken from the 'chat_state' attribute of the chat box.
* See XEP-0085 Chat State Notifications. * See XEP-0085 Chat State Notifications.
*/ */
if (this.model.get('connection_status') !== Strophe.Status.CONNECTED) { if (this.model.get('connection_status') !== ROOMSTATUS.ENTERED) {
return; return;
} }
var chat_state = this.model.get('chat_state'); var chat_state = this.model.get('chat_state');
@ -1102,7 +1110,7 @@
if (!nick) { if (!nick) {
return this.checkForReservedNick(); return this.checkForReservedNick();
} }
if (this.model.get('connection_status') === Strophe.Status.CONNECTED) { if (this.model.get('connection_status') === ROOMSTATUS.ENTERED) {
// We have restored a chat room from session storage, // We have restored a chat room from session storage,
// so we don't send out a presence stanza again. // so we don't send out a presence stanza again.
return this; return this;
@ -1115,13 +1123,13 @@
if (password) { if (password) {
stanza.cnode(Strophe.xmlElement("password", [], password)); stanza.cnode(Strophe.xmlElement("password", [], password));
} }
this.model.save('connection_status', Strophe.Status.CONNECTING); this.model.save('connection_status', ROOMSTATUS.CONNECTING);
_converse.connection.send(stanza); _converse.connection.send(stanza);
return this; return this;
}, },
cleanup: function () { cleanup: function () {
this.model.save('connection_status', Strophe.Status.DISCONNECTED); this.model.save('connection_status', ROOMSTATUS.DISCONNECTED);
this.removeHandlers(); this.removeHandlers();
_converse.ChatBoxView.prototype.close.apply(this, arguments); _converse.ChatBoxView.prototype.close.apply(this, arguments);
}, },
@ -1137,7 +1145,7 @@
this.occupantsview.model.reset(); this.occupantsview.model.reset();
this.occupantsview.model.browserStorage._clear(); this.occupantsview.model.browserStorage._clear();
if (!_converse.connection.connected || if (!_converse.connection.connected ||
this.model.get('connection_status') === Strophe.Status.DISCONNECTED) { this.model.get('connection_status') === ROOMSTATUS.DISCONNECTED) {
// Don't send out a stanza if we're not connected. // Don't send out a stanza if we're not connected.
this.cleanup(); this.cleanup();
return; return;
@ -1568,26 +1576,23 @@
* current user. * current user.
* (XMLElement) stanza: The original stanza received. * (XMLElement) stanza: The original stanza received.
*/ */
var code = stat.getAttribute('code'), var code = stat.getAttribute('code'), nick;
from_nick; if (code === '110') { return; }
if (is_self && code === "210") { if (code in _converse.muc.info_messages) {
from_nick = Strophe.unescapeNode(Strophe.getResourceFromJid(stanza.getAttribute('from')));
return __(_converse.muc.new_nickname_messages[code], from_nick);
} else if (is_self && code === "303") {
return __(
_converse.muc.new_nickname_messages[code],
stanza.querySelector('x item').getAttribute('nick')
);
} else if (!is_self && (code in _converse.muc.action_info_messages)) {
from_nick = Strophe.unescapeNode(Strophe.getResourceFromJid(stanza.getAttribute('from')));
return __(_converse.muc.action_info_messages[code], from_nick);
} else if (code in _converse.muc.info_messages) {
return _converse.muc.info_messages[code]; return _converse.muc.info_messages[code];
} else if (code !== '110') {
if (stat.textContent) {
// Sometimes the status contains human readable text and not a code.
return stat.textContent;
} }
if (!is_self) {
if (code in _converse.muc.action_info_messages) {
nick = Strophe.getResourceFromJid(stanza.getAttribute('from'));
return __(_converse.muc.action_info_messages[code], nick);
}
} else if (code in _converse.muc.new_nickname_messages) {
if (is_self && code === "210") {
nick = Strophe.getResourceFromJid(stanza.getAttribute('from'));
} else if (is_self && code === "303") {
nick = stanza.querySelector('x item').getAttribute('nick');
}
return __(_converse.muc.new_nickname_messages[code], nick);
} }
return; return;
}, },
@ -1622,9 +1627,11 @@
// 1. Get notification messages based on the <status> elements. // 1. Get notification messages based on the <status> elements.
var statuses = x.querySelectorAll('status'); var statuses = x.querySelectorAll('status');
var mapper = _.partial(this.getMessageFromStatus, _, stanza, is_self); var mapper = _.partial(this.getMessageFromStatus, _, stanza, is_self);
var notification = { var notification = {};
'messages': _.reject(_.map(statuses, mapper), _.isUndefined), var messages = _.reject(_.map(statuses, mapper), _.isUndefined);
}; if (messages.length) {
notification.messages = messages;
}
// 2. Get disconnection messages based on the <status> elements // 2. Get disconnection messages based on the <status> elements
var codes = _.invokeMap(statuses, Element.prototype.getAttribute, 'code'); var codes = _.invokeMap(statuses, Element.prototype.getAttribute, 'code');
var disconnection_codes = _.intersection(codes, _.keys(_converse.muc.disconnect_messages)); var disconnection_codes = _.intersection(codes, _.keys(_converse.muc.disconnect_messages));
@ -1666,7 +1673,7 @@
if (notification.reason) { if (notification.reason) {
this.showDisconnectMessage(__(___('The reason given is: <em>"%1$s"</em>.'), notification.reason)); this.showDisconnectMessage(__(___('The reason given is: <em>"%1$s"</em>.'), notification.reason));
} }
this.model.save('connection_status', Strophe.Status.DISCONNECTED); this.model.save('connection_status', ROOMSTATUS.DISCONNECTED);
return; return;
} }
_.each(notification.messages, function (message) { _.each(notification.messages, function (message) {
@ -1680,6 +1687,25 @@
} }
}, },
getJoinLeaveMessages: function (stanza) {
/* Parse the given stanza and return notification messages
* for join/leave events.
*/
// XXX: some mangling required to make the returned
// result look like the structure returned by
// parseXUserElement. Not nice...
var nick = Strophe.getResourceFromJid(stanza.getAttribute('from'));
if (stanza.getAttribute('type') === 'unavailable') {
var stat = stanza.querySelector('status');
if (!_.isNull(stat) && stat.textContent) {
return [{'messages': [__(nick+' has left the room. "'+stat.textContent+'"')]}];
} else {
return [{'messages': [__(nick+' has left the room')]}];
}
}
return [{'messages': [__(nick+' has joined the room')]}];
},
showStatusMessages: function (stanza) { showStatusMessages: function (stanza) {
/* Check for status codes and communicate their purpose to the user. /* Check for status codes and communicate their purpose to the user.
* See: http://xmpp.org/registrar/mucstatus.html * See: http://xmpp.org/registrar/mucstatus.html
@ -1688,12 +1714,17 @@
* (XMLElement) stanza: The message or presence stanza * (XMLElement) stanza: The message or presence stanza
* containing the status codes. * containing the status codes.
*/ */
var is_self = stanza.querySelectorAll("status[code='110']").length;
var elements = sizzle('x[xmlns="'+Strophe.NS.MUC_USER+'"]', stanza); var elements = sizzle('x[xmlns="'+Strophe.NS.MUC_USER+'"]', stanza);
var notifications = _.map( var is_self = stanza.querySelectorAll("status[code='110']").length;
elements, var iteratee = _.partial(this.parseXUserElement.bind(this), _, stanza, is_self);
_.partial(this.parseXUserElement.bind(this), _, stanza, is_self) var notifications = _.reject(_.map(elements, iteratee), _.isEmpty);
); if (_.isEmpty(notifications) &&
_converse.muc_show_join_leave &&
stanza.nodeName === 'presence' &&
this.model.get('connection_status') === ROOMSTATUS.ENTERED
) {
notifications = this.getJoinLeaveMessages(stanza);
}
_.each(notifications, this.displayNotificationsforUser.bind(this)); _.each(notifications, this.displayNotificationsforUser.bind(this));
return stanza; return stanza;
}, },
@ -1767,11 +1798,10 @@
* (XMLElement) pres: The stanza * (XMLElement) pres: The stanza
*/ */
if (pres.getAttribute('type') === 'error') { if (pres.getAttribute('type') === 'error') {
this.model.save('connection_status', Strophe.Status.DISCONNECTED); this.model.save('connection_status', ROOMSTATUS.DISCONNECTED);
this.showErrorMessage(pres); this.showErrorMessage(pres);
return true; return true;
} }
var show_status_messages = true;
var is_self = pres.querySelector("status[code='110']"); var is_self = pres.querySelector("status[code='110']");
var locked_room = pres.querySelector("status[code='201']"); var locked_room = pres.querySelector("status[code='201']");
if (is_self) { if (is_self) {
@ -1784,15 +1814,14 @@
} else { } else {
this.configureChatRoom(); this.configureChatRoom();
if (!this.model.get('auto_configure')) { if (!this.model.get('auto_configure')) {
// We don't show status messages if the return;
// configuration form is being shown.
show_status_messages = false;
} }
} }
} }
this.model.save('connection_status', ROOMSTATUS.ENTERED);
} }
if (!locked_room && !this.model.get('features_fetched') && if (!locked_room && !this.model.get('features_fetched') &&
this.model.get('connection_status') !== Strophe.Status.CONNECTED) { this.model.get('connection_status') !== ROOMSTATUS.CONNECTED) {
// The features for this room weren't fetched yet, perhaps // The features for this room weren't fetched yet, perhaps
// because it's a new room without locking (in which // because it's a new room without locking (in which
// case Prosody doesn't send a 201 status). // case Prosody doesn't send a 201 status).
@ -1800,12 +1829,11 @@
// so a good time to fetch the features. // so a good time to fetch the features.
this.getRoomFeatures(); this.getRoomFeatures();
} }
if (show_status_messages) {
this.hideSpinner().showStatusMessages(pres); this.hideSpinner().showStatusMessages(pres);
}
this.occupantsview.updateOccupantsOnPresence(pres); this.occupantsview.updateOccupantsOnPresence(pres);
if (this.model.get('role') !== 'none') { if (this.model.get('role') !== 'none' &&
this.model.save('connection_status', Strophe.Status.CONNECTED); this.model.get('connection_status') === ROOMSTATUS.CONNECTING) {
this.model.save('connection_status', ROOMSTATUS.CONNECTED);
} }
return true; return true;
}, },
@ -2425,10 +2453,7 @@
'box_id': b64_sha1(room_jid), 'box_id': b64_sha1(room_jid),
'password': $x.attr('password') 'password': $x.attr('password')
}); });
if (!_.includes( if (chatroom.get('connection_status') === ROOMSTATUS.DISCONNECTED) {
[Strophe.Status.CONNECTING, Strophe.Status.CONNECTED],
chatroom.get('connection_status'))
) {
_converse.chatboxviews.get(room_jid).join(); _converse.chatboxviews.get(room_jid).join();
} }
} }
@ -2549,7 +2574,7 @@
*/ */
_converse.chatboxviews.each(function (view) { _converse.chatboxviews.each(function (view) {
if (view.model.get('type') === 'chatroom') { if (view.model.get('type') === 'chatroom') {
view.model.save('connection_status', Strophe.Status.DISCONNECTED); view.model.save('connection_status', ROOMSTATUS.DISCONNECTED);
view.join(); view.join();
} }
}); });
@ -2563,7 +2588,7 @@
*/ */
_converse.chatboxes.each(function (model) { _converse.chatboxes.each(function (model) {
if (model.get('type') === 'chatroom') { if (model.get('type') === 'chatroom') {
model.save('connection_status', Strophe.Status.DISCONNECTED); model.save('connection_status', ROOMSTATUS.DISCONNECTED);
} }
}); });
}; };