Commit 71cc98d6 authored by JC Brand's avatar JC Brand

Reduce join/leave clutter by removing subsequent ones

Previously we checked only if the last message was a join message from
the same person.

Now instead we check the last n messages that are join or leave
notifications.
parent cf75d375
......@@ -2,6 +2,7 @@
## 4.0.3 (Unreleased)
- Reduce join/leave clutter by removing subsequent ones (without text messages in between)
- Bugfix. Handler not triggered when submitting MUC password form 2nd time
- Bugfix. MUC features weren't being refreshed when saving the config form
- #1063 URLs in the topic / subject are not clickable
......
......@@ -69771,6 +69771,22 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
this.scrollDown();
},
getImmediateJoinNotification(el, nick) {
while (!_.isNil(el)) {
const data = _.get(el, 'dataset', {});
if (!_.includes(_.get(el, 'classList', []), 'chat-info')) {
return;
}
if (moment(el.getAttribute('data-isodate')).isSame(new Date(), "day") && (data.join === `"${nick}"` || data.leavejoin === `"${nick}"`)) {
return el;
}
el = el.previousElementSibling;
}
},
showLeaveNotification(occupant) {
if (_.includes(occupant.get('states'), '303') || _.includes(occupant.get('states'), '307')) {
return;
......@@ -69778,10 +69794,10 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
const nick = occupant.get('nick'),
stat = occupant.get('status'),
last_el = this.content.lastElementChild,
data = _.get(last_el, 'dataset', {});
last_join_el = this.getImmediateJoinNotification(this.content.lastElementChild, nick),
data = _.get(last_join_el, 'dataset', {});
if (last_el && _.includes(_.get(last_el, 'classList', []), 'chat-info') && moment(last_el.getAttribute('data-isodate')).isSame(new Date(), "day") && (data.join === `"${nick}"` || data.leavejoin === `"${nick}"`)) {
if (last_join_el) {
let message;
if (data.join === `"${nick}"`) {
......@@ -69791,13 +69807,15 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
message = __('%1$s has entered and left the groupchat. "%2$s"', nick, stat);
}
last_el.outerHTML = tpl_info({
let el = this.content.lastElementChild;
el.insertAdjacentElement('afterend', last_join_el);
last_join_el.outerHTML = tpl_info({
'data': `data-joinleave="${nick}"`,
'isodate': moment().format(),
'extra_classes': 'chat-event',
'message': message
});
const el = this.content.lastElementChild;
el = this.content.lastElementChild;
setTimeout(() => u.addClass('fade-out', el), 5000);
setTimeout(() => el.parentElement && el.parentElement.removeChild(el), 5250);
} else if (data.leavejoin === `"${nick}"`) {
......@@ -69807,7 +69825,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
message = __('%1$s has left the groupchat. "%2$s"', nick, stat);
}
last_el.outerHTML = tpl_info({
last_join_el.outerHTML = tpl_info({
'data': `data-leave="${nick}"`,
'isodate': moment().format(),
'extra_classes': 'chat-event',
......@@ -69829,14 +69847,9 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
'extra_classes': 'chat-event',
'data': `data-leave="${nick}"`
};
if (last_el && _.includes(_.get(last_el, 'classList', []), 'chat-info') && _.get(last_el, 'dataset', {}).leavejoin === `"${nick}"`) {
last_el.outerHTML = tpl_info(data);
} else {
const el = u.stringToElement(tpl_info(data));
this.content.insertAdjacentElement('beforeend', el);
this.insertDayIndicator(el);
}
const el = u.stringToElement(tpl_info(data));
this.content.insertAdjacentElement('beforeend', el);
this.insertDayIndicator(el);
}
this.scrollDown();
......@@ -422,7 +422,8 @@
test_utils.openChatRoom(_converse, "coven", 'chat.shakespeare.lit', 'some1')
.then(() => {
const view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
const $chat_content = $(view.el).find('.chat-content');
const chat_content = view.el.querySelector('.chat-content');
const $chat_content = $(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.
......@@ -474,6 +475,15 @@
expect($chat_content[0].querySelectorAll('div.chat-info').length).toBe(2);
expect($chat_content.find('div.chat-info:last').html()).toBe("newguy has entered the groupchat");
const msg = $msg({
'from': 'coven@chat.shakespeare.lit/some1',
'id': (new Date()).getTime(),
'to': 'dummy@localhost',
'type': 'groupchat'
}).c('body').t('hello world').tree();
_converse.connection._dataRecv(test_utils.createRequest(msg));
// Add another entrant, otherwise the above message will be
// collapsed if "newguy" leaves immediately again
presence = $pres({
......@@ -563,9 +573,9 @@
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($chat_content.find('div.chat-info').length).toBe(4);
$msg_el = $chat_content.find('div.chat-info:last');
expect($msg_el.html()).toBe('newguy has left the groupchat');
expect($msg_el.data('leave')).toBe('"newguy"');
const msg_el = sizzle('div.chat-info', chat_content).pop();
expect(msg_el.textContent).toBe('newguy has left the groupchat');
expect(msg_el.getAttribute('data-leave')).toBe('"newguy"');
presence = $pres({
to: 'dummy@localhost/_converse.js-29092160',
......@@ -648,13 +658,31 @@
'role': 'none'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($chat_content.find('div.chat-info').length).toBe(6);
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(6);
expect($chat_content.find('div.chat-info:last').html()).toBe(
'insider has entered and left the groupchat. '+
'"Disconnected: Replaced by new connection"');
expect(view.model.occupants.length).toBe(5);
expect(view.model.occupants.findWhere({'jid': 'insider@localhost'}).get('show')).toBe('offline');
// New girl leaves
presence = $pres({
'to': 'dummy@localhost/_converse.js-29092160',
'type': 'unavailable',
'from': 'coven@chat.shakespeare.lit/newgirl'
})
.c('x', {xmlns: Strophe.NS.MUC_USER})
.c('item', {
'affiliation': 'none',
'jid': 'newgirl@localhost/_converse.js-213098781',
'role': 'none'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(6);
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe("newgirl has entered and left the groupchat");
expect(view.model.occupants.length).toBe(4);
done();
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
}));
......
......@@ -1546,20 +1546,30 @@
this.scrollDown();
},
getImmediateJoinNotification (el, nick) {
while (!_.isNil(el)) {
const data = _.get(el, 'dataset', {});
if (!_.includes(_.get(el, 'classList', []), 'chat-info')) {
return;
}
if (moment(el.getAttribute('data-isodate')).isSame(new Date(), "day") &&
(data.join === `"${nick}"` || data.leavejoin === `"${nick}"`)) {
return el;
}
el = el.previousElementSibling;
}
},
showLeaveNotification (occupant) {
if (_.includes(occupant.get('states'), '303') || _.includes(occupant.get('states'), '307')) {
return;
}
const nick = occupant.get('nick'),
stat = occupant.get('status'),
last_el = this.content.lastElementChild,
data = _.get(last_el, 'dataset', {});
if (last_el &&
_.includes(_.get(last_el, 'classList', []), 'chat-info') &&
moment(last_el.getAttribute('data-isodate')).isSame(new Date(), "day") &&
(data.join === `"${nick}"` || data.leavejoin === `"${nick}"`)) {
last_join_el = this.getImmediateJoinNotification(this.content.lastElementChild, nick),
data = _.get(last_join_el, 'dataset', {});
if (last_join_el) {
let message;
if (data.join === `"${nick}"`) {
if (_.isNil(stat)) {
......@@ -1567,24 +1577,25 @@
} else {
message = __('%1$s has entered and left the groupchat. "%2$s"', nick, stat);
}
last_el.outerHTML =
let el = this.content.lastElementChild;
el.insertAdjacentElement('afterend', last_join_el);
last_join_el.outerHTML =
tpl_info({
'data': `data-joinleave="${nick}"`,
'isodate': moment().format(),
'extra_classes': 'chat-event',
'message': message
});
const el = this.content.lastElementChild;
el = this.content.lastElementChild;
setTimeout(() => u.addClass('fade-out', el), 5000);
setTimeout(() => el.parentElement && el.parentElement.removeChild(el), 5250);
} else if (data.leavejoin === `"${nick}"`) {
if (_.isNil(stat)) {
message = __('%1$s has left the groupchat', nick);
} else {
message = __('%1$s has left the groupchat. "%2$s"', nick, stat);
}
last_el.outerHTML =
last_join_el.outerHTML =
tpl_info({
'data': `data-leave="${nick}"`,
'isodate': moment().format(),
......@@ -1605,16 +1616,9 @@
'extra_classes': 'chat-event',
'data': `data-leave="${nick}"`
}
if (last_el &&
_.includes(_.get(last_el, 'classList', []), 'chat-info') &&
_.get(last_el, 'dataset', {}).leavejoin === `"${nick}"`) {
last_el.outerHTML = tpl_info(data);
} else {
const el = u.stringToElement(tpl_info(data));
this.content.insertAdjacentElement('beforeend', el);
this.insertDayIndicator(el);
}
const el = u.stringToElement(tpl_info(data));
this.content.insertAdjacentElement('beforeend', el);
this.insertDayIndicator(el);
}
this.scrollDown();
},
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment