Commit 36549bf6 authored by JC Brand's avatar JC Brand

Revert `flexbox`, `column-reverse` changes

Unfortunately this doesn't work on Firefox and there's no proper
workaround. https://github.com/philipwalton/flexbugs/issues/108

Reverts:

Revert "Bugfix. Properly insert error messages and spinner"
This reverts commit 6a419cc1.

Revert "Use flexbox to keep the chat scrolled down"
This reverts commit dd91d3cc.
parent 303a8b63
......@@ -61,8 +61,6 @@
- Removed events `statusChanged` and `statusMessageChanged`. Instead, you can
listen on the `change:status` or `change:status\_message` events on
`_converse.xmppstatus`.
- Use flexbox instead of JavaScript to keep chat scrolled down. Due to this
change, messages are now inserted into the DOM in reverse order than before.
### API changes
......
......@@ -207,8 +207,6 @@
margin-bottom: 0.25em;
}
.chat-content {
display: flex;
flex-direction: column-reverse;
padding: 1em 0;
height: 100%;
font-size: var(--message-font-size);
......
......@@ -77,9 +77,10 @@
}
&.chat-msg {
display: flex;
display: inline-flex;
width: 100%;
flex-direction: row;
overflow: auto; // Ensures that content stays inside
padding: 0.125rem 1rem;
&.onload {
......@@ -151,6 +152,7 @@
display: flex;
flex-direction: row;
justify-content: space-between;
width: 100%;
}
.chat-msg__message {
......@@ -267,7 +269,7 @@
}
&.chat-msg--action {
.chat-msg__content {
flex-wrap: wrap;
flex-wrap: nowrap;
flex-direction: row;
justify-content: flex-start;
}
......
......@@ -44,7 +44,7 @@
}).c('body').t('hello world').tree();
await _converse.chatboxes.onMessage(msg);
await test_utils.waitUntil(() => view.content.querySelectorAll('.chat-msg').length);
expect(view.content.firstElementChild.textContent.trim().indexOf('hello world')).not.toBe(-1);
expect(view.content.lastElementChild.textContent.trim().indexOf('hello world')).not.toBe(-1);
done();
}));
......@@ -78,22 +78,22 @@
message = '/me is as well';
await test_utils.sendMessage(view, message);
expect(view.el.querySelectorAll('.chat-msg--action').length).toBe(2);
await test_utils.waitUntil(() => sizzle('.chat-msg__author:first', view.el).pop().textContent.trim() === '**Romeo Montague');
const last_el = sizzle('.chat-msg__text:first', view.el).pop();
await test_utils.waitUntil(() => sizzle('.chat-msg__author:last', view.el).pop().textContent.trim() === '**Romeo Montague');
const last_el = sizzle('.chat-msg__text:last', view.el).pop();
expect(last_el.textContent).toBe('is as well');
expect(u.hasClass('chat-msg--followup', last_el)).toBe(false);
// Check that /me messages after a normal message don't
// get the 'chat-msg--followup' class.
message = 'This a normal message';
await test_utils.sendMessage(view, message);
let message_el = view.el.querySelector('.message:first-child');
let message_el = view.el.querySelector('.message:last-child');
expect(u.hasClass('chat-msg--followup', message_el)).toBeFalsy();
message = '/me wrote a 3rd person message';
await test_utils.sendMessage(view, message);
message_el = view.el.querySelector('.message:first-child');
message_el = view.el.querySelector('.message:last-child');
expect(view.el.querySelectorAll('.chat-msg--action').length).toBe(3);
expect(sizzle('.chat-msg__text:first', view.el).pop().textContent).toBe('wrote a 3rd person message');
expect(u.isVisible(sizzle('.chat-msg__author:first', view.el).pop())).toBeTruthy();
expect(sizzle('.chat-msg__text:last', view.el).pop().textContent).toBe('wrote a 3rd person message');
expect(u.isVisible(sizzle('.chat-msg__author:last', view.el).pop())).toBeTruthy();
expect(u.hasClass('chat-msg--followup', message_el)).toBeFalsy();
done();
}));
......@@ -267,7 +267,7 @@
const jid = el.textContent.replace(/ /g,'.').toLowerCase() + '@montague.lit';
spyOn(_converse.api, "trigger");
el.click();
await test_utils.waitUntil(() => _converse.api.trigger.calls.count(), 1000);
await test_utils.waitUntil(() => _converse.api.trigger.calls.count(), 500);
expect(_converse.chatboxes.length).toEqual(2);
expect(_converse.api.trigger).toHaveBeenCalledWith('chatBoxFocused', jasmine.any(Object));
done();
......
......@@ -260,7 +260,7 @@
expect(view.model.messages.at(0).get('correcting')).toBeFalsy();
expect(view.model.messages.at(1).get('correcting')).toBeFalsy();
expect(view.model.messages.at(2).get('correcting')).toBe(true);
await test_utils.waitUntil(() => u.hasClass('correcting', sizzle('.chat-msg:first', view.el).pop()), 500);
await test_utils.waitUntil(() => u.hasClass('correcting', sizzle('.chat-msg:last', view.el).pop()), 500);
textarea.selectionEnd = 0; // Happens by pressing up,
// but for some reason not in tests, so we set it manually.
......@@ -285,11 +285,11 @@
expect(textarea.value).toBe('');
const messages = view.el.querySelectorAll('.chat-msg');
expect(messages.length).toBe(3);
expect(messages[2].querySelector('.chat-msg__text').textContent)
expect(messages[0].querySelector('.chat-msg__text').textContent)
.toBe('But soft, what light through yonder window breaks?');
expect(messages[1].querySelector('.chat-msg__text').textContent)
.toBe('It is the east, and Juliet is the sun.');
expect(messages[0].querySelector('.chat-msg__text').textContent)
expect(messages[2].querySelector('.chat-msg__text').textContent)
.toBe('Arise, fair sun, and kill the envious moon');
expect(view.model.messages.at(0).get('correcting')).toBeFalsy();
......@@ -434,53 +434,53 @@
view.clearSpinner(); //cleanup
expect(chat_content.querySelectorAll('.date-separator').length).toEqual(4);
let day = sizzle('.date-separator:last', chat_content).pop();
let day = sizzle('.date-separator:first', chat_content).pop();
expect(day.getAttribute('data-isodate')).toEqual(dayjs('2017-12-31T00:00:00').toISOString());
let time = sizzle('time:last', chat_content).pop();
let time = sizzle('time:first', chat_content).pop();
expect(time.textContent).toEqual('Sunday Dec 31st 2017')
day = sizzle('.date-separator:last', chat_content).pop();
expect(day.previousElementSibling.querySelector('.chat-msg__text').textContent).toBe('Older message');
day = sizzle('.date-separator:first', chat_content).pop();
expect(day.nextElementSibling.querySelector('.chat-msg__text').textContent).toBe('Older message');
let el = sizzle('.chat-msg:last', chat_content).pop().querySelector('.chat-msg__text')
let el = sizzle('.chat-msg:first', chat_content).pop().querySelector('.chat-msg__text')
expect(u.hasClass('chat-msg--followup', el)).toBe(false);
expect(el.textContent).toEqual('Older message');
time = sizzle('time.separator-text:eq(2)', chat_content).pop();
time = sizzle('time.separator-text:eq(1)', chat_content).pop();
expect(time.textContent).toEqual("Monday Jan 1st 2018");
day = sizzle('.date-separator:eq(2)', chat_content).pop();
day = sizzle('.date-separator:eq(1)', chat_content).pop();
expect(day.getAttribute('data-isodate')).toEqual(dayjs('2018-01-01T00:00:00').toISOString());
expect(day.previousElementSibling.querySelector('.chat-msg__text').textContent).toBe('Inbetween message');
expect(day.nextElementSibling.querySelector('.chat-msg__text').textContent).toBe('Inbetween message');
el = sizzle('.chat-msg:eq(5)', chat_content).pop();
el = sizzle('.chat-msg:eq(1)', chat_content).pop();
expect(el.querySelector('.chat-msg__text').textContent).toEqual('Inbetween message');
expect(el.previousElementSibling.querySelector('.chat-msg__text').textContent).toEqual('another inbetween message');
el = sizzle('.chat-msg:eq(4)', chat_content).pop();
expect(el.nextElementSibling.querySelector('.chat-msg__text').textContent).toEqual('another inbetween message');
el = sizzle('.chat-msg:eq(2)', chat_content).pop();
expect(el.querySelector('.chat-msg__text').textContent)
.toEqual('another inbetween message');
expect(u.hasClass('chat-msg--followup', el)).toBe(true);
time = sizzle('time.separator-text:nth(1)', chat_content).pop();
time = sizzle('time.separator-text:nth(2)', chat_content).pop();
expect(time.textContent).toEqual("Tuesday Jan 2nd 2018");
day = sizzle('.date-separator:nth(1)', chat_content).pop();
day = sizzle('.date-separator:nth(2)', chat_content).pop();
expect(day.getAttribute('data-isodate')).toEqual(dayjs('2018-01-02T00:00:00').toISOString());
expect(day.previousElementSibling.querySelector('.chat-msg__text').textContent).toBe('An earlier message on the next day');
expect(day.nextElementSibling.querySelector('.chat-msg__text').textContent).toBe('An earlier message on the next day');
el = sizzle('.chat-msg:eq(3)', chat_content).pop();
expect(el.querySelector('.chat-msg__text').textContent).toEqual('An earlier message on the next day');
expect(u.hasClass('chat-msg--followup', el)).toBe(false);
el = sizzle('.chat-msg:eq(2)', chat_content).pop();
el = sizzle('.chat-msg:eq(4)', chat_content).pop();
expect(el.querySelector('.chat-msg__text').textContent).toEqual('message');
expect(el.previousElementSibling.querySelector('.chat-msg__text').textContent).toEqual('newer message from the next day');
expect(el.nextElementSibling.querySelector('.chat-msg__text').textContent).toEqual('newer message from the next day');
expect(u.hasClass('chat-msg--followup', el)).toBe(false);
day = sizzle('.date-separator:first', chat_content).pop();
day = sizzle('.date-separator:last', chat_content).pop();
expect(day.getAttribute('data-isodate')).toEqual(dayjs().startOf('day').toISOString());
expect(day.previousElementSibling.querySelector('.chat-msg__text').textContent).toBe('latest message');
expect(day.nextElementSibling.querySelector('.chat-msg__text').textContent).toBe('latest message');
expect(u.hasClass('chat-msg--followup', el)).toBe(false);
done();
}));
......@@ -800,12 +800,12 @@
expect(chat_content.querySelectorAll('time.separator-text').length).toEqual(2); // There are now two time elements
const message_date = new Date();
day = sizzle('.date-separator:first', chat_content);
day = sizzle('.date-separator:last', chat_content);
expect(day.length).toEqual(1);
expect(day[0].getAttribute('class')).toEqual('message date-separator');
expect(day[0].getAttribute('data-isodate')).toEqual(dayjs(message_date).startOf('day').toISOString());
time = sizzle('time.separator-text:first', chat_content).pop();
time = sizzle('time.separator-text:last', chat_content).pop();
expect(time.textContent).toEqual(dayjs(message_date).startOf('day').format("dddd MMM Do YYYY"));
// Normal checks for the 2nd message
......@@ -815,12 +815,12 @@
expect(msg_obj.get('fullname')).toBeUndefined();
expect(msg_obj.get('sender')).toEqual('them');
expect(msg_obj.get('is_delayed')).toEqual(false);
const msg_txt = sizzle('.chat-msg:first .chat-msg__text', chat_content).pop().textContent;
const msg_txt = sizzle('.chat-msg:last .chat-msg__text', chat_content).pop().textContent;
expect(msg_txt).toEqual(message);
expect(chat_content.querySelector('.chat-msg:first-child .chat-msg__text').textContent).toEqual(message);
expect(chat_content.querySelector('.chat-msg:first-child .chat-msg__time').textContent.match(/^[0-9][0-9]:[0-9][0-9]/)).toBeTruthy();
expect(chat_content.querySelector('.chat-msg:first-child .chat-msg__author').textContent.trim()).toBe('Juliet Capulet');
expect(chat_content.querySelector('.chat-msg:last-child .chat-msg__text').textContent).toEqual(message);
expect(chat_content.querySelector('.chat-msg:last-child .chat-msg__time').textContent.match(/^[0-9][0-9]:[0-9][0-9]/)).toBeTruthy();
expect(chat_content.querySelector('.chat-msg:last-child .chat-msg__author').textContent.trim()).toBe('Juliet Capulet');
done();
}));
......@@ -911,21 +911,21 @@
message = 'http://www.opkode.com/"onmouseover="alert(1)"whatever';
await test_utils.sendMessage(view, message);
msg = sizzle('.chat-content .chat-msg:first .chat-msg__text', view.el).pop();
msg = sizzle('.chat-content .chat-msg:last .chat-msg__text', view.el).pop();
expect(msg.textContent).toEqual(message);
expect(msg.innerHTML).toEqual('<a target="_blank" rel="noopener" href="http://www.opkode.com/%22onmouseover=%22alert%281%29%22whatever">http://www.opkode.com/"onmouseover="alert(1)"whatever</a>');
message = "https://en.wikipedia.org/wiki/Ender's_Game";
await test_utils.sendMessage(view, message);
msg = sizzle('.chat-content .chat-msg:first .chat-msg__text', view.el).pop();
msg = sizzle('.chat-content .chat-msg:last .chat-msg__text', view.el).pop();
expect(msg.textContent).toEqual(message);
expect(msg.innerHTML).toEqual('<a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Ender%27s_Game">'+message+'</a>');
message = "<https://bugs.documentfoundation.org/show_bug.cgi?id=123737>";
await test_utils.sendMessage(view, message);
msg = sizzle('.chat-content .chat-msg:first .chat-msg__text', view.el).pop();
msg = sizzle('.chat-content .chat-msg:last .chat-msg__text', view.el).pop();
expect(msg.textContent).toEqual(message);
expect(msg.innerHTML).toEqual(
`&lt;<a target="_blank" rel="noopener" href="https://bugs.documentfoundation.org/show_bug.cgi?id=123737">https://bugs.documentfoundation.org/show_bug.cgi?id=123737</a>&gt;`);
......@@ -933,7 +933,7 @@
message = '<http://www.opkode.com/"onmouseover="alert(1)"whatever>';
await test_utils.sendMessage(view, message);
msg = sizzle('.chat-content .chat-msg:first .chat-msg__text', view.el).pop();
msg = sizzle('.chat-content .chat-msg:last .chat-msg__text', view.el).pop();
expect(msg.textContent).toEqual(message);
expect(msg.innerHTML).toEqual(
'&lt;<a target="_blank" rel="noopener" href="http://www.opkode.com/%22onmouseover=%22alert%281%29%22whatever">http://www.opkode.com/"onmouseover="alert(1)"whatever</a>&gt;');
......@@ -941,7 +941,7 @@
message = `https://www.google.com/maps/place/Kochstraat+6,+2041+CE+Zandvoort/@52.3775999,4.548971,3a,15y,170.85h,88.39t/data=!3m6!1e1!3m4!1sQ7SdHo_bPLPlLlU8GSGWaQ!2e0!7i13312!8i6656!4m5!3m4!1s0x47c5ec1e56f845ad:0x1de0bc4a5771fb08!8m2!3d52.3773668!4d4.5489388!5m1!1e2`
await test_utils.sendMessage(view, message);
msg = sizzle('.chat-content .chat-msg:first .chat-msg__text', view.el).pop();
msg = sizzle('.chat-content .chat-msg:last .chat-msg__text', view.el).pop();
expect(msg.textContent).toEqual(message);
expect(msg.innerHTML).toEqual(
`<a target="_blank" rel="noopener" href="https://www.google.com/maps/place/Kochstraat+6,+2041+CE+Zandvoort/@52.3775999,4.548971,3a,15y,170.85h,88.39t/data=%213m6%211e1%213m4%211sQ7SdHo_bPLPlLlU8GSGWaQ%212e0%217i13312%218i6656%214m5%213m4%211s0x47c5ec1e56f845ad:0x1de0bc4a5771fb08%218m2%213d52.3773668%214d4.5489388%215m1%211e2">https://www.google.com/maps/place/Kochstraat+6,+2041+CE+Zandvoort/@52.3775999,4.548971,3a,15y,170.85h,88.39t/data=!3m6!1e1!3m4!1sQ7SdHo_bPLPlLlU8GSGWaQ!2e0!7i13312!8i6656!4m5!3m4!1s0x47c5ec1e56f845ad:0x1de0bc4a5771fb08!8m2!3d52.3773668!4d4.5489388!5m1!1e2</a>`);
......@@ -998,7 +998,7 @@
</message>`);
_converse.connection._dataRecv(test_utils.createRequest(stanza));
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
expect(chat_content.querySelector('.message:first-child .chat-msg__text').innerHTML).toBe('Hey<br><br>Have you heard the news?');
expect(chat_content.querySelector('.message:last-child .chat-msg__text').innerHTML).toBe('Hey<br><br>Have you heard the news?');
stanza = u.toStanza(`
<message from="${contact_jid}"
type="chat"
......@@ -1007,7 +1007,7 @@
</message>`);
_converse.connection._dataRecv(test_utils.createRequest(stanza));
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
expect(chat_content.querySelector('.message:first-child .chat-msg__text').innerHTML).toBe('Hey<br>Have you heard<br>the news?');
expect(chat_content.querySelector('.message:last-child .chat-msg__text').innerHTML).toBe('Hey<br>Have you heard<br>the news?');
done();
}));
......@@ -1026,7 +1026,7 @@
test_utils.sendMessage(view, message);
await test_utils.waitUntil(() => view.el.querySelectorAll('.chat-content .chat-image').length, 1000)
expect(view.model.sendMessage).toHaveBeenCalled();
let msg = sizzle('.chat-content .chat-msg:first .chat-msg__text').pop();
let msg = sizzle('.chat-content .chat-msg:last .chat-msg__text').pop();
expect(msg.innerHTML.trim()).toEqual(
'<!-- src/templates/image.html -->\n'+
'<a href="'+base_url+'/logo/conversejs-filled.svg" target="_blank" rel="noopener"><img class="chat-image img-thumbnail"'+
......@@ -1035,7 +1035,7 @@
test_utils.sendMessage(view, message);
await test_utils.waitUntil(() => view.el.querySelectorAll('.chat-content .chat-image').length === 2, 1000);
expect(view.model.sendMessage).toHaveBeenCalled();
msg = sizzle('.chat-content .chat-msg:first .chat-msg__text').pop();
msg = sizzle('.chat-content .chat-msg:last .chat-msg__text').pop();
expect(msg.innerHTML.trim()).toEqual(
'<!-- src/templates/image.html -->\n'+
'<a href="'+base_url+'/logo/conversejs-filled.svg?param1=val1&amp;param2=val2" target="_blank" rel="noopener"><img'+
......@@ -1046,7 +1046,7 @@
test_utils.sendMessage(view, message);
await test_utils.waitUntil(() => view.el.querySelectorAll('.chat-content .chat-image').length === 4, 1000);
expect(view.model.sendMessage).toHaveBeenCalled();
msg = sizzle('.chat-content .chat-msg:first .chat-msg__text').pop();
msg = sizzle('.chat-content .chat-msg:last .chat-msg__text').pop();
expect(msg.textContent.trim()).toEqual('hello world');
expect(msg.querySelectorAll('img').length).toEqual(2);
......@@ -1076,10 +1076,10 @@
expect(chatbox.messages.models.length, 1);
const msg_object = chatbox.messages.models[0];
const msg_author = view.el.querySelector('.chat-content .chat-msg:first-child .chat-msg__author');
const msg_author = view.el.querySelector('.chat-content .chat-msg:last-child .chat-msg__author');
expect(msg_author.textContent.trim()).toBe('Romeo Montague');
const msg_time = view.el.querySelector('.chat-content .chat-msg:first-child .chat-msg__time');
const msg_time = view.el.querySelector('.chat-content .chat-msg:last-child .chat-msg__time');
const time = dayjs(msg_object.get('time')).format(_converse.time_format);
expect(msg_time.textContent).toBe(time);
done();
......@@ -1165,19 +1165,19 @@
expect(chat_content.querySelectorAll('.message').length).toBe(6);
expect(chat_content.querySelectorAll('.chat-msg').length).toBe(5);
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(5)'))).toBe(false);
expect(chat_content.querySelector('.message:nth-child(5) .chat-msg__text').textContent).toBe("A message");
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(4)'))).toBe(true);
expect(chat_content.querySelector('.message:nth-child(4) .chat-msg__text').textContent).toBe(
"Another message 3 minutes later");
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(3)'))).toBe(false);
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(2)'))).toBe(false);
expect(chat_content.querySelector('.message:nth-child(2) .chat-msg__text').textContent).toBe("A message");
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(3)'))).toBe(true);
expect(chat_content.querySelector('.message:nth-child(3) .chat-msg__text').textContent).toBe(
"Another message 3 minutes later");
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(4)'))).toBe(false);
expect(chat_content.querySelector('.message:nth-child(4) .chat-msg__text').textContent).toBe(
"Another message 14 minutes since we started");
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(2)'))).toBe(true);
expect(chat_content.querySelector('.message:nth-child(2) .chat-msg__text').textContent).toBe(
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(5)'))).toBe(true);
expect(chat_content.querySelector('.message:nth-child(5) .chat-msg__text').textContent).toBe(
"Another message 1 minute and 1 second since the previous one");
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(1)'))).toBe(false);
expect(chat_content.querySelector('.message:nth-child(1) .chat-msg__text').textContent).toBe(
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(6)'))).toBe(false);
expect(chat_content.querySelector('.message:nth-child(6) .chat-msg__text').textContent).toBe(
"Another message within 10 minutes, but from a different person");
// Let's add a delayed, inbetween message
......@@ -1197,21 +1197,21 @@
expect(chat_content.querySelectorAll('.message').length).toBe(7);
expect(chat_content.querySelectorAll('.chat-msg').length).toBe(6);
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(6)'))).toBe(false);
expect(chat_content.querySelector('.message:nth-child(6) .chat-msg__text').textContent).toBe("A message");
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(5)'))).toBe(true);
expect(chat_content.querySelector('.message:nth-child(5) .chat-msg__text').textContent).toBe(
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(2)'))).toBe(false);
expect(chat_content.querySelector('.message:nth-child(2) .chat-msg__text').textContent).toBe("A message");
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(3)'))).toBe(true);
expect(chat_content.querySelector('.message:nth-child(3) .chat-msg__text').textContent).toBe(
"Another message 3 minutes later");
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(4)'))).toBe(true);
expect(chat_content.querySelector('.message:nth-child(4) .chat-msg__text').textContent).toBe(
"A delayed message, sent 5 minutes since we started");
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(3)'))).toBe(true);
expect(chat_content.querySelector('.message:nth-child(3) .chat-msg__text').textContent).toBe(
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(5)'))).toBe(true);
expect(chat_content.querySelector('.message:nth-child(5) .chat-msg__text').textContent).toBe(
"Another message 14 minutes since we started");
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(2)'))).toBe(true);
expect(chat_content.querySelector('.message:nth-child(2) .chat-msg__text').textContent).toBe(
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(6)'))).toBe(true);
expect(chat_content.querySelector('.message:nth-child(6) .chat-msg__text').textContent).toBe(
"Another message 1 minute and 1 second since the previous one");
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(1)'))).toBe(false);
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(7)'))).toBe(false);
_converse.chatboxes.onMessage($msg({'id': 'aeb213', 'to': _converse.bare_jid})
.c('forwarded', {'xmlns': 'urn:xmpp:forward:0'})
......@@ -1227,25 +1227,25 @@
expect(chat_content.querySelectorAll('.message').length).toBe(8);
expect(chat_content.querySelectorAll('.chat-msg').length).toBe(7);
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(7)'))).toBe(false);
expect(chat_content.querySelector('.message:nth-child(7) .chat-msg__text').textContent).toBe("A message");
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(6)'))).toBe(true);
expect(chat_content.querySelector('.message:nth-child(6) .chat-msg__text').textContent).toBe(
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(2)'))).toBe(false);
expect(chat_content.querySelector('.message:nth-child(2) .chat-msg__text').textContent).toBe("A message");
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(3)'))).toBe(true);
expect(chat_content.querySelector('.message:nth-child(3) .chat-msg__text').textContent).toBe(
"Another message 3 minutes later");
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(5)'))).toBe(false);
expect(chat_content.querySelector('.message:nth-child(5) .chat-msg__text').textContent).toBe(
"A carbon message 4 minutes later");
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(4)'))).toBe(false);
expect(chat_content.querySelector('.message:nth-child(4) .chat-msg__text').textContent).toBe(
"A carbon message 4 minutes later");
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(5)'))).toBe(false);
expect(chat_content.querySelector('.message:nth-child(5) .chat-msg__text').textContent).toBe(
"A delayed message, sent 5 minutes since we started");
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(3)'))).toBe(true);
expect(chat_content.querySelector('.message:nth-child(3) .chat-msg__text').textContent).toBe(
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(6)'))).toBe(true);
expect(chat_content.querySelector('.message:nth-child(6) .chat-msg__text').textContent).toBe(
"Another message 14 minutes since we started");
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(2)'))).toBe(true);
expect(chat_content.querySelector('.message:nth-child(2) .chat-msg__text').textContent).toBe(
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(7)'))).toBe(true);
expect(chat_content.querySelector('.message:nth-child(7) .chat-msg__text').textContent).toBe(
"Another message 1 minute and 1 second since the previous one");
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(1)'))).toBe(false);
expect(chat_content.querySelector('.message:nth-child(1) .chat-msg__text').textContent).toBe(
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(8)'))).toBe(false);
expect(chat_content.querySelector('.message:nth-child(8) .chat-msg__text').textContent).toBe(
"Another message within 10 minutes, but from a different person");
jasmine.clock().uninstall();
......@@ -1683,7 +1683,7 @@
});
view.model.sendMessage(msg_text);
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
msg_txt = sizzle('.chat-msg:first .chat-msg__text', chat_content).pop().textContent;
msg_txt = sizzle('.chat-msg:last .chat-msg__text', chat_content).pop().textContent;
expect(msg_txt).toEqual(msg_text);
/* <message xmlns="jabber:client"
......@@ -1751,7 +1751,7 @@
});
view.model.sendMessage(msg_text);
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
msg_txt = sizzle('.chat-msg:first .chat-msg__text', chat_content).pop().textContent;
msg_txt = sizzle('.chat-msg:last .chat-msg__text', chat_content).pop().textContent;
expect(msg_txt).toEqual(msg_text);
// A different error message will however render
......@@ -1831,20 +1831,14 @@
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree());
promises.push(new Promise((resolve, reject) => view.once('messageInserted', resolve)));
}
await Promise.all(promises);
// XXX Fails on Travis
// await test_utils.waitUntil(() => view.content.scrollTop, 1000)
await test_utils.waitUntil(() => !view.model.get('auto_scrolled'), 500);
view.content.scrollTop = 0;
// XXX Fails on Travis
// await test_utils.waitUntil(() => view.scrolled, 900);
view.scrolled = true;
const text = Array.from(view.el.querySelectorAll('.chat-content .chat-msg__text'))
.map(e => e.textContent)
.join(' ');
expect(text).toBe(_.range(20).reverse().map(n => `Message: ${n}`).join(' '));
// await test_utils.waitUntil(() => view.model.get('scrolled'), 900);
view.model.set('scrolled', true);
const message = 'This message is received while the chat area is scrolled up';
_converse.chatboxes.onMessage($msg({
......@@ -1858,10 +1852,10 @@
await test_utils.waitUntil(() => view.model.messages.length > 20, 1000);
// Now check that the message appears inside the chatbox in the DOM
const chat_content = view.el.querySelector('.chat-content');
const msg_txt = sizzle('.chat-content .chat-msg:first .chat-msg__text', view.el).pop().textContent;
const msg_txt = sizzle('.chat-content .chat-msg:last .chat-msg__text', view.el).pop().textContent;
expect(msg_txt).toEqual(message);
await test_utils.waitUntil(() => u.isVisible(view.el.querySelector('.new-msgs-indicator')), 900);
expect(view.scrolled).toBe(true);
expect(view.model.get('scrolled')).toBe(true);
expect(view.content.scrollTop).toBe(0);
expect(u.isVisible(view.el.querySelector('.new-msgs-indicator'))).toBeTruthy();
// Scroll down again
......@@ -1962,9 +1956,9 @@
</message>`);
_converse.connection._dataRecv(test_utils.createRequest(stanza));
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
msg = view.el.querySelector('.chat-msg:first-child .chat-msg__text');
msg = view.el.querySelector('.chat-msg:last-child .chat-msg__text');
expect(msg.innerHTML).toEqual('<!-- message gets added here via renderMessage -->'); // Emtpy
media = view.el.querySelector('.chat-msg:first-child .chat-msg__media');
media = view.el.querySelector('.chat-msg:last-child .chat-msg__media');
expect(media.innerHTML.replace(/(\r\n|\n|\r)/gm, "")).toEqual(
'<!-- src/templates/audio.html -->'+
'<audio controls="" src="https://montague.lit/audio.mp3"></audio>'+
......@@ -2012,9 +2006,9 @@
</message>`);
_converse.connection._dataRecv(test_utils.createRequest(stanza));
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
msg = view.el.querySelector('.chat-msg:first-child .chat-msg__text');
msg = view.el.querySelector('.chat-msg:last-child .chat-msg__text');
expect(msg.innerHTML).toEqual('<!-- message gets added here via renderMessage -->'); // Emtpy
media = view.el.querySelector('.chat-msg:first-child .chat-msg__media');
media = view.el.querySelector('.chat-msg:last-child .chat-msg__media');
expect(media.innerHTML.replace(/(\r\n|\n|\r)/gm, "")).toEqual(
'<!-- src/templates/video.html -->'+
'<video controls="" src="https://montague.lit/video.mp4" style="max-height: 50vh"></video>'+
......@@ -2355,7 +2349,7 @@
expect(view.model.messages.last().get('affiliation')).toBe('member');
expect(view.model.messages.last().get('role')).toBe('participant');
expect(view.el.querySelectorAll('.chat-msg').length).toBe(2);
expect(sizzle('.chat-msg__author:first', view.el).pop().classList.value.trim()).toBe('chat-msg__author participant');
expect(sizzle('.chat-msg__author', view.el).pop().classList.value.trim()).toBe('chat-msg__author participant');
presence = $pres({
to:'romeo@montague.lit/orchard',
......@@ -2561,7 +2555,7 @@
expect(textarea.value).toBe('But soft, what light through yonder window breaks?');
expect(view.model.messages.at(0).get('correcting')).toBe(true);
expect(view.el.querySelectorAll('.chat-msg').length).toBe(2);
await test_utils.waitUntil(() => u.hasClass('correcting', sizzle('.chat-msg.groupchat:last', view.el).pop()), 500);
await test_utils.waitUntil(() => u.hasClass('correcting', view.el.querySelector('.chat-msg')), 500);
expect(textarea.value).toBe('But soft, what light through yonder window breaks?');
view.onKeyDown({
target: textarea,
......
......@@ -375,8 +375,8 @@
await test_utils.waitUntil(() => view.el.querySelectorAll('.chat-content .chat-info').length === 2);
const info_texts = Array.from(view.el.querySelectorAll('.chat-content .chat-info')).map(e => e.textContent);
expect(info_texts[1]).toBe('A new groupchat has been created');
expect(info_texts[0]).toBe('nicky has entered the groupchat');
expect(info_texts[0]).toBe('A new groupchat has been created');
expect(info_texts[1]).toBe('nicky has entered the groupchat');
// An instant room is created by saving the default configuratoin.
//
......@@ -482,9 +482,9 @@
_converse.connection._dataRecv(test_utils.createRequest(presence));
await test_utils.waitUntil(() => chat_content.querySelectorAll('.chat-info').length === 2);
expect(sizzle('div.chat-info:last', chat_content).pop().textContent)
.toBe("This groupchat is not anonymous");
expect(sizzle('div.chat-info:first', chat_content).pop().textContent)
.toBe("This groupchat is not anonymous");
expect(sizzle('div.chat-info:last', chat_content).pop().textContent)
.toBe("some1 has entered the groupchat");
done();
}));
......@@ -495,7 +495,7 @@
null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
async function (done, _converse) {
await test_utils.openAndEnterChatRoom(_converse, "coven", 'chat.shakespeare.lit', 'some1');
await test_utils.openChatRoom(_converse, "coven", 'chat.shakespeare.lit', 'some1');
const view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
const chat_content = view.el.querySelector('.chat-content');
/* We don't show join/leave messages for existing occupants. We
......@@ -512,10 +512,7 @@
'role': 'participant'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
const info_msgs = sizzle('.chat-info', chat_content);
expect(info_msgs.length).toBe(2);
expect(info_msgs.pop().textContent).toBe('some1 has entered the groupchat');
expect(info_msgs.pop().textContent).toBe('oldguy has entered the groupchat');
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(0);
/* <presence to="romeo@montague.lit/_converse.js-29092160"
* from="coven@chat.shakespeare.lit/some1">
......@@ -536,7 +533,7 @@
}).up()
.c('status', {code: '110'});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(sizzle('div.chat-info:last', chat_content).pop().textContent)
expect(sizzle('div.chat-info:first', chat_content).pop().textContent)
.toBe("some1 has entered the groupchat");
presence = $pres({
......@@ -550,8 +547,8 @@
'role': 'participant'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(3);
expect(sizzle('div.chat-info:first', chat_content).pop().textContent)
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(2);
expect(sizzle('div.chat-info:last', chat_content).pop().textContent)
.toBe("newguy has entered the groupchat");
const msg = $msg({
......@@ -576,8 +573,8 @@
'role': 'participant'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(4);
expect(sizzle('div.chat-info:first', chat_content).pop().textContent)
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(3);
expect(sizzle('div.chat-info:last', chat_content).pop().textContent)
.toBe("newgirl has entered the groupchat");
// Don't show duplicate join messages
......@@ -591,7 +588,7 @@
'role': 'participant'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(4);
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(3);
/* <presence
* from='coven@chat.shakespeare.lit/thirdwitch'
......@@ -618,8 +615,8 @@
'role': 'none'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(5);
expect(sizzle('div.chat-info:first', chat_content).pop().textContent).toBe(
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(4);
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe(
'newguy has left the groupchat. '+
'"Disconnected: Replaced by new connection"');
......@@ -635,9 +632,8 @@
'role': 'participant'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(5);
let msg_el = sizzle('div.chat-info:first', chat_content).pop();
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(4);
let msg_el = sizzle('div.chat-info:last', chat_content).pop();
expect(msg_el.textContent).toBe("newguy has left and re-entered the groupchat");
expect(msg_el.getAttribute('data-leavejoin')).toBe('newguy');
......@@ -653,8 +649,8 @@
'role': 'none'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(5);
msg_el = sizzle('.chat-info:first', chat_content).pop();
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(4);
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');
......@@ -669,8 +665,8 @@
'role': 'participant'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(chat_content.querySelectorAll('.chat-info').length).toBe(6);
expect(sizzle('.chat-info:first', chat_content).pop().textContent)
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(5);
expect(sizzle('div.chat-info:last', chat_content).pop().textContent)
.toBe("nomorenicks has entered the groupchat");
presence = $pres({
......@@ -684,8 +680,8 @@
'role': 'none'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(6);
expect(sizzle('div.chat-info:first', chat_content).pop().textContent)
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(5);
expect(sizzle('div.chat-info:last', chat_content).pop().textContent)
.toBe("nomorenicks has entered and left the groupchat");
presence = $pres({
......@@ -699,8 +695,8 @@
'role': 'participant'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(6);
expect(sizzle('div.chat-info:first', chat_content).pop().textContent)
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(5);
expect(sizzle('div.chat-info:last', chat_content).pop().textContent)
.toBe("nomorenicks has entered the groupchat");
// Test a member joining and leaving
......@@ -714,7 +710,7 @@
'role': 'participant'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(7);
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(6);
/* <presence
* from='coven@chat.shakespeare.lit/thirdwitch'
......@@ -741,8 +737,8 @@
'role': 'none'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(7);
expect(sizzle('.chat-info:first', chat_content).pop().textContent).toBe(
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(6);
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe(
'insider has entered and left the groupchat. '+
'"Disconnected: Replaced by new connection"');
......@@ -763,8 +759,8 @@
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(chat_content.querySelectorAll('.chat-info').length).toBe(7);
expect(sizzle('div.chat-info:first', chat_content).pop().textContent).toBe("newgirl has entered and left the groupchat");
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();
}));
......@@ -790,7 +786,7 @@
</presence>`);
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(sizzle('div.chat-info', chat_content).length).toBe(2);
expect(sizzle('div.chat-info:first', chat_content).pop().textContent).toBe("fabio has entered the groupchat");
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe("fabio has entered the groupchat");
presence = u.toStanza(
`<presence xmlns="jabber:client" to="romeo@montague.lit/orchard" from="coven@chat.shakespeare.lit/Dele Olajide">
......@@ -800,7 +796,7 @@
</presence>`);
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(sizzle('div.chat-info', chat_content).length).toBe(3);
expect(sizzle('div.chat-info:first', chat_content).pop().textContent).toBe("Dele Olajide has entered the groupchat");
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe("Dele Olajide has entered the groupchat");
presence = u.toStanza(
`<presence xmlns="jabber:client" to="romeo@montague.lit/orchard" from="coven@chat.shakespeare.lit/jcbrand">
......@@ -811,7 +807,7 @@
</presence>`);
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(sizzle('div.chat-info', chat_content).length).toBe(4);
expect(sizzle('div.chat-info:first', chat_content).pop().textContent).toBe("jcbrand has entered the groupchat");
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe("jcbrand has entered the groupchat");
presence = u.toStanza(
`<presence xmlns="jabber:client" to="romeo@montague.lit/orchard" type="unavailable" from="coven@chat.shakespeare.lit/Dele Olajide">
......@@ -821,7 +817,7 @@
</presence>`);
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(sizzle('div.chat-info', chat_content).length).toBe(4);
expect(sizzle('div.chat-info:first', chat_content).pop().textContent).toBe("Dele Olajide has entered and left the groupchat");
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe("Dele Olajide has entered and left the groupchat");
presence = u.toStanza(
`<presence xmlns="jabber:client" to="romeo@montague.lit/orchard" from="coven@chat.shakespeare.lit/Dele Olajide">
......@@ -831,7 +827,7 @@
</presence>`);
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(sizzle('div.chat-info', chat_content).length).toBe(4);
expect(sizzle('div.chat-info:first', chat_content).pop().textContent).toBe("Dele Olajide has entered the groupchat");
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe("Dele Olajide has entered the groupchat");
presence = u.toStanza(
`<presence xmlns="jabber:client" to="romeo@montague.lit/orchard" from="coven@chat.shakespeare.lit/fuvuv" xml:lang="en">
......@@ -843,7 +839,7 @@
</presence>`);
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(sizzle('div.chat-info', chat_content).length).toBe(5);
expect(sizzle('div.chat-info:first', chat_content).pop().textContent).toBe("fuvuv has entered the groupchat");
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe("fuvuv has entered the groupchat");
presence = u.toStanza(
`<presence xmlns="jabber:client" to="romeo@montague.lit/orchard" type="unavailable" from="coven@chat.shakespeare.lit/fuvuv">
......@@ -853,7 +849,7 @@
</presence>`);
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(sizzle('div.chat-info', chat_content).length).toBe(5);
expect(sizzle('div.chat-info:first', chat_content).pop().textContent).toBe("fuvuv has entered and left the groupchat");
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe("fuvuv has entered and left the groupchat");
presence = u.toStanza(
`<presence xmlns="jabber:client" to="romeo@montague.lit/orchard" type="unavailable" from="coven@chat.shakespeare.lit/fabio">
......@@ -864,7 +860,7 @@
</presence>`);
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(sizzle('div.chat-info', chat_content).length).toBe(5);
expect(sizzle('div.chat-info:first', chat_content).pop().textContent).toBe(
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe(
`fabio has entered and left the groupchat. "Disconnected: Replaced by new connection"`);
presence = u.toStanza(
......@@ -877,7 +873,7 @@
</presence>`);
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(sizzle('div.chat-info', chat_content).length).toBe(5);
expect(sizzle('div.chat-info:first', chat_content).pop().textContent).toBe(
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe(
`fabio has entered the groupchat. "Ready for a new day"`);
// XXX: hack so that we can test leave/enter of occupants
......@@ -904,7 +900,7 @@
</presence>`);
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(sizzle('div.chat-info', chat_content).length).toBe(2);
expect(sizzle('div.chat-info:first', chat_content).pop().textContent).toBe(
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe(
`Dele Olajide has left the groupchat`);
presence = u.toStanza(
......@@ -916,7 +912,7 @@
</presence>`);
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(sizzle('div.chat-info', chat_content).length).toBe(2);
expect(sizzle('div.chat-info:first', chat_content).pop().textContent).toBe(
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe(
`fabio has left and re-entered the groupchat`);
done();
}));
......@@ -942,7 +938,7 @@
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(2);
expect(sizzle('.chat-info:first', chat_content).pop().textContent).toBe('newguy has entered the groupchat');
expect(sizzle('div.chat-info', chat_content).pop().textContent).toBe('newguy has entered the groupchat');
presence = $pres({
to: 'romeo@montague.lit/orchard',
......@@ -958,7 +954,7 @@
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(2);
expect(sizzle('.chat-info:first', chat_content).pop().textContent).toBe('newguy has entered and left the groupchat');
expect(sizzle('div.chat-info', chat_content).pop().textContent).toBe('newguy has entered and left the groupchat');
presence = u.toStanza(
`<presence xmlns="jabber:client" to="romeo@montague.lit/orchard" from="coven@chat.shakespeare.lit/fabio">
......@@ -970,7 +966,7 @@
</presence>`);
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(sizzle('.chat-info:first', chat_content).pop().textContent).toBe(`fabio has entered the groupchat`);
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe(`fabio has entered the groupchat`);
presence = u.toStanza(
`<presence xmlns="jabber:client" to="romeo@montague.lit/orchard" from="coven@chat.shakespeare.lit/Dele Olajide">
......@@ -979,8 +975,8 @@
</x>
</presence>`);
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(sizzle('.chat-info', chat_content).length).toBe(4);
expect(sizzle('.chat-info:first', chat_content).pop().textContent).toBe("Dele Olajide has entered the groupchat");
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");
await test_utils.sendMessage(view, 'hello world');
presence = u.toStanza(
......@@ -991,8 +987,8 @@
</x>
</presence>`);
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(sizzle('.chat-info', chat_content).length).toBe(5);
expect(sizzle('.chat-info:first', chat_content).pop().textContent).toBe(`Dele Olajide has left the groupchat`);
expect(sizzle('div.chat-info', chat_content).length).toBe(5);
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe(`Dele Olajide has left the groupchat`);
done();
}));
......@@ -1049,11 +1045,11 @@
_converse.connection._dataRecv(test_utils.createRequest(stanza));
const chat_content = view.el.querySelector('.chat-content');
const messages = chat_content.querySelectorAll('.chat-info');
const messages = chat_content.querySelectorAll('div.chat-info');
expect(messages.length).toBe(3);
expect(messages[2].textContent).toBe('romeo has entered the groupchat');
expect(messages[0].textContent).toBe('romeo has entered the groupchat');
expect(messages[1].textContent).toBe('Guus has entered the groupchat');
expect(messages[0].textContent).toBe('Guus has left and re-entered the groupchat');
expect(messages[2].textContent).toBe('Guus has left and re-entered the groupchat');
done();
}));
......@@ -1106,8 +1102,8 @@
expect(indicator.getAttribute('data-isodate')).toEqual(dayjs().startOf('day').toISOString());
expect(indicator.querySelector('time').getAttribute('class')).toEqual('separator-text');
expect(indicator.querySelector('time').textContent).toEqual(dayjs().startOf('day').format("dddd MMM Do YYYY"));
expect(chat_content.querySelectorAll('.chat-info').length).toBe(2);
expect(chat_content.querySelector('.chat-info:first-child').textContent).toBe(
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(2);
expect(chat_content.querySelector('div.chat-info:last-child').textContent).toBe(
"some1 has entered the groupchat"
);
......@@ -1135,8 +1131,8 @@
expect(indicator.getAttribute('data-isodate')).toEqual(dayjs().startOf('day').toISOString());
expect(indicator.querySelector('time').textContent).toEqual(dayjs().startOf('day').format("dddd MMM Do YYYY"));
expect(chat_content.querySelectorAll('.chat-info').length).toBe(3);
expect(sizzle('div.chat-info:first', chat_content).pop().textContent).toBe(
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(3);
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe(
'some1 has left the groupchat. '+
'"Disconnected: Replaced by new connection"');
......@@ -1167,12 +1163,12 @@
let time = chat_content.querySelectorAll('time.separator-text');
expect(time.length).toEqual(4);
indicator = sizzle('.date-separator:eq(0)', chat_content).pop();
indicator = sizzle('.date-separator:eq(3)', chat_content).pop();
expect(indicator.getAttribute('class')).toEqual('message date-separator');
expect(indicator.getAttribute('data-isodate')).toEqual(dayjs().startOf('day').toISOString());
expect(indicator.querySelector('time').textContent).toEqual(dayjs().startOf('day').format("dddd MMM Do YYYY"));
expect(chat_content.querySelectorAll('.chat-info').length).toBe(4);
expect(sizzle('.chat-info:first', chat_content).pop().textContent)
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(4);
expect(sizzle('div.chat-info:last', chat_content).pop().textContent)
.toBe("newguy has entered the groupchat");
jasmine.clock().tick(ONE_DAY_LATER);
......@@ -1207,12 +1203,12 @@
time = chat_content.querySelectorAll('time.separator-text');
expect(time.length).toEqual(6);
indicator = sizzle('.date-separator:eq(0)', chat_content).pop();
indicator = sizzle('.date-separator:eq(5)', chat_content).pop();
expect(indicator.getAttribute('class')).toEqual('message date-separator');
expect(indicator.getAttribute('data-isodate')).toEqual(dayjs().startOf('day').toISOString());
expect(indicator.querySelector('time').textContent).toEqual(dayjs().startOf('day').format("dddd MMM Do YYYY"));
expect(chat_content.querySelectorAll('.chat-info').length).toBe(5);
expect(sizzle('.chat-info:first', chat_content).pop().textContent).toBe(
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(5);
expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe(
'newguy has left the groupchat. '+
'"Disconnected: Replaced by new connection"');
jasmine.clock().uninstall();
......@@ -1255,8 +1251,8 @@
}).c('body').t(message).tree();
await view.model.onMessage(msg);
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
expect(_.includes(sizzle('.chat-msg__author:first', view.el).pop().textContent, '**Romeo Montague')).toBeTruthy();
expect(sizzle('.chat-msg__text:first', view.el).pop().textContent).toBe('is as well');
expect(_.includes(sizzle('.chat-msg__author:last', view.el).pop().textContent, '**Romeo Montague')).toBeTruthy();
expect(sizzle('.chat-msg__text:last', view.el).pop().textContent).toBe('is as well');
done();
}));
......@@ -1803,7 +1799,7 @@
_converse.connection._dataRecv(test_utils.createRequest(presence));
await test_utils.waitUntil(() => view.el.querySelectorAll('.chat-content .chat-info').length === 2);
const info_text = sizzle('.chat-content .chat-info:last', view.el).pop().textContent;
const info_text = sizzle('.chat-content .chat-info:first', view.el).pop().textContent;
expect(info_text).toBe('Your nickname has been automatically set to thirdwitch');
done();
}));
......@@ -2021,7 +2017,7 @@
// Now check that the message appears inside the chatbox in the DOM
const chat_content = view.el.querySelector('.chat-content');
const msg_txt = sizzle('.chat-msg:first .chat-msg__text', chat_content).pop().textContent;
const msg_txt = sizzle('.chat-msg:last .chat-msg__text', chat_content).pop().textContent;
expect(msg_txt).toEqual(message);
expect(view.content.scrollTop).toBe(0);
done();
......@@ -2159,8 +2155,8 @@
_converse.connection._dataRecv(test_utils.createRequest(stanza));
await test_utils.waitUntil(() => view.el.querySelectorAll('.chat-content .chat-info').length === 2);
const info_messages = view.el.querySelectorAll('.chat-content .chat-info');
expect(info_messages[1].textContent).toBe('romeo has entered the groupchat');
expect(info_messages[0].textContent).toBe('groupchat logging is now enabled');
expect(info_messages[0].textContent).toBe('romeo has entered the groupchat');
expect(info_messages[1].textContent).toBe('groupchat logging is now enabled');
done();
}));
......@@ -2238,7 +2234,7 @@
_converse.connection._dataRecv(test_utils.createRequest(presence));
await test_utils.waitUntil(() => view.el.querySelectorAll('.chat-info').length === 2);
expect(sizzle('.chat-info:first').pop().textContent).toBe(
expect(sizzle('div.chat-info:last').pop().textContent).toBe(
__(_converse.muc.new_nickname_messages["303"], "newnick")
);
expect(view.model.get('connection_status')).toBe(converse.ROOMSTATUS.ENTERED);
......@@ -2581,7 +2577,7 @@
_converse.connection._dataRecv(test_utils.createRequest(message));
await test_utils.waitUntil(() => view.el.querySelectorAll('.chat-content .chat-info').length === 3);
const chat_body = view.el.querySelector('.chatroom-body');
expect(sizzle('.message:first', chat_body).pop().textContent)
expect(sizzle('.message:last', chat_body).pop().textContent)
.toBe('This groupchat is now no longer anonymous');
done();
}));
......@@ -3181,7 +3177,7 @@
});
expect(view.validateRoleOrAffiliationChangeArgs).toHaveBeenCalled();
expect(view.showErrorMessage).toHaveBeenCalled();
expect(view.el.querySelector('.message:first-child').textContent).toBe(
expect(view.el.querySelector('.message:last-child').textContent).toBe(
"Error: the \"ban\" command takes two arguments, the user's nickname and optionally a reason.");
expect(view.model.setAffiliation).not.toHaveBeenCalled();
......@@ -3235,7 +3231,7 @@
textarea.value = '/ban joe22';
view.onFormSubmitted(new Event('submit'));
expect(view.el.querySelector('.message:first-child').textContent).toBe(
expect(view.el.querySelector('.message:last-child').textContent).toBe(
"Error: couldn't find a groupchat participant based on your arguments");
done();
}));
......@@ -3326,7 +3322,7 @@
_converse.connection._dataRecv(test_utils.createRequest(presence));
await test_utils.waitUntil(() => view.el.querySelectorAll('.chat-info').length === 4);
expect(view.el.querySelectorAll('.chat-info')[0].textContent).toBe("annoying guy has been kicked out");
expect(view.el.querySelectorAll('.chat-info')[3].textContent).toBe("annoying guy has been kicked out");
expect(view.el.querySelectorAll('.chat-info').length).toBe(4);
done();
}));
......@@ -3372,10 +3368,10 @@
'role': 'participant'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
const info_msg = view.el.querySelector('.chat-info:first-child');
expect(info_msg.textContent).toBe("trustworthyguy has entered the groupchat");
var info_msgs = Array.prototype.slice.call(view.el.querySelectorAll('.chat-info'), 0);
expect(info_msgs.pop().textContent).toBe("trustworthyguy has entered the groupchat");
const textarea = view.el.querySelector('.chat-textarea')
var textarea = view.el.querySelector('.chat-textarea')
textarea.value = '/op';
view.onKeyDown({
target: textarea,
......@@ -3428,7 +3424,7 @@
'role': 'moderator'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
let info_msgs = Array.prototype.slice.call(view.el.querySelectorAll('.chat-info'), 0);
info_msgs = Array.prototype.slice.call(view.el.querySelectorAll('.chat-info'), 0);
expect(info_msgs.pop().textContent).toBe("trustworthyguy is now a moderator");
// Call now with the correct amount of arguments.
// XXX: Calling onFormSubmitted directly, trying
......@@ -3502,7 +3498,7 @@
* </x>
* </presence>
*/
let presence = $pres({
var presence = $pres({
'from': 'lounge@montague.lit/annoyingGuy',
'id':'27C55F89-1C6A-459A-9EB5-77690145D624',
'to': 'romeo@montague.lit/desktop'
......@@ -3514,8 +3510,8 @@
'role': 'participant'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
const info_msg = sizzle('.chat-info:first', view.el).pop();
expect(info_msg.textContent).toBe("annoyingGuy has entered the groupchat");
var info_msgs = Array.prototype.slice.call(view.el.querySelectorAll('.chat-info'), 0);
expect(info_msgs.pop().textContent).toBe("annoyingGuy has entered the groupchat");
const textarea = view.el.querySelector('.chat-textarea')
textarea.value = '/mute';
......@@ -3569,7 +3565,7 @@
'role': 'visitor'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
let info_msgs = Array.prototype.slice.call(view.el.querySelectorAll('.chat-info'), 0);
info_msgs = Array.prototype.slice.call(view.el.querySelectorAll('.chat-info'), 0);
expect(info_msgs.pop().textContent).toBe("annoyingGuy has been muted");
// Call now with the correct of arguments.
......@@ -4707,7 +4703,7 @@
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(2);
expect(sizzle('.chat-info:first', chat_content).pop().textContent)
expect(sizzle('div.chat-info:last', chat_content).pop().textContent)
.toBe("newguy has entered the groupchat");
presence = $pres({
......@@ -4722,7 +4718,7 @@
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(3);
expect(sizzle('.chat-info:first', chat_content).pop().textContent)
expect(sizzle('div.chat-info:last', chat_content).pop().textContent)
.toBe("nomorenicks has entered the groupchat");
// See XEP-0085 https://xmpp.org/extensions/xep-0085.html#definitions
......@@ -4741,9 +4737,9 @@
// Check that the notification appears inside the chatbox in the DOM
let events = view.el.querySelectorAll('.chat-event');
expect(events.length).toBe(3);
expect(events[2].textContent).toEqual('some1 has entered the groupchat');
expect(events[0].textContent).toEqual('some1 has entered the groupchat');
expect(events[1].textContent).toEqual('newguy has entered the groupchat');
expect(events[0].textContent).toEqual('nomorenicks has entered the groupchat');
expect(events[2].textContent).toEqual('nomorenicks has entered the groupchat');
let notifications = view.el.querySelectorAll('.chat-state-notification');
expect(notifications.length).toBe(1);
......@@ -4763,9 +4759,9 @@
events = view.el.querySelectorAll('.chat-event');
expect(events.length).toBe(3);
expect(events[2].textContent).toEqual('some1 has entered the groupchat');
expect(events[0].textContent).toEqual('some1 has entered the groupchat');
expect(events[1].textContent).toEqual('newguy has entered the groupchat');
expect(events[0].textContent).toEqual('nomorenicks has entered the groupchat');
expect(events[2].textContent).toEqual('nomorenicks has entered the groupchat');
notifications = view.el.querySelectorAll('.chat-state-notification');
expect(notifications.length).toBe(1);
......@@ -4782,15 +4778,15 @@
await view.model.onMessage(msg);
events = view.el.querySelectorAll('.chat-event');
expect(events.length).toBe(3);
expect(events[2].textContent).toEqual('some1 has entered the groupchat');
expect(events[0].textContent).toEqual('some1 has entered the groupchat');
expect(events[1].textContent).toEqual('newguy has entered the groupchat');
expect(events[0].textContent).toEqual('nomorenicks has entered the groupchat');
expect(events[2].textContent).toEqual('nomorenicks has entered the groupchat');
await test_utils.waitUntil(() => (view.el.querySelectorAll('.chat-state-notification').length === 2));
notifications = view.el.querySelectorAll('.chat-state-notification');
expect(notifications.length).toBe(2);
expect(notifications[1].textContent).toEqual('nomorenicks is typing');
expect(notifications[0].textContent).toEqual('newguy is typing');
expect(notifications[0].textContent).toEqual('nomorenicks is typing');
expect(notifications[1].textContent).toEqual('newguy is typing');
// Check that new messages appear under the chat state notifications
msg = $msg({
......@@ -4811,9 +4807,9 @@
timeout_functions[0]();
events = view.el.querySelectorAll('.chat-event');
expect(events.length).toBe(3);
expect(events[2].textContent).toEqual('some1 has entered the groupchat');
expect(events[0].textContent).toEqual('some1 has entered the groupchat');
expect(events[1].textContent).toEqual('newguy has entered the groupchat');
expect(events[0].textContent).toEqual('nomorenicks has entered the groupchat');
expect(events[2].textContent).toEqual('nomorenicks has entered the groupchat');
notifications = view.el.querySelectorAll('.chat-state-notification');
expect(notifications.length).toBe(1);
......@@ -4822,9 +4818,9 @@
timeout_functions[1]();
events = view.el.querySelectorAll('.chat-event');
expect(events.length).toBe(3);
expect(events[2].textContent).toEqual('some1 has entered the groupchat');
expect(events[0].textContent).toEqual('some1 has entered the groupchat');
expect(events[1].textContent).toEqual('newguy has entered the groupchat');
expect(events[0].textContent).toEqual('nomorenicks has entered the groupchat');
expect(events[2].textContent).toEqual('nomorenicks has entered the groupchat');
notifications = view.el.querySelectorAll('.chat-state-notification');
expect(notifications.length).toBe(0);
......@@ -4877,7 +4873,7 @@
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(2);
expect(sizzle('.chat-info:first', chat_content).pop().textContent)
expect(sizzle('div.chat-info:last', chat_content).pop().textContent)
.toBe("newguy has entered the groupchat");
presence = $pres({
......@@ -4892,7 +4888,7 @@
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(3);
expect(sizzle('.chat-info:first', chat_content).pop().textContent)
expect(sizzle('div.chat-info:last', chat_content).pop().textContent)
.toBe("nomorenicks has entered the groupchat");
// See XEP-0085 https://xmpp.org/extensions/xep-0085.html#definitions
......@@ -4909,9 +4905,9 @@
// Check that the notification appears inside the chatbox in the DOM
var events = view.el.querySelectorAll('.chat-event');
expect(events.length).toBe(3);
expect(events[2].textContent).toEqual('some1 has entered the groupchat');
expect(events[0].textContent).toEqual('some1 has entered the groupchat');
expect(events[1].textContent).toEqual('newguy has entered the groupchat');
expect(events[0].textContent).toEqual('nomorenicks has entered the groupchat');
expect(events[2].textContent).toEqual('nomorenicks has entered the groupchat');
await test_utils.waitUntil(() => view.el.querySelectorAll('.chat-state-notification').length);
let notifications = view.el.querySelectorAll('.chat-state-notification');
......@@ -4929,9 +4925,9 @@
events = view.el.querySelectorAll('.chat-event');
expect(events.length).toBe(3);
expect(events[2].textContent).toEqual('some1 has entered the groupchat');
expect(events[0].textContent).toEqual('some1 has entered the groupchat');
expect(events[1].textContent).toEqual('newguy has entered the groupchat');
expect(events[0].textContent).toEqual('nomorenicks has entered the groupchat');
expect(events[2].textContent).toEqual('nomorenicks has entered the groupchat');
notifications = view.el.querySelectorAll('.chat-state-notification');
expect(notifications.length).toBe(1);
......@@ -4947,9 +4943,9 @@
await view.model.onMessage(msg);
events = view.el.querySelectorAll('.chat-event');
expect(events.length).toBe(3);
expect(events[2].textContent).toEqual('some1 has entered the groupchat');
expect(events[0].textContent).toEqual('some1 has entered the groupchat');
expect(events[1].textContent).toEqual('newguy has entered the groupchat');
expect(events[0].textContent).toEqual('nomorenicks has entered the groupchat');
expect(events[2].textContent).toEqual('nomorenicks has entered the groupchat');
await test_utils.waitUntil(() => view.el.querySelectorAll('.chat-state-notification').length === 2);
notifications = view.el.querySelectorAll('.chat-state-notification');
......@@ -4970,9 +4966,9 @@
await view.model.onMessage(msg);
events = view.el.querySelectorAll('.chat-event');
expect(events.length).toBe(3);
expect(events[2].textContent).toEqual('some1 has entered the groupchat');
expect(events[0].textContent).toEqual('some1 has entered the groupchat');
expect(events[1].textContent).toEqual('newguy has entered the groupchat');
expect(events[0].textContent).toEqual('nomorenicks has entered the groupchat');
expect(events[2].textContent).toEqual('nomorenicks has entered the groupchat');
await test_utils.waitUntil(() => {
return _.map(
......
......@@ -208,7 +208,7 @@
_converse.connection._dataRecv(test_utils.createRequest(stanza));
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
expect(view.model.messages.length).toBe(2);
expect(view.el.querySelectorAll('.chat-msg__body')[0].textContent.trim())
expect(view.el.querySelectorAll('.chat-msg__body')[1].textContent.trim())
.toBe('This is an encrypted message from the contact');
// #1193 Check for a received message without <body> tag
......@@ -228,7 +228,7 @@
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
await test_utils.waitUntil(() => view.model.messages.length > 1);
expect(view.model.messages.length).toBe(3);
expect(view.el.querySelectorAll('.chat-msg__body')[0].textContent.trim())
expect(view.el.querySelectorAll('.chat-msg__body')[2].textContent.trim())
.toBe('Another received encrypted message without fallback');
done();
}));
......
......@@ -345,6 +345,7 @@ converse.plugins.add('converse-chatview', {
initialize () {
this.initDebounced();
this.model.messages.on('add', this.onMessageAdded, this);
this.model.messages.on('rendered', this.scrollDown, this);
this.model.messages.on('reset', () => {
this.content.innerHTML = '';
this.removeAll();
......@@ -366,6 +367,7 @@ converse.plugins.add('converse-chatview', {
},
initDebounced () {
this.scrollDown = _.debounce(this._scrollDown, 100);
this.markScrolled = _.debounce(this._markScrolled, 100);
this.show = _.debounce(this._show, 500, {'leading': true});
},
......@@ -544,7 +546,8 @@ converse.plugins.add('converse-chatview', {
await this.model.messages.fetched;
await Promise.all(this.model.messages.map(m => this.onMessageAdded(m)));
this.insertIntoDOM();
this.content.addEventListener('scroll', () => this.markScrolled());
this.scrollDown();
this.content.addEventListener('scroll', this.markScrolled.bind(this));
},
insertIntoDOM () {
......@@ -565,23 +568,26 @@ converse.plugins.add('converse-chatview', {
'message': message,
'isodate': isodate,
}));
this.insertDayIndicator(this.content.firstElementChild);
this.insertDayIndicator(this.content.lastElementChild);
this.scrollDown();
return isodate;
},
showErrorMessage (message) {
this.content.insertAdjacentHTML(
'afterBegin',
'beforeend',
tpl_error_message({'message': message, 'isodate': (new Date()).toISOString() })
);
this.scrollDown();
},
addSpinner (append=false) {
if (_.isNull(this.el.querySelector('.spinner'))) {
if (append) {
this.content.insertAdjacentHTML('afterBegin', tpl_spinner());
this.content.insertAdjacentHTML('beforeend', tpl_spinner());
this.scrollDown();
} else {
this.content.insertAdjacentHTML('beforeEnd', tpl_spinner());
this.content.insertAdjacentHTML('afterbegin', tpl_spinner());
}
}
},
......@@ -602,16 +608,16 @@ converse.plugins.add('converse-chatview', {
* which specifies its creation date.
*/
insertDayIndicator (next_msg_el) {
const earlier_msg_el = u.getNextElement(next_msg_el, ".message:not(.chat-state-notification)"),
earlier_msg_date = _.isNull(earlier_msg_el) ? null : earlier_msg_el.getAttribute('data-isodate'),
const prev_msg_el = u.getPreviousElement(next_msg_el, ".message:not(.chat-state-notification)"),
prev_msg_date = _.isNull(prev_msg_el) ? null : prev_msg_el.getAttribute('data-isodate'),
next_msg_date = next_msg_el.getAttribute('data-isodate');
if (_.isNull(earlier_msg_date) && _.isNull(next_msg_date)) {
if (_.isNull(prev_msg_date) && _.isNull(next_msg_date)) {
return;
}
if (_.isNull(earlier_msg_date) || dayjs(next_msg_date).isAfter(earlier_msg_date, 'day')) {
if (_.isNull(prev_msg_date) || dayjs(next_msg_date).isAfter(prev_msg_date, 'day')) {
const day_date = dayjs(next_msg_date).startOf('day');
next_msg_el.insertAdjacentHTML('afterEnd',
next_msg_el.insertAdjacentHTML('beforeBegin',
tpl_new_day({
'isodate': day_date.toISOString(),
'datestring': day_date.format("dddd MMM Do YYYY")
......@@ -629,15 +635,14 @@ converse.plugins.add('converse-chatview', {
* @returns { Date }
*/
getLastMessageDate (cutoff) {
const most_recent_msg = u.getFirstChildElement(this.content, '.message:not(.chat-state-notification)');
const most_recent_date = most_recent_msg ? most_recent_msg.getAttribute('data-isodate') : null;
if (_.isNull(most_recent_date)) {
const first_msg = u.getFirstChildElement(this.content, '.message:not(.chat-state-notification)');
const oldest_date = first_msg ? first_msg.getAttribute('data-isodate') : null;
if (!_.isNull(oldest_date) && dayjs(oldest_date).isAfter(cutoff)) {
return null;
}
const oldest_message = u.getLastChildElement(this.content, '.message:not(.chat-state-notification)');
const oldest_date = oldest_message ? oldest_message.getAttribute('data-isodate') : null;
if (!_.isNull(oldest_date) && dayjs(oldest_date).isAfter(cutoff)) {
const last_msg = u.getLastChildElement(this.content, '.message:not(.chat-state-notification)');
const most_recent_date = last_msg ? last_msg.getAttribute('data-isodate') : null;
if (_.isNull(most_recent_date)) {
return null;
}
if (dayjs(most_recent_date).isBefore(cutoff)) {
......@@ -664,6 +669,29 @@ converse.plugins.add('converse-chatview', {
}
},
setScrollPosition (message_el) {
/* Given a newly inserted message, determine whether we
* should keep the scrollbar in place (so as to not scroll
* up when using infinite scroll).
*/
if (this.model.get('scrolled')) {
const next_msg_el = u.getNextElement(message_el, ".chat-msg");
if (next_msg_el) {
// The currently received message is not new, there
// are newer messages after it. So let's see if we
// should maintain our current scroll position.
if (this.content.scrollTop === 0 || this.model.get('top_visible_message')) {
const top_visible_message = this.model.get('top_visible_message') || next_msg_el;
this.model.set('top_visible_message', top_visible_message);
this.content.scrollTop = top_visible_message.offsetTop - 30;
}
}
} else {
this.scrollDown();
}
},
showHelpMessages (msgs, type, spinner) {
msgs.forEach(msg => {
this.content.insertAdjacentHTML(
......@@ -680,6 +708,7 @@ converse.plugins.add('converse-chatview', {
} else if (spinner === false) {
this.clearSpinner();
}
return this.scrollDown();
},
shouldShowOnTextMessage () {
......@@ -697,24 +726,24 @@ converse.plugins.add('converse-chatview', {
if (view.model.get('type') === 'error') {
const previous_msg_el = this.content.querySelector(`[data-msgid="${view.model.get('msgid')}"]`);
if (previous_msg_el) {
previous_msg_el.insertAdjacentElement('beforeBegin', view.el);
previous_msg_el.insertAdjacentElement('afterend', view.el);
return this.trigger('messageInserted', view.el);
}
}
const current_msg_date = dayjs(view.model.get('time')).toDate() || new Date();
const previous_msg_date = this.getLastMessageDate(current_msg_date);
const current_msg_date = dayjs(view.model.get('time')).toDate() || new Date(),
previous_msg_date = this.getLastMessageDate(current_msg_date);
if (_.isNull(previous_msg_date)) {
this.content.insertAdjacentElement('beforeEnd', view.el);
this.content.insertAdjacentElement('afterbegin', view.el);
} else {
const previous_msg_el = sizzle(`[data-isodate="${previous_msg_date.toISOString()}"]:first`, this.content).pop();
const previous_msg_el = sizzle(`[data-isodate="${previous_msg_date.toISOString()}"]:last`, this.content).pop();
if (view.model.get('type') === 'error' &&
u.hasClass('chat-error', previous_msg_el) &&
previous_msg_el.textContent === view.model.get('message')) {
// We don't show a duplicate error message
return;
}
previous_msg_el.insertAdjacentElement('beforeBegin', view.el);
previous_msg_el.insertAdjacentElement('afterend', view.el);
this.markFollowups(view.el);
}
return this.trigger('messageInserted', view.el);
......@@ -736,27 +765,26 @@ converse.plugins.add('converse-chatview', {
* @param { HTMLElement } el - The message element
*/
markFollowups (el) {
const from = el.getAttribute('data-from');
const earlier_msg_el = el.nextElementSibling;
const date = dayjs(el.getAttribute('data-isodate'));
if (earlier_msg_el &&
!u.hasClass('chat-msg--action', el) && !u.hasClass('chat-msg--action', earlier_msg_el) &&
earlier_msg_el.getAttribute('data-from') === from &&
date.isBefore(dayjs(earlier_msg_el.getAttribute('data-isodate')).add(10, 'minutes')) &&
el.getAttribute('data-encrypted') === earlier_msg_el.getAttribute('data-encrypted')) {
const from = el.getAttribute('data-from'),
previous_el = el.previousElementSibling,
date = dayjs(el.getAttribute('data-isodate')),
next_el = el.nextElementSibling;
if (!u.hasClass('chat-msg--action', el) && !u.hasClass('chat-msg--action', previous_el) &&
previous_el.getAttribute('data-from') === from &&
date.isBefore(dayjs(previous_el.getAttribute('data-isodate')).add(10, 'minutes')) &&
el.getAttribute('data-encrypted') === previous_el.getAttribute('data-encrypted')) {
u.addClass('chat-msg--followup', el);
}
if (!next_el) { return; }
const later_msg_el = el.previousElementSibling;
if (!later_msg_el) { return; }
if (!u.hasClass('chat-msg--action', el) &&
later_msg_el.getAttribute('data-from') === from &&
dayjs(later_msg_el.getAttribute('data-isodate')).isBefore(date.add(10, 'minutes')) &&
el.getAttribute('data-encrypted') === later_msg_el.getAttribute('data-encrypted')) {
u.addClass('chat-msg--followup', later_msg_el);
next_el.getAttribute('data-from') === from &&
dayjs(next_el.getAttribute('data-isodate')).isBefore(date.add(10, 'minutes')) &&
el.getAttribute('data-encrypted') === next_el.getAttribute('data-encrypted')) {
u.addClass('chat-msg--followup', next_el);
} else {
u.removeClass('chat-msg--followup', later_msg_el);
u.removeClass('chat-msg--followup', next_el);
}
},
......@@ -781,6 +809,7 @@ converse.plugins.add('converse-chatview', {
this.insertMessage(view);
this.insertDayIndicator(view.el);
this.setScrollPosition(view.el);
if (u.isNewMessage(message)) {
if (message.get('sender') === 'me') {
......@@ -788,8 +817,8 @@ converse.plugins.add('converse-chatview', {
// gets scrolled down. We always want to scroll down
// when the user writes a message as opposed to when a
// message is received.
this.scrolled = false;
} else if (this.scrolled && !u.isOnlyChatStateNotification(message)) {
this.model.set('scrolled', false);
} else if (this.model.get('scrolled', true) && !u.isOnlyChatStateNotification(message)) {
this.showNewMessagesIndicator();
}
}
......@@ -1230,6 +1259,7 @@ converse.plugins.add('converse-chatview', {
'message': text,
'isodate': (new Date()).toISOString(),
}));
this.scrollDown();
}
}
},
......@@ -1300,6 +1330,7 @@ converse.plugins.add('converse-chatview', {
if (_converse.auto_focus) {
this.focus();
}
this.focus();
},
_show () {
......@@ -1341,16 +1372,26 @@ converse.plugins.add('converse-chatview', {
scrolled = false;
this.onScrolledDown();
}
this.scrolled = scrolled;
u.safeSave(this.model, {
'scrolled': scrolled,
'top_visible_message': null
});
},
viewUnreadMessages () {
this.scrolled = false;
this.model.save({
'scrolled': false,
'top_visible_message': null
});
this.scrollDown();
},
scrollDown () {
if (this.content && u.isVisible(this.content) && !this.scrolled) {
_scrollDown () {
/* Inner method that gets debounced */
if (_.isUndefined(this.content)) {
return;
}
if (u.isVisible(this.content) && !this.model.get('scrolled')) {
this.content.scrollTop = this.content.scrollHeight;
}
},
......
......@@ -470,6 +470,7 @@ converse.plugins.add('converse-muc-views', {
this.initDebounced();
this.model.messages.on('add', this.onMessageAdded, this);
this.model.messages.on('rendered', this.scrollDown, this);
this.model.messages.on('reset', () => {
this.content.innerHTML = '';
this.removeAll();
......@@ -679,6 +680,7 @@ converse.plugins.add('converse-muc-views', {
this.model.clearUnreadMsgCounter();
this.model.save();
}
this.scrollDown();
this.renderEmojiPicker();
},
......@@ -706,6 +708,7 @@ converse.plugins.add('converse-muc-views', {
} else if (conn_status === converse.ROOMSTATUS.ENTERED) {
this.hideSpinner();
this.setChatState(_converse.ACTIVE);
this.scrollDown();
if (_converse.auto_focus) {
this.focus();
}
......@@ -763,6 +766,7 @@ converse.plugins.add('converse-muc-views', {
ev.stopPropagation();
}
this.model.save({'hidden_occupants': true});
this.scrollDown();
},
toggleOccupants (ev) {
......@@ -774,6 +778,7 @@ converse.plugins.add('converse-muc-views', {
ev.stopPropagation();
}
this.model.save({'hidden_occupants': !this.model.get('hidden_occupants')});
this.scrollDown();
},
onOccupantClicked (ev) {
......@@ -1321,7 +1326,7 @@ converse.plugins.add('converse-muc-views', {
return;
}
if (!dayjs(el.getAttribute('data-isodate')).isSame(new Date(), "day")) {
el = el.nextElementSibling;
el = el.previousElementSibling;
continue;
}
if (data.join === nick ||
......@@ -1330,7 +1335,7 @@ converse.plugins.add('converse-muc-views', {
data.joinleave === nick) {
return el;
}
el = el.nextElementSibling;
el = el.previousElementSibling;
}
},
......@@ -1341,7 +1346,7 @@ converse.plugins.add('converse-muc-views', {
}
const nick = occupant.get('nick'),
stat = _converse.muc_show_join_leave_status ? occupant.get('status') : null,
prev_info_el = this.getPreviousJoinOrLeaveNotification(this.content.firstElementChild, nick),
prev_info_el = this.getPreviousJoinOrLeaveNotification(this.content.lastElementChild, nick),
data = _.get(prev_info_el, 'dataset', {});
if (data.leave === nick) {
......@@ -1359,8 +1364,8 @@ converse.plugins.add('converse-muc-views', {
'message': message
};
this.content.removeChild(prev_info_el);
this.content.insertAdjacentHTML('afterBegin', tpl_info(data));
const el = this.content.firstElementChild;
this.content.insertAdjacentHTML('beforeend', tpl_info(data));
const el = this.content.lastElementChild;
setTimeout(() => u.addClass('fade-out', el), 5000);
setTimeout(() => el.parentElement && el.parentElement.removeChild(el), 5500);
} else {
......@@ -1379,12 +1384,13 @@ converse.plugins.add('converse-muc-views', {
};
if (prev_info_el) {
this.content.removeChild(prev_info_el);
this.content.insertAdjacentHTML('afterBegin', tpl_info(data));
this.content.insertAdjacentHTML('beforeend', tpl_info(data));
} else {
this.content.insertAdjacentHTML('afterBegin', tpl_info(data));
this.insertDayIndicator(this.content.firstElementChild);
this.content.insertAdjacentHTML('beforeend', tpl_info(data));
this.insertDayIndicator(this.content.lastElementChild);
}
}
this.scrollDown();
},
showLeaveNotification (occupant) {
......@@ -1395,7 +1401,7 @@ converse.plugins.add('converse-muc-views', {
}
const nick = occupant.get('nick'),
stat = _converse.muc_show_join_leave_status ? occupant.get('status') : null,
prev_info_el = this.getPreviousJoinOrLeaveNotification(this.content.firstElementChild, nick),
prev_info_el = this.getPreviousJoinOrLeaveNotification(this.content.lastElementChild, nick),
dataset = _.get(prev_info_el, 'dataset', {});
if (dataset.join === nick) {
......@@ -1413,8 +1419,8 @@ converse.plugins.add('converse-muc-views', {
'message': message
};
this.content.removeChild(prev_info_el);
this.content.insertAdjacentHTML('afterBegin', tpl_info(data));
const el = this.content.firstElementChild;
this.content.insertAdjacentHTML('beforeend', tpl_info(data));
const el = this.content.lastElementChild;
setTimeout(() => u.addClass('fade-out', el), 5000);
setTimeout(() => el.parentElement && el.parentElement.removeChild(el), 5500);
} else {
......@@ -1433,12 +1439,13 @@ converse.plugins.add('converse-muc-views', {
}
if (prev_info_el) {
this.content.removeChild(prev_info_el);
this.content.insertAdjacentHTML('afterBegin', tpl_info(data));
this.content.insertAdjacentHTML('beforeend', tpl_info(data));
} else {
this.content.insertAdjacentHTML('afterBegin', tpl_info(data));
this.insertDayIndicator(this.content.firstElementChild);
this.content.insertAdjacentHTML('beforeend', tpl_info(data));
this.insertDayIndicator(this.content.lastElementChild);
}
}
this.scrollDown();
},
renderAfterTransition () {
......@@ -1453,6 +1460,7 @@ converse.plugins.add('converse-muc-views', {
} else {
u.showElement(this.el.querySelector('.chat-area'));
u.showElement(this.el.querySelector('.occupants'));
this.scrollDown();
}
},
......@@ -1502,6 +1510,7 @@ converse.plugins.add('converse-muc-views', {
'render_message': true
}));
}
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