Lazy load queue refactored
This commit is contained in:
parent
904b4a7eb9
commit
0333ecfcce
@ -1,7 +1,7 @@
|
||||
import appProfileManager from "../lib/appManagers/appProfileManager";
|
||||
import { $rootScope } from "../lib/utils";
|
||||
|
||||
$rootScope.$on('avatar_update', (e: CustomEvent) => {
|
||||
$rootScope.$on('avatar_update', (e) => {
|
||||
let peerID = e.detail;
|
||||
|
||||
appProfileManager.removeFromAvatarsCache(peerID);
|
||||
|
@ -46,7 +46,7 @@ export class ChatAudio {
|
||||
appMediaPlaybackController.toggle();
|
||||
});
|
||||
|
||||
$rootScope.$on('audio_play', (e: CustomEvent) => {
|
||||
$rootScope.$on('audio_play', (e) => {
|
||||
const {doc, mid} = e.detail;
|
||||
|
||||
let title: string, subtitle: string;
|
||||
|
@ -81,7 +81,7 @@ export class EmoticonsDropdown {
|
||||
}
|
||||
|
||||
clearTimeout(this.displayTimeout);
|
||||
this.displayTimeout = setTimeout(() => {
|
||||
this.displayTimeout = window.setTimeout(() => {
|
||||
this.toggle(false);
|
||||
}, 200);
|
||||
};
|
||||
@ -188,7 +188,7 @@ export class EmoticonsDropdown {
|
||||
if((this.element.style.display && enable === undefined) || enable) {
|
||||
this.events.onOpen.forEach(cb => cb());
|
||||
|
||||
EmoticonsDropdown.lazyLoadQueue.lockIntersection();
|
||||
EmoticonsDropdown.lazyLoadQueue.lock();
|
||||
//EmoticonsDropdown.lazyLoadQueue.unlock();
|
||||
animationIntersector.lockIntersectionGroup(EMOTICONSSTICKERGROUP);
|
||||
|
||||
@ -197,9 +197,10 @@ export class EmoticonsDropdown {
|
||||
this.element.classList.add('active');
|
||||
|
||||
clearTimeout(this.displayTimeout);
|
||||
this.displayTimeout = setTimeout(() => {
|
||||
this.displayTimeout = window.setTimeout(() => {
|
||||
animationIntersector.unlockIntersectionGroup(EMOTICONSSTICKERGROUP);
|
||||
EmoticonsDropdown.lazyLoadQueue.unlockIntersection();
|
||||
EmoticonsDropdown.lazyLoadQueue.unlock();
|
||||
EmoticonsDropdown.lazyLoadQueue.refresh();
|
||||
|
||||
this.events.onOpenAfter.forEach(cb => cb());
|
||||
}, touchSupport ? 0 : 200);
|
||||
@ -210,7 +211,7 @@ export class EmoticonsDropdown {
|
||||
} else {
|
||||
this.events.onClose.forEach(cb => cb());
|
||||
|
||||
EmoticonsDropdown.lazyLoadQueue.lockIntersection();
|
||||
EmoticonsDropdown.lazyLoadQueue.lock();
|
||||
//EmoticonsDropdown.lazyLoadQueue.lock();
|
||||
|
||||
// нужно залочить группу и выключить стикеры
|
||||
@ -220,12 +221,13 @@ export class EmoticonsDropdown {
|
||||
this.element.classList.remove('active');
|
||||
|
||||
clearTimeout(this.displayTimeout);
|
||||
this.displayTimeout = setTimeout(() => {
|
||||
this.displayTimeout = window.setTimeout(() => {
|
||||
this.element.style.display = 'none';
|
||||
|
||||
// теперь можно убрать visible, чтобы они не включились после фокуса
|
||||
animationIntersector.unlockIntersectionGroup(EMOTICONSSTICKERGROUP);
|
||||
EmoticonsDropdown.lazyLoadQueue.unlockIntersection();
|
||||
EmoticonsDropdown.lazyLoadQueue.unlock();
|
||||
EmoticonsDropdown.lazyLoadQueue.refresh();
|
||||
|
||||
this.events.onCloseAfter.forEach(cb => cb());
|
||||
}, touchSupport ? 0 : 200);
|
||||
|
@ -13,6 +13,7 @@ import apiManager from "../../../lib/mtproto/mtprotoworker";
|
||||
import StickyIntersector from "../../stickyIntersector";
|
||||
import appDocsManager, {MyDocument} from "../../../lib/appManagers/appDocsManager";
|
||||
import animationIntersector from "../../animationIntersector";
|
||||
import LazyLoadQueue, { LazyLoadQueueRepeat } from "../../lazyLoadQueue";
|
||||
|
||||
export default class StickersTab implements EmoticonsTab {
|
||||
public content: HTMLElement;
|
||||
@ -36,7 +37,7 @@ export default class StickersTab implements EmoticonsTab {
|
||||
private stickyIntersector: StickyIntersector;
|
||||
|
||||
private animatedDivs: Set<HTMLDivElement> = new Set();
|
||||
private animatedIntersector: IntersectionObserver;
|
||||
private lazyLoadQueue: LazyLoadQueueRepeat;
|
||||
|
||||
categoryPush(categoryDiv: HTMLElement, categoryTitle: string, promise: Promise<MyDocument[]>, prepend?: boolean) {
|
||||
//if((docs.length % 5) != 0) categoryDiv.classList.add('not-full');
|
||||
@ -83,7 +84,11 @@ export default class StickersTab implements EmoticonsTab {
|
||||
|
||||
if(doc.sticker == 2) {
|
||||
this.animatedDivs.add(div);
|
||||
this.animatedIntersector.observe(div);
|
||||
|
||||
this.lazyLoadQueue.observe({
|
||||
div,
|
||||
load: this.processVisibleDiv
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -163,6 +168,58 @@ export default class StickersTab implements EmoticonsTab {
|
||||
}
|
||||
}
|
||||
|
||||
checkAnimationContainer = (div: HTMLElement, visible: boolean) => {
|
||||
//console.error('checkAnimationContainer', div, visible);
|
||||
const players = animationIntersector.getAnimations(div);
|
||||
players.forEach(player => {
|
||||
if(!visible) {
|
||||
animationIntersector.checkAnimation(player, true, true);
|
||||
} else {
|
||||
animationIntersector.checkAnimation(player, false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
processVisibleDiv = (div: HTMLElement) => {
|
||||
const docID = div.dataset.docID;
|
||||
const doc = appDocsManager.getDoc(docID);
|
||||
|
||||
const promise = wrapSticker({
|
||||
doc,
|
||||
div: div as HTMLDivElement,
|
||||
width: 80,
|
||||
height: 80,
|
||||
lazyLoadQueue: null,
|
||||
group: EMOTICONSSTICKERGROUP,
|
||||
onlyThumb: false,
|
||||
play: true,
|
||||
loop: true
|
||||
});
|
||||
|
||||
promise.then(() => {
|
||||
//clearTimeout(timeout);
|
||||
this.checkAnimationContainer(div, this.lazyLoadQueue.intersector.isVisible(div));
|
||||
});
|
||||
|
||||
/* let timeout = window.setTimeout(() => {
|
||||
console.error('processVisibleDiv timeout', div, doc);
|
||||
}, 1e3); */
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
processInvisibleDiv = (div: HTMLElement) => {
|
||||
const docID = div.dataset.docID;
|
||||
const doc = appDocsManager.getDoc(docID);
|
||||
|
||||
//console.log('STICKER INvisible:', /* div, */docID);
|
||||
|
||||
this.checkAnimationContainer(div, false);
|
||||
|
||||
div.innerHTML = '';
|
||||
this.renderSticker(doc, div as HTMLDivElement);
|
||||
};
|
||||
|
||||
init() {
|
||||
this.content = document.getElementById('content-stickers');
|
||||
//let stickersDiv = contentStickersDiv.querySelector('.os-content') as HTMLDivElement;
|
||||
@ -196,7 +253,7 @@ export default class StickersTab implements EmoticonsTab {
|
||||
}
|
||||
}); */
|
||||
|
||||
$rootScope.$on('stickers_installed', (e: CustomEvent) => {
|
||||
$rootScope.$on('stickers_installed', (e) => {
|
||||
const set: StickerSet.stickerSet = e.detail;
|
||||
|
||||
if(!this.stickerSets[set.id] && this.mounted) {
|
||||
@ -204,7 +261,7 @@ export default class StickersTab implements EmoticonsTab {
|
||||
}
|
||||
});
|
||||
|
||||
$rootScope.$on('stickers_deleted', (e: CustomEvent) => {
|
||||
$rootScope.$on('stickers_deleted', (e) => {
|
||||
const set: StickerSet.stickerSet = e.detail;
|
||||
|
||||
if(this.stickerSets[set.id] && this.mounted) {
|
||||
@ -256,98 +313,59 @@ export default class StickersTab implements EmoticonsTab {
|
||||
this.mounted = true;
|
||||
});
|
||||
|
||||
const checkAnimationDiv = (div: HTMLDivElement) => {
|
||||
const players = animationIntersector.getAnimations(div);
|
||||
players.forEach(player => {
|
||||
if(!visible.has(div)) {
|
||||
animationIntersector.checkAnimation(player, true, true);
|
||||
} else {
|
||||
animationIntersector.checkAnimation(player, false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const processInvisibleDiv = (div: HTMLDivElement) => {
|
||||
visible.delete(div);
|
||||
//console.log('STICKER INvisible:', target, docID);
|
||||
|
||||
const docID = div.dataset.docID;
|
||||
const doc = appDocsManager.getDoc(docID);
|
||||
|
||||
checkAnimationDiv(div);
|
||||
|
||||
div.innerHTML = '';
|
||||
this.renderSticker(doc, div);
|
||||
};
|
||||
|
||||
let locked = false;
|
||||
const visible: Set<HTMLDivElement> = new Set();
|
||||
this.animatedIntersector = new IntersectionObserver((entries) => {
|
||||
if(locked) {
|
||||
return;
|
||||
this.lazyLoadQueue = new LazyLoadQueueRepeat(undefined, (target, visible) => {
|
||||
if(!visible) {
|
||||
this.processInvisibleDiv(target as HTMLDivElement);
|
||||
}
|
||||
|
||||
entries.forEach(entry => {
|
||||
const {target, isIntersecting} = entry;
|
||||
|
||||
const div = target as HTMLDivElement;
|
||||
const docID = div.dataset.docID;
|
||||
const doc = appDocsManager.getDoc(docID);
|
||||
if(isIntersecting) {
|
||||
//console.log('STICKER visible:', target, docID);
|
||||
if(visible.has(div)) {
|
||||
return;
|
||||
}
|
||||
|
||||
visible.add(div);
|
||||
|
||||
wrapSticker({
|
||||
doc,
|
||||
div,
|
||||
width: 80,
|
||||
height: 80,
|
||||
lazyLoadQueue: null,
|
||||
group: EMOTICONSSTICKERGROUP,
|
||||
onlyThumb: false,
|
||||
play: true,
|
||||
loop: true
|
||||
}).then(() => {
|
||||
checkAnimationDiv(div);
|
||||
});
|
||||
} else {
|
||||
processInvisibleDiv(div);
|
||||
}
|
||||
});
|
||||
|
||||
//animationIntersector.checkAnimations(true, EMOTICONSSTICKERGROUP, false);
|
||||
});
|
||||
|
||||
|
||||
/* let closed = true;
|
||||
emoticonsDropdown.events.onClose.push(() => {
|
||||
locked = true;
|
||||
closed = false;
|
||||
this.lazyLoadQueue.lock();
|
||||
});
|
||||
|
||||
emoticonsDropdown.events.onCloseAfter.push(() => {
|
||||
const divs = [...visible];
|
||||
const divs = this.lazyLoadQueue.intersector.getVisible();
|
||||
|
||||
for(const div of divs) {
|
||||
processInvisibleDiv(div);
|
||||
this.processInvisibleDiv(div);
|
||||
}
|
||||
|
||||
closed = true;
|
||||
});
|
||||
|
||||
emoticonsDropdown.events.onOpenAfter.push(() => {
|
||||
locked = false;
|
||||
|
||||
// refresh
|
||||
this.animatedIntersector.disconnect();
|
||||
const divs = [...this.animatedDivs];
|
||||
for(const div of divs) {
|
||||
this.animatedIntersector.observe(div);
|
||||
if(closed) {
|
||||
this.lazyLoadQueue.unlockAndRefresh();
|
||||
closed = false;
|
||||
} else {
|
||||
this.lazyLoadQueue.unlock();
|
||||
}
|
||||
}); */
|
||||
emoticonsDropdown.events.onClose.push(() => {
|
||||
this.lazyLoadQueue.lock();
|
||||
});
|
||||
|
||||
emoticonsDropdown.events.onCloseAfter.push(() => {
|
||||
const divs = this.lazyLoadQueue.intersector.getVisible();
|
||||
|
||||
for(const div of divs) {
|
||||
this.processInvisibleDiv(div);
|
||||
}
|
||||
|
||||
this.lazyLoadQueue.intersector.clearVisible();
|
||||
});
|
||||
|
||||
emoticonsDropdown.events.onOpenAfter.push(() => {
|
||||
this.lazyLoadQueue.unlockAndRefresh();
|
||||
});
|
||||
|
||||
/* setInterval(() => {
|
||||
// @ts-ignore
|
||||
console.log('STICKERS RENDERED IN PANEL:', Object.values(lottieLoader.players).filter(p => p.width == 80).length);
|
||||
const players = Object.values(lottieLoader.players).filter(p => p.width == 80);
|
||||
|
||||
console.log('STICKERS RENDERED IN PANEL:', players.length, players.filter(p => !p.paused).length, this.lazyLoadQueue.intersector.getVisible().length);
|
||||
}, .25e3); */
|
||||
|
||||
|
||||
|
@ -1,67 +1,35 @@
|
||||
import { logger, LogLevels } from "../lib/logger";
|
||||
import VisibilityIntersector, { OnVisibilityChange } from "./visibilityIntersector";
|
||||
|
||||
type LazyLoadElement = {
|
||||
type LazyLoadElementBase = {
|
||||
div: HTMLDivElement,
|
||||
load: () => Promise<any>,
|
||||
load: (target?: HTMLDivElement) => Promise<any>
|
||||
};
|
||||
|
||||
type LazyLoadElement = LazyLoadElementBase & {
|
||||
wasSeen?: boolean
|
||||
};
|
||||
|
||||
export default class LazyLoadQueue {
|
||||
private lazyLoadMedia: Array<LazyLoadElement> = [];
|
||||
private inProcess: Array<LazyLoadElement> = [];
|
||||
export class LazyLoadQueueBase {
|
||||
protected lazyLoadMedia: Array<LazyLoadElementBase> = [];
|
||||
protected inProcess: Set<LazyLoadElementBase> = new Set();
|
||||
|
||||
private lockPromise: Promise<void> = null;
|
||||
private unlockResolve: () => void = null;
|
||||
protected lockPromise: Promise<void> = null;
|
||||
protected unlockResolve: () => void = null;
|
||||
|
||||
private log = logger('LL', LogLevels.error);
|
||||
protected log = logger('LL', LogLevels.error);
|
||||
|
||||
// Observer will call entry only 1 time per element
|
||||
private observer: IntersectionObserver;
|
||||
|
||||
private intersectionLocked = false;
|
||||
|
||||
constructor(private parallelLimit = 5, private noObserver = false) {
|
||||
if(noObserver) return;
|
||||
|
||||
this.observer = new IntersectionObserver(entries => {
|
||||
if(this.lockPromise || this.intersectionLocked) return;
|
||||
|
||||
const intersecting = entries.filter(entry => entry.isIntersecting);
|
||||
intersecting.forEachReverse(entry => {
|
||||
const target = entry.target as HTMLElement;
|
||||
|
||||
this.log('isIntersecting', target);
|
||||
|
||||
// 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);
|
||||
}
|
||||
});
|
||||
|
||||
if(intersecting.length) {
|
||||
this.processQueue();
|
||||
}
|
||||
});
|
||||
constructor(protected parallelLimit = 5) {
|
||||
}
|
||||
|
||||
public clear() {
|
||||
this.inProcess.length = 0; // ацтеки забьются, будет плохо
|
||||
this.inProcess.clear(); // ацтеки забьются, будет плохо
|
||||
|
||||
this.lazyLoadMedia.length = 0;
|
||||
for(let item of this.inProcess) {
|
||||
// unreachable code
|
||||
/* for(let item of this.inProcess) {
|
||||
this.lazyLoadMedia.push(item);
|
||||
}
|
||||
|
||||
if(this.observer) {
|
||||
this.observer.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public length() {
|
||||
return this.lazyLoadMedia.length + this.inProcess.length;
|
||||
} */
|
||||
}
|
||||
|
||||
public lock() {
|
||||
@ -78,92 +46,200 @@ export default class LazyLoadQueue {
|
||||
this.unlockResolve = null;
|
||||
}
|
||||
|
||||
public async processQueue(item?: LazyLoadElement) {
|
||||
if(this.parallelLimit > 0 && this.inProcess.length >= this.parallelLimit) return;
|
||||
public async processItem(item: LazyLoadElementBase) {
|
||||
this.inProcess.add(item);
|
||||
|
||||
if(item) {
|
||||
this.lazyLoadMedia.findAndSplice(i => i == item);
|
||||
} else {
|
||||
item = this.lazyLoadMedia.findAndSplice(i => i.wasSeen);
|
||||
this.log('will load media', this.lockPromise, item);
|
||||
|
||||
try {
|
||||
if(this.lockPromise/* && false */) {
|
||||
const perf = performance.now();
|
||||
await this.lockPromise;
|
||||
|
||||
this.log('waited lock:', performance.now() - perf);
|
||||
}
|
||||
|
||||
//await new Promise((resolve) => setTimeout(resolve, 2e3));
|
||||
//await new Promise((resolve, reject) => window.requestAnimationFrame(() => window.requestAnimationFrame(resolve)));
|
||||
await item.load(item.div);
|
||||
} catch(err) {
|
||||
this.log.error('loadMediaQueue error:', err/* , item */);
|
||||
}
|
||||
|
||||
if(item) {
|
||||
this.inProcess.push(item);
|
||||
this.inProcess.delete(item);
|
||||
|
||||
this.log('will load media', this.lockPromise, item);
|
||||
this.log('loaded media', item);
|
||||
|
||||
try {
|
||||
if(this.lockPromise/* && false */) {
|
||||
let perf = performance.now();
|
||||
await this.lockPromise;
|
||||
|
||||
this.log('waited lock:', performance.now() - perf);
|
||||
}
|
||||
|
||||
//await new Promise((resolve) => setTimeout(resolve, 2e3));
|
||||
//await new Promise((resolve, reject) => window.requestAnimationFrame(() => window.requestAnimationFrame(resolve)));
|
||||
await item.load();
|
||||
} catch(err) {
|
||||
this.log.error('loadMediaQueue error:', err/* , item */);
|
||||
}
|
||||
|
||||
if(!this.noObserver) {
|
||||
this.observer.unobserve(item.div);
|
||||
}
|
||||
|
||||
this.inProcess.findAndSplice(i => i == item);
|
||||
|
||||
this.log('loaded media', item);
|
||||
|
||||
if(this.lazyLoadMedia.length) {
|
||||
this.processQueue();
|
||||
}
|
||||
if(this.lazyLoadMedia.length) {
|
||||
this.processQueue();
|
||||
}
|
||||
}
|
||||
|
||||
public addElement(el: LazyLoadElement) {
|
||||
if(el.wasSeen) {
|
||||
this.processQueue(el);
|
||||
} else {
|
||||
el.wasSeen = false;
|
||||
|
||||
if(this.observer) {
|
||||
this.observer.observe(el.div);
|
||||
}
|
||||
}
|
||||
protected getItem() {
|
||||
return this.lazyLoadMedia.shift();
|
||||
}
|
||||
|
||||
protected addElement(el: LazyLoadElementBase) {
|
||||
this.processQueue(el);
|
||||
}
|
||||
|
||||
public async processQueue(item?: LazyLoadElementBase) {
|
||||
if(this.parallelLimit > 0 && this.inProcess.size >= this.parallelLimit) return;
|
||||
|
||||
do {
|
||||
if(item) {
|
||||
this.lazyLoadMedia.findAndSplice(i => i == item);
|
||||
} else {
|
||||
item = this.getItem();
|
||||
}
|
||||
|
||||
public push(el: LazyLoadElement) {
|
||||
if(item) {
|
||||
this.processItem(item);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
item = null;
|
||||
} while(this.inProcess.size < this.parallelLimit && this.lazyLoadMedia.length);
|
||||
}
|
||||
|
||||
public push(el: LazyLoadElementBase) {
|
||||
this.lazyLoadMedia.push(el);
|
||||
this.addElement(el);
|
||||
}
|
||||
|
||||
public unshift(el: LazyLoadElement) {
|
||||
public unshift(el: LazyLoadElementBase) {
|
||||
this.lazyLoadMedia.unshift(el);
|
||||
this.addElement(el);
|
||||
}
|
||||
}
|
||||
|
||||
export class LazyLoadQueueIntersector extends LazyLoadQueueBase {
|
||||
public intersector: VisibilityIntersector;
|
||||
protected intersectorTimeout: number;
|
||||
|
||||
constructor(protected parallelLimit = 5) {
|
||||
super(parallelLimit);
|
||||
}
|
||||
|
||||
public lock() {
|
||||
super.lock();
|
||||
this.intersector.lock();
|
||||
}
|
||||
|
||||
public unlock() {
|
||||
super.unlock();
|
||||
this.intersector.unlock();
|
||||
}
|
||||
|
||||
public unlockAndRefresh() {
|
||||
super.unlock();
|
||||
this.intersector.unlockAndRefresh();
|
||||
}
|
||||
|
||||
public clear() {
|
||||
super.clear();
|
||||
this.intersector.disconnect();
|
||||
}
|
||||
|
||||
public refresh() {
|
||||
const items = this.lazyLoadMedia;
|
||||
if(items && items.length) {
|
||||
items.forEach(item => {
|
||||
this.observer.unobserve(item.div);
|
||||
});
|
||||
this.intersector.refresh();
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(() => {
|
||||
items.forEach(item => {
|
||||
this.observer.observe(item.div);
|
||||
});
|
||||
});
|
||||
protected setProcessQueueTimeout() {
|
||||
if(!this.intersectorTimeout) {
|
||||
this.intersectorTimeout = window.setTimeout(() => {
|
||||
this.intersectorTimeout = 0;
|
||||
this.processQueue();
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default class LazyLoadQueue extends LazyLoadQueueIntersector {
|
||||
protected lazyLoadMedia: Array<LazyLoadElement> = [];
|
||||
protected inProcess: Set<LazyLoadElement> = new Set();
|
||||
|
||||
constructor(protected parallelLimit = 5) {
|
||||
super(parallelLimit);
|
||||
|
||||
this.intersector = new VisibilityIntersector(this.onVisibilityChange);
|
||||
}
|
||||
|
||||
private onVisibilityChange = (target: HTMLElement, visible: boolean) => {
|
||||
if(visible) {
|
||||
this.log('isIntersecting', target);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
this.setProcessQueueTimeout();
|
||||
}
|
||||
};
|
||||
|
||||
protected getItem() {
|
||||
return this.lazyLoadMedia.findAndSplice(item => item.wasSeen);
|
||||
}
|
||||
|
||||
public async processItem(item: LazyLoadElement) {
|
||||
await super.processItem(item);
|
||||
this.intersector.unobserve(item.div);
|
||||
}
|
||||
|
||||
protected addElement(el: LazyLoadElement) {
|
||||
//super.addElement(el);
|
||||
if(el.wasSeen) {
|
||||
super.processQueue(el);
|
||||
} else {
|
||||
el.wasSeen = false;
|
||||
this.intersector.observe(el.div);
|
||||
}
|
||||
}
|
||||
|
||||
public lockIntersection() {
|
||||
this.intersectionLocked = true;
|
||||
public push(el: LazyLoadElement) {
|
||||
super.push(el);
|
||||
}
|
||||
|
||||
public unlockIntersection() {
|
||||
this.intersectionLocked = false;
|
||||
this.refresh();
|
||||
public unshift(el: LazyLoadElement) {
|
||||
super.unshift(el);
|
||||
}
|
||||
}
|
||||
|
||||
export class LazyLoadQueueRepeat extends LazyLoadQueueIntersector {
|
||||
private _lazyLoadMedia: Map<HTMLElement, LazyLoadElementBase> = new Map();
|
||||
|
||||
constructor(protected parallelLimit = 5, protected onVisibilityChange?: OnVisibilityChange) {
|
||||
super(parallelLimit);
|
||||
|
||||
this.intersector = new VisibilityIntersector((target, visible) => {
|
||||
if(visible) {
|
||||
const item = this.lazyLoadMedia.findAndSplice(i => i.div == target);
|
||||
this.lazyLoadMedia.unshift(item || this._lazyLoadMedia.get(target));
|
||||
} else {
|
||||
this.lazyLoadMedia.findAndSplice(i => i.div == target);
|
||||
}
|
||||
|
||||
this.onVisibilityChange && this.onVisibilityChange(target, visible);
|
||||
this.setProcessQueueTimeout();
|
||||
});
|
||||
}
|
||||
|
||||
/* public async processItem(item: LazyLoadElement) {
|
||||
//await super.processItem(item);
|
||||
await LazyLoadQueueBase.prototype.processItem.call(this, item);
|
||||
|
||||
if(this.lazyLoadMedia.length) {
|
||||
this.processQueue();
|
||||
}
|
||||
} */
|
||||
|
||||
public observe(el: LazyLoadElementBase) {
|
||||
this._lazyLoadMedia.set(el.div, el);
|
||||
this.intersector.observe(el.div);
|
||||
}
|
||||
}
|
||||
|
@ -155,6 +155,19 @@ let closeBtnMenu = () => {
|
||||
window.removeEventListener('contextmenu', onClick);
|
||||
};
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
if(openedMenu) {
|
||||
closeBtnMenu();
|
||||
}
|
||||
|
||||
/* if(openedMenu && (openedMenu.style.top || openedMenu.style.left)) {
|
||||
const rect = openedMenu.getBoundingClientRect();
|
||||
const {innerWidth, innerHeight} = window;
|
||||
|
||||
console.log(innerWidth, innerHeight, rect);
|
||||
} */
|
||||
});
|
||||
|
||||
let openedMenu: HTMLDivElement = null, openedMenuOnClose: () => void = null;
|
||||
export function openBtnMenu(menuElement: HTMLDivElement, onClose?: () => void) {
|
||||
closeBtnMenu();
|
||||
@ -248,7 +261,7 @@ export function attachContextMenuListener(element: HTMLElement, callback: (e: To
|
||||
}
|
||||
}; */
|
||||
|
||||
timeout = setTimeout(() => {
|
||||
timeout = window.setTimeout(() => {
|
||||
callback(e.touches[0]);
|
||||
onCancel();
|
||||
|
||||
|
@ -63,7 +63,7 @@ export const roundPercents = (percents: number[]) => {
|
||||
};
|
||||
|
||||
const connectedPolls: {id: string, element: PollElement}[] = [];
|
||||
$rootScope.$on('poll_update', (e: CustomEvent) => {
|
||||
$rootScope.$on('poll_update', (e) => {
|
||||
const {poll, results} = e.detail as {poll: Poll, results: PollResults};
|
||||
|
||||
//console.log('poll_update', poll, results);
|
||||
@ -123,7 +123,7 @@ const setQuizHint = (solution: string, solution_entities: any[], onHide: () => v
|
||||
|
||||
prevQuizHint = element;
|
||||
prevQuizHintOnHide = onHide;
|
||||
prevQuizHintTimeout = setTimeout(() => {
|
||||
prevQuizHintTimeout = window.setTimeout(() => {
|
||||
hideQuizHint(element, onHide, prevQuizHintTimeout);
|
||||
}, touchSupport ? 5000 : 7000);
|
||||
};
|
||||
@ -280,7 +280,7 @@ export default class PollElement extends HTMLElement {
|
||||
// circle.style.strokeDashoffset = circumference + percents * circumference;
|
||||
// circle.style.strokeDasharray = ${circumference} ${circumference};
|
||||
|
||||
this.quizInterval = setInterval(() => {
|
||||
this.quizInterval = window.setInterval(() => {
|
||||
const time = Date.now();
|
||||
const percents = (closeTime - time) / period;
|
||||
const timeLeft = (closeTime - time) / 1000 + 1 | 0;
|
||||
|
@ -118,8 +118,8 @@ export default class AppChatFoldersTab implements SliderTab {
|
||||
}
|
||||
});
|
||||
|
||||
$rootScope.$on('filter_update', (e: CustomEvent) => {
|
||||
const filter: DialogFilter = e.detail;
|
||||
$rootScope.$on('filter_update', (e) => {
|
||||
const filter = e.detail;
|
||||
if(this.filtersRendered.hasOwnProperty(filter.id)) {
|
||||
this.renderFolder(filter, null, this.filtersRendered[filter.id]);
|
||||
} else {
|
||||
@ -129,8 +129,8 @@ export default class AppChatFoldersTab implements SliderTab {
|
||||
this.getSuggestedFilters();
|
||||
});
|
||||
|
||||
$rootScope.$on('filter_delete', (e: CustomEvent) => {
|
||||
const filter: DialogFilter = e.detail;
|
||||
$rootScope.$on('filter_delete', (e) => {
|
||||
const filter = e.detail;
|
||||
if(this.filtersRendered.hasOwnProperty(filter.id)) {
|
||||
/* for(const suggested of this.suggestedFilters) {
|
||||
if(deepEqual(suggested.filter, filter)) {
|
||||
|
@ -26,7 +26,7 @@ export default class AppSettingsTab implements SliderTab {
|
||||
constructor() {
|
||||
parseMenuButtonsTo(this.buttons, this.container.querySelector('.profile-buttons').children);
|
||||
|
||||
$rootScope.$on('user_auth', (e: CustomEvent) => {
|
||||
$rootScope.$on('user_auth', (e) => {
|
||||
this.fillElements();
|
||||
});
|
||||
|
||||
|
117
src/components/visibilityIntersector.ts
Normal file
117
src/components/visibilityIntersector.ts
Normal file
@ -0,0 +1,117 @@
|
||||
type TargetType = HTMLElement;
|
||||
export type OnVisibilityChange = (target: TargetType, visible: boolean) => void;
|
||||
|
||||
export default class VisibilityIntersector {
|
||||
private observer: IntersectionObserver;
|
||||
private items: Map<TargetType, boolean> = new Map();
|
||||
private locked = false;
|
||||
|
||||
constructor(onVisibilityChange: OnVisibilityChange) {
|
||||
this.observer = new IntersectionObserver((entries) => {
|
||||
if(this.locked) {
|
||||
return;
|
||||
}
|
||||
|
||||
const changed: {target: TargetType, visible: boolean}[] = [];
|
||||
|
||||
entries.forEach(entry => {
|
||||
const target = entry.target as TargetType;
|
||||
|
||||
if(this.items.get(target) == entry.isIntersecting) {
|
||||
return;
|
||||
} else {
|
||||
this.items.set(target, entry.isIntersecting);
|
||||
}
|
||||
|
||||
/* if(entry.isIntersecting) {
|
||||
console.log('ooo', entry);
|
||||
} */
|
||||
|
||||
/* if(this.locked) {
|
||||
return;
|
||||
} */
|
||||
|
||||
changed[entry.isIntersecting ? 'unshift' : 'push']({target, visible: entry.isIntersecting});
|
||||
|
||||
//onVisibilityChange(target, entry.isIntersecting);
|
||||
});
|
||||
|
||||
changed.forEach(smth => {
|
||||
onVisibilityChange(smth.target, smth.visible);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public getVisible() {
|
||||
const items: TargetType[] = [];
|
||||
this.items.forEach((value, key) => {
|
||||
if(value) {
|
||||
items.push(key);
|
||||
}
|
||||
});
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
public clearVisible() {
|
||||
const visible = this.getVisible();
|
||||
for(const target of visible) {
|
||||
this.items.set(target, false);
|
||||
}
|
||||
}
|
||||
|
||||
public isVisible(target: TargetType) {
|
||||
return this.items.get(target);
|
||||
}
|
||||
|
||||
public disconnect() {
|
||||
this.observer.disconnect();
|
||||
this.items.clear();
|
||||
}
|
||||
|
||||
public refresh() {
|
||||
this.observer.disconnect();
|
||||
|
||||
//window.requestAnimationFrame(() => {
|
||||
const targets = [...this.items.keys()];
|
||||
for(const target of targets) {
|
||||
//this.items.set(target, false);
|
||||
this.observer.observe(target);
|
||||
}
|
||||
//});
|
||||
}
|
||||
|
||||
public refreshVisible() {
|
||||
const visible = this.getVisible();
|
||||
for(const target of visible) {
|
||||
this.observer.unobserve(target);
|
||||
}
|
||||
|
||||
for(const target of visible) {
|
||||
this.observer.observe(target);
|
||||
}
|
||||
}
|
||||
|
||||
public observe(target: TargetType) {
|
||||
this.items.set(target, false);
|
||||
this.observer.observe(target);
|
||||
}
|
||||
|
||||
public unobserve(target: TargetType) {
|
||||
this.observer.unobserve(target);
|
||||
this.items.delete(target);
|
||||
}
|
||||
|
||||
public unlock() {
|
||||
this.locked = false;
|
||||
}
|
||||
|
||||
public unlockAndRefresh() {
|
||||
this.unlock();
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
public lock() {
|
||||
this.locked = true;
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ import webpWorkerController from '../lib/webp/webpWorkerController';
|
||||
import { readBlobAsText } from '../helpers/blob';
|
||||
import appMediaPlaybackController from './appMediaPlaybackController';
|
||||
import { PhotoSize } from '../layer';
|
||||
import { deferredPromise } from '../lib/polyfill';
|
||||
|
||||
export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue, noInfo, group}: {
|
||||
doc: MyDocument,
|
||||
@ -595,12 +596,12 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
|
||||
|
||||
//appDocsManager.downloadDocNew(doc.id).promise.then(res => res.json()).then(async(json) => {
|
||||
//fetch(doc.url).then(res => res.json()).then(async(json) => {
|
||||
await appDocsManager.downloadDocNew(doc)
|
||||
/* return */ await appDocsManager.downloadDocNew(doc)
|
||||
.then(readBlobAsText)
|
||||
.then(JSON.parse)
|
||||
.then(async(json) => {
|
||||
//console.timeEnd('download sticker' + doc.id);
|
||||
//console.log('loaded sticker:', doc, div, blob);
|
||||
//console.log('loaded sticker:', doc, div/* , blob */);
|
||||
if(middleware && !middleware()) return;
|
||||
|
||||
let animation = await LottieLoader.loadAnimationWorker/* loadAnimation */({
|
||||
@ -611,6 +612,8 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
|
||||
width,
|
||||
height
|
||||
}, group, toneIndex);
|
||||
|
||||
//const deferred = deferredPromise<void>();
|
||||
|
||||
animation.addListener('firstFrame', () => {
|
||||
if(div.firstElementChild && div.firstElementChild.tagName == 'IMG') {
|
||||
@ -618,6 +621,8 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
|
||||
} else {
|
||||
animation.canvas.classList.add('fade-in');
|
||||
}
|
||||
|
||||
//deferred.resolve();
|
||||
}, true);
|
||||
|
||||
if(emoji) {
|
||||
@ -629,6 +634,8 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//return deferred;
|
||||
//await new Promise((resolve) => setTimeout(resolve, 5e3));
|
||||
});
|
||||
|
||||
|
@ -68,7 +68,7 @@ export class AppChatsManager {
|
||||
public megagroupOnlines: {[id: number]: {timestamp: number, onlines: number}} = {};
|
||||
|
||||
constructor() {
|
||||
$rootScope.$on('apiUpdate', (e: CustomEvent) => {
|
||||
$rootScope.$on('apiUpdate', (e) => {
|
||||
// console.log('on apiUpdate', update)
|
||||
const update = e.detail;
|
||||
switch(update._) {
|
||||
|
@ -424,7 +424,7 @@ export class AppDialogsManager {
|
||||
(window as any).addElement = add;
|
||||
} */
|
||||
|
||||
$rootScope.$on('user_update', (e: CustomEvent) => {
|
||||
$rootScope.$on('user_update', (e) => {
|
||||
let userID = e.detail;
|
||||
|
||||
let user = appUsersManager.getUser(userID);
|
||||
@ -450,7 +450,7 @@ export class AppDialogsManager {
|
||||
}
|
||||
});
|
||||
|
||||
$rootScope.$on('dialog_top', (e: CustomEvent) => {
|
||||
$rootScope.$on('dialog_top', (e) => {
|
||||
let dialog: any = e.detail;
|
||||
|
||||
this.setLastMessage(dialog);
|
||||
@ -460,7 +460,7 @@ export class AppDialogsManager {
|
||||
this.setFiltersUnreadCount();
|
||||
});
|
||||
|
||||
$rootScope.$on('dialog_flush', (e: CustomEvent) => {
|
||||
$rootScope.$on('dialog_flush', (e) => {
|
||||
let peerID: number = e.detail.peerID;
|
||||
let dialog = appMessagesManager.getDialogByPeerID(peerID)[0];
|
||||
if(dialog) {
|
||||
@ -470,7 +470,7 @@ export class AppDialogsManager {
|
||||
}
|
||||
});
|
||||
|
||||
$rootScope.$on('dialogs_multiupdate', (e: CustomEvent) => {
|
||||
$rootScope.$on('dialogs_multiupdate', (e) => {
|
||||
const dialogs = e.detail;
|
||||
|
||||
for(const id in dialogs) {
|
||||
@ -483,7 +483,7 @@ export class AppDialogsManager {
|
||||
this.setFiltersUnreadCount();
|
||||
});
|
||||
|
||||
$rootScope.$on('dialog_drop', (e: CustomEvent) => {
|
||||
$rootScope.$on('dialog_drop', (e) => {
|
||||
let {peerID, dialog} = e.detail;
|
||||
|
||||
let dom = this.getDialogDom(peerID);
|
||||
@ -496,11 +496,8 @@ export class AppDialogsManager {
|
||||
this.setFiltersUnreadCount();
|
||||
});
|
||||
|
||||
$rootScope.$on('dialog_unread', (e: CustomEvent) => {
|
||||
let info: {
|
||||
peerID: number,
|
||||
count: number
|
||||
} = e.detail;
|
||||
$rootScope.$on('dialog_unread', (e) => {
|
||||
let info = e.detail;
|
||||
|
||||
let dialog = appMessagesManager.getDialogByPeerID(info.peerID)[0];
|
||||
if(dialog) {
|
||||
@ -522,7 +519,7 @@ export class AppDialogsManager {
|
||||
}
|
||||
});
|
||||
|
||||
$rootScope.$on('peer_changed', (e: CustomEvent) => {
|
||||
$rootScope.$on('peer_changed', (e) => {
|
||||
let peerID = e.detail;
|
||||
|
||||
let lastPeerID = this.lastActiveListElement && +this.lastActiveListElement.getAttribute('data-peerID');
|
||||
@ -540,7 +537,7 @@ export class AppDialogsManager {
|
||||
}
|
||||
});
|
||||
|
||||
$rootScope.$on('filter_update', (e: CustomEvent) => {
|
||||
$rootScope.$on('filter_update', (e) => {
|
||||
const filter: DialogFilter = e.detail;
|
||||
if(!this.filtersRendered[filter.id]) {
|
||||
this.addFilter(filter);
|
||||
@ -560,7 +557,7 @@ export class AppDialogsManager {
|
||||
elements.title.innerHTML = RichTextProcessor.wrapEmojiText(filter.title);
|
||||
});
|
||||
|
||||
$rootScope.$on('filter_delete', (e: CustomEvent) => {
|
||||
$rootScope.$on('filter_delete', (e) => {
|
||||
const filter: DialogFilter = e.detail;
|
||||
const elements = this.filtersRendered[filter.id];
|
||||
if(!elements) return;
|
||||
@ -580,7 +577,7 @@ export class AppDialogsManager {
|
||||
}
|
||||
});
|
||||
|
||||
/* $rootScope.$on('filter_pinned_order', (e: CustomEvent) => {
|
||||
/* $rootScope.$on('filter_pinned_order', (e) => {
|
||||
const {order, id} = e.detail as {order: number[], id: number};
|
||||
if(this.prevTabID != id) {
|
||||
return;
|
||||
|
@ -154,13 +154,13 @@ export class AppImManager {
|
||||
this.myID = $rootScope.myID = id;
|
||||
});
|
||||
|
||||
$rootScope.$on('user_auth', (e: CustomEvent) => {
|
||||
$rootScope.$on('user_auth', (e) => {
|
||||
let userAuth = e.detail;
|
||||
this.myID = $rootScope.myID = userAuth ? userAuth.id : 0;
|
||||
});
|
||||
|
||||
// will call when message is sent (only 1)
|
||||
$rootScope.$on('history_append', (e: CustomEvent) => {
|
||||
$rootScope.$on('history_append', (e) => {
|
||||
let details = e.detail;
|
||||
|
||||
if(!this.scrolledAllDown) {
|
||||
@ -171,7 +171,7 @@ export class AppImManager {
|
||||
});
|
||||
|
||||
// will call when sent for update pos
|
||||
$rootScope.$on('history_update', (e: CustomEvent) => {
|
||||
$rootScope.$on('history_update', (e) => {
|
||||
let details = e.detail;
|
||||
|
||||
if(details.mid && details.peerID == this.peerID) {
|
||||
@ -192,7 +192,7 @@ export class AppImManager {
|
||||
}
|
||||
});
|
||||
|
||||
$rootScope.$on('history_multiappend', (e: CustomEvent) => {
|
||||
$rootScope.$on('history_multiappend', (e) => {
|
||||
let msgIDsByPeer = e.detail;
|
||||
if(!(this.peerID in msgIDsByPeer)) return;
|
||||
|
||||
@ -201,23 +201,20 @@ export class AppImManager {
|
||||
this.renderNewMessagesByIDs(msgIDs);
|
||||
});
|
||||
|
||||
$rootScope.$on('history_delete', (e: CustomEvent) => {
|
||||
let detail: {
|
||||
peerID: string,
|
||||
msgs: {[x: number]: boolean}
|
||||
} = e.detail;
|
||||
$rootScope.$on('history_delete', (e) => {
|
||||
let detail = e.detail;
|
||||
|
||||
this.deleteMessagesByIDs(Object.keys(detail.msgs).map(s => +s));
|
||||
});
|
||||
|
||||
$rootScope.$on('dialog_flush', (e: CustomEvent) => {
|
||||
$rootScope.$on('dialog_flush', (e) => {
|
||||
let peerID: number = e.detail.peerID;
|
||||
if(this.peerID == peerID) {
|
||||
this.deleteMessagesByIDs(Object.keys(this.bubbles).map(m => +m));
|
||||
}
|
||||
});
|
||||
|
||||
$rootScope.$on('chat_update', (e: CustomEvent) => {
|
||||
$rootScope.$on('chat_update', (e) => {
|
||||
const peerID: number = e.detail;
|
||||
if(this.peerID == -peerID) {
|
||||
const chat = appChatsManager.getChat(peerID) as Channel | Chat;
|
||||
@ -227,7 +224,7 @@ export class AppImManager {
|
||||
});
|
||||
|
||||
// Calls when message successfully sent and we have an ID
|
||||
$rootScope.$on('message_sent', (e: CustomEvent) => {
|
||||
$rootScope.$on('message_sent', (e) => {
|
||||
const {tempID, mid} = e.detail;
|
||||
|
||||
this.log('message_sent', e.detail);
|
||||
@ -236,14 +233,14 @@ export class AppImManager {
|
||||
const message = appMessagesManager.getMessage(mid);
|
||||
if(message.media) {
|
||||
if(message.media.photo) {
|
||||
const photo = appPhotosManager.getPhoto(tempID);
|
||||
const photo = appPhotosManager.getPhoto('' + tempID);
|
||||
if(/* photo._ != 'photoEmpty' */photo) {
|
||||
const newPhoto = message.media.photo;
|
||||
newPhoto.downloaded = photo.downloaded;
|
||||
newPhoto.url = photo.url;
|
||||
}
|
||||
} else if(message.media.document) {
|
||||
const doc = appDocsManager.getDoc(tempID);
|
||||
const doc = appDocsManager.getDoc('' + tempID);
|
||||
if(/* doc._ != 'documentEmpty' && */doc?.type && doc.type != 'sticker') {
|
||||
const newDoc = message.media.document;
|
||||
newDoc.downloaded = doc.downloaded;
|
||||
@ -272,7 +269,7 @@ export class AppImManager {
|
||||
const pollElement = bubble.querySelector('poll-element');
|
||||
if(pollElement) {
|
||||
pollElement.setAttribute('poll-id', newPoll.id);
|
||||
pollElement.setAttribute('message-id', mid);
|
||||
pollElement.setAttribute('message-id', '' + mid);
|
||||
delete appPollsManager.polls[tempID];
|
||||
delete appPollsManager.results[tempID];
|
||||
}
|
||||
@ -286,7 +283,7 @@ export class AppImManager {
|
||||
|
||||
bubble.classList.remove('is-sending');
|
||||
bubble.classList.add('is-sent');
|
||||
bubble.dataset.mid = mid;
|
||||
bubble.dataset.mid = '' + mid;
|
||||
|
||||
this.bubbleGroups.removeBubble(bubble, tempID);
|
||||
|
||||
@ -301,7 +298,7 @@ export class AppImManager {
|
||||
}
|
||||
});
|
||||
|
||||
$rootScope.$on('message_edit', (e: CustomEvent) => {
|
||||
$rootScope.$on('message_edit', (e) => {
|
||||
let {peerID, mid, id, justMedia} = e.detail;
|
||||
|
||||
if(peerID != this.peerID) return;
|
||||
@ -318,7 +315,7 @@ export class AppImManager {
|
||||
this.renderMessage(message, true, false, bubble, false);
|
||||
});
|
||||
|
||||
$rootScope.$on('messages_downloaded', (e: CustomEvent) => {
|
||||
$rootScope.$on('messages_downloaded', (e) => {
|
||||
const mids: number[] = e.detail;
|
||||
|
||||
const pinnedMessage = appMessagesManager.getPinnedMessage(this.peerID);
|
||||
@ -351,7 +348,7 @@ export class AppImManager {
|
||||
});
|
||||
});
|
||||
|
||||
$rootScope.$on('apiUpdate', (e: CustomEvent) => {
|
||||
$rootScope.$on('apiUpdate', (e) => {
|
||||
let update = e.detail;
|
||||
|
||||
this.handleUpdate(update);
|
||||
|
@ -9,7 +9,7 @@ import appDocsManager, {MyDocument} from "./appDocsManager";
|
||||
import VideoPlayer from "../mediaPlayer";
|
||||
import { renderImageFromUrl, parseMenuButtonsTo } from "../../components/misc";
|
||||
import AvatarElement from "../../components/avatar";
|
||||
import LazyLoadQueue from "../../components/lazyLoadQueue";
|
||||
import LazyLoadQueue, { LazyLoadQueueBase } from "../../components/lazyLoadQueue";
|
||||
import appForward from "../../components/appForward";
|
||||
import { isSafari, mediaSizes, touchSupport } from "../config";
|
||||
import { deferredPromise } from "../polyfill";
|
||||
@ -118,7 +118,7 @@ export class AppMediaViewer {
|
||||
private setMoverPromise: Promise<void>;
|
||||
private setMoverAnimationPromise: Promise<void>;
|
||||
|
||||
private lazyLoadQueue: LazyLoadQueue;
|
||||
private lazyLoadQueue: LazyLoadQueueBase;
|
||||
|
||||
private highlightSwitchersTimeout: number;
|
||||
|
||||
@ -128,7 +128,7 @@ export class AppMediaViewer {
|
||||
|
||||
this.preloaderStreamable = new ProgressivePreloader(undefined, false, true);
|
||||
|
||||
this.lazyLoadQueue = new LazyLoadQueue(undefined, true);
|
||||
this.lazyLoadQueue = new LazyLoadQueueBase();
|
||||
|
||||
parseMenuButtonsTo(this.buttons, this.wholeDiv.querySelectorAll(`[class*='menu']`) as NodeListOf<HTMLElement>);
|
||||
|
||||
@ -271,7 +271,7 @@ export class AppMediaViewer {
|
||||
this.wholeDiv.classList.add('highlight-switchers');
|
||||
}
|
||||
|
||||
this.highlightSwitchersTimeout = setTimeout(() => {
|
||||
this.highlightSwitchersTimeout = window.setTimeout(() => {
|
||||
this.wholeDiv.classList.remove('highlight-switchers');
|
||||
this.highlightSwitchersTimeout = 0;
|
||||
}, 3e3);
|
||||
@ -1075,8 +1075,7 @@ export class AppMediaViewer {
|
||||
|
||||
this.lazyLoadQueue.unshift({
|
||||
div: null,
|
||||
load,
|
||||
wasSeen: true
|
||||
load
|
||||
});
|
||||
//} else createPlayer();
|
||||
});
|
||||
@ -1140,8 +1139,7 @@ export class AppMediaViewer {
|
||||
|
||||
this.lazyLoadQueue.unshift({
|
||||
div: null,
|
||||
load,
|
||||
wasSeen: true
|
||||
load
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { $rootScope, copy, tsNow, safeReplaceObject, dT, listMergeSorted, deepEqual, langPack } from "../utils";
|
||||
import { $rootScope, copy, tsNow, safeReplaceObject, listMergeSorted, deepEqual, langPack } from "../utils";
|
||||
import appMessagesIDsManager from "./appMessagesIDsManager";
|
||||
import appChatsManager from "./appChatsManager";
|
||||
import appUsersManager from "./appUsersManager";
|
||||
@ -231,7 +231,7 @@ export class FiltersStorage {
|
||||
public orderIndex = 0;
|
||||
|
||||
constructor() {
|
||||
$rootScope.$on('apiUpdate', (e: CustomEvent) => {
|
||||
$rootScope.$on('apiUpdate', (e) => {
|
||||
this.handleUpdate(e.detail);
|
||||
});
|
||||
}
|
||||
@ -476,7 +476,7 @@ export class AppMessagesManager {
|
||||
public lastSearchFilter: any = {};
|
||||
public lastSearchResults: any = [];
|
||||
|
||||
public needSingleMessages: any = [];
|
||||
public needSingleMessages: number[] = [];
|
||||
public fetchSingleMessagesTimeout = 0;
|
||||
private fetchSingleMessagesPromise: Promise<any> = null;
|
||||
|
||||
@ -486,7 +486,7 @@ export class AppMessagesManager {
|
||||
public migratedToFrom: {[peerID: number]: number} = {};
|
||||
|
||||
public newMessagesHandlePromise = 0;
|
||||
public newMessagesToHandle: any = {};
|
||||
public newMessagesToHandle: {[peerID: string]: number[]} = {};
|
||||
public newDialogsHandlePromise = 0;
|
||||
public newDialogsToHandle: {[peerID: string]: {reload: true} | Dialog} = {};
|
||||
public newUpdatesAfterReloadToHandle: any = {};
|
||||
@ -511,11 +511,11 @@ export class AppMessagesManager {
|
||||
public filtersStorage = new FiltersStorage();
|
||||
|
||||
constructor() {
|
||||
$rootScope.$on('apiUpdate', (e: CustomEvent) => {
|
||||
$rootScope.$on('apiUpdate', (e) => {
|
||||
this.handleUpdate(e.detail);
|
||||
});
|
||||
|
||||
$rootScope.$on('webpage_updated', (e: CustomEvent) => {
|
||||
$rootScope.$on('webpage_updated', (e) => {
|
||||
let eventData = e.detail;
|
||||
eventData.msgs.forEach((msgID: number) => {
|
||||
let message = this.getMessage(msgID);
|
||||
@ -529,7 +529,7 @@ export class AppMessagesManager {
|
||||
});
|
||||
});
|
||||
|
||||
$rootScope.$on('draft_updated', (e: CustomEvent) => {
|
||||
/* $rootScope.$on('draft_updated', (e) => {
|
||||
let eventData = e.detail;;
|
||||
var peerID = eventData.peerID;
|
||||
var draft = eventData.draft;
|
||||
@ -558,12 +558,12 @@ export class AppMessagesManager {
|
||||
this.dialogsStorage.pushDialog(dialog);
|
||||
|
||||
$rootScope.$broadcast('dialog_draft', {
|
||||
peerID: peerID,
|
||||
draft: draft,
|
||||
peerID,
|
||||
draft,
|
||||
index: dialog.index
|
||||
});
|
||||
}
|
||||
});
|
||||
}); */
|
||||
}
|
||||
|
||||
|
||||
@ -847,7 +847,7 @@ export class AppMessagesManager {
|
||||
|
||||
this.saveMessages([message]);
|
||||
historyStorage.pending.unshift(messageID);
|
||||
$rootScope.$broadcast('history_append', {peerID: peerID, messageID: messageID, my: true});
|
||||
$rootScope.$broadcast('history_append', {peerID, messageID, my: true});
|
||||
|
||||
setTimeout(() => message.send(), 0);
|
||||
// setTimeout(function () {
|
||||
@ -1216,7 +1216,7 @@ export class AppMessagesManager {
|
||||
|
||||
this.saveMessages([message]);
|
||||
historyStorage.pending.unshift(messageID);
|
||||
$rootScope.$broadcast('history_append', {peerID: peerID, messageID: messageID, my: true});
|
||||
$rootScope.$broadcast('history_append', {peerID, messageID, my: true});
|
||||
|
||||
setTimeout(message.send.bind(this), 0);
|
||||
|
||||
@ -1370,7 +1370,7 @@ export class AppMessagesManager {
|
||||
return message;
|
||||
});
|
||||
|
||||
$rootScope.$broadcast('history_append', {peerID: peerID, messageID: messages[messages.length - 1].id, my: true});
|
||||
$rootScope.$broadcast('history_append', {peerID, messageID: messages[messages.length - 1].id, my: true});
|
||||
|
||||
let toggleError = (message: any, on: boolean) => {
|
||||
if(on) {
|
||||
@ -1730,7 +1730,7 @@ export class AppMessagesManager {
|
||||
|
||||
this.saveMessages([message]);
|
||||
historyStorage.pending.unshift(messageID);
|
||||
$rootScope.$broadcast('history_append', {peerID: peerID, messageID: messageID, my: true});
|
||||
$rootScope.$broadcast('history_append', {peerID, messageID, my: true});
|
||||
|
||||
setTimeout(message.send, 0);
|
||||
|
||||
@ -1876,20 +1876,25 @@ export class AppMessagesManager {
|
||||
//flags |= 1; // means pinned already loaded
|
||||
}
|
||||
|
||||
/* if(this.dialogsStorage.dialogsOffsetDate[0]) {
|
||||
flags |= 1; // means pinned already loaded
|
||||
} */
|
||||
|
||||
//if(folderID > 0) {
|
||||
//flags |= 1;
|
||||
flags |= 2;
|
||||
//}
|
||||
|
||||
// ! ВНИМАНИЕ: ОЧЕНЬ СЛОЖНАЯ ЛОГИКА:
|
||||
// ! если делать запрос сначала по папке 0, потом по папке 1, по индексу 0 в массиве будет один и тот же диалог, при условии что он закреплён в обеих папках, ЛОЛ???
|
||||
// ! если делать запрос сначала по папке 0, потом по папке 1, по индексу 0 в массиве будет один и тот же диалог, с dialog.pFlags.pinned, ЛОЛ???
|
||||
// ! т.е., с запросом folder_id: 1, и exclude_pinned: 0, в результате будут ещё и закреплённые с папки 0
|
||||
return apiManager.invokeApi('messages.getDialogs', {
|
||||
flags: flags,
|
||||
flags,
|
||||
folder_id: folderID,
|
||||
offset_date: offsetDate,
|
||||
offset_id: appMessagesIDsManager.getMessageLocalID(offsetID),
|
||||
offset_peer: appPeersManager.getInputPeerByID(offsetPeerID),
|
||||
limit: limit,
|
||||
limit,
|
||||
hash: 0
|
||||
}, {
|
||||
//timeout: APITIMEOUT,
|
||||
@ -1897,7 +1902,7 @@ export class AppMessagesManager {
|
||||
}).then((dialogsResult) => {
|
||||
if(dialogsResult._ == 'messages.dialogsNotModified') return null;
|
||||
|
||||
//this.log.error('messages.getDialogs result:', {...dialogsResult.dialogs[0]});
|
||||
//this.log.error('messages.getDialogs result:', dialogsResult.dialogs, {...dialogsResult.dialogs[0]});
|
||||
|
||||
if(!offsetDate) {
|
||||
telegramMeWebService.setAuthorized(true);
|
||||
@ -1909,7 +1914,7 @@ export class AppMessagesManager {
|
||||
|
||||
let maxSeenIdIncremented = offsetDate ? true : false;
|
||||
let hasPrepend = false;
|
||||
let noIDsDialogs: {[peerID: number]: Dialog} = {};
|
||||
const noIDsDialogs: {[peerID: number]: Dialog} = {};
|
||||
(dialogsResult.dialogs as Dialog[]).forEachReverse(dialog => {
|
||||
//const d = Object.assign({}, dialog);
|
||||
// ! нужно передавать folderID, так как по папке != 0 нет свойства folder_id
|
||||
@ -2561,7 +2566,7 @@ export class AppMessagesManager {
|
||||
$rootScope.$broadcast('dialog_drop', {peerID: migrateFrom, dialog: dropped[0]});
|
||||
}
|
||||
|
||||
$rootScope.$broadcast('dialog_migrate', {migrateFrom: migrateFrom, migrateTo: migrateTo});
|
||||
$rootScope.$broadcast('dialog_migrate', {migrateFrom, migrateTo});
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
@ -2756,7 +2761,7 @@ export class AppMessagesManager {
|
||||
}
|
||||
this.historiesStorage[peerID] = historyStorage;
|
||||
if(this.mergeReplyKeyboard(historyStorage, message)) {
|
||||
$rootScope.$broadcast('history_reply_markup', {peerID: peerID});
|
||||
$rootScope.$broadcast('history_reply_markup', {peerID});
|
||||
}
|
||||
}
|
||||
|
||||
@ -3209,7 +3214,7 @@ export class AppMessagesManager {
|
||||
apiPromise = apiManager.invokeApi('messages.readHistory', {
|
||||
peer: appPeersManager.getInputPeerByID(peerID),
|
||||
max_id: maxID
|
||||
}).then((affectedMessages: any) => {
|
||||
}).then((affectedMessages) => {
|
||||
apiUpdatesManager.processUpdateMessage({
|
||||
_: 'updateShort',
|
||||
update: {
|
||||
@ -3299,7 +3304,7 @@ export class AppMessagesManager {
|
||||
} else {
|
||||
apiManager.invokeApi('messages.readMessageContents', {
|
||||
id: msgIDs
|
||||
}).then((affectedMessages: any) => {
|
||||
}).then((affectedMessages) => {
|
||||
apiUpdatesManager.processUpdateMessage({
|
||||
_: 'updateShort',
|
||||
update: {
|
||||
@ -3385,7 +3390,7 @@ export class AppMessagesManager {
|
||||
var topMsgID = history[0];
|
||||
history.unshift(message.mid);
|
||||
if(message.mid > 0 && message.mid < topMsgID) {
|
||||
history.sort((a: any, b: any) => {
|
||||
history.sort((a, b) => {
|
||||
return b - a;
|
||||
});
|
||||
}
|
||||
@ -3396,7 +3401,7 @@ export class AppMessagesManager {
|
||||
}
|
||||
|
||||
if(this.mergeReplyKeyboard(historyStorage, message)) {
|
||||
$rootScope.$broadcast('history_reply_markup', {peerID: peerID});
|
||||
$rootScope.$broadcast('history_reply_markup', {peerID});
|
||||
}
|
||||
|
||||
if(!message.pFlags.out && message.from_id) {
|
||||
@ -3408,7 +3413,7 @@ export class AppMessagesManager {
|
||||
|
||||
if(randomID) {
|
||||
if(pendingMessage = this.finalizePendingMessage(randomID, message)) {
|
||||
$rootScope.$broadcast('history_update', {peerID: peerID, mid: message.mid});
|
||||
$rootScope.$broadcast('history_update', {peerID, mid: message.mid});
|
||||
}
|
||||
|
||||
delete this.pendingByMessageID[message.mid];
|
||||
@ -3542,11 +3547,11 @@ export class AppMessagesManager {
|
||||
dialogsResult.dialogs.reverse();
|
||||
this.applyConversations(dialogsResult);
|
||||
|
||||
dialogsResult.dialogs.forEach((dialog: any) => {
|
||||
dialogsResult.dialogs.forEach((dialog) => {
|
||||
newPinned[dialog.peerID] = true;
|
||||
});
|
||||
|
||||
this.dialogsStorage.getFolder(folderID).forEach((dialog: any) => {
|
||||
this.dialogsStorage.getFolder(folderID).forEach((dialog) => {
|
||||
const peerID = dialog.peerID;
|
||||
if(dialog.pFlags.pinned && !newPinned[peerID]) {
|
||||
this.newDialogsToHandle[peerID] = {reload: true};
|
||||
@ -3626,7 +3631,7 @@ export class AppMessagesManager {
|
||||
});
|
||||
|
||||
if(isTopMessage) {
|
||||
var updatedDialogs: any = {};
|
||||
var updatedDialogs: {[peerID: number]: Dialog} = {};
|
||||
updatedDialogs[peerID] = dialog;
|
||||
$rootScope.$broadcast('dialogs_multiupdate', updatedDialogs);
|
||||
}
|
||||
@ -3889,10 +3894,7 @@ export class AppMessagesManager {
|
||||
let message = this.getMessage(mid);
|
||||
if(message && message.views && message.views < views) {
|
||||
message.views = views;
|
||||
$rootScope.$broadcast('message_views', {
|
||||
mid: mid,
|
||||
views: views
|
||||
});
|
||||
$rootScope.$broadcast('message_views', {mid, views});
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -3987,7 +3989,7 @@ export class AppMessagesManager {
|
||||
|
||||
public finalizePendingMessageCallbacks(tempID: number, mid: number) {
|
||||
var callbacks = this.tempFinalizeCallbacks[tempID];
|
||||
this.log.warn(dT(), callbacks, tempID);
|
||||
this.log.warn(callbacks, tempID);
|
||||
if(callbacks !== undefined) {
|
||||
callbacks.forEach((callback: any) => {
|
||||
callback(mid);
|
||||
@ -4003,9 +4005,9 @@ export class AppMessagesManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
AppStorage.set({
|
||||
max_seen_msg: maxID
|
||||
});
|
||||
this.maxSeenID = maxID;
|
||||
|
||||
AppStorage.set({max_seen_msg: maxID});
|
||||
|
||||
apiManager.invokeApi('messages.receivedMessages', {
|
||||
max_id: maxID
|
||||
@ -4149,7 +4151,7 @@ export class AppMessagesManager {
|
||||
historyStorage.history.splice(offset, historyStorage.history.length - offset);
|
||||
historyResult.messages.forEach((message: any) => {
|
||||
if(this.mergeReplyKeyboard(historyStorage, message)) {
|
||||
$rootScope.$broadcast('history_reply_markup', {peerID: peerID});
|
||||
$rootScope.$broadcast('history_reply_markup', {peerID});
|
||||
}
|
||||
|
||||
historyStorage.history.push(message.mid);
|
||||
|
@ -75,7 +75,7 @@ class AppPollsManager {
|
||||
private log = logger('POLLS', LogLevels.error);
|
||||
|
||||
constructor() {
|
||||
$rootScope.$on('apiUpdate', (e: CustomEvent) => {
|
||||
$rootScope.$on('apiUpdate', (e) => {
|
||||
let update = e.detail;
|
||||
|
||||
this.handleUpdate(update);
|
||||
|
@ -249,7 +249,7 @@ export class AppSidebarLeft extends SidebarSlider {
|
||||
});
|
||||
});
|
||||
|
||||
$rootScope.$on('dialogs_archived_unread', (e: CustomEvent) => {
|
||||
$rootScope.$on('dialogs_archived_unread', (e) => {
|
||||
this.archivedCount.innerText = '' + e.detail.count;
|
||||
});
|
||||
|
||||
|
@ -42,7 +42,7 @@ class AppStickersManager {
|
||||
//}
|
||||
});
|
||||
|
||||
$rootScope.$on('apiUpdate', (e: CustomEvent) => {
|
||||
$rootScope.$on('apiUpdate', (e) => {
|
||||
const update = e.detail;
|
||||
|
||||
switch(update._) {
|
||||
@ -121,7 +121,7 @@ class AppStickersManager {
|
||||
|
||||
//console.log('stickers wrote', this.stickerSets);
|
||||
if(this.saveSetsTimeout) return;
|
||||
this.saveSetsTimeout = setTimeout(() => {
|
||||
this.saveSetsTimeout = window.setTimeout(() => {
|
||||
const savedSets: {[id: string]: MessagesStickerSet} = {};
|
||||
for(const id in this.stickerSets) {
|
||||
const set = this.stickerSets[id];
|
||||
|
@ -64,7 +64,7 @@ export class AppUsersManager {
|
||||
this.myID = id;
|
||||
});
|
||||
|
||||
$rootScope.$on('user_auth', (e: CustomEvent) => {
|
||||
$rootScope.$on('user_auth', (e) => {
|
||||
let userAuth = e.detail;
|
||||
this.myID = userAuth ? userAuth.id : 0;
|
||||
});
|
||||
@ -73,7 +73,7 @@ export class AppUsersManager {
|
||||
|
||||
$rootScope.$on('stateSynchronized', this.updateUsersStatuses.bind(this));
|
||||
|
||||
$rootScope.$on('apiUpdate', (e: CustomEvent) => {
|
||||
$rootScope.$on('apiUpdate', (e) => {
|
||||
let update = e.detail;
|
||||
//console.log('on apiUpdate', update);
|
||||
switch(update._) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { $rootScope, safeReplaceObject, copy } from "../utils";
|
||||
import { $rootScope, safeReplaceObject } from "../utils";
|
||||
import appPhotosManager from "./appPhotosManager";
|
||||
import appDocsManager from "./appDocsManager";
|
||||
import { RichTextProcessor } from "../richtextprocessor";
|
||||
@ -8,7 +8,7 @@ class AppWebPagesManager {
|
||||
pendingWebPages: any = {};
|
||||
|
||||
constructor() {
|
||||
$rootScope.$on('apiUpdate', (e: CustomEvent) => {
|
||||
$rootScope.$on('apiUpdate', (e) => {
|
||||
let update = e.detail;
|
||||
|
||||
switch(update._) {
|
||||
@ -91,9 +91,9 @@ class AppWebPagesManager {
|
||||
}
|
||||
|
||||
if(!messageID && this.pendingWebPages[apiWebPage.id] !== undefined) {
|
||||
var msgs = [];
|
||||
const msgs: number[] = [];
|
||||
for(let msgID in this.pendingWebPages[apiWebPage.id]) {
|
||||
msgs.push(msgID);
|
||||
msgs.push(+msgID);
|
||||
}
|
||||
|
||||
$rootScope.$broadcast('webpage_updated', {
|
||||
|
@ -279,7 +279,7 @@ export class RLottiePlayer {
|
||||
|
||||
if(delta < 0) {
|
||||
if(this.rafId) clearTimeout(this.rafId);
|
||||
return this.rafId = setTimeout(() => {
|
||||
return this.rafId = window.setTimeout(() => {
|
||||
this.renderFrame2(frame, frameNo);
|
||||
}, this.frInterval > -delta ? -delta % this.frInterval : this.frInterval);
|
||||
//await new Promise((resolve) => setTimeout(resolve, -delta % this.frInterval));
|
||||
|
@ -1,4 +1,7 @@
|
||||
import { Dialog } from "./appManagers/appMessagesManager";
|
||||
import type { StickerSet } from "../layer";
|
||||
import type { MyDocument } from "./appManagers/appDocsManager";
|
||||
import type { Poll, PollResults } from "./appManagers/appPollsManager";
|
||||
import type { AppMessagesManager, Dialog, MyDialogFilter } from "./appManagers/appMessagesManager";
|
||||
/*!
|
||||
* Webogram v0.7.0 - messaging web application for MTProto
|
||||
* https://github.com/zhukov/webogram
|
||||
@ -153,55 +156,58 @@ export function getRichElementValue(node: any, lines: string[], line: string[],
|
||||
} */
|
||||
|
||||
type BroadcastEvents = {
|
||||
'download_progress': any,
|
||||
'user_update': any,
|
||||
'user_auth': any,
|
||||
'peer_changed': any,
|
||||
'user_update': number,
|
||||
'user_auth': {dcID?: number, id: number},
|
||||
'peer_changed': number,
|
||||
|
||||
'filter_delete': any,
|
||||
'filter_update': any,
|
||||
'filter_delete': MyDialogFilter,
|
||||
'filter_update': MyDialogFilter,
|
||||
|
||||
'dialog_draft': any,
|
||||
'dialog_draft': {peerID: number, draft: any, index: number},
|
||||
'dialog_unread': {peerID: number, count?: number},
|
||||
'dialog_flush': {peerID: number},
|
||||
'dialog_drop': {peerID: number, dialog?: Dialog},
|
||||
'dialog_migrate': any,
|
||||
'dialog_migrate': {migrateFrom: number, migrateTo: number},
|
||||
'dialog_top': Dialog,
|
||||
'dialog_notify_settings': number,
|
||||
'dialogs_multiupdate': {[peerID: string]: Dialog},
|
||||
'dialogs_archived_unread': any,
|
||||
'dialogs_archived_unread': {count: number},
|
||||
|
||||
'history_append': any,
|
||||
'history_update': any,
|
||||
'history_reply_markup': any,
|
||||
'history_multiappend': any,
|
||||
'history_append': {peerID: number, messageID: number, my?: boolean},
|
||||
'history_update': {peerID: number, mid: number},
|
||||
'history_reply_markup': {peerID: number},
|
||||
'history_multiappend': AppMessagesManager['newMessagesToHandle'],
|
||||
'history_delete': {peerID: number, msgs: {[mid: number]: true}},
|
||||
'history_forbidden': number,
|
||||
'history_reload': number,
|
||||
'history_request': any,
|
||||
'history_request': void,
|
||||
|
||||
'message_edit': any,
|
||||
'message_views': any,
|
||||
'message_sent': any,
|
||||
'message_edit': {peerID: number, id: number, mid: number, justMedia: boolean},
|
||||
'message_views': {mid: number, views: number},
|
||||
'message_sent': {tempID: number, mid: number},
|
||||
'messages_pending': void,
|
||||
'messages_read': any,
|
||||
'messages_downloaded': any,
|
||||
'messages_read': void,
|
||||
'messages_downloaded': number[],
|
||||
|
||||
'stickers_installed': StickerSet.stickerSet,
|
||||
'stickers_deleted': StickerSet.stickerSet,
|
||||
|
||||
'audio_play': {doc: MyDocument, mid: number},
|
||||
'audio_pause': void,
|
||||
|
||||
//'contacts_update': any,
|
||||
'avatar_update': number,
|
||||
'chat_full_update': number,
|
||||
'peer_pinned_message': number,
|
||||
'poll_update': {poll: Poll, results: PollResults},
|
||||
'chat_update': number,
|
||||
'stateSynchronized': void,
|
||||
'channel_settings': {channelID: number},
|
||||
'webpage_updated': {id: string, msgs: number[]},
|
||||
|
||||
'contacts_update': any,
|
||||
'avatar_update': any,
|
||||
'stickers_installed': any,
|
||||
'stickers_deleted': any,
|
||||
'chat_full_update': any,
|
||||
'peer_pinned_message': any,
|
||||
'poll_update': any,
|
||||
'audio_play': any,
|
||||
'audio_pause': any,
|
||||
'chat_update': any,
|
||||
'apiUpdate': any,
|
||||
'stateSynchronized': any,
|
||||
'channel_settings': any,
|
||||
'webpage_updated': any,
|
||||
'draft_updated': any,
|
||||
'download_progress': any,
|
||||
//'draft_updated': any,
|
||||
};
|
||||
|
||||
export const $rootScope = {
|
||||
|
Loading…
x
Reference in New Issue
Block a user