Also squash leave/join messages
And fix an HTML rendering bug for info messages and nicks that contain spaces
This commit is contained in:
parent
71cc98d6f6
commit
7b9c97dfd3
12503
dist/converse.js
vendored
12503
dist/converse.js
vendored
File diff suppressed because one or more lines are too long
158
spec/chatroom.js
158
spec/chatroom.js
@ -558,7 +558,7 @@
|
||||
expect($chat_content.find('div.chat-info').length).toBe(4);
|
||||
var $msg_el = $chat_content.find('div.chat-info:last');
|
||||
expect($msg_el.html()).toBe("newguy has left and re-entered the groupchat");
|
||||
expect($msg_el.data('leavejoin')).toBe('"newguy"');
|
||||
expect($msg_el.data('leavejoin')).toBe('newguy');
|
||||
|
||||
presence = $pres({
|
||||
to: 'dummy@localhost/_converse.js-29092160',
|
||||
@ -575,7 +575,7 @@
|
||||
expect($chat_content.find('div.chat-info').length).toBe(4);
|
||||
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"');
|
||||
expect(msg_el.getAttribute('data-leave')).toBe('newguy');
|
||||
|
||||
presence = $pres({
|
||||
to: 'dummy@localhost/_converse.js-29092160',
|
||||
@ -687,6 +687,160 @@
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
|
||||
}));
|
||||
|
||||
it("combines subsequent join/leave messages when users enter or exit a groupchat",
|
||||
mock.initConverseWithPromises(
|
||||
null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
|
||||
function (done, _converse) {
|
||||
|
||||
test_utils.openAndEnterChatRoom(_converse, 'coven', 'chat.shakespeare.lit', 'dummy')
|
||||
.then(() => {
|
||||
const view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
|
||||
const chat_content = view.el.querySelector('.chat-content');
|
||||
|
||||
expect(sizzle('div.chat-info', chat_content).length).toBe(1);
|
||||
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe("dummy has entered the groupchat");
|
||||
|
||||
let presence = Strophe.xmlHtmlNode(
|
||||
`<presence xmlns="jabber:client" to="dummy@localhost/resource" from="coven@chat.shakespeare.lit/fabio">
|
||||
<c xmlns="http://jabber.org/protocol/caps" node="http://conversations.im" ver="INI3xjRUioclBTP/aACfWi5m9UY=" hash="sha-1"/>
|
||||
<x xmlns="http://jabber.org/protocol/muc#user">
|
||||
<item affiliation="none" jid="fabio@montefuscolo.com.br/Conversations.ZvLu" role="participant"/>
|
||||
</x>
|
||||
</presence>`).firstElementChild;
|
||||
_converse.connection._dataRecv(test_utils.createRequest(presence));
|
||||
expect(sizzle('div.chat-info', chat_content).length).toBe(2);
|
||||
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe("fabio has entered the groupchat");
|
||||
|
||||
presence = Strophe.xmlHtmlNode(
|
||||
`<presence xmlns="jabber:client" to="dummy@localhost/resource" from="coven@chat.shakespeare.lit/Dele Olajide">
|
||||
<x xmlns="http://jabber.org/protocol/muc#user">
|
||||
<item affiliation="none" jid="deleo@traderlynk.4ng.net/converse.js-39320524" role="participant"/>
|
||||
</x>
|
||||
</presence>`).firstElementChild;
|
||||
_converse.connection._dataRecv(test_utils.createRequest(presence));
|
||||
expect(sizzle('div.chat-info', chat_content).length).toBe(3);
|
||||
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe("Dele Olajide has entered the groupchat");
|
||||
|
||||
presence = Strophe.xmlHtmlNode(
|
||||
`<presence xmlns="jabber:client" to="dummy@localhost/resource" from="coven@chat.shakespeare.lit/jcbrand">
|
||||
<x xmlns="http://jabber.org/protocol/muc#user">
|
||||
<item affiliation="owner" jid="jc@opkode.com/converse.js-30645022" role="moderator"/>
|
||||
<status code="110"/>
|
||||
</x>
|
||||
</presence>`).firstElementChild;
|
||||
_converse.connection._dataRecv(test_utils.createRequest(presence));
|
||||
expect(sizzle('div.chat-info', chat_content).length).toBe(4);
|
||||
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe("jcbrand has entered the groupchat");
|
||||
|
||||
presence = Strophe.xmlHtmlNode(
|
||||
`<presence xmlns="jabber:client" to="dummy@localhost/resource" type="unavailable" from="coven@chat.shakespeare.lit/Dele Olajide">
|
||||
<x xmlns="http://jabber.org/protocol/muc#user">
|
||||
<item affiliation="none" jid="deleo@traderlynk.4ng.net/converse.js-39320524" role="none"/>
|
||||
</x>
|
||||
</presence>`).firstElementChild;
|
||||
_converse.connection._dataRecv(test_utils.createRequest(presence));
|
||||
expect(sizzle('div.chat-info', chat_content).length).toBe(4);
|
||||
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe("Dele Olajide has entered and left the groupchat");
|
||||
|
||||
presence = Strophe.xmlHtmlNode(
|
||||
`<presence xmlns="jabber:client" to="dummy@localhost/resource" from="coven@chat.shakespeare.lit/Dele Olajide">
|
||||
<x xmlns="http://jabber.org/protocol/muc#user">
|
||||
<item affiliation="none" jid="deleo@traderlynk.4ng.net/converse.js-74567907" role="participant"/>
|
||||
</x>
|
||||
</presence>`).firstElementChild;
|
||||
_converse.connection._dataRecv(test_utils.createRequest(presence));
|
||||
expect(sizzle('div.chat-info', chat_content).length).toBe(4);
|
||||
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe("Dele Olajide has entered the groupchat");
|
||||
|
||||
presence = Strophe.xmlHtmlNode(
|
||||
`<presence xmlns="jabber:client" to="dummy@localhost/resource" from="coven@chat.shakespeare.lit/fuvuv" xml:lang="en">
|
||||
<c xmlns="http://jabber.org/protocol/caps" node="http://jabber.pix-art.de" ver="5tOurnuFnp2h50hKafeUyeN4Yl8=" hash="sha-1"/>
|
||||
<x xmlns="vcard-temp:x:update"/>
|
||||
<x xmlns="http://jabber.org/protocol/muc#user">
|
||||
<item affiliation="none" jid="fuvuv@blabber.im/Pix-Art Messenger.8zoB" role="participant"/>
|
||||
</x>
|
||||
</presence>`).firstElementChild;
|
||||
_converse.connection._dataRecv(test_utils.createRequest(presence));
|
||||
expect(sizzle('div.chat-info', chat_content).length).toBe(5);
|
||||
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe("fuvuv has entered the groupchat");
|
||||
|
||||
presence = Strophe.xmlHtmlNode(
|
||||
`<presence xmlns="jabber:client" to="dummy@localhost/resource" type="unavailable" from="coven@chat.shakespeare.lit/fuvuv">
|
||||
<x xmlns="http://jabber.org/protocol/muc#user">
|
||||
<item affiliation="none" jid="fuvuv@blabber.im/Pix-Art Messenger.8zoB" role="none"/>
|
||||
</x>
|
||||
</presence>`).firstElementChild;
|
||||
_converse.connection._dataRecv(test_utils.createRequest(presence));
|
||||
expect(sizzle('div.chat-info', chat_content).length).toBe(5);
|
||||
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe("fuvuv has entered and left the groupchat");
|
||||
|
||||
presence = Strophe.xmlHtmlNode(
|
||||
`<presence xmlns="jabber:client" to="dummy@localhost/resource" type="unavailable" from="coven@chat.shakespeare.lit/fabio">
|
||||
<status>Disconnected: Replaced by new connection</status>
|
||||
<x xmlns="http://jabber.org/protocol/muc#user">
|
||||
<item affiliation="none" jid="fabio@montefuscolo.com.br/Conversations.ZvLu" role="none"/>
|
||||
</x>
|
||||
</presence>`).firstElementChild;
|
||||
_converse.connection._dataRecv(test_utils.createRequest(presence));
|
||||
expect(sizzle('div.chat-info', chat_content).length).toBe(5);
|
||||
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe(
|
||||
`fabio has entered and left the groupchat. "Disconnected: Replaced by new connection"`);
|
||||
|
||||
presence = Strophe.xmlHtmlNode(
|
||||
`<presence xmlns="jabber:client" to="dummy@localhost/resource" from="coven@chat.shakespeare.lit/fabio">
|
||||
<c xmlns="http://jabber.org/protocol/caps" node="http://conversations.im" ver="INI3xjRUioclBTP/aACfWi5m9UY=" hash="sha-1"/>
|
||||
<x xmlns="http://jabber.org/protocol/muc#user">
|
||||
<item affiliation="none" jid="fabio@montefuscolo.com.br/Conversations.ZvLu" role="participant"/>
|
||||
</x>
|
||||
</presence>`).firstElementChild;
|
||||
_converse.connection._dataRecv(test_utils.createRequest(presence));
|
||||
expect(sizzle('div.chat-info', chat_content).length).toBe(5);
|
||||
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe(
|
||||
`fabio has entered the groupchat`);
|
||||
|
||||
// XXX: hack so that we can test leave/enter of occupants
|
||||
// who were already in the room when we joined.
|
||||
chat_content.innerHTML = '';
|
||||
|
||||
presence = Strophe.xmlHtmlNode(
|
||||
`<presence xmlns="jabber:client" to="dummy@localhost/resource" type="unavailable" from="coven@chat.shakespeare.lit/fabio">
|
||||
<status>Disconnected: closed</status>
|
||||
<x xmlns="http://jabber.org/protocol/muc#user">
|
||||
<item affiliation="none" jid="fabio@montefuscolo.com.br/Conversations.ZvLu" role="none"/>
|
||||
</x>
|
||||
</presence>`).firstElementChild;
|
||||
_converse.connection._dataRecv(test_utils.createRequest(presence));
|
||||
expect(sizzle('div.chat-info', chat_content).length).toBe(1);
|
||||
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe(
|
||||
`fabio has left the groupchat. "Disconnected: closed"`);
|
||||
|
||||
presence = Strophe.xmlHtmlNode(
|
||||
`<presence xmlns="jabber:client" to="dummy@localhost/resource" type="unavailable" from="coven@chat.shakespeare.lit/Dele Olajide">
|
||||
<x xmlns="http://jabber.org/protocol/muc#user">
|
||||
<item affiliation="none" jid="deleo@traderlynk.4ng.net/converse.js-74567907" role="none"/>
|
||||
</x>
|
||||
</presence>`).firstElementChild;
|
||||
_converse.connection._dataRecv(test_utils.createRequest(presence));
|
||||
expect(sizzle('div.chat-info', chat_content).length).toBe(2);
|
||||
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe(
|
||||
`Dele Olajide has left the groupchat`);
|
||||
|
||||
presence = Strophe.xmlHtmlNode(
|
||||
`<presence xmlns="jabber:client" to="dummy@localhost/resource" from="coven@chat.shakespeare.lit/fabio">
|
||||
<c xmlns="http://jabber.org/protocol/caps" node="http://conversations.im" ver="INI3xjRUioclBTP/aACfWi5m9UY=" hash="sha-1"/>
|
||||
<x xmlns="http://jabber.org/protocol/muc#user">
|
||||
<item affiliation="none" jid="fabio@montefuscolo.com.br/Conversations.ZvLu" role="participant"/>
|
||||
</x>
|
||||
</presence>`).firstElementChild;
|
||||
_converse.connection._dataRecv(test_utils.createRequest(presence));
|
||||
expect(sizzle('div.chat-info', chat_content).length).toBe(2);
|
||||
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe(
|
||||
`fabio has left and re-entered the groupchat`);
|
||||
|
||||
done();
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
|
||||
}));
|
||||
|
||||
it("shows a new day indicator if a join/leave message is received on a new day",
|
||||
mock.initConverseWithPromises(
|
||||
null, ['rosterGroupsFetched'], {},
|
||||
|
@ -499,7 +499,7 @@
|
||||
return this;
|
||||
},
|
||||
|
||||
showChatEvent (message, data='') {
|
||||
showChatEvent (message) {
|
||||
const isodate = moment().format();
|
||||
this.content.insertAdjacentHTML(
|
||||
'beforeend',
|
||||
@ -507,7 +507,6 @@
|
||||
'extra_classes': 'chat-event',
|
||||
'message': message,
|
||||
'isodate': isodate,
|
||||
'data': data
|
||||
}));
|
||||
this.insertDayIndicator(this.content.lastElementChild);
|
||||
this.scrollDown();
|
||||
|
@ -163,8 +163,7 @@
|
||||
msg = u.stringToElement(
|
||||
tpl_info(_.extend(this.model.toJSON(), {
|
||||
'extra_classes': 'chat-error',
|
||||
'isodate': moment_time.format(),
|
||||
'data': ''
|
||||
'isodate': moment_time.format()
|
||||
})));
|
||||
return this.replaceElement(msg);
|
||||
},
|
||||
|
@ -1462,7 +1462,6 @@
|
||||
this.content.insertAdjacentHTML(
|
||||
'beforeend',
|
||||
tpl_info({
|
||||
'data': '',
|
||||
'isodate': moment().format(),
|
||||
'extra_classes': 'chat-event',
|
||||
'message': message
|
||||
@ -1505,19 +1504,22 @@
|
||||
}
|
||||
const nick = occupant.get('nick'),
|
||||
stat = occupant.get('status'),
|
||||
last_el = this.content.lastElementChild;
|
||||
last_leave_el = this.getImmediateNotification(this.content.lastElementChild, nick, 'leave');
|
||||
|
||||
if (_.includes(_.get(last_el, 'classList', []), 'chat-info') &&
|
||||
_.get(last_el, 'dataset', {}).leave === `"${nick}"`) {
|
||||
if (_.includes(_.get(last_leave_el, 'classList', []), 'chat-info') &&
|
||||
_.get(last_leave_el, 'dataset', {}).leave === nick) {
|
||||
|
||||
last_el.outerHTML =
|
||||
let el = this.content.lastElementChild;
|
||||
el.insertAdjacentElement('afterend', last_leave_el);
|
||||
last_leave_el.outerHTML =
|
||||
tpl_info({
|
||||
'data': `data-leavejoin="${nick}"`,
|
||||
'data_name': 'leavejoin',
|
||||
'data_value': nick,
|
||||
'isodate': moment().format(),
|
||||
'extra_classes': 'chat-event',
|
||||
'message': __('%1$s has left and re-entered the groupchat', nick)
|
||||
});
|
||||
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 {
|
||||
@ -1528,15 +1530,16 @@
|
||||
message = __('%1$s has entered the groupchat. "%2$s"', nick, stat);
|
||||
}
|
||||
const data = {
|
||||
'data': `data-join="${nick}"`,
|
||||
'data_name': 'join',
|
||||
'data_value': nick,
|
||||
'isodate': moment().format(),
|
||||
'extra_classes': 'chat-event',
|
||||
'message': message
|
||||
};
|
||||
if (_.includes(_.get(last_el, 'classList', []), 'chat-info') &&
|
||||
_.get(last_el, 'dataset', {}).joinleave === `"${nick}"`) {
|
||||
if (_.includes(_.get(last_leave_el, 'classList', []), 'chat-info') &&
|
||||
_.get(last_leave_el, 'dataset', {}).joinleave === nick) {
|
||||
|
||||
last_el.outerHTML = tpl_info(data);
|
||||
last_leave_el.outerHTML = tpl_info(data);
|
||||
} else {
|
||||
const el = u.stringToElement(tpl_info(data));
|
||||
this.content.insertAdjacentElement('beforeend', el);
|
||||
@ -1546,16 +1549,25 @@
|
||||
this.scrollDown();
|
||||
},
|
||||
|
||||
getImmediateJoinNotification (el, nick) {
|
||||
getImmediateNotification (el, nick, type='join') {
|
||||
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}"`)) {
|
||||
if (!moment(el.getAttribute('data-isodate')).isSame(new Date(), "day")) {
|
||||
el = el.previousElementSibling;
|
||||
continue;
|
||||
}
|
||||
if (type === 'join') {
|
||||
if (data.join === nick || data.leavejoin === nick) {
|
||||
return el;
|
||||
}
|
||||
} else {
|
||||
if (data.leave === nick || data.joinleave === nick) {
|
||||
return el;
|
||||
}
|
||||
}
|
||||
el = el.previousElementSibling;
|
||||
}
|
||||
},
|
||||
@ -1566,12 +1578,12 @@
|
||||
}
|
||||
const nick = occupant.get('nick'),
|
||||
stat = occupant.get('status'),
|
||||
last_join_el = this.getImmediateJoinNotification(this.content.lastElementChild, nick),
|
||||
last_join_el = this.getImmediateNotification(this.content.lastElementChild, nick, 'join'),
|
||||
data = _.get(last_join_el, 'dataset', {});
|
||||
|
||||
if (last_join_el) {
|
||||
let message;
|
||||
if (data.join === `"${nick}"`) {
|
||||
if (data.join === nick) {
|
||||
if (_.isNil(stat)) {
|
||||
message = __('%1$s has entered and left the groupchat', nick);
|
||||
} else {
|
||||
@ -1581,7 +1593,8 @@
|
||||
el.insertAdjacentElement('afterend', last_join_el);
|
||||
last_join_el.outerHTML =
|
||||
tpl_info({
|
||||
'data': `data-joinleave="${nick}"`,
|
||||
'data_name': 'joinleave',
|
||||
'data_value': nick,
|
||||
'isodate': moment().format(),
|
||||
'extra_classes': 'chat-event',
|
||||
'message': message
|
||||
@ -1589,7 +1602,7 @@
|
||||
el = this.content.lastElementChild;
|
||||
setTimeout(() => u.addClass('fade-out', el), 5000);
|
||||
setTimeout(() => el.parentElement && el.parentElement.removeChild(el), 5250);
|
||||
} else if (data.leavejoin === `"${nick}"`) {
|
||||
} else if (data.leavejoin === nick) {
|
||||
if (_.isNil(stat)) {
|
||||
message = __('%1$s has left the groupchat', nick);
|
||||
} else {
|
||||
@ -1597,7 +1610,8 @@
|
||||
}
|
||||
last_join_el.outerHTML =
|
||||
tpl_info({
|
||||
'data': `data-leave="${nick}"`,
|
||||
'data_name': 'leave',
|
||||
'data_value': nick,
|
||||
'isodate': moment().format(),
|
||||
'extra_classes': 'chat-event',
|
||||
'message': message
|
||||
@ -1614,7 +1628,8 @@
|
||||
'message': message,
|
||||
'isodate': moment().format(),
|
||||
'extra_classes': 'chat-event',
|
||||
'data': `data-leave="${nick}"`
|
||||
'data_name': 'leave',
|
||||
'data_value': nick
|
||||
}
|
||||
const el = u.stringToElement(tpl_info(data));
|
||||
this.content.insertAdjacentElement('beforeend', el);
|
||||
@ -1727,7 +1742,6 @@
|
||||
this.content.insertAdjacentHTML(
|
||||
'beforeend',
|
||||
tpl_info({
|
||||
'data': '',
|
||||
'isodate': date,
|
||||
'extra_classes': 'chat-event',
|
||||
'message': message
|
||||
@ -1737,7 +1751,6 @@
|
||||
this.content.insertAdjacentHTML(
|
||||
'beforeend',
|
||||
tpl_info({
|
||||
'data': '',
|
||||
'isodate': date,
|
||||
'extra_classes': 'chat-topic',
|
||||
'message': u.addHyperlinks(xss.filterXSS(_.get(this.model.get('subject'), 'text'), {'whiteList': {}})),
|
||||
|
@ -1,10 +1,8 @@
|
||||
{[ if (o.render_message) { ]}
|
||||
<!-- XXX: Should only ever be rendered if the message text has been sanitized already -->
|
||||
<div class="message chat-info {{{o.extra_classes}}}"
|
||||
data-isodate="{{{o.isodate}}}"
|
||||
{{{o.data}}}>{{o.message}}</div>
|
||||
data-isodate="{{{o.isodate}}}" {[ if (o.data_name) { ]} data-{{{o.data_name}}}="{{{o.data_value}}}"{[ } ]}>{{o.message}}</div>
|
||||
{[ } else { ]}
|
||||
<div class="message chat-info {{{o.extra_classes}}}"
|
||||
data-isodate="{{{o.isodate}}}"
|
||||
{{{o.data}}}>{{{o.message}}}</div>
|
||||
data-isodate="{{{o.isodate}}}" {[ if (o.data_name) { ]} data-{{{o.data_name}}}="{{{o.data_value}}}"{[ } ]}>{{{o.message}}}</div>
|
||||
{[ } ]}
|
||||
|
Loading…
Reference in New Issue
Block a user