Telegram Web K with changes to work inside I2P https://web.telegram.i2p/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

170 lines
4.1 KiB

4 years ago
import { logger, LogLevels } from "../lib/logger";
5 years ago
type LazyLoadElement = {
div: HTMLDivElement,
load: () => Promise<any>,
5 years ago
wasSeen?: boolean
};
export default class LazyLoadQueue {
5 years ago
private lazyLoadMedia: Array<LazyLoadElement> = [];
4 years ago
private inProcess: Array<LazyLoadElement> = [];
5 years ago
private lockPromise: Promise<void> = null;
private unlockResolve: () => void = null;
private log = logger('LL', LogLevels.error);
// Observer will call entry only 1 time per element
private observer: IntersectionObserver;
4 years ago
private intersectionLocked = false;
constructor(private parallelLimit = 5, private noObserver = false) {
if(noObserver) return;
this.observer = new IntersectionObserver(entries => {
if(this.lockPromise || this.intersectionLocked) return;
4 years ago
const intersecting = entries.filter(entry => entry.isIntersecting);
intersecting.forEachReverse(entry => {
const target = entry.target as HTMLElement;
4 years ago
this.log('isIntersecting', target);
4 years ago
// need for set element first if scrolled
const item = this.lazyLoadMedia.findAndSplice(i => i.div == target);
if(item) {
item.wasSeen = true;
this.lazyLoadMedia.unshift(item);
//this.processQueue(item);
}
4 years ago
});
if(intersecting.length) {
this.processQueue();
}
});
}
public clear() {
4 years ago
this.inProcess.length = 0; // ацтеки забьются, будет плохо
this.lazyLoadMedia.length = 0;
4 years ago
for(let item of this.inProcess) {
this.lazyLoadMedia.push(item);
}
if(this.observer) {
this.observer.disconnect();
}
}
5 years ago
public length() {
4 years ago
return this.lazyLoadMedia.length + this.inProcess.length;
5 years ago
}
public lock() {
if(this.lockPromise) return;
this.lockPromise = new Promise((resolve, reject) => {
this.unlockResolve = resolve;
});
}
public unlock() {
if(!this.unlockResolve) return;
this.lockPromise = null;
this.unlockResolve();
this.unlockResolve = null;
}
public async processQueue(item?: LazyLoadElement) {
4 years ago
if(this.parallelLimit > 0 && this.inProcess.length >= this.parallelLimit) return;
if(item) {
this.lazyLoadMedia.findAndSplice(i => i == item);
} else {
item = this.lazyLoadMedia.findAndSplice(i => i.wasSeen);
}
if(item) {
4 years ago
this.inProcess.push(item);
this.log('will load media', this.lockPromise, item);
5 years ago
try {
if(this.lockPromise/* && false */) {
5 years ago
let perf = performance.now();
await this.lockPromise;
this.log('waited lock:', performance.now() - perf);
5 years ago
}
4 years ago
//await new Promise((resolve) => setTimeout(resolve, 2e3));
//await new Promise((resolve, reject) => window.requestAnimationFrame(() => window.requestAnimationFrame(resolve)));
await item.load();
} catch(err) {
4 years ago
this.log.error('loadMediaQueue error:', err/* , item */);
}
if(!this.noObserver) {
this.observer.unobserve(item.div);
}
4 years ago
this.inProcess.findAndSplice(i => i == item);
this.log('loaded media', item);
5 years ago
if(this.lazyLoadMedia.length) {
this.processQueue();
}
}
}
5 years ago
public addElement(el: LazyLoadElement) {
5 years ago
if(el.wasSeen) {
this.processQueue(el);
5 years ago
} else {
el.wasSeen = false;
if(this.observer) {
this.observer.observe(el.div);
}
5 years ago
}
}
public push(el: LazyLoadElement) {
this.lazyLoadMedia.push(el);
this.addElement(el);
}
public unshift(el: LazyLoadElement) {
this.lazyLoadMedia.unshift(el);
this.addElement(el);
}
4 years ago
public refresh() {
const items = this.lazyLoadMedia;
if(items && items.length) {
items.forEach(item => {
this.observer.unobserve(item.div);
});
window.requestAnimationFrame(() => {
items.forEach(item => {
this.observer.observe(item.div);
});
});
}
}
public lockIntersection() {
this.intersectionLocked = true;
}
public unlockIntersection() {
this.intersectionLocked = false;
this.refresh();
}
}