From 825e2643aee75499b13e374ba6ae0ed40fa7e140 Mon Sep 17 00:00:00 2001 From: JC Brand Date: Mon, 7 Jun 2021 19:26:16 +0200 Subject: [PATCH] Use `flex-direction: column-reverse` On the `` element. This removes the need for all the manual scrolling. Firefox finally supports this feature. Unfortunately Firefox ESR doesn't yet, but I can't wait anymore. --- src/plugins/chatview/styles/chatbox.scss | 7 --- src/plugins/chatview/view.js | 1 - src/plugins/headlines-view/view.js | 1 - src/plugins/muc-views/muc.js | 14 ----- src/shared/chat/chat-content.js | 77 +++++------------------- src/shared/chat/message-history.js | 2 - src/shared/chat/message.js | 7 +-- src/shared/chat/styles/chat-content.scss | 8 +++ 8 files changed, 25 insertions(+), 92 deletions(-) create mode 100644 src/shared/chat/styles/chat-content.scss diff --git a/src/plugins/chatview/styles/chatbox.scss b/src/plugins/chatview/styles/chatbox.scss index 9978fdb35..00ad02f9c 100644 --- a/src/plugins/chatview/styles/chatbox.scss +++ b/src/plugins/chatview/styles/chatbox.scss @@ -138,13 +138,6 @@ flex-direction: column; justify-content: space-between; - converse-chat-content { - display: flex; - flex-direction: column; - height: 100%; - justify-content: space-between; - } - converse-chat-message { .spinner { width: 100%; diff --git a/src/plugins/chatview/view.js b/src/plugins/chatview/view.js index d1aa2bfa6..84ad1ac00 100644 --- a/src/plugins/chatview/view.js +++ b/src/plugins/chatview/view.js @@ -75,7 +75,6 @@ export default class ChatView extends BaseChatView { afterShown () { this.model.setChatState(_converse.ACTIVE); - this.scrollDown(); this.maybeFocus(); } } diff --git a/src/plugins/headlines-view/view.js b/src/plugins/headlines-view/view.js index 23e6d8cc1..7dea88d7b 100644 --- a/src/plugins/headlines-view/view.js +++ b/src/plugins/headlines-view/view.js @@ -25,7 +25,6 @@ class HeadlinesView extends BaseChatView { await this.model.messages.fetched; this.model.maybeShow(); - this.scrollDown(); /** * Triggered once the {@link _converse.HeadlinesBoxView} has been initialized * @event _converse#headlinesBoxViewInitialized diff --git a/src/plugins/muc-views/muc.js b/src/plugins/muc-views/muc.js index 4f2a9f5a1..e45d33eb0 100644 --- a/src/plugins/muc-views/muc.js +++ b/src/plugins/muc-views/muc.js @@ -21,15 +21,12 @@ export default class MUCView extends BaseChatView { this.listenTo(_converse, 'windowStateChanged', this.onWindowStateChanged); this.listenTo(this.model, 'change:composing_spoiler', this.requestUpdateMessageForm); - this.listenTo(this.model, 'change:hidden', () => this.afterShown()); - this.listenTo(this.model, 'change:minimized', () => this.afterShown()); this.listenTo(this.model, 'show', this.show); this.listenTo(this.model.session, 'change:connection_status', this.updateAfterTransition); this.listenTo(this.model.session, 'change:view', this.requestUpdate); this.updateAfterTransition(); this.model.maybeShow(); - this.scrollDown(); /** * Triggered once a { @link _converse.ChatRoomView } has been opened * @event _converse#chatRoomViewInitialized @@ -43,17 +40,6 @@ export default class MUCView extends BaseChatView { return tpl_muc({ 'model': this.model }); } - /** - * Callback method that gets called after the chat has become visible. - * @private - * @method _converse.ChatRoomView#afterShown - */ - afterShown () { - if (!this.model.get('hidden') && !this.model.get('minimized')) { - this.scrollDown(); - } - } - /** * Closes this chat, which implies leaving the MUC as well. * @private diff --git a/src/shared/chat/chat-content.js b/src/shared/chat/chat-content.js index 4614ecb0f..1db22813c 100644 --- a/src/shared/chat/chat-content.js +++ b/src/shared/chat/chat-content.js @@ -6,6 +6,8 @@ import { html } from 'lit'; import { onScrolledDown } from './utils.js'; import { safeSave } from '@converse/headless/utils/core.js'; +import './styles/chat-content.scss'; + export default class ChatContent extends CustomElement { @@ -17,12 +19,11 @@ export default class ChatContent extends CustomElement { connectedCallback () { super.connectedCallback(); - this.debouncedMaintainScroll = debounce(this.maintainScrollPosition, 100); this.markScrolled = debounce(this._markScrolled, 50); this.model = _converse.chatboxes.get(this.jid); this.listenTo(this.model, 'change:hidden_occupants', this.requestUpdate); - this.listenTo(this.model, 'change:scrolled', this.requestUpdate); + this.listenTo(this.model, 'change:scrolled', this.scrollDown); this.listenTo(this.model.messages, 'add', this.requestUpdate); this.listenTo(this.model.messages, 'change', this.requestUpdate); this.listenTo(this.model.messages, 'remove', this.requestUpdate); @@ -34,55 +35,22 @@ export default class ChatContent extends CustomElement { if (this.model.occupants) { this.listenTo(this.model.occupants, 'change', this.requestUpdate); } - // We jot down whether we were scrolled down before rendering, because when an - // image loads, it triggers 'scroll' and the chat will be marked as scrolled, - // which is technically true, but not what we want because the user - // didn't initiate the scrolling. - this.was_scrolled_up = this.model.get('scrolled'); - this.addEventListener('imageLoaded', () => { - this.debouncedMaintainScroll(); - }); this.addEventListener('scroll', () => this.markScrolled()); - this.initIntersectionObserver(); } render () { + // This element has "flex-direction: reverse", so elements here are + // shown in reverse order. return html` - ${ this.model.ui?.get('chat-content-spinner-top') ? html`` : '' } +
${this.model.getNotificationsText()}
-
${this.model.getNotificationsText()}
+ ${ this.model.ui?.get('chat-content-spinner-top') ? html`` : '' } `; } - updated () { - const scrolled = this.model.get('scrolled'); - if (this.was_scrolled_up === scrolled) { - this.debouncedMaintainScroll(); - } else { - this.was_scrolled_up = scrolled; - if (!this.scrolled) { - this.scrollDown(); - } - } - } - - initIntersectionObserver () { - if (this.observer) { - this.observer.disconnect(); - } else { - const options = { - root: this, - threshold: [0.1] - } - const handler = ev => this.setAnchoredMessage(ev); - this.observer = new IntersectionObserver(handler, options); - } - } - /** * Called when the chat content is scrolled up or down. * We want to record when the user has scrolled away from @@ -95,11 +63,14 @@ export default class ChatContent extends CustomElement { */ _markScrolled () { let scrolled = true; - const is_at_bottom = this.scrollTop + this.clientHeight >= this.scrollHeight; + const is_at_bottom = this.scrollTop === 0; + const is_at_top = + Math.ceil(this.clientHeight-this.scrollTop) >= (this.scrollHeight-Math.ceil(this.scrollHeight/20)); + if (is_at_bottom) { scrolled = false; onScrolledDown(this.model); - } else if (this.scrollTop === 0) { + } else if (is_at_top) { /** * Triggered once the chat's message area has been scrolled to the top * @event _converse#chatBoxScrolledUp @@ -113,31 +84,15 @@ export default class ChatContent extends CustomElement { } } - setAnchoredMessage (entries) { - if (!this.model?.ui || this.model.ui.get('chat-content-spinner-top')) { + scrollDown () { + if (this.model.get('scrolled')) { return; } - entries = entries.filter(e => e.isIntersecting); - const current = entries.reduce((p, c) => c.boundingClientRect.y >= (p?.boundingClientRect.y || 0) ? c : p, null); - if (current) { - this.anchored_message = current.target; - } - } - - maintainScrollPosition () { - if (this.was_scrolled_up) { - this.anchored_message?.scrollIntoView(true); - } else { - this.scrollDown(); - } - } - - scrollDown () { if (this.scrollTo) { const behavior = this.scrollTop ? 'smooth' : 'auto'; - this.scrollTo({ 'top': this.scrollHeight, behavior }); + this.scrollTo({ 'top': 0, behavior }); } else { - this.scrollTop = this.scrollHeight; + this.scrollTop = 0; } /** * Triggered once the converse-chat-content element has been scrolled down to the bottom. diff --git a/src/shared/chat/message-history.js b/src/shared/chat/message-history.js index 089a3ea06..fcded81be 100644 --- a/src/shared/chat/message-history.js +++ b/src/shared/chat/message-history.js @@ -51,7 +51,6 @@ export default class MessageHistory extends CustomElement { static get properties () { return { model: { type: Object }, - observer: { type: Object }, messages: { type: Array } } } @@ -68,7 +67,6 @@ export default class MessageHistory extends CustomElement { const day = getDayIndicator(model); const templates = day ? [day] : []; const message = html`` diff --git a/src/shared/chat/message.js b/src/shared/chat/message.js index e68772118..7d4c05a42 100644 --- a/src/shared/chat/message.js +++ b/src/shared/chat/message.js @@ -24,8 +24,7 @@ export default class Message extends CustomElement { static get properties () { return { jid: { type: String }, - mid: { type: String }, - observer: { type: Object } + mid: { type: String } } } @@ -60,10 +59,6 @@ export default class Message extends CustomElement { } } - firstUpdated () { - this.observer.observe(this); - } - getProps () { return Object.assign( this.model.toJSON(), diff --git a/src/shared/chat/styles/chat-content.scss b/src/shared/chat/styles/chat-content.scss new file mode 100644 index 000000000..44b3cb35a --- /dev/null +++ b/src/shared/chat/styles/chat-content.scss @@ -0,0 +1,8 @@ + +converse-chat-content { + display: flex; + flex-direction: column-reverse; + height: 100%; + justify-content: space-between; + overflow: auto; +}