diff --git a/CHANGES.md b/CHANGES.md
index 48c812ca0..cefec9ec7 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -3,6 +3,7 @@
## 4.0.7 (Unreleased)
- Bugfix: MUC commands were being ignored
+- Bugfix: Multiple rooms shown active in the rooms list
- UI: Always show the OMEMO lock icon (grayed out if not available).
- Use `publish-options` with `pubsub#access_model` set to `open` when publishing OMEMO public keys and devices
- Add a new `converse-pubsub` plugin, for generic PubSub operations
diff --git a/dist/converse.js b/dist/converse.js
index 68686fb4e..889d9966c 100644
--- a/dist/converse.js
+++ b/dist/converse.js
@@ -53857,11 +53857,12 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
return templates_chatroom_details_modal_html__WEBPACK_IMPORTED_MODULE_9___default()(_.extend(this.model.toJSON(), {
'_': _,
'__': __,
+ 'display_name': __('Groupchat info for %1$s', this.model.getDisplayName()),
+ 'features': this.model.features.toJSON(),
+ 'num_occupants': this.model.occupants.length,
'topic': u.addHyperlinks(xss__WEBPACK_IMPORTED_MODULE_26___default.a.filterXSS(_.get(this.model.get('subject'), 'text'), {
'whiteList': {}
- })),
- 'display_name': __('Groupchat info for %1$s', this.model.getDisplayName()),
- 'num_occupants': this.model.occupants.length
+ }))
}));
}
@@ -55361,13 +55362,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
this.chatroomview = this.model.chatroomview;
this.chatroomview.model.on('change:open', this.renderInviteWidget, this);
this.chatroomview.model.on('change:affiliation', this.renderInviteWidget, this);
- this.chatroomview.model.on('change', () => {
- if (_.intersection(_converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].ROOM_FEATURES, Object.keys(this.chatroomview.model.changed)).length === 0) {
- return;
- }
-
- this.renderRoomFeatures();
- }, this);
+ this.chatroomview.model.features.on('change', this.renderRoomFeatures, this);
this.render();
this.model.fetch({
'add': true,
@@ -55409,15 +55404,18 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
},
renderRoomFeatures() {
- const picks = _.pick(this.chatroomview.model.attributes, _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].ROOM_FEATURES),
- iteratee = (a, v) => a || v,
- el = this.el.querySelector('.chatroom-features');
+ const features = this.chatroomview.model.features,
+ picks = _.pick(features.attributes, _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].ROOM_FEATURES),
+ iteratee = (a, v) => a || v;
+
+ if (_.reduce(_.values(picks), iteratee)) {
+ const el = this.el.querySelector('.chatroom-features');
+ el.innerHTML = templates_chatroom_features_html__WEBPACK_IMPORTED_MODULE_11___default()(_.extend(features.toJSON(), {
+ __
+ }));
+ this.setOccupantsHeight();
+ }
- el.innerHTML = templates_chatroom_features_html__WEBPACK_IMPORTED_MODULE_11___default()(_.extend(this.chatroomview.model.toJSON(), {
- '__': __,
- 'has_features': _.reduce(_.values(picks), iteratee)
- }));
- this.setOccupantsHeight();
return this;
},
@@ -56448,7 +56446,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
},
renderOMEMOToolbarButton() {
- if (this.model.get('membersonly') && this.model.get('nonanonymous')) {
+ if (this.model.features.get('membersonly') && this.model.features.get('nonanonymous')) {
this.__super__.renderOMEMOToolbarButton.apply(arguments);
} else {
const icon = this.el.querySelector('.toggle-omemo');
@@ -57265,7 +57263,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
}
async function onOccupantAdded(chatroom, occupant) {
- if (occupant.isSelf() || !chatroom.get('nonanonymous') || !chatroom.get('membersonly')) {
+ if (occupant.isSelf() || !chatroom.features.get('nonanonymous') || !chatroom.features.get('membersonly')) {
return;
}
@@ -57290,7 +57288,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
if (chatbox.get('type') === _converse.CHATROOMS_TYPE) {
await _converse.api.waitUntil('OMEMOInitialized');
- supported = chatbox.get('nonanonymous') && chatbox.get('membersonly');
+ supported = chatbox.features.get('nonanonymous') && chatbox.features.get('membersonly');
} else if (chatbox.get('type') === _converse.PRIVATE_CHAT_TYPE) {
supported = await _converse.contactHasOMEMOSupport(chatbox.get('jid'));
}
@@ -57303,8 +57301,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
if (chatbox.get('type') === _converse.CHATROOMS_TYPE) {
chatbox.occupants.on('add', o => onOccupantAdded(chatbox, o));
- chatbox.on('change:nonanonymous', checkOMEMOSupported);
- chatbox.on('change:membersonly', checkOMEMOSupported);
+ chatbox.features.on('change', () => checkOMEMOSupported(chatbox));
}
}));
@@ -63515,7 +63512,7 @@ _converse.initialize = function (settings, callback) {
};
this.initSession = function () {
- const id = b64_sha1('converse.bosh-session');
+ const id = 'converse.bosh-session';
_converse.session = new Backbone.Model({
id
});
@@ -66188,7 +66185,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc
_converse.ChatRoom = _converse.ChatBox.extend({
defaults() {
- return _.assign(_.clone(_converse.ChatBox.prototype.defaults), _.zipObject(_converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].ROOM_FEATURES, _.map(_converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].ROOM_FEATURES, _.stubFalse)), {
+ return _.assign(_.clone(_converse.ChatBox.prototype.defaults), {
// For group chats, we distinguish between generally unread
// messages and those ones that specifically mention the
// user.
@@ -66203,7 +66200,6 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc
'name': '',
'nick': _converse.xmppstatus.get('nickname') || _converse.nickname,
'description': '',
- 'features_fetched': false,
'roomconfig': {},
'type': _converse.CHATROOMS_TYPE,
'message_type': 'groupchat'
@@ -66214,6 +66210,15 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc
this.constructor.__super__.initialize.apply(this, arguments);
this.on('change:connection_status', this.onConnectionStatusChanged, this);
+
+ const storage = _converse.config.get('storage');
+
+ const id = `converse.muc-features-${_converse.bare_jid}-${this.get('jid')}`;
+ this.features = new Backbone.Model(_.assign({
+ id
+ }, _.zipObject(_converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].ROOM_FEATURES, _.map(_converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].ROOM_FEATURES, _.stubFalse))));
+ this.features.browserStorage = new Backbone.BrowserStorage.session(id);
+ this.features.fetch();
this.occupants = new _converse.ChatRoomOccupants();
this.occupants.browserStorage = new Backbone.BrowserStorage.session(b64_sha1(`converse.occupants-${_converse.bare_jid}${this.get('jid')}`));
this.occupants.chatroom = this;
@@ -66342,6 +66347,8 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc
* (String) exit_msg: Optional message to indicate your
* reason for leaving.
*/
+ this.features.destroy();
+
this.occupants.browserStorage._clear();
this.occupants.reset();
@@ -66583,13 +66590,27 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc
return this.getRoomFeatures();
},
+ async getRoomIdentity() {
+ const _ref = await Promise.all([_converse.api.disco.getIdentity('conference', 'text', this.get('jid')), _converse.api.disco.getFields(this.get('jid'))]),
+ _ref2 = _slicedToArray(_ref, 2),
+ identity = _ref2[0],
+ fields = _ref2[1];
+
+ this.save({
+ 'name': identity && identity.get('name'),
+ 'description': _.get(fields.findWhere({
+ 'var': "muc#roominfo_description"
+ }), 'attributes.value')
+ });
+ },
+
async getRoomFeatures() {
+ // XXX: not sure whet the right place is to get the room identitiy
+ this.getRoomIdentity();
+
const features = await _converse.api.disco.getFeatures(this.get('jid')),
- fields = await _converse.api.disco.getFields(this.get('jid')),
- identity = await _converse.api.disco.getIdentity('conference', 'text', this.get('jid')),
attrs = _.extend(_.zipObject(_converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].ROOM_FEATURES, _.map(_converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].ROOM_FEATURES, _.stubFalse)), {
- 'features_fetched': moment().format(),
- 'name': identity && identity.get('name')
+ 'fetched': moment().format()
});
features.each(feature => {
@@ -66605,10 +66626,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc
attrs[fieldname.replace('muc_', '')] = true;
});
- attrs.description = _.get(fields.findWhere({
- 'var': "muc#roominfo_description"
- }), 'attributes.value');
- this.save(attrs);
+ this.features.save(attrs);
},
requestMemberList(affiliation) {
@@ -67230,7 +67248,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc
this.trigger('configurationNeeded');
return; // We haven't yet entered the groupchat, so bail here.
}
- } else if (!this.get('features_fetched')) {
+ } else if (!this.features.get('fetched')) {
// The features for this groupchat weren't fetched.
// That must mean it's a new groupchat without locking
// (in which case Prosody doesn't send a 201 status),
@@ -92568,7 +92586,7 @@ __e(o.num_occupants) +
'
\n ' +
__e(o.__('Features')) +
':\n
\n
\n ';
- if (o.passwordprotected) { ;
+ if (o.features.passwordprotected) { ;
__p += '\n - ' +
__e( o.__('Password protected') ) +
' - ' +
@@ -92576,7 +92594,7 @@ __e( o.__('This groupchat requires a password before entry') ) +
'
\n ';
} ;
__p += '\n ';
- if (o.unsecured) { ;
+ if (o.features.unsecured) { ;
__p += '\n - ' +
__e( o.__('No password required') ) +
' - ' +
@@ -92584,7 +92602,7 @@ __e( o.__('This groupchat does not require a password upon entry') ) +
'
\n ';
} ;
__p += '\n ';
- if (o.hidden) { ;
+ if (o.features.hidden) { ;
__p += '\n - ' +
__e( o.__('Hidden') ) +
' - ' +
@@ -92592,7 +92610,7 @@ __e( o.__('This groupchat is not publicly searchable') ) +
'
\n ';
} ;
__p += '\n ';
- if (o.public_room) { ;
+ if (o.features.public_room) { ;
__p += '\n - ' +
__e( o.__('Public') ) +
' - ' +
@@ -92600,7 +92618,7 @@ __e( o.__('This groupchat is publicly searchable') ) +
'
\n ';
} ;
__p += '\n ';
- if (o.membersonly) { ;
+ if (o.features.membersonly) { ;
__p += '\n - ' +
__e( o.__('Members only') ) +
' - ' +
@@ -92608,7 +92626,7 @@ __e( o.__('This groupchat is restricted to members only') ) +
'
\n ';
} ;
__p += '\n ';
- if (o.open) { ;
+ if (o.features.open) { ;
__p += '\n - ' +
__e( o.__('Open') ) +
' - ' +
@@ -92616,7 +92634,7 @@ __e( o.__('Anyone can join this groupchat') ) +
'
\n ';
} ;
__p += '\n ';
- if (o.persistent) { ;
+ if (o.features.persistent) { ;
__p += '\n - ' +
__e( o.__('Persistent') ) +
' - ' +
@@ -92624,7 +92642,7 @@ __e( o.__('This groupchat persists even if it\'s unoccupied') ) +
'
\n ';
} ;
__p += '\n ';
- if (o.temporary) { ;
+ if (o.features.temporary) { ;
__p += '\n - ' +
__e( o.__('Temporary') ) +
' - ' +
@@ -92632,7 +92650,7 @@ __e( o.__('This groupchat will disappear once the last person leaves') ) +
'
\n ';
} ;
__p += '\n ';
- if (o.nonanonymous) { ;
+ if (o.features.nonanonymous) { ;
__p += '\n - ' +
__e( o.__('Not anonymous') ) +
' - ' +
@@ -92640,7 +92658,7 @@ __e( o.__('All other groupchat participants can see your XMPP username') ) +
'
\n ';
} ;
__p += '\n ';
- if (o.semianonymous) { ;
+ if (o.features.semianonymous) { ;
__p += '\n - ' +
__e( o.__('Semi-anonymous') ) +
' - ' +
@@ -92648,7 +92666,7 @@ __e( o.__('Only moderators can see your XMPP username') ) +
'
\n ';
} ;
__p += '\n ';
- if (o.moderated) { ;
+ if (o.features.moderated) { ;
__p += '\n - ' +
__e( o.__('Moderated') ) +
' - ' +
@@ -92656,7 +92674,7 @@ __e( o.__('Participants entering this groupchat need to request permission to wr
'
\n ';
} ;
__p += '\n ';
- if (o.unmoderated) { ;
+ if (o.features.unmoderated) { ;
__p += '\n - ' +
__e( o.__('Not moderated') ) +
' - ' +
@@ -92664,7 +92682,7 @@ __e( o.__('Participants entering this groupchat can write right away') ) +
'
\n ';
} ;
__p += '\n ';
- if (o.mam_enabled) { ;
+ if (o.features.mam_enabled) { ;
__p += '\n - ' +
__e( o.__('Message archiving') ) +
' - ' +
@@ -92715,13 +92733,9 @@ var _ = {escape:__webpack_require__(/*! ./node_modules/lodash/escape.js */ "./no
module.exports = function(o) {
var __t, __p = '', __e = _.escape, __j = Array.prototype.join;
function print() { __p += __j.call(arguments, '') }
-__p += '\n';
- if (o.has_features) { ;
-__p += '\n
' +
+__p += '\n
' +
__e(o.__('Features')) +
-'
\n';
- } ;
-__p += '\n\n';
+'\n\n';
if (o.passwordprotected) { ;
__p += '\n- ' +
+'">' +
__e( o.__('Temporary') ) +
'
\n';
} ;
diff --git a/spec/chatroom.js b/spec/chatroom.js
index 628c70035..4a7c7b226 100644
--- a/spec/chatroom.js
+++ b/spec/chatroom.js
@@ -2141,13 +2141,13 @@
let view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
await test_utils.waitUntil(() => (view.model.get('connection_status') === converse.ROOMSTATUS.CONNECTING));
view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
- expect(view.model.get('features_fetched')).toBeTruthy();
- expect(view.model.get('passwordprotected')).toBe(true);
- expect(view.model.get('hidden')).toBe(true);
- expect(view.model.get('temporary')).toBe(true);
- expect(view.model.get('open')).toBe(true);
- expect(view.model.get('unmoderated')).toBe(true);
- expect(view.model.get('nonanonymous')).toBe(true);
+ expect(view.model.features.get('fetched')).toBeTruthy();
+ expect(view.model.features.get('passwordprotected')).toBe(true);
+ expect(view.model.features.get('hidden')).toBe(true);
+ expect(view.model.features.get('temporary')).toBe(true);
+ expect(view.model.features.get('open')).toBe(true);
+ expect(view.model.features.get('unmoderated')).toBe(true);
+ expect(view.model.features.get('nonanonymous')).toBe(true);
done();
}));
@@ -2172,19 +2172,19 @@
let features_list = chatroomview.el.querySelector('.features-list');
let features_shown = features_list.textContent.split('\n').map(s => s.trim()).filter(s => s);
expect(_.difference(["Password protected", "Open", "Temporary", "Not anonymous", "Not moderated"], features_shown).length).toBe(0);
- expect(chatroomview.model.get('hidden')).toBe(false);
- expect(chatroomview.model.get('mam_enabled')).toBe(false);
- expect(chatroomview.model.get('membersonly')).toBe(false);
- expect(chatroomview.model.get('moderated')).toBe(false);
- expect(chatroomview.model.get('nonanonymous')).toBe(true);
- expect(chatroomview.model.get('open')).toBe(true);
- expect(chatroomview.model.get('passwordprotected')).toBe(true);
- expect(chatroomview.model.get('persistent')).toBe(false);
- expect(chatroomview.model.get('publicroom')).toBe(true);
- expect(chatroomview.model.get('semianonymous')).toBe(false);
- expect(chatroomview.model.get('temporary')).toBe(true);
- expect(chatroomview.model.get('unmoderated')).toBe(true);
- expect(chatroomview.model.get('unsecured')).toBe(false);
+ expect(chatroomview.model.features.get('hidden')).toBe(false);
+ expect(chatroomview.model.features.get('mam_enabled')).toBe(false);
+ expect(chatroomview.model.features.get('membersonly')).toBe(false);
+ expect(chatroomview.model.features.get('moderated')).toBe(false);
+ expect(chatroomview.model.features.get('nonanonymous')).toBe(true);
+ expect(chatroomview.model.features.get('open')).toBe(true);
+ expect(chatroomview.model.features.get('passwordprotected')).toBe(true);
+ expect(chatroomview.model.features.get('persistent')).toBe(false);
+ expect(chatroomview.model.features.get('publicroom')).toBe(true);
+ expect(chatroomview.model.features.get('semianonymous')).toBe(false);
+ expect(chatroomview.model.features.get('temporary')).toBe(true);
+ expect(chatroomview.model.features.get('unmoderated')).toBe(true);
+ expect(chatroomview.model.features.get('unsecured')).toBe(false);
expect(chatroomview.el.querySelector('.chat-title').textContent.trim()).toBe('Room');
chatroomview.el.querySelector('.configure-chatroom-button').click();
@@ -2304,7 +2304,7 @@
'muc_passwordprotected',
'muc_hidden',
'muc_temporary',
- 'muc_open',
+ 'muc_membersonly',
'muc_unmoderated',
'muc_nonanonymous'
];
@@ -2316,27 +2316,26 @@
.c('value').t('This is the description').up().up()
.c('field', {'type':'text-single', 'var':'muc#roominfo_occupants', 'label':'Number of occupants'})
.c('value').t(0);
+
_converse.connection._dataRecv(test_utils.createRequest(features_stanza));
- spyOn(chatroomview.occupantsview, 'renderRoomFeatures').and.callThrough();
-
- await test_utils.waitUntil(() => chatroomview.occupantsview.renderRoomFeatures.calls.count());
+ await test_utils.waitUntil(() => new Promise(success => chatroomview.model.features.on('change', success)));
features_list = chatroomview.el.querySelector('.features-list');
features_shown = features_list.textContent.split('\n').map(s => s.trim()).filter(s => s);
- expect(_.difference(["Password protected", "Hidden", "Open", "Temporary", "Not anonymous", "Not moderated"], features_shown).length).toBe(0);
- expect(chatroomview.model.get('hidden')).toBe(true);
- expect(chatroomview.model.get('mam_enabled')).toBe(false);
- expect(chatroomview.model.get('membersonly')).toBe(false);
- expect(chatroomview.model.get('moderated')).toBe(false);
- expect(chatroomview.model.get('nonanonymous')).toBe(true);
- expect(chatroomview.model.get('open')).toBe(true);
- expect(chatroomview.model.get('passwordprotected')).toBe(true);
- expect(chatroomview.model.get('persistent')).toBe(false);
- expect(chatroomview.model.get('publicroom')).toBe(false);
- expect(chatroomview.model.get('semianonymous')).toBe(false);
- expect(chatroomview.model.get('temporary')).toBe(true);
- expect(chatroomview.model.get('unmoderated')).toBe(true);
- expect(chatroomview.model.get('unsecured')).toBe(false);
+ expect(_.difference(["Password protected", "Hidden", "Members only", "Temporary", "Not anonymous", "Not moderated"], features_shown).length).toBe(0);
+ expect(chatroomview.model.features.get('hidden')).toBe(true);
+ expect(chatroomview.model.features.get('mam_enabled')).toBe(false);
+ expect(chatroomview.model.features.get('membersonly')).toBe(true);
+ expect(chatroomview.model.features.get('moderated')).toBe(false);
+ expect(chatroomview.model.features.get('nonanonymous')).toBe(true);
+ expect(chatroomview.model.features.get('open')).toBe(false);
+ expect(chatroomview.model.features.get('passwordprotected')).toBe(true);
+ expect(chatroomview.model.features.get('persistent')).toBe(false);
+ expect(chatroomview.model.features.get('publicroom')).toBe(false);
+ expect(chatroomview.model.features.get('semianonymous')).toBe(false);
+ expect(chatroomview.model.features.get('temporary')).toBe(true);
+ expect(chatroomview.model.features.get('unmoderated')).toBe(true);
+ expect(chatroomview.model.features.get('unsecured')).toBe(false);
expect(chatroomview.el.querySelector('.chat-title').textContent.trim()).toBe('New room name');
done();
}));
@@ -3708,7 +3707,7 @@
.c('feature', {'var': 'muc_membersonly'}).up();
_converse.connection._dataRecv(test_utils.createRequest(features_stanza));
await test_utils.waitUntil(() => (view.model.get('connection_status') === converse.ROOMSTATUS.CONNECTING));
- expect(view.model.get('membersonly')).toBeTruthy();
+ expect(view.model.features.get('membersonly')).toBeTruthy();
test_utils.createContacts(_converse, 'current');
diff --git a/spec/omemo.js b/spec/omemo.js
index 34780c0de..acef33021 100644
--- a/spec/omemo.js
+++ b/spec/omemo.js
@@ -1254,13 +1254,13 @@
// Test that the button gets disabled when the room becomes
// anonymous or semi-anonymous
- view.model.save({'nonanonymous': false, 'semianonymous': true});
+ view.model.features.save({'nonanonymous': false, 'semianonymous': true});
await test_utils.waitUntil(() => !view.model.get('omemo_supported'));
toggle = toolbar.querySelector('.toggle-omemo');
expect(_.isNull(toggle)).toBe(true);
expect(view.model.get('omemo_supported')).toBe(false);
- view.model.save({'nonanonymous': true, 'semianonymous': false});
+ view.model.features.save({'nonanonymous': true, 'semianonymous': false});
await test_utils.waitUntil(() => view.model.get('omemo_supported'));
toggle = toolbar.querySelector('.toggle-omemo');
expect(_.isNull(toggle)).toBe(false);
@@ -1269,12 +1269,12 @@
expect(u.hasClass('disabled', toggle)).toBe(false);
// Test that the button gets disabled when the room becomes open
- view.model.save({'membersonly': false, 'open': true});
+ view.model.features.save({'membersonly': false, 'open': true});
await test_utils.waitUntil(() => !view.model.get('omemo_supported'));
toggle = toolbar.querySelector('.toggle-omemo');
expect(_.isNull(toggle)).toBe(true);
- view.model.save({'membersonly': true, 'open': false});
+ view.model.features.save({'membersonly': true, 'open': false});
await test_utils.waitUntil(() => view.model.get('omemo_supported'));
toggle = toolbar.querySelector('.toggle-omemo');
expect(_.isNull(toggle)).toBe(false);
diff --git a/src/converse-muc-views.js b/src/converse-muc-views.js
index d00b5ac1f..bd18a4171 100644
--- a/src/converse-muc-views.js
+++ b/src/converse-muc-views.js
@@ -236,7 +236,7 @@ converse.plugins.add('converse-muc-views', {
function toggleRoomInfo (ev) {
/* Show/hide extra information about a groupchat in a listing. */
const parent_el = u.ancestor(ev.target, '.room-item'),
- div_el = parent_el.querySelector('div.room-info');
+ div_el = parent_el.querySelector('div.room-info');
if (div_el) {
u.slideIn(div_el).then(u.removeElement)
parent_el.querySelector('a.room-info').classList.remove('selected');
@@ -439,9 +439,10 @@ converse.plugins.add('converse-muc-views', {
this.model.toJSON(), {
'_': _,
'__': __,
- 'topic': u.addHyperlinks(xss.filterXSS(_.get(this.model.get('subject'), 'text'), {'whiteList': {}})),
'display_name': __('Groupchat info for %1$s', this.model.getDisplayName()),
- 'num_occupants': this.model.occupants.length
+ 'features': this.model.features.toJSON(),
+ 'num_occupants': this.model.occupants.length,
+ 'topic': u.addHyperlinks(xss.filterXSS(_.get(this.model.get('subject'), 'text'), {'whiteList': {}}))
})
);
}
@@ -1831,12 +1832,7 @@ converse.plugins.add('converse-muc-views', {
this.chatroomview = this.model.chatroomview;
this.chatroomview.model.on('change:open', this.renderInviteWidget, this);
this.chatroomview.model.on('change:affiliation', this.renderInviteWidget, this);
- this.chatroomview.model.on('change', () => {
- if (_.intersection(converse.ROOM_FEATURES, Object.keys(this.chatroomview.model.changed)).length === 0) {
- return;
- }
- this.renderRoomFeatures();
- }, this);
+ this.chatroomview.model.features.on('change', this.renderRoomFeatures, this);
this.render();
this.model.fetch({
@@ -1882,16 +1878,15 @@ converse.plugins.add('converse-muc-views', {
},
renderRoomFeatures () {
- const picks = _.pick(this.chatroomview.model.attributes, converse.ROOM_FEATURES),
- iteratee = (a, v) => a || v,
- el = this.el.querySelector('.chatroom-features');
+ const features = this.chatroomview.model.features,
+ picks = _.pick(features.attributes, converse.ROOM_FEATURES),
+ iteratee = (a, v) => a || v;
- el.innerHTML = tpl_chatroom_features(
- _.extend(this.chatroomview.model.toJSON(), {
- '__': __,
- 'has_features': _.reduce(_.values(picks), iteratee)
- }));
- this.setOccupantsHeight();
+ if (_.reduce(_.values(picks), iteratee)) {
+ const el = this.el.querySelector('.chatroom-features');
+ el.innerHTML = tpl_chatroom_features(_.extend(features.toJSON(), {__}));
+ this.setOccupantsHeight();
+ }
return this;
},
diff --git a/src/converse-omemo.js b/src/converse-omemo.js
index 3f8d8ad37..a285158e9 100644
--- a/src/converse-omemo.js
+++ b/src/converse-omemo.js
@@ -446,7 +446,7 @@ converse.plugins.add('converse-omemo', {
},
renderOMEMOToolbarButton () {
- if (this.model.get('membersonly') && this.model.get('nonanonymous')) {
+ if (this.model.features.get('membersonly') && this.model.features.get('nonanonymous')) {
this.__super__.renderOMEMOToolbarButton.apply(arguments);
} else {
const icon = this.el.querySelector('.toggle-omemo');
@@ -1137,7 +1137,7 @@ converse.plugins.add('converse-omemo', {
}
async function onOccupantAdded (chatroom, occupant) {
- if (occupant.isSelf() || !chatroom.get('nonanonymous') || !chatroom.get('membersonly')) {
+ if (occupant.isSelf() || !chatroom.features.get('nonanonymous') || !chatroom.features.get('membersonly')) {
return;
}
if (chatroom.get('omemo_active')) {
@@ -1157,7 +1157,7 @@ converse.plugins.add('converse-omemo', {
let supported;
if (chatbox.get('type') === _converse.CHATROOMS_TYPE) {
await _converse.api.waitUntil('OMEMOInitialized');
- supported = chatbox.get('nonanonymous') && chatbox.get('membersonly');
+ supported = chatbox.features.get('nonanonymous') && chatbox.features.get('membersonly');
} else if (chatbox.get('type') === _converse.PRIVATE_CHAT_TYPE) {
supported = await _converse.contactHasOMEMOSupport(chatbox.get('jid'));
}
@@ -1169,8 +1169,7 @@ converse.plugins.add('converse-omemo', {
checkOMEMOSupported(chatbox);
if (chatbox.get('type') === _converse.CHATROOMS_TYPE) {
chatbox.occupants.on('add', o => onOccupantAdded(chatbox, o));
- chatbox.on('change:nonanonymous', checkOMEMOSupported);
- chatbox.on('change:membersonly', checkOMEMOSupported);
+ chatbox.features.on('change', () => checkOMEMOSupported(chatbox));
}
})
);
diff --git a/src/headless/converse-core.js b/src/headless/converse-core.js
index 6c6845f9e..906246084 100644
--- a/src/headless/converse-core.js
+++ b/src/headless/converse-core.js
@@ -795,7 +795,7 @@ _converse.initialize = function (settings, callback) {
this.initSession = function () {
- const id = b64_sha1('converse.bosh-session');
+ const id = 'converse.bosh-session';
_converse.session = new Backbone.Model({id});
_converse.session.browserStorage = new Backbone.BrowserStorage.session(id);
_converse.session.fetch();
diff --git a/src/headless/converse-muc.js b/src/headless/converse-muc.js
index 471f83757..9526a5caf 100644
--- a/src/headless/converse-muc.js
+++ b/src/headless/converse-muc.js
@@ -159,9 +159,7 @@ converse.plugins.add('converse-muc', {
defaults () {
return _.assign(
- _.clone(_converse.ChatBox.prototype.defaults),
- _.zipObject(converse.ROOM_FEATURES, _.map(converse.ROOM_FEATURES, _.stubFalse)),
- {
+ _.clone(_converse.ChatBox.prototype.defaults), {
// For group chats, we distinguish between generally unread
// messages and those ones that specifically mention the
// user.
@@ -177,7 +175,6 @@ converse.plugins.add('converse-muc', {
'name': '',
'nick': _converse.xmppstatus.get('nickname') || _converse.nickname,
'description': '',
- 'features_fetched': false,
'roomconfig': {},
'type': _converse.CHATROOMS_TYPE,
'message_type': 'groupchat'
@@ -189,6 +186,14 @@ converse.plugins.add('converse-muc', {
this.constructor.__super__.initialize.apply(this, arguments);
this.on('change:connection_status', this.onConnectionStatusChanged, this);
+ const storage = _converse.config.get('storage');
+ const id = `converse.muc-features-${_converse.bare_jid}-${this.get('jid')}`;
+ this.features = new Backbone.Model(
+ _.assign({id}, _.zipObject(converse.ROOM_FEATURES, _.map(converse.ROOM_FEATURES, _.stubFalse)))
+ );
+ this.features.browserStorage = new Backbone.BrowserStorage.session(id);
+ this.features.fetch();
+
this.occupants = new _converse.ChatRoomOccupants();
this.occupants.browserStorage = new Backbone.BrowserStorage.session(
b64_sha1(`converse.occupants-${_converse.bare_jid}${this.get('jid')}`)
@@ -304,6 +309,7 @@ converse.plugins.add('converse-muc', {
* (String) exit_msg: Optional message to indicate your
* reason for leaving.
*/
+ this.features.destroy();
this.occupants.browserStorage._clear();
this.occupants.reset();
if (_converse.disco_entities) {
@@ -496,14 +502,25 @@ converse.plugins.add('converse-muc', {
return this.getRoomFeatures();
},
+ async getRoomIdentity () {
+ const [identity, fields] = await Promise.all([
+ _converse.api.disco.getIdentity('conference', 'text', this.get('jid')),
+ _converse.api.disco.getFields(this.get('jid'))
+ ]);
+ this.save({
+ 'name': identity && identity.get('name'),
+ 'description': _.get(fields.findWhere({'var': "muc#roominfo_description"}), 'attributes.value')
+ });
+ },
+
async getRoomFeatures () {
+ // XXX: not sure whet the right place is to get the room identitiy
+ this.getRoomIdentity();
const features = await _converse.api.disco.getFeatures(this.get('jid')),
- fields = await _converse.api.disco.getFields(this.get('jid')),
- identity = await _converse.api.disco.getIdentity('conference', 'text', this.get('jid')),
- attrs = _.extend(_.zipObject(converse.ROOM_FEATURES, _.map(converse.ROOM_FEATURES, _.stubFalse)), {
- 'features_fetched': moment().format(),
- 'name': identity && identity.get('name')
- });
+ attrs = _.extend(
+ _.zipObject(converse.ROOM_FEATURES, _.map(converse.ROOM_FEATURES, _.stubFalse)),
+ {'fetched': moment().format()}
+ );
features.each(feature => {
const fieldname = feature.get('var');
@@ -515,8 +532,7 @@ converse.plugins.add('converse-muc', {
}
attrs[fieldname.replace('muc_', '')] = true;
});
- attrs.description = _.get(fields.findWhere({'var': "muc#roominfo_description"}), 'attributes.value');
- this.save(attrs);
+ this.features.save(attrs);
},
requestMemberList (affiliation) {
@@ -1053,7 +1069,7 @@ converse.plugins.add('converse-muc', {
this.trigger('configurationNeeded');
return; // We haven't yet entered the groupchat, so bail here.
}
- } else if (!this.get('features_fetched')) {
+ } else if (!this.features.get('fetched')) {
// The features for this groupchat weren't fetched.
// That must mean it's a new groupchat without locking
// (in which case Prosody doesn't send a 201 status),
diff --git a/src/templates/chatroom_details_modal.html b/src/templates/chatroom_details_modal.html
index 707e3fe1c..6a4c20b33 100644
--- a/src/templates/chatroom_details_modal.html
+++ b/src/templates/chatroom_details_modal.html
@@ -18,43 +18,43 @@
{{{o.__('Features')}}}:
- {[ if (o.passwordprotected) { ]}
+ {[ if (o.features.passwordprotected) { ]}
- {{{ o.__('Password protected') }}} - {{{ o.__('This groupchat requires a password before entry') }}}
{[ } ]}
- {[ if (o.unsecured) { ]}
+ {[ if (o.features.unsecured) { ]}
- {{{ o.__('No password required') }}} - {{{ o.__('This groupchat does not require a password upon entry') }}}
{[ } ]}
- {[ if (o.hidden) { ]}
+ {[ if (o.features.hidden) { ]}
- {{{ o.__('Hidden') }}} - {{{ o.__('This groupchat is not publicly searchable') }}}
{[ } ]}
- {[ if (o.public_room) { ]}
+ {[ if (o.features.public_room) { ]}
- {{{ o.__('Public') }}} - {{{ o.__('This groupchat is publicly searchable') }}}
{[ } ]}
- {[ if (o.membersonly) { ]}
+ {[ if (o.features.membersonly) { ]}
- {{{ o.__('Members only') }}} - {{{ o.__('This groupchat is restricted to members only') }}}
{[ } ]}
- {[ if (o.open) { ]}
+ {[ if (o.features.open) { ]}
- {{{ o.__('Open') }}} - {{{ o.__('Anyone can join this groupchat') }}}
{[ } ]}
- {[ if (o.persistent) { ]}
+ {[ if (o.features.persistent) { ]}
- {{{ o.__('Persistent') }}} - {{{ o.__('This groupchat persists even if it\'s unoccupied') }}}
{[ } ]}
- {[ if (o.temporary) { ]}
+ {[ if (o.features.temporary) { ]}
- {{{ o.__('Temporary') }}} - {{{ o.__('This groupchat will disappear once the last person leaves') }}}
{[ } ]}
- {[ if (o.nonanonymous) { ]}
+ {[ if (o.features.nonanonymous) { ]}
- {{{ o.__('Not anonymous') }}} - {{{ o.__('All other groupchat participants can see your XMPP username') }}}
{[ } ]}
- {[ if (o.semianonymous) { ]}
+ {[ if (o.features.semianonymous) { ]}
- {{{ o.__('Semi-anonymous') }}} - {{{ o.__('Only moderators can see your XMPP username') }}}
{[ } ]}
- {[ if (o.moderated) { ]}
+ {[ if (o.features.moderated) { ]}
- {{{ o.__('Moderated') }}} - {{{ o.__('Participants entering this groupchat need to request permission to write') }}}
{[ } ]}
- {[ if (o.unmoderated) { ]}
+ {[ if (o.features.unmoderated) { ]}
- {{{ o.__('Not moderated') }}} - {{{ o.__('Participants entering this groupchat can write right away') }}}
{[ } ]}
- {[ if (o.mam_enabled) { ]}
+ {[ if (o.features.mam_enabled) { ]}
- {{{ o.__('Message archiving') }}} - {{{ o.__('Messages are archived on the server') }}}
{[ } ]}
diff --git a/src/templates/chatroom_features.html b/src/templates/chatroom_features.html
index 067f43545..7796f7e93 100644
--- a/src/templates/chatroom_features.html
+++ b/src/templates/chatroom_features.html
@@ -1,6 +1,4 @@
-{[ if (o.has_features) { ]}
{{{o.__('Features')}}}
-{[ } ]}
{[ if (o.passwordprotected) { ]}
- {{{ o.__('Password protected') }}}
@@ -24,7 +22,7 @@
- {{{ o.__('Persistent') }}}
{[ } ]}
{[ if (o.temporary) { ]}
-- {{{ o.__('Temporary') }}}
+- {{{ o.__('Temporary') }}}
{[ } ]}
{[ if (o.nonanonymous) { ]}
- {{{ o.__('Not anonymous') }}}