Skip to content

Commit

Permalink
Use flexbox to keep the chat scrolled down
Browse files Browse the repository at this point in the history
By using `display: flex` and `flex-direction: column-reverse`, the chat
now automatically scrolls down when loaded, without requiring any
extra JavaScript.

We still need to scroll down with JavaScript when sending a message.

By using `column-reverse`, the messages container now works in reverse.
So the newest message is the first element in the container and the
oldest message is the last. This is the reverse of before.

Due to this, this change will likely break some plugins.
  • Loading branch information
jcbrand committed Jun 18, 2019
1 parent 21b0f24 commit dd91d3c
Show file tree
Hide file tree
Showing 12 changed files with 310 additions and 321 deletions.
1 change: 1 addition & 0 deletions .eslintrc.json
Expand Up @@ -19,6 +19,7 @@
"window": true
},
"rules": {
"lodash/prefer-lodash-chain": "off",
"lodash/prefer-lodash-method": [2, {
"ignoreMethods": [
"assign", "every", "keys", "find", "endsWith", "startsWith", "filter",
Expand Down
2 changes: 2 additions & 0 deletions CHANGES.md
Expand Up @@ -56,6 +56,8 @@
- 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

Expand Down
33 changes: 28 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions sass/_chatbox.scss
Expand Up @@ -207,6 +207,9 @@
margin-bottom: 0.25em;
}
.chat-content {
display: flex;
flex-direction: column-reverse;
padding: 1em;
height: 100%;
font-size: var(--message-font-size);
color: var(--text-color);
Expand Down
11 changes: 4 additions & 7 deletions sass/_messages.scss
Expand Up @@ -33,7 +33,6 @@
color: var(--separator-text-color);
display: inline-block;
line-height: 2em;
padding: 0 1em;
position: relative;
z-index: 5;
}
Expand All @@ -44,7 +43,7 @@
font-size: var(--message-font-size);
line-height: var(--line-height-small);
font-size: 90%;
padding: 0.17rem 1rem;
padding: 0.17rem 0;

&.badge {
color: var(--chat-head-text-color);
Expand Down Expand Up @@ -77,11 +76,10 @@
}

&.chat-msg {
display: inline-flex;
display: flex;
width: 100%;
flex-direction: row;
overflow: auto; // Ensures that content stays inside
padding: 0.125rem 1rem;
padding: 0.125rem 0;

&.onload {
animation: colorchange-chatmessage 1s;
Expand Down Expand Up @@ -152,7 +150,6 @@
display: flex;
flex-direction: row;
justify-content: space-between;
width: 100%;
}

.chat-msg__message {
Expand Down Expand Up @@ -269,7 +266,7 @@
}
&.chat-msg--action {
.chat-msg__content {
flex-wrap: nowrap;
flex-wrap: wrap;
flex-direction: row;
justify-content: flex-start;
}
Expand Down
18 changes: 8 additions & 10 deletions spec/chatbox.js
Expand Up @@ -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.lastElementChild.textContent.trim().indexOf('hello world')).not.toBe(-1);
expect(view.content.firstElementChild.textContent.trim().indexOf('hello world')).not.toBe(-1);
done();
}));

Expand Down Expand Up @@ -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:last', view.el).pop().textContent.trim() === '**Romeo Montague');
const last_el = sizzle('.chat-msg__text:last', view.el).pop();
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();
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:last-child');
let message_el = view.el.querySelector('.message:first-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:last-child');
message_el = view.el.querySelector('.message:first-child');
expect(view.el.querySelectorAll('.chat-msg--action').length).toBe(3);
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(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(u.hasClass('chat-msg--followup', message_el)).toBeFalsy();
done();
}));
Expand Down Expand Up @@ -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(), 500);
await test_utils.waitUntil(() => _converse.api.trigger.calls.count(), 1000);
expect(_converse.chatboxes.length).toEqual(2);
expect(_converse.api.trigger).toHaveBeenCalledWith('chatBoxFocused', jasmine.any(Object));
done();
Expand Down Expand Up @@ -361,7 +361,6 @@
spyOn(_converse.api, "trigger");
// We need to rebind all events otherwise our spy won't be called
chatview.delegateEvents();

chatview.el.querySelector('.toggle-chatbox-button').click();

expect(chatview.minimize).toHaveBeenCalled();
Expand All @@ -377,7 +376,6 @@

expect(trimmedview.restore).toHaveBeenCalled();
expect(_converse.api.trigger).toHaveBeenCalledWith('chatBoxMaximized', jasmine.any(Object));
await test_utils.waitUntil(() => u.isVisible(chatview.el.querySelector('.chat-body')), 500);
const toggle_el = sizzle('.toggle-chatbox-button', chatview.el).pop();
expect(u.hasClass('fa-minus', toggle_el)).toBeTruthy();
expect(u.hasClass('fa-plus', toggle_el)).toBeFalsy();
Expand Down

0 comments on commit dd91d3c

Please sign in to comment.