2021-06-03 12:10:30 +02:00
|
|
|
import './message-history';
|
|
|
|
import debounce from 'lodash/debounce';
|
2021-03-24 11:59:09 +01:00
|
|
|
import { CustomElement } from 'shared/components/element.js';
|
2021-06-03 16:29:31 +02:00
|
|
|
import { _converse, api } from '@converse/headless/core';
|
2021-04-14 22:56:59 +02:00
|
|
|
import { html } from 'lit';
|
2021-06-03 16:29:31 +02:00
|
|
|
import { onScrolledDown } from './utils.js';
|
|
|
|
import { safeSave } from '@converse/headless/utils/core.js';
|
2020-05-15 14:33:31 +02:00
|
|
|
|
2021-06-03 15:32:39 +02:00
|
|
|
|
2020-07-01 21:45:18 +02:00
|
|
|
export default class ChatContent extends CustomElement {
|
2020-05-15 14:33:31 +02:00
|
|
|
|
|
|
|
static get properties () {
|
|
|
|
return {
|
2021-02-09 11:14:06 +01:00
|
|
|
jid: { type: String }
|
2021-02-08 11:27:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
connectedCallback () {
|
|
|
|
super.connectedCallback();
|
2021-06-03 12:10:30 +02:00
|
|
|
this.debouncedMaintainScroll = debounce(this.maintainScrollPosition, 100);
|
2021-06-03 16:29:31 +02:00
|
|
|
this.markScrolled = debounce(this._markScrolled, 100);
|
2021-06-03 12:10:30 +02:00
|
|
|
|
2021-02-09 11:14:06 +01:00
|
|
|
this.model = _converse.chatboxes.get(this.jid);
|
2021-06-03 12:10:30 +02:00
|
|
|
this.listenTo(this.model, 'change:hidden_occupants', this.requestUpdate);
|
|
|
|
this.listenTo(this.model, 'change:scrolled', this.requestUpdate);
|
2021-02-09 11:14:06 +01:00
|
|
|
this.listenTo(this.model.messages, 'add', this.requestUpdate);
|
|
|
|
this.listenTo(this.model.messages, 'change', this.requestUpdate);
|
|
|
|
this.listenTo(this.model.messages, 'remove', this.requestUpdate);
|
2021-06-03 12:10:30 +02:00
|
|
|
this.listenTo(this.model.messages, 'rendered', this.requestUpdate);
|
2021-02-09 11:14:06 +01:00
|
|
|
this.listenTo(this.model.messages, 'reset', this.requestUpdate);
|
|
|
|
this.listenTo(this.model.notifications, 'change', this.requestUpdate);
|
2021-06-02 23:52:16 +02:00
|
|
|
this.listenTo(this.model.ui, 'change', this.requestUpdate);
|
2021-06-03 12:10:30 +02:00
|
|
|
|
2021-02-09 11:14:06 +01:00
|
|
|
if (this.model.occupants) {
|
|
|
|
this.listenTo(this.model.occupants, 'change', this.requestUpdate);
|
2020-05-15 14:33:31 +02:00
|
|
|
}
|
2021-02-09 11:14:06 +01:00
|
|
|
// 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', () => {
|
2021-06-03 12:10:30 +02:00
|
|
|
this.debouncedMaintainScroll(this.was_scrolled_up);
|
2021-02-09 11:14:06 +01:00
|
|
|
});
|
2021-06-03 16:29:31 +02:00
|
|
|
this.addEventListener('scroll', () => this.markScrolled());
|
|
|
|
this.initIntersectionObserver();
|
2020-05-15 14:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
render () {
|
|
|
|
return html`
|
2021-06-02 23:52:16 +02:00
|
|
|
${ this.model.ui.get('chat-content-spinner-top') ? html`<span class="spinner fa fa-spinner centered"></span>` : '' }
|
2020-05-15 14:33:31 +02:00
|
|
|
<converse-message-history
|
2021-02-09 11:14:06 +01:00
|
|
|
.model=${this.model}
|
2021-06-03 16:29:31 +02:00
|
|
|
.observer=${this.observer}
|
2021-02-09 11:14:06 +01:00
|
|
|
.messages=${[...this.model.messages.models]}>
|
2020-05-15 14:33:31 +02:00
|
|
|
</converse-message-history>
|
2021-02-09 11:14:06 +01:00
|
|
|
<div class="chat-content__notifications">${this.model.getNotificationsText()}</div>
|
2020-05-15 14:33:31 +02:00
|
|
|
`;
|
|
|
|
}
|
2021-02-09 11:14:06 +01:00
|
|
|
|
2021-02-09 18:20:33 +01:00
|
|
|
updated () {
|
2021-06-03 12:10:30 +02:00
|
|
|
this.was_scrolled_up = this.model.get('scrolled');
|
|
|
|
this.debouncedMaintainScroll();
|
|
|
|
}
|
|
|
|
|
2021-06-03 16:29:31 +02:00
|
|
|
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
|
|
|
|
* the bottom, so that we don't automatically scroll away
|
|
|
|
* from what the user is reading when new messages are received.
|
|
|
|
*
|
|
|
|
* Don't call this method directly, instead, call `markScrolled`,
|
|
|
|
* which debounces this method by 100ms.
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
_markScrolled () {
|
|
|
|
let scrolled = true;
|
|
|
|
const is_at_bottom = this.scrollTop + this.clientHeight >= this.scrollHeight;
|
|
|
|
if (is_at_bottom) {
|
|
|
|
scrolled = false;
|
|
|
|
onScrolledDown(this.model);
|
|
|
|
} else if (this.scrollTop === 0) {
|
|
|
|
/**
|
|
|
|
* Triggered once the chat's message area has been scrolled to the top
|
|
|
|
* @event _converse#chatBoxScrolledUp
|
|
|
|
* @property { _converse.ChatBoxView | _converse.ChatRoomView } view
|
|
|
|
* @example _converse.api.listen.on('chatBoxScrolledUp', obj => { ... });
|
|
|
|
*/
|
|
|
|
api.trigger('chatBoxScrolledUp', this);
|
|
|
|
}
|
|
|
|
safeSave(this.model, { scrolled });
|
|
|
|
}
|
|
|
|
|
|
|
|
setAnchoredMessage (entries) {
|
|
|
|
if (this.model.ui.get('chat-content-spinner-top')) {
|
|
|
|
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;
|
2021-06-03 15:32:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-03 12:10:30 +02:00
|
|
|
maintainScrollPosition () {
|
|
|
|
if (this.was_scrolled_up) {
|
2021-06-03 16:29:31 +02:00
|
|
|
console.warn('scrolling into view');
|
|
|
|
this.anchored_message?.scrollIntoView(true);
|
2021-06-03 12:10:30 +02:00
|
|
|
} else {
|
|
|
|
this.scrollDown();
|
|
|
|
}
|
2021-02-09 18:20:33 +01:00
|
|
|
}
|
|
|
|
|
2021-02-09 11:14:06 +01:00
|
|
|
scrollDown () {
|
|
|
|
if (this.scrollTo) {
|
|
|
|
const behavior = this.scrollTop ? 'smooth' : 'auto';
|
|
|
|
this.scrollTo({ 'top': this.scrollHeight, behavior });
|
|
|
|
} else {
|
|
|
|
this.scrollTop = this.scrollHeight;
|
|
|
|
}
|
2021-06-03 12:10:30 +02:00
|
|
|
/**
|
|
|
|
* Triggered once the converse-chat-content element has been scrolled down to the bottom.
|
|
|
|
* @event _converse#chatBoxScrolledDown
|
|
|
|
* @type {object}
|
|
|
|
* @property { _converse.ChatBox | _converse.ChatRoom } chatbox - The chat model
|
|
|
|
* @example _converse.api.listen.on('chatBoxScrolledDown', obj => { ... });
|
|
|
|
*/
|
|
|
|
api.trigger('chatBoxScrolledDown', { 'chatbox': this.model });
|
2021-02-09 11:14:06 +01:00
|
|
|
}
|
2020-05-15 14:33:31 +02:00
|
|
|
}
|
|
|
|
|
2020-07-01 21:45:18 +02:00
|
|
|
api.elements.define('converse-chat-content', ChatContent);
|