Maintain scroll position when re-inserting #conversejs element
This commit is contained in:
parent
867f80e95e
commit
4927d561a5
@ -176,6 +176,10 @@ converse.plugins.add('converse-chatboxviews', {
|
|||||||
const el = _converse.chatboxviews?.el;
|
const el = _converse.chatboxviews?.el;
|
||||||
if (el && !container.contains(el)) {
|
if (el && !container.contains(el)) {
|
||||||
container.insertAdjacentElement('afterBegin', el);
|
container.insertAdjacentElement('afterBegin', el);
|
||||||
|
api.chatviews.get()
|
||||||
|
.filter(v => v.model.get('id') !== 'controlbox')
|
||||||
|
.forEach(v => v.maintainScrollTop());
|
||||||
|
|
||||||
} else if (!el) {
|
} else if (!el) {
|
||||||
throw new Error("Cannot insert non-existing #conversejs element into the DOM");
|
throw new Error("Cannot insert non-existing #conversejs element into the DOM");
|
||||||
}
|
}
|
||||||
|
@ -208,7 +208,7 @@ converse.plugins.add('converse-chatview', {
|
|||||||
this.listenTo(this.model.messages, 'add', this.onMessageAdded);
|
this.listenTo(this.model.messages, 'add', this.onMessageAdded);
|
||||||
this.listenTo(this.model.messages, 'change', this.renderChatHistory);
|
this.listenTo(this.model.messages, 'change', this.renderChatHistory);
|
||||||
this.listenTo(this.model.messages, 'remove', this.renderChatHistory);
|
this.listenTo(this.model.messages, 'remove', this.renderChatHistory);
|
||||||
this.listenTo(this.model.messages, 'rendered', this.maybeScrollDownOnMessage);
|
this.listenTo(this.model.messages, 'rendered', this.maybeScrollDown);
|
||||||
this.listenTo(this.model.messages, 'reset', this.renderChatHistory);
|
this.listenTo(this.model.messages, 'reset', this.renderChatHistory);
|
||||||
this.listenTo(this.model.notifications, 'change', this.renderNotifications);
|
this.listenTo(this.model.notifications, 'change', this.renderNotifications);
|
||||||
this.listenTo(this.model, 'change:show_help_messages', this.renderHelpMessages);
|
this.listenTo(this.model, 'change:show_help_messages', this.renderHelpMessages);
|
||||||
@ -241,7 +241,7 @@ converse.plugins.add('converse-chatview', {
|
|||||||
|
|
||||||
render () {
|
render () {
|
||||||
const result = tpl_chatbox(
|
const result = tpl_chatbox(
|
||||||
Object.assign(this.model.toJSON(), {'markScrolled': () => this.markScrolled()})
|
Object.assign(this.model.toJSON(), {'markScrolled': ev => this.markScrolled(ev)})
|
||||||
);
|
);
|
||||||
render(result, this.el);
|
render(result, this.el);
|
||||||
this.content = this.el.querySelector('.chat-content');
|
this.content = this.el.querySelector('.chat-content');
|
||||||
@ -486,19 +486,35 @@ converse.plugins.add('converse-chatview', {
|
|||||||
api.trigger('afterMessagesFetched', this.model);
|
api.trigger('afterMessagesFetched', this.model);
|
||||||
},
|
},
|
||||||
|
|
||||||
maybeScrollDownOnMessage (message) {
|
/**
|
||||||
if (message.get('sender') === 'me' || !this.model.get('scrolled')) {
|
* Scrolls the chat down, *if* appropriate.
|
||||||
|
*
|
||||||
|
* Will only scroll down if we have received a message from
|
||||||
|
* ourselves, or if the chat was scrolled down before (i.e. the
|
||||||
|
* `scrolled` flag is `false`);
|
||||||
|
* @param { _converse.Message|_converse.ChatRoomMessage } [message]
|
||||||
|
* - An optional message that serves as the cause for needing to scroll down.
|
||||||
|
*/
|
||||||
|
maybeScrollDown (message) {
|
||||||
|
if (message?.get('sender') === 'me' || !this.model.get('scrolled')) {
|
||||||
this.debouncedScrollDown();
|
this.debouncedScrollDown();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scrolls the chat down.
|
||||||
|
*
|
||||||
|
* This method will always scroll the chat down, regardless of
|
||||||
|
* whether the user scrolled up manually or not.
|
||||||
|
* @param { Event } [ev] - An optional event that is the cause for needing to scroll down.
|
||||||
|
*/
|
||||||
scrollDown (ev) {
|
scrollDown (ev) {
|
||||||
ev?.preventDefault?.();
|
ev?.preventDefault?.();
|
||||||
ev?.stopPropagation?.();
|
ev?.stopPropagation?.();
|
||||||
if (this.model.get('scrolled')) {
|
if (this.model.get('scrolled')) {
|
||||||
u.safeSave(this.model, {
|
u.safeSave(this.model, {
|
||||||
'scrolled': false,
|
'scrolled': false,
|
||||||
'top_visible_message': null,
|
'scrollTop': null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (this.msgs_container.scrollTo) {
|
if (this.msgs_container.scrollTo) {
|
||||||
@ -510,6 +526,19 @@ converse.plugins.add('converse-chatview', {
|
|||||||
this.onScrolledDown();
|
this.onScrolledDown();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll to the previously saved scrollTop position, or scroll
|
||||||
|
* down if it wasn't set.
|
||||||
|
*/
|
||||||
|
maintainScrollTop () {
|
||||||
|
const pos = this.model.get('scrollTop');
|
||||||
|
if (pos) {
|
||||||
|
this.msgs_container.scrollTop = pos;
|
||||||
|
} else {
|
||||||
|
this.scrollDown();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
insertIntoDOM () {
|
insertIntoDOM () {
|
||||||
_converse.chatboxviews.insertRowColumn(this.el);
|
_converse.chatboxviews.insertRowColumn(this.el);
|
||||||
/**
|
/**
|
||||||
@ -537,28 +566,6 @@ converse.plugins.add('converse-chatview', {
|
|||||||
this.content.querySelectorAll('.spinner').forEach(u.removeElement);
|
this.content.querySelectorAll('.spinner').forEach(u.removeElement);
|
||||||
},
|
},
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onStatusMessageChanged (item) {
|
onStatusMessageChanged (item) {
|
||||||
this.renderHeading();
|
this.renderHeading();
|
||||||
/**
|
/**
|
||||||
@ -1091,8 +1098,9 @@ converse.plugins.add('converse-chatview', {
|
|||||||
* which debounces this method by 100ms.
|
* which debounces this method by 100ms.
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_markScrolled: function () {
|
_markScrolled: function (ev) {
|
||||||
let scrolled = true;
|
let scrolled = true;
|
||||||
|
let scrollTop = null;
|
||||||
const is_at_bottom =
|
const is_at_bottom =
|
||||||
(this.msgs_container.scrollTop + this.msgs_container.clientHeight) >=
|
(this.msgs_container.scrollTop + this.msgs_container.clientHeight) >=
|
||||||
this.msgs_container.scrollHeight - 62; // sigh...
|
this.msgs_container.scrollHeight - 62; // sigh...
|
||||||
@ -1108,15 +1116,14 @@ converse.plugins.add('converse-chatview', {
|
|||||||
* @example _converse.api.listen.on('chatBoxScrolledUp', obj => { ... });
|
* @example _converse.api.listen.on('chatBoxScrolledUp', obj => { ... });
|
||||||
*/
|
*/
|
||||||
api.trigger('chatBoxScrolledUp', this);
|
api.trigger('chatBoxScrolledUp', this);
|
||||||
|
} else {
|
||||||
|
scrollTop = ev.target.scrollTop;
|
||||||
}
|
}
|
||||||
u.safeSave(this.model, {
|
u.safeSave(this.model, { scrolled, scrollTop });
|
||||||
'scrolled': scrolled,
|
|
||||||
'top_visible_message': null
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
viewUnreadMessages () {
|
viewUnreadMessages () {
|
||||||
this.model.save({'scrolled': false, 'top_visible_message': null});
|
this.model.save({'scrolled': false, 'scrollTop': null});
|
||||||
this.scrollDown();
|
this.scrollDown();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -190,7 +190,7 @@ converse.plugins.add('converse-muc-views', {
|
|||||||
this.listenTo(this.model, 'show', this.show);
|
this.listenTo(this.model, 'show', this.show);
|
||||||
this.listenTo(this.model.features, 'change:moderated', this.renderBottomPanel);
|
this.listenTo(this.model.features, 'change:moderated', this.renderBottomPanel);
|
||||||
this.listenTo(this.model.features, 'change:open', this.renderHeading);
|
this.listenTo(this.model.features, 'change:open', this.renderHeading);
|
||||||
this.listenTo(this.model.messages, 'rendered', this.maybeScrollDownOnMessage);
|
this.listenTo(this.model.messages, 'rendered', this.maybeScrollDown);
|
||||||
this.listenTo(this.model.session, 'change:connection_status', this.onConnectionStatusChanged);
|
this.listenTo(this.model.session, 'change:connection_status', this.onConnectionStatusChanged);
|
||||||
|
|
||||||
// Bind so that we can pass it to addEventListener and removeEventListener
|
// Bind so that we can pass it to addEventListener and removeEventListener
|
||||||
|
@ -168,12 +168,12 @@ class MessageBodyRenderer {
|
|||||||
// image loads, it triggers 'scroll' and the chat will be marked as scrolled,
|
// 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
|
// which is technically true, but not what we want because the user
|
||||||
// didn't initiate the scrolling.
|
// didn't initiate the scrolling.
|
||||||
this.scrolled = this.chatview.model.get('scrolled');
|
this.was_scrolled_up = this.chatview.model.get('scrolled');
|
||||||
this.text = this.component.model.getMessageText();
|
this.text = this.component.model.getMessageText();
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollDownOnImageLoad () {
|
scrollDownOnImageLoad () {
|
||||||
if (!this.scrolled) {
|
if (!this.was_scrolled_up) {
|
||||||
this.chatview.scrollDown();
|
this.chatview.scrollDown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user