Use intersection observer to remember scrolling position
This commit is contained in:
parent
279a3c3413
commit
58d96c8594
@ -362,13 +362,16 @@ u.onMultipleEvents = function (events=[], callback) {
|
|||||||
events.forEach(e => e.object.on(e.event, handler));
|
events.forEach(e => e.object.on(e.event, handler));
|
||||||
};
|
};
|
||||||
|
|
||||||
u.safeSave = function (model, attributes, options) {
|
|
||||||
|
export function safeSave (model, attributes, options) {
|
||||||
if (u.isPersistableModel(model)) {
|
if (u.isPersistableModel(model)) {
|
||||||
model.save(attributes, options);
|
model.save(attributes, options);
|
||||||
} else {
|
} else {
|
||||||
model.set(attributes, options);
|
model.set(attributes, options);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
u.safeSave = safeSave;
|
||||||
|
|
||||||
u.siblingIndex = function (el) {
|
u.siblingIndex = function (el) {
|
||||||
/* eslint-disable no-cond-assign */
|
/* eslint-disable no-cond-assign */
|
||||||
|
@ -84,7 +84,7 @@ export default class ChatBottomPanel extends ElementView {
|
|||||||
|
|
||||||
viewUnreadMessages (ev) {
|
viewUnreadMessages (ev) {
|
||||||
ev?.preventDefault?.();
|
ev?.preventDefault?.();
|
||||||
this.model.save({ 'scrolled': false, 'scrollTop': null });
|
this.model.save({ 'scrolled': false });
|
||||||
}
|
}
|
||||||
|
|
||||||
onMessageCorrecting (message) {
|
onMessageCorrecting (message) {
|
||||||
|
@ -8,8 +8,7 @@ export default (o) => html`
|
|||||||
<div class="chat-content ${ o.show_send_button ? 'chat-content-sendbutton' : '' }" aria-live="polite">
|
<div class="chat-content ${ o.show_send_button ? 'chat-content-sendbutton' : '' }" aria-live="polite">
|
||||||
<converse-chat-content
|
<converse-chat-content
|
||||||
class="chat-content__messages"
|
class="chat-content__messages"
|
||||||
jid="${o.jid}"
|
jid="${o.jid}"></converse-chat-content>
|
||||||
@scroll=${o.markScrolled}></converse-chat-content>
|
|
||||||
|
|
||||||
<div class="chat-content__help"></div>
|
<div class="chat-content__help"></div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -22,10 +22,7 @@ export default class ChatView extends BaseChatView {
|
|||||||
async initialize () {
|
async initialize () {
|
||||||
const jid = this.getAttribute('jid');
|
const jid = this.getAttribute('jid');
|
||||||
_converse.chatboxviews.add(jid, this);
|
_converse.chatboxviews.add(jid, this);
|
||||||
|
|
||||||
this.model = _converse.chatboxes.get(jid);
|
this.model = _converse.chatboxes.get(jid);
|
||||||
this.initDebounced();
|
|
||||||
|
|
||||||
this.listenTo(_converse, 'windowStateChanged', this.onWindowStateChanged);
|
this.listenTo(_converse, 'windowStateChanged', this.onWindowStateChanged);
|
||||||
this.listenTo(this.model, 'change:hidden', () => !this.model.get('hidden') && this.afterShown());
|
this.listenTo(this.model, 'change:hidden', () => !this.model.get('hidden') && this.afterShown());
|
||||||
this.listenTo(this.model, 'change:status', this.onStatusMessageChanged);
|
this.listenTo(this.model, 'change:status', this.onStatusMessageChanged);
|
||||||
@ -46,9 +43,7 @@ export default class ChatView extends BaseChatView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const result = tpl_chat(Object.assign(
|
const result = tpl_chat(this.model.toJSON());
|
||||||
this.model.toJSON(), { 'markScrolled': ev => this.markScrolled(ev) })
|
|
||||||
);
|
|
||||||
render(result, this);
|
render(result, this);
|
||||||
this.help_container = this.querySelector('.chat-content__help');
|
this.help_container = this.querySelector('.chat-content__help');
|
||||||
return this;
|
return this;
|
||||||
|
@ -9,8 +9,7 @@ export default (o) => html`
|
|||||||
<div class="chat-content ${ o.show_send_button ? 'chat-content-sendbutton' : '' }" aria-live="polite">
|
<div class="chat-content ${ o.show_send_button ? 'chat-content-sendbutton' : '' }" aria-live="polite">
|
||||||
<converse-chat-content
|
<converse-chat-content
|
||||||
class="chat-content__messages"
|
class="chat-content__messages"
|
||||||
jid="${o.jid}"
|
jid="${o.jid}"></converse-chat-content>
|
||||||
@scroll=${o.markScrolled}></converse-chat-content>
|
|
||||||
|
|
||||||
<div class="chat-content__help"></div>
|
<div class="chat-content__help"></div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -11,8 +11,6 @@ class HeadlinesView extends BaseChatView {
|
|||||||
_converse.chatboxviews.add(jid, this);
|
_converse.chatboxviews.add(jid, this);
|
||||||
|
|
||||||
this.model = _converse.chatboxes.get(jid);
|
this.model = _converse.chatboxes.get(jid);
|
||||||
this.initDebounced();
|
|
||||||
|
|
||||||
this.model.disable_mam = true; // Don't do MAM queries for this box
|
this.model.disable_mam = true; // Don't do MAM queries for this box
|
||||||
this.listenTo(this.model, 'change:hidden', () => this.afterShown());
|
this.listenTo(this.model, 'change:hidden', () => this.afterShown());
|
||||||
this.listenTo(this.model, 'destroy', this.remove);
|
this.listenTo(this.model, 'destroy', this.remove);
|
||||||
|
@ -14,10 +14,11 @@ export async function fetchMessagesOnScrollUp (view) {
|
|||||||
} else {
|
} else {
|
||||||
await fetchArchivedMessages(view.model, { 'end': oldest_message.get('time') });
|
await fetchArchivedMessages(view.model, { 'end': oldest_message.get('time') });
|
||||||
}
|
}
|
||||||
view.model.ui.set('chat-content-spinner-top', false);
|
|
||||||
if (api.settings.get('allow_url_history_change')) {
|
if (api.settings.get('allow_url_history_change')) {
|
||||||
_converse.router.history.navigate(`#${oldest_message.get('msgid')}`);
|
_converse.router.history.navigate(`#${oldest_message.get('msgid')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTimeout(() => view.model.ui.set('chat-content-spinner-top', false), 250);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,9 +151,6 @@ export function minimize (ev, model) {
|
|||||||
} else {
|
} else {
|
||||||
model = ev;
|
model = ev;
|
||||||
}
|
}
|
||||||
// save the scroll position to restore it on maximize
|
|
||||||
const view = _converse.chatboxviews.get(model.get('jid'));
|
|
||||||
view.querySelector('.chat-content__messages')?.saveScrollPosition();
|
|
||||||
model.setChatState(_converse.INACTIVE);
|
model.setChatState(_converse.INACTIVE);
|
||||||
u.safeSave(model, {
|
u.safeSave(model, {
|
||||||
'hidden': true,
|
'hidden': true,
|
||||||
|
@ -3,6 +3,8 @@ import tpl_muc_chatarea from './templates/muc-chatarea.js';
|
|||||||
import { CustomElement } from 'shared/components/element.js';
|
import { CustomElement } from 'shared/components/element.js';
|
||||||
import { __ } from 'i18n';
|
import { __ } from 'i18n';
|
||||||
import { _converse, api, converse } from '@converse/headless/core';
|
import { _converse, api, converse } from '@converse/headless/core';
|
||||||
|
import { onScrolledDown } from 'shared/chat/utils.js';
|
||||||
|
import { safeSave } from '@converse/headless/utils/core.js';
|
||||||
|
|
||||||
|
|
||||||
const { u } = converse.env;
|
const { u } = converse.env;
|
||||||
@ -93,17 +95,13 @@ export default class MUCChatArea extends CustomElement {
|
|||||||
* which debounces this method by 100ms.
|
* which debounces this method by 100ms.
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_markScrolled (ev) {
|
_markScrolled () {
|
||||||
let scrolled = true;
|
let scrolled = true;
|
||||||
let scrollTop = null;
|
const is_at_bottom = this.scrollTop + this.clientHeight >= this.scrollHeight;
|
||||||
const msgs_container = this.querySelector('.chat-content__messages');
|
|
||||||
const is_at_bottom =
|
|
||||||
msgs_container.scrollTop + msgs_container.clientHeight >= msgs_container.scrollHeight - 62; // sigh...
|
|
||||||
|
|
||||||
if (is_at_bottom) {
|
if (is_at_bottom) {
|
||||||
scrolled = false;
|
scrolled = false;
|
||||||
this.onScrolledDown();
|
onScrolledDown(this.model);
|
||||||
} else if (msgs_container.scrollTop === 0) {
|
} else if (this.scrollTop === 0) {
|
||||||
/**
|
/**
|
||||||
* Triggered once the chat's message area has been scrolled to the top
|
* Triggered once the chat's message area has been scrolled to the top
|
||||||
* @event _converse#chatBoxScrolledUp
|
* @event _converse#chatBoxScrolledUp
|
||||||
@ -111,29 +109,8 @@ export default class MUCChatArea extends CustomElement {
|
|||||||
* @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, { scrolled, scrollTop });
|
safeSave(this.model, { scrolled });
|
||||||
}
|
|
||||||
|
|
||||||
onScrolledDown () {
|
|
||||||
if (!this.model.isHidden()) {
|
|
||||||
this.model.clearUnreadMsgCounter();
|
|
||||||
if (api.settings.get('allow_url_history_change')) {
|
|
||||||
// Clear location hash if set to one of the messages in our history
|
|
||||||
const hash = window.location.hash;
|
|
||||||
hash && this.model.messages.get(hash.slice(1)) && _converse.router.history.navigate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Triggered once the chat's message area 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 });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMousedown (ev) {
|
onMousedown (ev) {
|
||||||
|
@ -14,8 +14,6 @@ export default class MUCView extends BaseChatView {
|
|||||||
const jid = this.getAttribute('jid');
|
const jid = this.getAttribute('jid');
|
||||||
this.model = await api.rooms.get(jid);
|
this.model = await api.rooms.get(jid);
|
||||||
_converse.chatboxviews.add(jid, this);
|
_converse.chatboxviews.add(jid, this);
|
||||||
this.initDebounced();
|
|
||||||
|
|
||||||
this.setAttribute('id', this.model.get('box_id'));
|
this.setAttribute('id', this.model.get('box_id'));
|
||||||
|
|
||||||
this.listenTo(_converse, 'windowStateChanged', this.onWindowStateChanged);
|
this.listenTo(_converse, 'windowStateChanged', this.onWindowStateChanged);
|
||||||
|
@ -10,8 +10,7 @@ export default (o) => html`
|
|||||||
<div class="chat-content ${ o.show_send_button ? 'chat-content-sendbutton' : '' }" aria-live="polite">
|
<div class="chat-content ${ o.show_send_button ? 'chat-content-sendbutton' : '' }" aria-live="polite">
|
||||||
<converse-chat-content
|
<converse-chat-content
|
||||||
class="chat-content__messages"
|
class="chat-content__messages"
|
||||||
jid="${o.jid}"
|
jid="${o.jid}"></converse-chat-content>
|
||||||
@scroll=${o.markScrolled}></converse-chat-content>
|
|
||||||
|
|
||||||
${o.show_help_messages ? html`<div class="chat-content__help">
|
${o.show_help_messages ? html`<div class="chat-content__help">
|
||||||
<converse-chat-help
|
<converse-chat-help
|
||||||
|
@ -1,16 +1,12 @@
|
|||||||
import debounce from 'lodash-es/debounce';
|
|
||||||
import log from '@converse/headless/log';
|
import log from '@converse/headless/log';
|
||||||
import { ElementView } from '@converse/skeletor/src/element.js';
|
import { ElementView } from '@converse/skeletor/src/element.js';
|
||||||
import { _converse, api, converse } from '@converse/headless/core';
|
import { _converse, api, converse } from '@converse/headless/core';
|
||||||
|
import { onScrolledDown } from './utils.js';
|
||||||
|
|
||||||
const u = converse.env.utils;
|
const u = converse.env.utils;
|
||||||
|
|
||||||
export default class BaseChatView extends ElementView {
|
export default class BaseChatView extends ElementView {
|
||||||
|
|
||||||
initDebounced () {
|
|
||||||
this.markScrolled = debounce(this._markScrolled, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnectedCallback () {
|
disconnectedCallback () {
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
const jid = this.getAttribute('jid');
|
const jid = this.getAttribute('jid');
|
||||||
@ -93,38 +89,6 @@ export default class BaseChatView extends ElementView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 (ev) {
|
|
||||||
let scrolled = true;
|
|
||||||
let scrollTop = null;
|
|
||||||
const is_at_bottom = ev.target.scrollTop + ev.target.clientHeight >= ev.target.scrollHeight - 62; // sigh...
|
|
||||||
if (is_at_bottom) {
|
|
||||||
scrolled = false;
|
|
||||||
this.onScrolledDown();
|
|
||||||
} else if (ev.target.scrollTop === 0) {
|
|
||||||
scrollTop = ev.target.scrollTop;
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
} else {
|
|
||||||
scrollTop = ev.target.scrollTop;
|
|
||||||
}
|
|
||||||
u.safeSave(this.model, { scrolled, scrollTop });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scrolls the chat down.
|
* Scrolls the chat down.
|
||||||
*
|
*
|
||||||
@ -136,23 +100,9 @@ export default class BaseChatView extends ElementView {
|
|||||||
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,
|
|
||||||
'scrollTop': null
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.onScrolledDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
onScrolledDown () {
|
|
||||||
if (!this.model.isHidden()) {
|
|
||||||
this.model.clearUnreadMsgCounter();
|
|
||||||
if (api.settings.get('allow_url_history_change')) {
|
|
||||||
// Clear location hash if set to one of the messages in our history
|
|
||||||
const hash = window.location.hash;
|
|
||||||
hash && this.model.messages.get(hash.slice(1)) && _converse.router.history.navigate();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
onScrolledDown(this.model);
|
||||||
}
|
}
|
||||||
|
|
||||||
onWindowStateChanged (data) {
|
onWindowStateChanged (data) {
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import './message-history';
|
import './message-history';
|
||||||
import debounce from 'lodash/debounce';
|
import debounce from 'lodash/debounce';
|
||||||
import { CustomElement } from 'shared/components/element.js';
|
import { CustomElement } from 'shared/components/element.js';
|
||||||
import { _converse, api, converse } from '@converse/headless/core';
|
import { _converse, api } from '@converse/headless/core';
|
||||||
import { html } from 'lit';
|
import { html } from 'lit';
|
||||||
|
import { onScrolledDown } from './utils.js';
|
||||||
|
import { safeSave } from '@converse/headless/utils/core.js';
|
||||||
|
|
||||||
const { u } = converse;
|
|
||||||
|
|
||||||
export default class ChatContent extends CustomElement {
|
export default class ChatContent extends CustomElement {
|
||||||
|
|
||||||
@ -17,6 +18,7 @@ export default class ChatContent extends CustomElement {
|
|||||||
connectedCallback () {
|
connectedCallback () {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
this.debouncedMaintainScroll = debounce(this.maintainScrollPosition, 100);
|
this.debouncedMaintainScroll = debounce(this.maintainScrollPosition, 100);
|
||||||
|
this.markScrolled = debounce(this._markScrolled, 100);
|
||||||
|
|
||||||
this.model = _converse.chatboxes.get(this.jid);
|
this.model = _converse.chatboxes.get(this.jid);
|
||||||
this.listenTo(this.model, 'change:hidden_occupants', this.requestUpdate);
|
this.listenTo(this.model, 'change:hidden_occupants', this.requestUpdate);
|
||||||
@ -40,6 +42,8 @@ export default class ChatContent extends CustomElement {
|
|||||||
this.addEventListener('imageLoaded', () => {
|
this.addEventListener('imageLoaded', () => {
|
||||||
this.debouncedMaintainScroll(this.was_scrolled_up);
|
this.debouncedMaintainScroll(this.was_scrolled_up);
|
||||||
});
|
});
|
||||||
|
this.addEventListener('scroll', () => this.markScrolled());
|
||||||
|
this.initIntersectionObserver();
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
@ -47,6 +51,7 @@ export default class ChatContent extends CustomElement {
|
|||||||
${ this.model.ui.get('chat-content-spinner-top') ? html`<span class="spinner fa fa-spinner centered"></span>` : '' }
|
${ this.model.ui.get('chat-content-spinner-top') ? html`<span class="spinner fa fa-spinner centered"></span>` : '' }
|
||||||
<converse-message-history
|
<converse-message-history
|
||||||
.model=${this.model}
|
.model=${this.model}
|
||||||
|
.observer=${this.observer}
|
||||||
.messages=${[...this.model.messages.models]}>
|
.messages=${[...this.model.messages.models]}>
|
||||||
</converse-message-history>
|
</converse-message-history>
|
||||||
<div class="chat-content__notifications">${this.model.getNotificationsText()}</div>
|
<div class="chat-content__notifications">${this.model.getNotificationsText()}</div>
|
||||||
@ -58,19 +63,62 @@ export default class ChatContent extends CustomElement {
|
|||||||
this.debouncedMaintainScroll();
|
this.debouncedMaintainScroll();
|
||||||
}
|
}
|
||||||
|
|
||||||
saveScrollPosition () {
|
initIntersectionObserver () {
|
||||||
const scrollTop = this.scrollTop;
|
if (this.observer) {
|
||||||
if (scrollTop) {
|
this.observer.disconnect();
|
||||||
u.safeSave(this.model, { 'scrolled': true, scrollTop });
|
} 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
maintainScrollPosition () {
|
maintainScrollPosition () {
|
||||||
if (this.was_scrolled_up) {
|
if (this.was_scrolled_up) {
|
||||||
const pos = this.model.get('scrollTop');
|
console.warn('scrolling into view');
|
||||||
if (pos) {
|
this.anchored_message?.scrollIntoView(true);
|
||||||
this.scrollTop = pos;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
this.scrollDown();
|
this.scrollDown();
|
||||||
}
|
}
|
||||||
|
@ -54,16 +54,16 @@ export default class EmojiPickerContent extends CustomElement {
|
|||||||
sizzle('.emoji-picker', this).forEach(a => this.observer.observe(a));
|
sizzle('.emoji-picker', this).forEach(a => this.observer.observe(a));
|
||||||
}
|
}
|
||||||
|
|
||||||
setCategoryOnVisibilityChange (ev) {
|
setCategoryOnVisibilityChange (entries) {
|
||||||
const selected = this.parentElement.navigator.selected;
|
const selected = this.parentElement.navigator.selected;
|
||||||
const intersection_with_selected = ev.filter(i => i.target.contains(selected)).pop();
|
const intersection_with_selected = entries.filter(i => i.target.contains(selected)).pop();
|
||||||
let current;
|
let current;
|
||||||
// Choose the intersection that contains the currently selected
|
// Choose the intersection that contains the currently selected
|
||||||
// element, or otherwise the one with the largest ratio.
|
// element, or otherwise the one with the largest ratio.
|
||||||
if (intersection_with_selected) {
|
if (intersection_with_selected) {
|
||||||
current = intersection_with_selected;
|
current = intersection_with_selected;
|
||||||
} else {
|
} else {
|
||||||
current = ev.reduce((p, c) => c.intersectionRatio >= (p?.intersectionRatio || 0) ? c : p, null);
|
current = entries.reduce((p, c) => c.intersectionRatio >= (p?.intersectionRatio || 0) ? c : p, null);
|
||||||
}
|
}
|
||||||
if (current && current.isIntersecting) {
|
if (current && current.isIntersecting) {
|
||||||
const category = current.target.getAttribute('data-category');
|
const category = current.target.getAttribute('data-category');
|
||||||
|
@ -51,6 +51,7 @@ export default class MessageHistory extends CustomElement {
|
|||||||
static get properties () {
|
static get properties () {
|
||||||
return {
|
return {
|
||||||
model: { type: Object },
|
model: { type: Object },
|
||||||
|
observer: { type: Object },
|
||||||
messages: { type: Array }
|
messages: { type: Array }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,6 +68,7 @@ export default class MessageHistory extends CustomElement {
|
|||||||
const day = getDayIndicator(model);
|
const day = getDayIndicator(model);
|
||||||
const templates = day ? [day] : [];
|
const templates = day ? [day] : [];
|
||||||
const message = html`<converse-chat-message
|
const message = html`<converse-chat-message
|
||||||
|
.observer=${this.observer}
|
||||||
jid="${this.model.get('jid')}"
|
jid="${this.model.get('jid')}"
|
||||||
mid="${model.get('id')}"></converse-chat-message>`
|
mid="${model.get('id')}"></converse-chat-message>`
|
||||||
|
|
||||||
|
@ -24,7 +24,8 @@ export default class Message extends CustomElement {
|
|||||||
static get properties () {
|
static get properties () {
|
||||||
return {
|
return {
|
||||||
jid: { type: String },
|
jid: { type: String },
|
||||||
mid: { type: String }
|
mid: { type: String },
|
||||||
|
observer: { type: Object }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,6 +60,10 @@ export default class Message extends CustomElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
firstUpdated () {
|
||||||
|
this.observer.observe(this);
|
||||||
|
}
|
||||||
|
|
||||||
getProps () {
|
getProps () {
|
||||||
return Object.assign(
|
return Object.assign(
|
||||||
this.model.toJSON(),
|
this.model.toJSON(),
|
||||||
|
12
src/shared/chat/utils.js
Normal file
12
src/shared/chat/utils.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { _converse, api } from '@converse/headless/core';
|
||||||
|
|
||||||
|
export function onScrolledDown (model) {
|
||||||
|
if (!model.isHidden()) {
|
||||||
|
model.clearUnreadMsgCounter();
|
||||||
|
if (api.settings.get('allow_url_history_change')) {
|
||||||
|
// Clear location hash if set to one of the messages in our history
|
||||||
|
const hash = window.location.hash;
|
||||||
|
hash && model.messages.get(hash.slice(1)) && _converse.router.history.navigate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user