MUC: Use converse-disco to query for room features

By doing so we create a new DiscoEntity for the room, which allows us to
query for features via the disco API.

We also avoid duplication of functionality between converse-muc and
converse-disco
This commit is contained in:
JC Brand 2018-09-12 12:32:50 +02:00
parent 197451db54
commit 6457bc765a
3 changed files with 68 additions and 66 deletions

View File

@ -1141,9 +1141,8 @@
form_el.addEventListener('submit', form_el.addEventListener('submit',
(ev) => { (ev) => {
ev.preventDefault(); ev.preventDefault();
this.model.saveConfiguration(ev.target).then( this.model.saveConfiguration(ev.target)
this.model.getRoomFeatures.bind(this.model) .then(() => this.model.refreshRoomFeatures());
);
this.closeForm(); this.closeForm();
}, },
false false

View File

@ -403,22 +403,6 @@
}; };
}, },
getRoomFeatures () {
/* Fetch the groupchat disco info, parse it and then save it.
*/
// XXX: Currently we store disco info on the room itself.
// A better design would probably be to create a
// DiscoEntity for this room, and then to let
// converse-disco manage all disco-related tasks.
// Then we can also use _converse.api.disco.supports.
return _converse.api.disco.info(this.get('jid'), null)
.then(stanza => this.parseRoomFeatures(stanza))
.catch(err => {
_converse.log("Could not parse the groupchat features", Strophe.LogLevel.WARN);
_converse.log(err, Strophe.LogLevel.WARN);
});
},
getRoomJIDAndNick (nick) { getRoomJIDAndNick (nick) {
/* Utility method to construct the JID for the current user /* Utility method to construct the JID for the current user
* as occupant of the groupchat. * as occupant of the groupchat.
@ -499,43 +483,34 @@
}); });
}, },
parseRoomFeatures (iq) { refreshRoomFeatures () {
/* Parses an IQ stanza containing the groupchat's features. const entity = _converse.disco_entities.get(this.get('jid'));
* if (entity) {
* See http://xmpp.org/extensions/xep-0045.html#disco-roominfo entity.destroy();
*
* <identity
* category='conference'
* name='A Dark Cave'
* type='text'/>
* <feature var='http://jabber.org/protocol/muc'/>
* <feature var='muc_passwordprotected'/>
* <feature var='muc_hidden'/>
* <feature var='muc_temporary'/>
* <feature var='muc_open'/>
* <feature var='muc_unmoderated'/>
* <feature var='muc_nonanonymous'/>
* <feature var='urn:xmpp:mam:0'/>
*/
const features = {
'features_fetched': moment().format(),
'name': iq.querySelector('identity').getAttribute('name')
} }
_.each(iq.querySelectorAll('feature'), function (field) { return this.getRoomFeatures();
const fieldname = field.getAttribute('var'); },
async getRoomFeatures () {
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 = {
'features_fetched': moment().format(),
'name': identity && identity.get('name')
};
features.each(feature => {
const fieldname = feature.get('var');
if (!fieldname.startsWith('muc_')) { if (!fieldname.startsWith('muc_')) {
if (fieldname === Strophe.NS.MAM) { if (fieldname === Strophe.NS.MAM) {
features.mam_enabled = true; attrs.mam_enabled = true;
} }
return; return;
} }
features[fieldname.replace('muc_', '')] = true; attrs[fieldname.replace('muc_', '')] = true;
}); });
const desc_field = iq.querySelector('field[var="muc#roominfo_description"] value'); attrs.description = _.get(fields.findWhere({'var': "muc#roominfo_description"}), 'attributes.value');
if (!_.isNull(desc_field)) { this.save(attrs);
features.description = desc_field.textContent;
}
this.save(features);
}, },
requestMemberList (affiliation) { requestMemberList (affiliation) {
@ -969,7 +944,7 @@
if (configuration_changed || logging_enabled || logging_disabled || if (configuration_changed || logging_enabled || logging_disabled ||
room_no_longer_anon || room_now_semi_anon || room_now_fully_anon) { room_no_longer_anon || room_now_semi_anon || room_now_fully_anon) {
this.getRoomFeatures(); this.refreshRoomFeatures();
} }
}, },
@ -1053,10 +1028,10 @@
const locked_room = pres.querySelector("status[code='201']"); const locked_room = pres.querySelector("status[code='201']");
if (locked_room) { if (locked_room) {
if (this.get('auto_configure')) { if (this.get('auto_configure')) {
this.autoConfigureChatRoom().then(this.getRoomFeatures.bind(this)); this.autoConfigureChatRoom().then(() => this.refreshRoomFeatures());
} else if (_converse.muc_instant_rooms) { } else if (_converse.muc_instant_rooms) {
// Accept default configuration // Accept default configuration
this.saveConfiguration().then(this.getRoomFeatures.bind(this)); this.saveConfiguration().then(() => this.getRoomFeatures());
} else { } else {
this.trigger('configurationNeeded'); this.trigger('configurationNeeded');
return; // We haven't yet entered the groupchat, so bail here. return; // We haven't yet entered the groupchat, so bail here.
@ -1068,7 +1043,7 @@
// otherwise the features would have been fetched in // otherwise the features would have been fetched in
// the "initialize" method already. // the "initialize" method already.
if (this.get('affiliation') === 'owner' && this.get('auto_configure')) { if (this.get('affiliation') === 'owner' && this.get('auto_configure')) {
this.autoConfigureChatRoom().then(this.getRoomFeatures.bind(this)); this.autoConfigureChatRoom().then(() => this.refreshRoomFeatures());
} else { } else {
this.getRoomFeatures(); this.getRoomFeatures();
} }

View File

@ -127,19 +127,44 @@
utils.openAndEnterChatRoom = function (_converse, room, server, nick) { utils.openAndEnterChatRoom = function (_converse, room, server, nick) {
let last_stanza, view; let last_stanza, view;
const room_jid = `${room}@${server}`.toLowerCase();
return _converse.api.rooms.open(`${room}@${server}`).then(() => { return _converse.api.rooms.open(room_jid).then(() => {
view = _converse.chatboxviews.get((room+'@'+server).toLowerCase()); view = _converse.chatboxviews.get(room_jid);
// We pretend this is a new room, so no disco info is returned. return utils.waitUntil(() => _.get(_.filter(
last_stanza = _.last(_converse.connection.IQ_stanzas).nodeTree; _converse.connection.IQ_stanzas,
const IQ_id = last_stanza.getAttribute('id'); iq => iq.nodeTree.querySelector(
`iq[to="coven@chat.shakespeare.lit"] query[xmlns="http://jabber.org/protocol/disco#info"]`
)
).pop(), 'nodeTree'));
}).then(stanza => {
const features_stanza = $iq({ const features_stanza = $iq({
'from': room+'@'+server, 'from': room_jid,
'id': IQ_id, 'id': stanza.getAttribute('id'),
'to': nick+'@'+server, 'to': 'dummy@localhost/desktop',
'type': 'error' 'type': 'result'
}).c('error', {'type': 'cancel'}) })
.c('item-not-found', {'xmlns': "urn:ietf:params:xml:ns:xmpp-stanzas"}); .c('query', { 'xmlns': 'http://jabber.org/protocol/disco#info'})
.c('identity', {
'category': 'conference',
'name': 'A Dark Cave',
'type': 'text'
}).up()
.c('feature', {'var': 'http://jabber.org/protocol/muc'}).up()
.c('feature', {'var': 'jabber:iq:register'}).up()
.c('feature', {'var': 'muc_passwordprotected'}).up()
.c('feature', {'var': 'muc_hidden'}).up()
.c('feature', {'var': 'muc_temporary'}).up()
.c('feature', {'var': 'muc_open'}).up()
.c('feature', {'var': 'muc_unmoderated'}).up()
.c('feature', {'var': 'muc_nonanonymous'})
.c('x', { 'xmlns':'jabber:x:data', 'type':'result'})
.c('field', {'var':'FORM_TYPE', 'type':'hidden'})
.c('value').t('http://jabber.org/protocol/muc#roominfo').up().up()
.c('field', {'type':'text-single', 'var':'muc#roominfo_description', 'label':'Description'})
.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(utils.createRequest(features_stanza)); _converse.connection._dataRecv(utils.createRequest(features_stanza));
return utils.waitUntil(() => { return utils.waitUntil(() => {
return _.filter( return _.filter(
@ -179,8 +204,11 @@
}).up() }).up()
.c('status').attrs({code:'110'}); .c('status').attrs({code:'110'});
_converse.connection._dataRecv(utils.createRequest(presence)); _converse.connection._dataRecv(utils.createRequest(presence));
return utils.waitUntil(() => view.model.get('connection_status') === converse.ROOMSTATUS.ENTERED); return utils.waitUntil(() => (view.model.get('connection_status') === converse.ROOMSTATUS.ENTERED));
}).catch(_.partial(console.error, _)); }).catch(e => {
console.error(e);
throw e;
});
}; };
utils.clearBrowserStorage = function () { utils.clearBrowserStorage = function () {