Fix reading messages in hidden chat
This commit is contained in:
parent
d270d58e89
commit
b9ccab6735
@ -96,6 +96,7 @@ import forEachReverse from "../../helpers/array/forEachReverse";
|
|||||||
import formatNumber from "../../helpers/number/formatNumber";
|
import formatNumber from "../../helpers/number/formatNumber";
|
||||||
import findAndSplice from "../../helpers/array/findAndSplice";
|
import findAndSplice from "../../helpers/array/findAndSplice";
|
||||||
import getViewportSlice from "../../helpers/dom/getViewportSlice";
|
import getViewportSlice from "../../helpers/dom/getViewportSlice";
|
||||||
|
import SuperIntersectionObserver from "../../helpers/dom/superIntersectionObserver";
|
||||||
|
|
||||||
const USE_MEDIA_TAILS = false;
|
const USE_MEDIA_TAILS = false;
|
||||||
const IGNORE_ACTIONS: Set<Message.messageService['action']['_']> = new Set([
|
const IGNORE_ACTIONS: Set<Message.messageService['action']['_']> = new Set([
|
||||||
@ -149,7 +150,6 @@ export default class ChatBubbles {
|
|||||||
|
|
||||||
private stickyIntersector: StickyIntersector;
|
private stickyIntersector: StickyIntersector;
|
||||||
|
|
||||||
private unreadedObserver: IntersectionObserver;
|
|
||||||
private unreaded: Map<HTMLElement, number> = new Map();
|
private unreaded: Map<HTMLElement, number> = new Map();
|
||||||
private unreadedSeen: Set<number> = new Set();
|
private unreadedSeen: Set<number> = new Set();
|
||||||
private readPromise: Promise<void>;
|
private readPromise: Promise<void>;
|
||||||
@ -194,7 +194,6 @@ export default class ChatBubbles {
|
|||||||
private resolveLadderAnimation: () => Promise<any>;
|
private resolveLadderAnimation: () => Promise<any>;
|
||||||
private emptyPlaceholderMid: number;
|
private emptyPlaceholderMid: number;
|
||||||
|
|
||||||
private viewsObserver: IntersectionObserver;
|
|
||||||
private viewsMids: Set<number> = new Set();
|
private viewsMids: Set<number> = new Set();
|
||||||
private sendViewCountersDebounced: () => Promise<void>;
|
private sendViewCountersDebounced: () => Promise<void>;
|
||||||
|
|
||||||
@ -210,6 +209,7 @@ export default class ChatBubbles {
|
|||||||
private sliceViewportDebounced: DebounceReturnType<ChatBubbles['sliceViewport']>;
|
private sliceViewportDebounced: DebounceReturnType<ChatBubbles['sliceViewport']>;
|
||||||
private resizeObserver: ResizeObserver;
|
private resizeObserver: ResizeObserver;
|
||||||
private willScrollOnLoad: boolean;
|
private willScrollOnLoad: boolean;
|
||||||
|
private observer: SuperIntersectionObserver;
|
||||||
|
|
||||||
// private reactions: Map<number, ReactionsElement>;
|
// private reactions: Map<number, ReactionsElement>;
|
||||||
|
|
||||||
@ -839,21 +839,44 @@ export default class ChatBubbles {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.unreadedObserver = new IntersectionObserver((entries) => {
|
this.observer = new SuperIntersectionObserver({root: this.scrollable.container});
|
||||||
entries.forEach(entry => {
|
|
||||||
|
this.listenerSetter.add(rootScope)('chat_changing', ({to}) => {
|
||||||
|
const freeze = to !== this.chat;
|
||||||
|
|
||||||
|
const cb = () => {
|
||||||
|
this.observer.toggleObservingNew(freeze);
|
||||||
|
};
|
||||||
|
|
||||||
|
if(!freeze) {
|
||||||
|
setTimeout(() => {
|
||||||
|
cb();
|
||||||
|
}, 400);
|
||||||
|
} else {
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.sendViewCountersDebounced = debounce(() => {
|
||||||
|
const mids = [...this.viewsMids];
|
||||||
|
this.viewsMids.clear();
|
||||||
|
|
||||||
|
this.appMessagesManager.incrementMessageViews(this.peerId, mids);
|
||||||
|
}, 1000, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private unreadedObserverCallback = (entry: IntersectionObserverEntry) => {
|
||||||
if(entry.isIntersecting) {
|
if(entry.isIntersecting) {
|
||||||
const target = entry.target as HTMLElement;
|
const target = entry.target as HTMLElement;
|
||||||
const mid = this.unreaded.get(target as HTMLElement);
|
const mid = this.unreaded.get(target as HTMLElement);
|
||||||
this.onUnreadedInViewport(target, mid);
|
this.onUnreadedInViewport(target, mid);
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
}, {root: this.scrollable.container});
|
|
||||||
|
|
||||||
this.viewsObserver = new IntersectionObserver((entries) => {
|
private viewsObserverCallback = (entry: IntersectionObserverEntry) => {
|
||||||
entries.forEach(entry => {
|
|
||||||
if(entry.isIntersecting) {
|
if(entry.isIntersecting) {
|
||||||
const mid = +(entry.target as HTMLElement).dataset.mid;
|
const mid = +(entry.target as HTMLElement).dataset.mid;
|
||||||
this.viewsObserver.unobserve(entry.target);
|
this.observer.unobserve(entry.target, this.viewsObserverCallback);
|
||||||
|
|
||||||
if(mid) {
|
if(mid) {
|
||||||
this.viewsMids.add(mid);
|
this.viewsMids.add(mid);
|
||||||
@ -869,16 +892,7 @@ export default class ChatBubbles {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
}, {root: this.scrollable.container});
|
|
||||||
|
|
||||||
this.sendViewCountersDebounced = debounce(() => {
|
|
||||||
const mids = [...this.viewsMids];
|
|
||||||
this.viewsMids.clear();
|
|
||||||
|
|
||||||
this.appMessagesManager.incrementMessageViews(this.peerId, mids);
|
|
||||||
}, 1000, false, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private createResizeObserver() {
|
private createResizeObserver() {
|
||||||
if(!('ResizeObserver' in window) || this.resizeObserver) {
|
if(!('ResizeObserver' in window) || this.resizeObserver) {
|
||||||
@ -1158,7 +1172,7 @@ export default class ChatBubbles {
|
|||||||
|
|
||||||
private onUnreadedInViewport(target: HTMLElement, mid: number) {
|
private onUnreadedInViewport(target: HTMLElement, mid: number) {
|
||||||
this.unreadedSeen.add(mid);
|
this.unreadedSeen.add(mid);
|
||||||
this.unreadedObserver.unobserve(target);
|
this.observer.unobserve(target, this.unreadedObserverCallback);
|
||||||
this.unreaded.delete(target);
|
this.unreaded.delete(target);
|
||||||
this.readUnreaded();
|
this.readUnreaded();
|
||||||
}
|
}
|
||||||
@ -1912,12 +1926,11 @@ export default class ChatBubbles {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.bubbleGroups.removeBubble(bubble);
|
this.bubbleGroups.removeBubble(bubble);
|
||||||
if(this.unreadedObserver) {
|
if(this.observer) {
|
||||||
this.unreadedObserver.unobserve(bubble);
|
this.observer.unobserve(bubble, this.unreadedObserverCallback);
|
||||||
this.unreaded.delete(bubble);
|
this.unreaded.delete(bubble);
|
||||||
}
|
|
||||||
if(this.viewsObserver) {
|
this.observer.unobserve(bubble, this.viewsObserverCallback);
|
||||||
this.viewsObserver.unobserve(bubble);
|
|
||||||
this.viewsMids.delete(mid);
|
this.viewsMids.delete(mid);
|
||||||
}
|
}
|
||||||
//this.unreaded.findAndSplice(mid => mid === id);
|
//this.unreaded.findAndSplice(mid => mid === id);
|
||||||
@ -2271,13 +2284,11 @@ export default class ChatBubbles {
|
|||||||
this.listenerSetter.removeAll();
|
this.listenerSetter.removeAll();
|
||||||
|
|
||||||
this.lazyLoadQueue.clear();
|
this.lazyLoadQueue.clear();
|
||||||
this.unreadedObserver && this.unreadedObserver.disconnect();
|
this.observer && this.observer.disconnect();
|
||||||
this.viewsObserver && this.viewsObserver.disconnect();
|
|
||||||
this.stickyIntersector && this.stickyIntersector.disconnect();
|
this.stickyIntersector && this.stickyIntersector.disconnect();
|
||||||
|
|
||||||
delete this.lazyLoadQueue;
|
delete this.lazyLoadQueue;
|
||||||
this.unreadedObserver && delete this.unreadedObserver;
|
this.observer && delete this.observer;
|
||||||
this.viewsObserver && delete this.viewsObserver;
|
|
||||||
this.stickyIntersector && delete this.stickyIntersector;
|
this.stickyIntersector && delete this.stickyIntersector;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2323,15 +2334,13 @@ export default class ChatBubbles {
|
|||||||
this.stickyIntersector.disconnect();
|
this.stickyIntersector.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.unreadedObserver) {
|
if(this.observer) {
|
||||||
this.unreadedObserver.disconnect();
|
this.observer.disconnect();
|
||||||
|
|
||||||
this.unreaded.clear();
|
this.unreaded.clear();
|
||||||
this.unreadedSeen.clear();
|
this.unreadedSeen.clear();
|
||||||
this.readPromise = undefined;
|
this.readPromise = undefined;
|
||||||
}
|
|
||||||
|
|
||||||
if(this.viewsObserver) {
|
|
||||||
this.viewsObserver.disconnect();
|
|
||||||
this.viewsMids.clear();
|
this.viewsMids.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2914,13 +2923,13 @@ export default class ChatBubbles {
|
|||||||
contentWrapper.appendChild(bubbleContainer);
|
contentWrapper.appendChild(bubbleContainer);
|
||||||
bubble.appendChild(contentWrapper);
|
bubble.appendChild(contentWrapper);
|
||||||
|
|
||||||
if(!our && !message.pFlags.out && this.unreadedObserver) {
|
if(!our && !message.pFlags.out && this.observer) {
|
||||||
//this.log('not our message', message, message.pFlags.unread);
|
//this.log('not our message', message, message.pFlags.unread);
|
||||||
const isUnread = message.pFlags.unread ||
|
const isUnread = message.pFlags.unread ||
|
||||||
this.appMessagesManager.isMentionUnread(message) ||
|
this.appMessagesManager.isMentionUnread(message) ||
|
||||||
(this.historyStorage.readMaxId !== undefined && this.historyStorage.readMaxId < message.mid);
|
(this.historyStorage.readMaxId !== undefined && this.historyStorage.readMaxId < message.mid);
|
||||||
if(isUnread) {
|
if(isUnread) {
|
||||||
this.unreadedObserver.observe(bubble);
|
this.observer.observe(bubble, this.unreadedObserverCallback);
|
||||||
this.unreaded.set(bubble, message.mid);
|
this.unreaded.set(bubble, message.mid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3107,8 +3116,8 @@ export default class ChatBubbles {
|
|||||||
bubble.classList.add('with-beside-button');
|
bubble.classList.add('with-beside-button');
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!message.pFlags.is_outgoing && this.viewsObserver) {
|
if(!message.pFlags.is_outgoing && this.observer) {
|
||||||
this.viewsObserver.observe(bubble);
|
this.observer.observe(bubble, this.viewsObserverCallback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4429,7 +4438,7 @@ export default class ChatBubbles {
|
|||||||
text
|
text
|
||||||
});
|
});
|
||||||
|
|
||||||
this.viewsObserver.observe(button);
|
this.observer.observe(button, this.viewsObserverCallback);
|
||||||
|
|
||||||
if(callback) {
|
if(callback) {
|
||||||
attachClickEvent(button, callback);
|
attachClickEvent(button, callback);
|
||||||
|
105
src/helpers/dom/superIntersectionObserver.ts
Normal file
105
src/helpers/dom/superIntersectionObserver.ts
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* https://github.com/morethanwords/tweb
|
||||||
|
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||||
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type IntersectionTarget = Element;
|
||||||
|
export type IntersectionCallback = (entry: IntersectionObserverEntry) => void;
|
||||||
|
|
||||||
|
export default class SuperIntersectionObserver {
|
||||||
|
private observing: Map<IntersectionTarget, Set<IntersectionCallback>>;
|
||||||
|
private observingQueue: SuperIntersectionObserver['observing'];
|
||||||
|
private observer: IntersectionObserver;
|
||||||
|
private freezedObservingNew: boolean;
|
||||||
|
|
||||||
|
constructor(init?: IntersectionObserverInit) {
|
||||||
|
this.observing = new Map();
|
||||||
|
this.observingQueue = new Map();
|
||||||
|
this.freezedObservingNew = false;
|
||||||
|
|
||||||
|
this.observer = new IntersectionObserver((entries) => {
|
||||||
|
const observing = this.observing;
|
||||||
|
for(let i = 0, length = entries.length; i < length; ++i) {
|
||||||
|
const entry = entries[i];
|
||||||
|
const callbacks = observing.get(entry.target);
|
||||||
|
if(!callbacks) {
|
||||||
|
debugger;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const callback of callbacks) {
|
||||||
|
try {
|
||||||
|
callback(entry);
|
||||||
|
} catch(err) {
|
||||||
|
console.error('intersection process callback error:', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, init);
|
||||||
|
}
|
||||||
|
|
||||||
|
public disconnect() {
|
||||||
|
this.observer.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
public toggleObservingNew(value: boolean) {
|
||||||
|
if(this.freezedObservingNew === value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.freezedObservingNew = value;
|
||||||
|
|
||||||
|
const queue = this.observingQueue;
|
||||||
|
if(!value && queue.size) {
|
||||||
|
for(const [target, callbacks] of queue) {
|
||||||
|
for(const callback of callbacks) {
|
||||||
|
this.observe(target, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public has(target: IntersectionTarget, callback: IntersectionCallback, observing = this.observing) {
|
||||||
|
const callbacks = observing.get(target);
|
||||||
|
return !!(callbacks && callbacks.has(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
public observe(target: IntersectionTarget, callback: IntersectionCallback) {
|
||||||
|
if(this.freezedObservingNew && this.has(target, callback)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const observing = this.freezedObservingNew ? this.observingQueue : this.observing;
|
||||||
|
let callbacks = observing.get(target);
|
||||||
|
if(callbacks && callbacks.has(callback)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!callbacks) {
|
||||||
|
callbacks = new Set();
|
||||||
|
observing.set(target, callbacks);
|
||||||
|
|
||||||
|
if(observing === this.observing) {
|
||||||
|
this.observer.observe(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks.add(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public unobserve(target: IntersectionTarget, callback: IntersectionCallback) {
|
||||||
|
const observing = this.freezedObservingNew && !this.has(target, callback) ? this.observingQueue : this.observing;
|
||||||
|
const callbacks = observing.get(target);
|
||||||
|
if(!callbacks) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks.delete(callback);
|
||||||
|
if(!callbacks.size) {
|
||||||
|
observing.delete(target);
|
||||||
|
this.observer.unobserve(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user