Lazy load queue refactored

This commit is contained in:
morethanwords 2020-09-20 01:38:00 +03:00
parent 904b4a7eb9
commit 0333ecfcce
23 changed files with 568 additions and 335 deletions

View File

@ -1,7 +1,7 @@
import appProfileManager from "../lib/appManagers/appProfileManager"; import appProfileManager from "../lib/appManagers/appProfileManager";
import { $rootScope } from "../lib/utils"; import { $rootScope } from "../lib/utils";
$rootScope.$on('avatar_update', (e: CustomEvent) => { $rootScope.$on('avatar_update', (e) => {
let peerID = e.detail; let peerID = e.detail;
appProfileManager.removeFromAvatarsCache(peerID); appProfileManager.removeFromAvatarsCache(peerID);

View File

@ -46,7 +46,7 @@ export class ChatAudio {
appMediaPlaybackController.toggle(); appMediaPlaybackController.toggle();
}); });
$rootScope.$on('audio_play', (e: CustomEvent) => { $rootScope.$on('audio_play', (e) => {
const {doc, mid} = e.detail; const {doc, mid} = e.detail;
let title: string, subtitle: string; let title: string, subtitle: string;

View File

@ -81,7 +81,7 @@ export class EmoticonsDropdown {
} }
clearTimeout(this.displayTimeout); clearTimeout(this.displayTimeout);
this.displayTimeout = setTimeout(() => { this.displayTimeout = window.setTimeout(() => {
this.toggle(false); this.toggle(false);
}, 200); }, 200);
}; };
@ -188,7 +188,7 @@ export class EmoticonsDropdown {
if((this.element.style.display && enable === undefined) || enable) { if((this.element.style.display && enable === undefined) || enable) {
this.events.onOpen.forEach(cb => cb()); this.events.onOpen.forEach(cb => cb());
EmoticonsDropdown.lazyLoadQueue.lockIntersection(); EmoticonsDropdown.lazyLoadQueue.lock();
//EmoticonsDropdown.lazyLoadQueue.unlock(); //EmoticonsDropdown.lazyLoadQueue.unlock();
animationIntersector.lockIntersectionGroup(EMOTICONSSTICKERGROUP); animationIntersector.lockIntersectionGroup(EMOTICONSSTICKERGROUP);
@ -197,9 +197,10 @@ export class EmoticonsDropdown {
this.element.classList.add('active'); this.element.classList.add('active');
clearTimeout(this.displayTimeout); clearTimeout(this.displayTimeout);
this.displayTimeout = setTimeout(() => { this.displayTimeout = window.setTimeout(() => {
animationIntersector.unlockIntersectionGroup(EMOTICONSSTICKERGROUP); animationIntersector.unlockIntersectionGroup(EMOTICONSSTICKERGROUP);
EmoticonsDropdown.lazyLoadQueue.unlockIntersection(); EmoticonsDropdown.lazyLoadQueue.unlock();
EmoticonsDropdown.lazyLoadQueue.refresh();
this.events.onOpenAfter.forEach(cb => cb()); this.events.onOpenAfter.forEach(cb => cb());
}, touchSupport ? 0 : 200); }, touchSupport ? 0 : 200);
@ -210,7 +211,7 @@ export class EmoticonsDropdown {
} else { } else {
this.events.onClose.forEach(cb => cb()); this.events.onClose.forEach(cb => cb());
EmoticonsDropdown.lazyLoadQueue.lockIntersection(); EmoticonsDropdown.lazyLoadQueue.lock();
//EmoticonsDropdown.lazyLoadQueue.lock(); //EmoticonsDropdown.lazyLoadQueue.lock();
// нужно залочить группу и выключить стикеры // нужно залочить группу и выключить стикеры
@ -220,12 +221,13 @@ export class EmoticonsDropdown {
this.element.classList.remove('active'); this.element.classList.remove('active');
clearTimeout(this.displayTimeout); clearTimeout(this.displayTimeout);
this.displayTimeout = setTimeout(() => { this.displayTimeout = window.setTimeout(() => {
this.element.style.display = 'none'; this.element.style.display = 'none';
// теперь можно убрать visible, чтобы они не включились после фокуса // теперь можно убрать visible, чтобы они не включились после фокуса
animationIntersector.unlockIntersectionGroup(EMOTICONSSTICKERGROUP); animationIntersector.unlockIntersectionGroup(EMOTICONSSTICKERGROUP);
EmoticonsDropdown.lazyLoadQueue.unlockIntersection(); EmoticonsDropdown.lazyLoadQueue.unlock();
EmoticonsDropdown.lazyLoadQueue.refresh();
this.events.onCloseAfter.forEach(cb => cb()); this.events.onCloseAfter.forEach(cb => cb());
}, touchSupport ? 0 : 200); }, touchSupport ? 0 : 200);

View File

@ -13,6 +13,7 @@ import apiManager from "../../../lib/mtproto/mtprotoworker";
import StickyIntersector from "../../stickyIntersector"; import StickyIntersector from "../../stickyIntersector";
import appDocsManager, {MyDocument} from "../../../lib/appManagers/appDocsManager"; import appDocsManager, {MyDocument} from "../../../lib/appManagers/appDocsManager";
import animationIntersector from "../../animationIntersector"; import animationIntersector from "../../animationIntersector";
import LazyLoadQueue, { LazyLoadQueueRepeat } from "../../lazyLoadQueue";
export default class StickersTab implements EmoticonsTab { export default class StickersTab implements EmoticonsTab {
public content: HTMLElement; public content: HTMLElement;
@ -36,7 +37,7 @@ export default class StickersTab implements EmoticonsTab {
private stickyIntersector: StickyIntersector; private stickyIntersector: StickyIntersector;
private animatedDivs: Set<HTMLDivElement> = new Set(); private animatedDivs: Set<HTMLDivElement> = new Set();
private animatedIntersector: IntersectionObserver; private lazyLoadQueue: LazyLoadQueueRepeat;
categoryPush(categoryDiv: HTMLElement, categoryTitle: string, promise: Promise<MyDocument[]>, prepend?: boolean) { categoryPush(categoryDiv: HTMLElement, categoryTitle: string, promise: Promise<MyDocument[]>, prepend?: boolean) {
//if((docs.length % 5) != 0) categoryDiv.classList.add('not-full'); //if((docs.length % 5) != 0) categoryDiv.classList.add('not-full');
@ -83,7 +84,11 @@ export default class StickersTab implements EmoticonsTab {
if(doc.sticker == 2) { if(doc.sticker == 2) {
this.animatedDivs.add(div); 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() { init() {
this.content = document.getElementById('content-stickers'); this.content = document.getElementById('content-stickers');
//let stickersDiv = contentStickersDiv.querySelector('.os-content') as HTMLDivElement; //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; const set: StickerSet.stickerSet = e.detail;
if(!this.stickerSets[set.id] && this.mounted) { 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; const set: StickerSet.stickerSet = e.detail;
if(this.stickerSets[set.id] && this.mounted) { if(this.stickerSets[set.id] && this.mounted) {
@ -256,98 +313,59 @@ export default class StickersTab implements EmoticonsTab {
this.mounted = true; this.mounted = true;
}); });
const checkAnimationDiv = (div: HTMLDivElement) => { this.lazyLoadQueue = new LazyLoadQueueRepeat(undefined, (target, visible) => {
const players = animationIntersector.getAnimations(div); if(!visible) {
players.forEach(player => { this.processInvisibleDiv(target as HTMLDivElement);
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;
}
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(() => { emoticonsDropdown.events.onClose.push(() => {
locked = true; closed = false;
this.lazyLoadQueue.lock();
}); });
emoticonsDropdown.events.onCloseAfter.push(() => { emoticonsDropdown.events.onCloseAfter.push(() => {
const divs = [...visible]; const divs = this.lazyLoadQueue.intersector.getVisible();
for(const div of divs) { for(const div of divs) {
processInvisibleDiv(div); this.processInvisibleDiv(div);
} }
closed = true;
}); });
emoticonsDropdown.events.onOpenAfter.push(() => { emoticonsDropdown.events.onOpenAfter.push(() => {
locked = false; if(closed) {
this.lazyLoadQueue.unlockAndRefresh();
// refresh closed = false;
this.animatedIntersector.disconnect(); } else {
const divs = [...this.animatedDivs]; this.lazyLoadQueue.unlock();
for(const div of divs) {
this.animatedIntersector.observe(div);
} }
}); */
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(() => { /* setInterval(() => {
// @ts-ignore // @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); */ }, .25e3); */

View File

@ -1,67 +1,35 @@
import { logger, LogLevels } from "../lib/logger"; import { logger, LogLevels } from "../lib/logger";
import VisibilityIntersector, { OnVisibilityChange } from "./visibilityIntersector";
type LazyLoadElement = { type LazyLoadElementBase = {
div: HTMLDivElement, div: HTMLDivElement,
load: () => Promise<any>, load: (target?: HTMLDivElement) => Promise<any>
};
type LazyLoadElement = LazyLoadElementBase & {
wasSeen?: boolean wasSeen?: boolean
}; };
export default class LazyLoadQueue { export class LazyLoadQueueBase {
private lazyLoadMedia: Array<LazyLoadElement> = []; protected lazyLoadMedia: Array<LazyLoadElementBase> = [];
private inProcess: Array<LazyLoadElement> = []; protected inProcess: Set<LazyLoadElementBase> = new Set();
private lockPromise: Promise<void> = null; protected lockPromise: Promise<void> = null;
private unlockResolve: () => 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 constructor(protected parallelLimit = 5) {
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();
}
});
} }
public clear() { public clear() {
this.inProcess.length = 0; // ацтеки забьются, будет плохо this.inProcess.clear(); // ацтеки забьются, будет плохо
this.lazyLoadMedia.length = 0; this.lazyLoadMedia.length = 0;
for(let item of this.inProcess) { // unreachable code
/* for(let item of this.inProcess) {
this.lazyLoadMedia.push(item); this.lazyLoadMedia.push(item);
} } */
if(this.observer) {
this.observer.disconnect();
}
}
public length() {
return this.lazyLoadMedia.length + this.inProcess.length;
} }
public lock() { public lock() {
@ -78,23 +46,14 @@ export default class LazyLoadQueue {
this.unlockResolve = null; this.unlockResolve = null;
} }
public async processQueue(item?: LazyLoadElement) { public async processItem(item: LazyLoadElementBase) {
if(this.parallelLimit > 0 && this.inProcess.length >= this.parallelLimit) return; this.inProcess.add(item);
if(item) {
this.lazyLoadMedia.findAndSplice(i => i == item);
} else {
item = this.lazyLoadMedia.findAndSplice(i => i.wasSeen);
}
if(item) {
this.inProcess.push(item);
this.log('will load media', this.lockPromise, item); this.log('will load media', this.lockPromise, item);
try { try {
if(this.lockPromise/* && false */) { if(this.lockPromise/* && false */) {
let perf = performance.now(); const perf = performance.now();
await this.lockPromise; await this.lockPromise;
this.log('waited lock:', performance.now() - perf); this.log('waited lock:', performance.now() - perf);
@ -102,16 +61,12 @@ export default class LazyLoadQueue {
//await new Promise((resolve) => setTimeout(resolve, 2e3)); //await new Promise((resolve) => setTimeout(resolve, 2e3));
//await new Promise((resolve, reject) => window.requestAnimationFrame(() => window.requestAnimationFrame(resolve))); //await new Promise((resolve, reject) => window.requestAnimationFrame(() => window.requestAnimationFrame(resolve)));
await item.load(); await item.load(item.div);
} catch(err) { } catch(err) {
this.log.error('loadMediaQueue error:', err/* , item */); this.log.error('loadMediaQueue error:', err/* , item */);
} }
if(!this.noObserver) { this.inProcess.delete(item);
this.observer.unobserve(item.div);
}
this.inProcess.findAndSplice(i => i == item);
this.log('loaded media', item); this.log('loaded media', item);
@ -119,51 +74,172 @@ export default class LazyLoadQueue {
this.processQueue(); this.processQueue();
} }
} }
protected getItem() {
return this.lazyLoadMedia.shift();
} }
public addElement(el: LazyLoadElement) { protected addElement(el: LazyLoadElementBase) {
if(el.wasSeen) {
this.processQueue(el); 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 { } else {
el.wasSeen = false; item = this.getItem();
if(this.observer) {
this.observer.observe(el.div);
}
}
} }
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.lazyLoadMedia.push(el);
this.addElement(el); this.addElement(el);
} }
public unshift(el: LazyLoadElement) { public unshift(el: LazyLoadElementBase) {
this.lazyLoadMedia.unshift(el); this.lazyLoadMedia.unshift(el);
this.addElement(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() { public refresh() {
const items = this.lazyLoadMedia; this.intersector.refresh();
if(items && items.length) { }
items.forEach(item => {
this.observer.unobserve(item.div);
});
window.requestAnimationFrame(() => { protected setProcessQueueTimeout() {
items.forEach(item => { if(!this.intersectorTimeout) {
this.observer.observe(item.div); this.intersectorTimeout = window.setTimeout(() => {
}); this.intersectorTimeout = 0;
}); this.processQueue();
}, 0);
}
} }
} }
public lockIntersection() { export default class LazyLoadQueue extends LazyLoadQueueIntersector {
this.intersectionLocked = true; protected lazyLoadMedia: Array<LazyLoadElement> = [];
protected inProcess: Set<LazyLoadElement> = new Set();
constructor(protected parallelLimit = 5) {
super(parallelLimit);
this.intersector = new VisibilityIntersector(this.onVisibilityChange);
} }
public unlockIntersection() { private onVisibilityChange = (target: HTMLElement, visible: boolean) => {
this.intersectionLocked = false; if(visible) {
this.refresh(); 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 push(el: LazyLoadElement) {
super.push(el);
}
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);
} }
} }

View File

@ -155,6 +155,19 @@ let closeBtnMenu = () => {
window.removeEventListener('contextmenu', onClick); 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; let openedMenu: HTMLDivElement = null, openedMenuOnClose: () => void = null;
export function openBtnMenu(menuElement: HTMLDivElement, onClose?: () => void) { export function openBtnMenu(menuElement: HTMLDivElement, onClose?: () => void) {
closeBtnMenu(); closeBtnMenu();
@ -248,7 +261,7 @@ export function attachContextMenuListener(element: HTMLElement, callback: (e: To
} }
}; */ }; */
timeout = setTimeout(() => { timeout = window.setTimeout(() => {
callback(e.touches[0]); callback(e.touches[0]);
onCancel(); onCancel();

View File

@ -63,7 +63,7 @@ export const roundPercents = (percents: number[]) => {
}; };
const connectedPolls: {id: string, element: PollElement}[] = []; 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}; const {poll, results} = e.detail as {poll: Poll, results: PollResults};
//console.log('poll_update', poll, results); //console.log('poll_update', poll, results);
@ -123,7 +123,7 @@ const setQuizHint = (solution: string, solution_entities: any[], onHide: () => v
prevQuizHint = element; prevQuizHint = element;
prevQuizHintOnHide = onHide; prevQuizHintOnHide = onHide;
prevQuizHintTimeout = setTimeout(() => { prevQuizHintTimeout = window.setTimeout(() => {
hideQuizHint(element, onHide, prevQuizHintTimeout); hideQuizHint(element, onHide, prevQuizHintTimeout);
}, touchSupport ? 5000 : 7000); }, touchSupport ? 5000 : 7000);
}; };
@ -280,7 +280,7 @@ export default class PollElement extends HTMLElement {
// circle.style.strokeDashoffset = circumference + percents * circumference; // circle.style.strokeDashoffset = circumference + percents * circumference;
// circle.style.strokeDasharray = ${circumference} ${circumference}; // circle.style.strokeDasharray = ${circumference} ${circumference};
this.quizInterval = setInterval(() => { this.quizInterval = window.setInterval(() => {
const time = Date.now(); const time = Date.now();
const percents = (closeTime - time) / period; const percents = (closeTime - time) / period;
const timeLeft = (closeTime - time) / 1000 + 1 | 0; const timeLeft = (closeTime - time) / 1000 + 1 | 0;

View File

@ -118,8 +118,8 @@ export default class AppChatFoldersTab implements SliderTab {
} }
}); });
$rootScope.$on('filter_update', (e: CustomEvent) => { $rootScope.$on('filter_update', (e) => {
const filter: DialogFilter = e.detail; const filter = e.detail;
if(this.filtersRendered.hasOwnProperty(filter.id)) { if(this.filtersRendered.hasOwnProperty(filter.id)) {
this.renderFolder(filter, null, this.filtersRendered[filter.id]); this.renderFolder(filter, null, this.filtersRendered[filter.id]);
} else { } else {
@ -129,8 +129,8 @@ export default class AppChatFoldersTab implements SliderTab {
this.getSuggestedFilters(); this.getSuggestedFilters();
}); });
$rootScope.$on('filter_delete', (e: CustomEvent) => { $rootScope.$on('filter_delete', (e) => {
const filter: DialogFilter = e.detail; const filter = e.detail;
if(this.filtersRendered.hasOwnProperty(filter.id)) { if(this.filtersRendered.hasOwnProperty(filter.id)) {
/* for(const suggested of this.suggestedFilters) { /* for(const suggested of this.suggestedFilters) {
if(deepEqual(suggested.filter, filter)) { if(deepEqual(suggested.filter, filter)) {

View File

@ -26,7 +26,7 @@ export default class AppSettingsTab implements SliderTab {
constructor() { constructor() {
parseMenuButtonsTo(this.buttons, this.container.querySelector('.profile-buttons').children); parseMenuButtonsTo(this.buttons, this.container.querySelector('.profile-buttons').children);
$rootScope.$on('user_auth', (e: CustomEvent) => { $rootScope.$on('user_auth', (e) => {
this.fillElements(); this.fillElements();
}); });

View 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;
}
}

View File

@ -18,6 +18,7 @@ import webpWorkerController from '../lib/webp/webpWorkerController';
import { readBlobAsText } from '../helpers/blob'; import { readBlobAsText } from '../helpers/blob';
import appMediaPlaybackController from './appMediaPlaybackController'; import appMediaPlaybackController from './appMediaPlaybackController';
import { PhotoSize } from '../layer'; import { PhotoSize } from '../layer';
import { deferredPromise } from '../lib/polyfill';
export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue, noInfo, group}: { export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue, noInfo, group}: {
doc: MyDocument, 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) => { //appDocsManager.downloadDocNew(doc.id).promise.then(res => res.json()).then(async(json) => {
//fetch(doc.url).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(readBlobAsText)
.then(JSON.parse) .then(JSON.parse)
.then(async(json) => { .then(async(json) => {
//console.timeEnd('download sticker' + doc.id); //console.timeEnd('download sticker' + doc.id);
//console.log('loaded sticker:', doc, div, blob); //console.log('loaded sticker:', doc, div/* , blob */);
if(middleware && !middleware()) return; if(middleware && !middleware()) return;
let animation = await LottieLoader.loadAnimationWorker/* loadAnimation */({ let animation = await LottieLoader.loadAnimationWorker/* loadAnimation */({
@ -612,12 +613,16 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
height height
}, group, toneIndex); }, group, toneIndex);
//const deferred = deferredPromise<void>();
animation.addListener('firstFrame', () => { animation.addListener('firstFrame', () => {
if(div.firstElementChild && div.firstElementChild.tagName == 'IMG') { if(div.firstElementChild && div.firstElementChild.tagName == 'IMG') {
div.firstElementChild.remove(); div.firstElementChild.remove();
} else { } else {
animation.canvas.classList.add('fade-in'); animation.canvas.classList.add('fade-in');
} }
//deferred.resolve();
}, true); }, true);
if(emoji) { 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)); //await new Promise((resolve) => setTimeout(resolve, 5e3));
}); });

View File

@ -68,7 +68,7 @@ export class AppChatsManager {
public megagroupOnlines: {[id: number]: {timestamp: number, onlines: number}} = {}; public megagroupOnlines: {[id: number]: {timestamp: number, onlines: number}} = {};
constructor() { constructor() {
$rootScope.$on('apiUpdate', (e: CustomEvent) => { $rootScope.$on('apiUpdate', (e) => {
// console.log('on apiUpdate', update) // console.log('on apiUpdate', update)
const update = e.detail; const update = e.detail;
switch(update._) { switch(update._) {

View File

@ -424,7 +424,7 @@ export class AppDialogsManager {
(window as any).addElement = add; (window as any).addElement = add;
} */ } */
$rootScope.$on('user_update', (e: CustomEvent) => { $rootScope.$on('user_update', (e) => {
let userID = e.detail; let userID = e.detail;
let user = appUsersManager.getUser(userID); 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; let dialog: any = e.detail;
this.setLastMessage(dialog); this.setLastMessage(dialog);
@ -460,7 +460,7 @@ export class AppDialogsManager {
this.setFiltersUnreadCount(); this.setFiltersUnreadCount();
}); });
$rootScope.$on('dialog_flush', (e: CustomEvent) => { $rootScope.$on('dialog_flush', (e) => {
let peerID: number = e.detail.peerID; let peerID: number = e.detail.peerID;
let dialog = appMessagesManager.getDialogByPeerID(peerID)[0]; let dialog = appMessagesManager.getDialogByPeerID(peerID)[0];
if(dialog) { if(dialog) {
@ -470,7 +470,7 @@ export class AppDialogsManager {
} }
}); });
$rootScope.$on('dialogs_multiupdate', (e: CustomEvent) => { $rootScope.$on('dialogs_multiupdate', (e) => {
const dialogs = e.detail; const dialogs = e.detail;
for(const id in dialogs) { for(const id in dialogs) {
@ -483,7 +483,7 @@ export class AppDialogsManager {
this.setFiltersUnreadCount(); this.setFiltersUnreadCount();
}); });
$rootScope.$on('dialog_drop', (e: CustomEvent) => { $rootScope.$on('dialog_drop', (e) => {
let {peerID, dialog} = e.detail; let {peerID, dialog} = e.detail;
let dom = this.getDialogDom(peerID); let dom = this.getDialogDom(peerID);
@ -496,11 +496,8 @@ export class AppDialogsManager {
this.setFiltersUnreadCount(); this.setFiltersUnreadCount();
}); });
$rootScope.$on('dialog_unread', (e: CustomEvent) => { $rootScope.$on('dialog_unread', (e) => {
let info: { let info = e.detail;
peerID: number,
count: number
} = e.detail;
let dialog = appMessagesManager.getDialogByPeerID(info.peerID)[0]; let dialog = appMessagesManager.getDialogByPeerID(info.peerID)[0];
if(dialog) { 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 peerID = e.detail;
let lastPeerID = this.lastActiveListElement && +this.lastActiveListElement.getAttribute('data-peerID'); 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; const filter: DialogFilter = e.detail;
if(!this.filtersRendered[filter.id]) { if(!this.filtersRendered[filter.id]) {
this.addFilter(filter); this.addFilter(filter);
@ -560,7 +557,7 @@ export class AppDialogsManager {
elements.title.innerHTML = RichTextProcessor.wrapEmojiText(filter.title); elements.title.innerHTML = RichTextProcessor.wrapEmojiText(filter.title);
}); });
$rootScope.$on('filter_delete', (e: CustomEvent) => { $rootScope.$on('filter_delete', (e) => {
const filter: DialogFilter = e.detail; const filter: DialogFilter = e.detail;
const elements = this.filtersRendered[filter.id]; const elements = this.filtersRendered[filter.id];
if(!elements) return; 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}; const {order, id} = e.detail as {order: number[], id: number};
if(this.prevTabID != id) { if(this.prevTabID != id) {
return; return;

View File

@ -154,13 +154,13 @@ export class AppImManager {
this.myID = $rootScope.myID = id; this.myID = $rootScope.myID = id;
}); });
$rootScope.$on('user_auth', (e: CustomEvent) => { $rootScope.$on('user_auth', (e) => {
let userAuth = e.detail; let userAuth = e.detail;
this.myID = $rootScope.myID = userAuth ? userAuth.id : 0; this.myID = $rootScope.myID = userAuth ? userAuth.id : 0;
}); });
// will call when message is sent (only 1) // will call when message is sent (only 1)
$rootScope.$on('history_append', (e: CustomEvent) => { $rootScope.$on('history_append', (e) => {
let details = e.detail; let details = e.detail;
if(!this.scrolledAllDown) { if(!this.scrolledAllDown) {
@ -171,7 +171,7 @@ export class AppImManager {
}); });
// will call when sent for update pos // will call when sent for update pos
$rootScope.$on('history_update', (e: CustomEvent) => { $rootScope.$on('history_update', (e) => {
let details = e.detail; let details = e.detail;
if(details.mid && details.peerID == this.peerID) { 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; let msgIDsByPeer = e.detail;
if(!(this.peerID in msgIDsByPeer)) return; if(!(this.peerID in msgIDsByPeer)) return;
@ -201,23 +201,20 @@ export class AppImManager {
this.renderNewMessagesByIDs(msgIDs); this.renderNewMessagesByIDs(msgIDs);
}); });
$rootScope.$on('history_delete', (e: CustomEvent) => { $rootScope.$on('history_delete', (e) => {
let detail: { let detail = e.detail;
peerID: string,
msgs: {[x: number]: boolean}
} = e.detail;
this.deleteMessagesByIDs(Object.keys(detail.msgs).map(s => +s)); 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; let peerID: number = e.detail.peerID;
if(this.peerID == peerID) { if(this.peerID == peerID) {
this.deleteMessagesByIDs(Object.keys(this.bubbles).map(m => +m)); 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; const peerID: number = e.detail;
if(this.peerID == -peerID) { if(this.peerID == -peerID) {
const chat = appChatsManager.getChat(peerID) as Channel | Chat; 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 // 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; const {tempID, mid} = e.detail;
this.log('message_sent', e.detail); this.log('message_sent', e.detail);
@ -236,14 +233,14 @@ export class AppImManager {
const message = appMessagesManager.getMessage(mid); const message = appMessagesManager.getMessage(mid);
if(message.media) { if(message.media) {
if(message.media.photo) { if(message.media.photo) {
const photo = appPhotosManager.getPhoto(tempID); const photo = appPhotosManager.getPhoto('' + tempID);
if(/* photo._ != 'photoEmpty' */photo) { if(/* photo._ != 'photoEmpty' */photo) {
const newPhoto = message.media.photo; const newPhoto = message.media.photo;
newPhoto.downloaded = photo.downloaded; newPhoto.downloaded = photo.downloaded;
newPhoto.url = photo.url; newPhoto.url = photo.url;
} }
} else if(message.media.document) { } else if(message.media.document) {
const doc = appDocsManager.getDoc(tempID); const doc = appDocsManager.getDoc('' + tempID);
if(/* doc._ != 'documentEmpty' && */doc?.type && doc.type != 'sticker') { if(/* doc._ != 'documentEmpty' && */doc?.type && doc.type != 'sticker') {
const newDoc = message.media.document; const newDoc = message.media.document;
newDoc.downloaded = doc.downloaded; newDoc.downloaded = doc.downloaded;
@ -272,7 +269,7 @@ export class AppImManager {
const pollElement = bubble.querySelector('poll-element'); const pollElement = bubble.querySelector('poll-element');
if(pollElement) { if(pollElement) {
pollElement.setAttribute('poll-id', newPoll.id); pollElement.setAttribute('poll-id', newPoll.id);
pollElement.setAttribute('message-id', mid); pollElement.setAttribute('message-id', '' + mid);
delete appPollsManager.polls[tempID]; delete appPollsManager.polls[tempID];
delete appPollsManager.results[tempID]; delete appPollsManager.results[tempID];
} }
@ -286,7 +283,7 @@ export class AppImManager {
bubble.classList.remove('is-sending'); bubble.classList.remove('is-sending');
bubble.classList.add('is-sent'); bubble.classList.add('is-sent');
bubble.dataset.mid = mid; bubble.dataset.mid = '' + mid;
this.bubbleGroups.removeBubble(bubble, tempID); 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; let {peerID, mid, id, justMedia} = e.detail;
if(peerID != this.peerID) return; if(peerID != this.peerID) return;
@ -318,7 +315,7 @@ export class AppImManager {
this.renderMessage(message, true, false, bubble, false); this.renderMessage(message, true, false, bubble, false);
}); });
$rootScope.$on('messages_downloaded', (e: CustomEvent) => { $rootScope.$on('messages_downloaded', (e) => {
const mids: number[] = e.detail; const mids: number[] = e.detail;
const pinnedMessage = appMessagesManager.getPinnedMessage(this.peerID); 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; let update = e.detail;
this.handleUpdate(update); this.handleUpdate(update);

View File

@ -9,7 +9,7 @@ import appDocsManager, {MyDocument} from "./appDocsManager";
import VideoPlayer from "../mediaPlayer"; import VideoPlayer from "../mediaPlayer";
import { renderImageFromUrl, parseMenuButtonsTo } from "../../components/misc"; import { renderImageFromUrl, parseMenuButtonsTo } from "../../components/misc";
import AvatarElement from "../../components/avatar"; import AvatarElement from "../../components/avatar";
import LazyLoadQueue from "../../components/lazyLoadQueue"; import LazyLoadQueue, { LazyLoadQueueBase } from "../../components/lazyLoadQueue";
import appForward from "../../components/appForward"; import appForward from "../../components/appForward";
import { isSafari, mediaSizes, touchSupport } from "../config"; import { isSafari, mediaSizes, touchSupport } from "../config";
import { deferredPromise } from "../polyfill"; import { deferredPromise } from "../polyfill";
@ -118,7 +118,7 @@ export class AppMediaViewer {
private setMoverPromise: Promise<void>; private setMoverPromise: Promise<void>;
private setMoverAnimationPromise: Promise<void>; private setMoverAnimationPromise: Promise<void>;
private lazyLoadQueue: LazyLoadQueue; private lazyLoadQueue: LazyLoadQueueBase;
private highlightSwitchersTimeout: number; private highlightSwitchersTimeout: number;
@ -128,7 +128,7 @@ export class AppMediaViewer {
this.preloaderStreamable = new ProgressivePreloader(undefined, false, true); 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>); 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.wholeDiv.classList.add('highlight-switchers');
} }
this.highlightSwitchersTimeout = setTimeout(() => { this.highlightSwitchersTimeout = window.setTimeout(() => {
this.wholeDiv.classList.remove('highlight-switchers'); this.wholeDiv.classList.remove('highlight-switchers');
this.highlightSwitchersTimeout = 0; this.highlightSwitchersTimeout = 0;
}, 3e3); }, 3e3);
@ -1075,8 +1075,7 @@ export class AppMediaViewer {
this.lazyLoadQueue.unshift({ this.lazyLoadQueue.unshift({
div: null, div: null,
load, load
wasSeen: true
}); });
//} else createPlayer(); //} else createPlayer();
}); });
@ -1140,8 +1139,7 @@ export class AppMediaViewer {
this.lazyLoadQueue.unshift({ this.lazyLoadQueue.unshift({
div: null, div: null,
load, load
wasSeen: true
}); });
}); });
} }

View File

@ -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 appMessagesIDsManager from "./appMessagesIDsManager";
import appChatsManager from "./appChatsManager"; import appChatsManager from "./appChatsManager";
import appUsersManager from "./appUsersManager"; import appUsersManager from "./appUsersManager";
@ -231,7 +231,7 @@ export class FiltersStorage {
public orderIndex = 0; public orderIndex = 0;
constructor() { constructor() {
$rootScope.$on('apiUpdate', (e: CustomEvent) => { $rootScope.$on('apiUpdate', (e) => {
this.handleUpdate(e.detail); this.handleUpdate(e.detail);
}); });
} }
@ -476,7 +476,7 @@ export class AppMessagesManager {
public lastSearchFilter: any = {}; public lastSearchFilter: any = {};
public lastSearchResults: any = []; public lastSearchResults: any = [];
public needSingleMessages: any = []; public needSingleMessages: number[] = [];
public fetchSingleMessagesTimeout = 0; public fetchSingleMessagesTimeout = 0;
private fetchSingleMessagesPromise: Promise<any> = null; private fetchSingleMessagesPromise: Promise<any> = null;
@ -486,7 +486,7 @@ export class AppMessagesManager {
public migratedToFrom: {[peerID: number]: number} = {}; public migratedToFrom: {[peerID: number]: number} = {};
public newMessagesHandlePromise = 0; public newMessagesHandlePromise = 0;
public newMessagesToHandle: any = {}; public newMessagesToHandle: {[peerID: string]: number[]} = {};
public newDialogsHandlePromise = 0; public newDialogsHandlePromise = 0;
public newDialogsToHandle: {[peerID: string]: {reload: true} | Dialog} = {}; public newDialogsToHandle: {[peerID: string]: {reload: true} | Dialog} = {};
public newUpdatesAfterReloadToHandle: any = {}; public newUpdatesAfterReloadToHandle: any = {};
@ -511,11 +511,11 @@ export class AppMessagesManager {
public filtersStorage = new FiltersStorage(); public filtersStorage = new FiltersStorage();
constructor() { constructor() {
$rootScope.$on('apiUpdate', (e: CustomEvent) => { $rootScope.$on('apiUpdate', (e) => {
this.handleUpdate(e.detail); this.handleUpdate(e.detail);
}); });
$rootScope.$on('webpage_updated', (e: CustomEvent) => { $rootScope.$on('webpage_updated', (e) => {
let eventData = e.detail; let eventData = e.detail;
eventData.msgs.forEach((msgID: number) => { eventData.msgs.forEach((msgID: number) => {
let message = this.getMessage(msgID); 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;; let eventData = e.detail;;
var peerID = eventData.peerID; var peerID = eventData.peerID;
var draft = eventData.draft; var draft = eventData.draft;
@ -558,12 +558,12 @@ export class AppMessagesManager {
this.dialogsStorage.pushDialog(dialog); this.dialogsStorage.pushDialog(dialog);
$rootScope.$broadcast('dialog_draft', { $rootScope.$broadcast('dialog_draft', {
peerID: peerID, peerID,
draft: draft, draft,
index: dialog.index index: dialog.index
}); });
} }
}); }); */
} }
@ -847,7 +847,7 @@ export class AppMessagesManager {
this.saveMessages([message]); this.saveMessages([message]);
historyStorage.pending.unshift(messageID); 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(() => message.send(), 0);
// setTimeout(function () { // setTimeout(function () {
@ -1216,7 +1216,7 @@ export class AppMessagesManager {
this.saveMessages([message]); this.saveMessages([message]);
historyStorage.pending.unshift(messageID); 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); setTimeout(message.send.bind(this), 0);
@ -1370,7 +1370,7 @@ export class AppMessagesManager {
return message; 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) => { let toggleError = (message: any, on: boolean) => {
if(on) { if(on) {
@ -1730,7 +1730,7 @@ export class AppMessagesManager {
this.saveMessages([message]); this.saveMessages([message]);
historyStorage.pending.unshift(messageID); 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(message.send, 0);
@ -1876,20 +1876,25 @@ export class AppMessagesManager {
//flags |= 1; // means pinned already loaded //flags |= 1; // means pinned already loaded
} }
/* if(this.dialogsStorage.dialogsOffsetDate[0]) {
flags |= 1; // means pinned already loaded
} */
//if(folderID > 0) { //if(folderID > 0) {
//flags |= 1; //flags |= 1;
flags |= 2; flags |= 2;
//} //}
// ! ВНИМАНИЕ: ОЧЕНЬ СЛОЖНАЯ ЛОГИКА: // ! ВНИМАНИЕ: ОЧЕНЬ СЛОЖНАЯ ЛОГИКА:
// ! если делать запрос сначала по папке 0, потом по папке 1, по индексу 0 в массиве будет один и тот же диалог, при условии что он закреплён в обеих папках, ЛОЛ??? // ! если делать запрос сначала по папке 0, потом по папке 1, по индексу 0 в массиве будет один и тот же диалог, с dialog.pFlags.pinned, ЛОЛ???
// ! т.е., с запросом folder_id: 1, и exclude_pinned: 0, в результате будут ещё и закреплённые с папки 0
return apiManager.invokeApi('messages.getDialogs', { return apiManager.invokeApi('messages.getDialogs', {
flags: flags, flags,
folder_id: folderID, folder_id: folderID,
offset_date: offsetDate, offset_date: offsetDate,
offset_id: appMessagesIDsManager.getMessageLocalID(offsetID), offset_id: appMessagesIDsManager.getMessageLocalID(offsetID),
offset_peer: appPeersManager.getInputPeerByID(offsetPeerID), offset_peer: appPeersManager.getInputPeerByID(offsetPeerID),
limit: limit, limit,
hash: 0 hash: 0
}, { }, {
//timeout: APITIMEOUT, //timeout: APITIMEOUT,
@ -1897,7 +1902,7 @@ export class AppMessagesManager {
}).then((dialogsResult) => { }).then((dialogsResult) => {
if(dialogsResult._ == 'messages.dialogsNotModified') return null; 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) { if(!offsetDate) {
telegramMeWebService.setAuthorized(true); telegramMeWebService.setAuthorized(true);
@ -1909,7 +1914,7 @@ export class AppMessagesManager {
let maxSeenIdIncremented = offsetDate ? true : false; let maxSeenIdIncremented = offsetDate ? true : false;
let hasPrepend = false; let hasPrepend = false;
let noIDsDialogs: {[peerID: number]: Dialog} = {}; const noIDsDialogs: {[peerID: number]: Dialog} = {};
(dialogsResult.dialogs as Dialog[]).forEachReverse(dialog => { (dialogsResult.dialogs as Dialog[]).forEachReverse(dialog => {
//const d = Object.assign({}, dialog); //const d = Object.assign({}, dialog);
// ! нужно передавать folderID, так как по папке != 0 нет свойства folder_id // ! нужно передавать folderID, так как по папке != 0 нет свойства folder_id
@ -2561,7 +2566,7 @@ export class AppMessagesManager {
$rootScope.$broadcast('dialog_drop', {peerID: migrateFrom, dialog: dropped[0]}); $rootScope.$broadcast('dialog_drop', {peerID: migrateFrom, dialog: dropped[0]});
} }
$rootScope.$broadcast('dialog_migrate', {migrateFrom: migrateFrom, migrateTo: migrateTo}); $rootScope.$broadcast('dialog_migrate', {migrateFrom, migrateTo});
}, 100); }, 100);
} }
} }
@ -2756,7 +2761,7 @@ export class AppMessagesManager {
} }
this.historiesStorage[peerID] = historyStorage; this.historiesStorage[peerID] = historyStorage;
if(this.mergeReplyKeyboard(historyStorage, message)) { 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', { apiPromise = apiManager.invokeApi('messages.readHistory', {
peer: appPeersManager.getInputPeerByID(peerID), peer: appPeersManager.getInputPeerByID(peerID),
max_id: maxID max_id: maxID
}).then((affectedMessages: any) => { }).then((affectedMessages) => {
apiUpdatesManager.processUpdateMessage({ apiUpdatesManager.processUpdateMessage({
_: 'updateShort', _: 'updateShort',
update: { update: {
@ -3299,7 +3304,7 @@ export class AppMessagesManager {
} else { } else {
apiManager.invokeApi('messages.readMessageContents', { apiManager.invokeApi('messages.readMessageContents', {
id: msgIDs id: msgIDs
}).then((affectedMessages: any) => { }).then((affectedMessages) => {
apiUpdatesManager.processUpdateMessage({ apiUpdatesManager.processUpdateMessage({
_: 'updateShort', _: 'updateShort',
update: { update: {
@ -3385,7 +3390,7 @@ export class AppMessagesManager {
var topMsgID = history[0]; var topMsgID = history[0];
history.unshift(message.mid); history.unshift(message.mid);
if(message.mid > 0 && message.mid < topMsgID) { if(message.mid > 0 && message.mid < topMsgID) {
history.sort((a: any, b: any) => { history.sort((a, b) => {
return b - a; return b - a;
}); });
} }
@ -3396,7 +3401,7 @@ export class AppMessagesManager {
} }
if(this.mergeReplyKeyboard(historyStorage, message)) { 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) { if(!message.pFlags.out && message.from_id) {
@ -3408,7 +3413,7 @@ export class AppMessagesManager {
if(randomID) { if(randomID) {
if(pendingMessage = this.finalizePendingMessage(randomID, message)) { 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]; delete this.pendingByMessageID[message.mid];
@ -3542,11 +3547,11 @@ export class AppMessagesManager {
dialogsResult.dialogs.reverse(); dialogsResult.dialogs.reverse();
this.applyConversations(dialogsResult); this.applyConversations(dialogsResult);
dialogsResult.dialogs.forEach((dialog: any) => { dialogsResult.dialogs.forEach((dialog) => {
newPinned[dialog.peerID] = true; newPinned[dialog.peerID] = true;
}); });
this.dialogsStorage.getFolder(folderID).forEach((dialog: any) => { this.dialogsStorage.getFolder(folderID).forEach((dialog) => {
const peerID = dialog.peerID; const peerID = dialog.peerID;
if(dialog.pFlags.pinned && !newPinned[peerID]) { if(dialog.pFlags.pinned && !newPinned[peerID]) {
this.newDialogsToHandle[peerID] = {reload: true}; this.newDialogsToHandle[peerID] = {reload: true};
@ -3626,7 +3631,7 @@ export class AppMessagesManager {
}); });
if(isTopMessage) { if(isTopMessage) {
var updatedDialogs: any = {}; var updatedDialogs: {[peerID: number]: Dialog} = {};
updatedDialogs[peerID] = dialog; updatedDialogs[peerID] = dialog;
$rootScope.$broadcast('dialogs_multiupdate', updatedDialogs); $rootScope.$broadcast('dialogs_multiupdate', updatedDialogs);
} }
@ -3889,10 +3894,7 @@ export class AppMessagesManager {
let message = this.getMessage(mid); let message = this.getMessage(mid);
if(message && message.views && message.views < views) { if(message && message.views && message.views < views) {
message.views = views; message.views = views;
$rootScope.$broadcast('message_views', { $rootScope.$broadcast('message_views', {mid, views});
mid: mid,
views: views
});
} }
break; break;
} }
@ -3987,7 +3989,7 @@ export class AppMessagesManager {
public finalizePendingMessageCallbacks(tempID: number, mid: number) { public finalizePendingMessageCallbacks(tempID: number, mid: number) {
var callbacks = this.tempFinalizeCallbacks[tempID]; var callbacks = this.tempFinalizeCallbacks[tempID];
this.log.warn(dT(), callbacks, tempID); this.log.warn(callbacks, tempID);
if(callbacks !== undefined) { if(callbacks !== undefined) {
callbacks.forEach((callback: any) => { callbacks.forEach((callback: any) => {
callback(mid); callback(mid);
@ -4003,9 +4005,9 @@ export class AppMessagesManager {
return false; return false;
} }
AppStorage.set({ this.maxSeenID = maxID;
max_seen_msg: maxID
}); AppStorage.set({max_seen_msg: maxID});
apiManager.invokeApi('messages.receivedMessages', { apiManager.invokeApi('messages.receivedMessages', {
max_id: maxID max_id: maxID
@ -4149,7 +4151,7 @@ export class AppMessagesManager {
historyStorage.history.splice(offset, historyStorage.history.length - offset); historyStorage.history.splice(offset, historyStorage.history.length - offset);
historyResult.messages.forEach((message: any) => { historyResult.messages.forEach((message: any) => {
if(this.mergeReplyKeyboard(historyStorage, message)) { if(this.mergeReplyKeyboard(historyStorage, message)) {
$rootScope.$broadcast('history_reply_markup', {peerID: peerID}); $rootScope.$broadcast('history_reply_markup', {peerID});
} }
historyStorage.history.push(message.mid); historyStorage.history.push(message.mid);

View File

@ -75,7 +75,7 @@ class AppPollsManager {
private log = logger('POLLS', LogLevels.error); private log = logger('POLLS', LogLevels.error);
constructor() { constructor() {
$rootScope.$on('apiUpdate', (e: CustomEvent) => { $rootScope.$on('apiUpdate', (e) => {
let update = e.detail; let update = e.detail;
this.handleUpdate(update); this.handleUpdate(update);

View File

@ -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; this.archivedCount.innerText = '' + e.detail.count;
}); });

View File

@ -42,7 +42,7 @@ class AppStickersManager {
//} //}
}); });
$rootScope.$on('apiUpdate', (e: CustomEvent) => { $rootScope.$on('apiUpdate', (e) => {
const update = e.detail; const update = e.detail;
switch(update._) { switch(update._) {
@ -121,7 +121,7 @@ class AppStickersManager {
//console.log('stickers wrote', this.stickerSets); //console.log('stickers wrote', this.stickerSets);
if(this.saveSetsTimeout) return; if(this.saveSetsTimeout) return;
this.saveSetsTimeout = setTimeout(() => { this.saveSetsTimeout = window.setTimeout(() => {
const savedSets: {[id: string]: MessagesStickerSet} = {}; const savedSets: {[id: string]: MessagesStickerSet} = {};
for(const id in this.stickerSets) { for(const id in this.stickerSets) {
const set = this.stickerSets[id]; const set = this.stickerSets[id];

View File

@ -64,7 +64,7 @@ export class AppUsersManager {
this.myID = id; this.myID = id;
}); });
$rootScope.$on('user_auth', (e: CustomEvent) => { $rootScope.$on('user_auth', (e) => {
let userAuth = e.detail; let userAuth = e.detail;
this.myID = userAuth ? userAuth.id : 0; this.myID = userAuth ? userAuth.id : 0;
}); });
@ -73,7 +73,7 @@ export class AppUsersManager {
$rootScope.$on('stateSynchronized', this.updateUsersStatuses.bind(this)); $rootScope.$on('stateSynchronized', this.updateUsersStatuses.bind(this));
$rootScope.$on('apiUpdate', (e: CustomEvent) => { $rootScope.$on('apiUpdate', (e) => {
let update = e.detail; let update = e.detail;
//console.log('on apiUpdate', update); //console.log('on apiUpdate', update);
switch(update._) { switch(update._) {

View File

@ -1,4 +1,4 @@
import { $rootScope, safeReplaceObject, copy } from "../utils"; import { $rootScope, safeReplaceObject } from "../utils";
import appPhotosManager from "./appPhotosManager"; import appPhotosManager from "./appPhotosManager";
import appDocsManager from "./appDocsManager"; import appDocsManager from "./appDocsManager";
import { RichTextProcessor } from "../richtextprocessor"; import { RichTextProcessor } from "../richtextprocessor";
@ -8,7 +8,7 @@ class AppWebPagesManager {
pendingWebPages: any = {}; pendingWebPages: any = {};
constructor() { constructor() {
$rootScope.$on('apiUpdate', (e: CustomEvent) => { $rootScope.$on('apiUpdate', (e) => {
let update = e.detail; let update = e.detail;
switch(update._) { switch(update._) {
@ -91,9 +91,9 @@ class AppWebPagesManager {
} }
if(!messageID && this.pendingWebPages[apiWebPage.id] !== undefined) { if(!messageID && this.pendingWebPages[apiWebPage.id] !== undefined) {
var msgs = []; const msgs: number[] = [];
for(let msgID in this.pendingWebPages[apiWebPage.id]) { for(let msgID in this.pendingWebPages[apiWebPage.id]) {
msgs.push(msgID); msgs.push(+msgID);
} }
$rootScope.$broadcast('webpage_updated', { $rootScope.$broadcast('webpage_updated', {

View File

@ -279,7 +279,7 @@ export class RLottiePlayer {
if(delta < 0) { if(delta < 0) {
if(this.rafId) clearTimeout(this.rafId); if(this.rafId) clearTimeout(this.rafId);
return this.rafId = setTimeout(() => { return this.rafId = window.setTimeout(() => {
this.renderFrame2(frame, frameNo); this.renderFrame2(frame, frameNo);
}, this.frInterval > -delta ? -delta % this.frInterval : this.frInterval); }, this.frInterval > -delta ? -delta % this.frInterval : this.frInterval);
//await new Promise((resolve) => setTimeout(resolve, -delta % this.frInterval)); //await new Promise((resolve) => setTimeout(resolve, -delta % this.frInterval));

View File

@ -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 * Webogram v0.7.0 - messaging web application for MTProto
* https://github.com/zhukov/webogram * https://github.com/zhukov/webogram
@ -153,55 +156,58 @@ export function getRichElementValue(node: any, lines: string[], line: string[],
} */ } */
type BroadcastEvents = { type BroadcastEvents = {
'download_progress': any, 'user_update': number,
'user_update': any, 'user_auth': {dcID?: number, id: number},
'user_auth': any, 'peer_changed': number,
'peer_changed': any,
'filter_delete': any, 'filter_delete': MyDialogFilter,
'filter_update': any, 'filter_update': MyDialogFilter,
'dialog_draft': any, 'dialog_draft': {peerID: number, draft: any, index: number},
'dialog_unread': {peerID: number, count?: number}, 'dialog_unread': {peerID: number, count?: number},
'dialog_flush': {peerID: number}, 'dialog_flush': {peerID: number},
'dialog_drop': {peerID: number, dialog?: Dialog}, 'dialog_drop': {peerID: number, dialog?: Dialog},
'dialog_migrate': any, 'dialog_migrate': {migrateFrom: number, migrateTo: number},
'dialog_top': Dialog, 'dialog_top': Dialog,
'dialog_notify_settings': number, 'dialog_notify_settings': number,
'dialogs_multiupdate': {[peerID: string]: Dialog}, 'dialogs_multiupdate': {[peerID: string]: Dialog},
'dialogs_archived_unread': any, 'dialogs_archived_unread': {count: number},
'history_append': any, 'history_append': {peerID: number, messageID: number, my?: boolean},
'history_update': any, 'history_update': {peerID: number, mid: number},
'history_reply_markup': any, 'history_reply_markup': {peerID: number},
'history_multiappend': any, 'history_multiappend': AppMessagesManager['newMessagesToHandle'],
'history_delete': {peerID: number, msgs: {[mid: number]: true}}, 'history_delete': {peerID: number, msgs: {[mid: number]: true}},
'history_forbidden': number, 'history_forbidden': number,
'history_reload': number, 'history_reload': number,
'history_request': any, 'history_request': void,
'message_edit': any, 'message_edit': {peerID: number, id: number, mid: number, justMedia: boolean},
'message_views': any, 'message_views': {mid: number, views: number},
'message_sent': any, 'message_sent': {tempID: number, mid: number},
'messages_pending': void, 'messages_pending': void,
'messages_read': any, 'messages_read': void,
'messages_downloaded': any, '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, 'apiUpdate': any,
'stateSynchronized': any, 'download_progress': any,
'channel_settings': any, //'draft_updated': any,
'webpage_updated': any,
'draft_updated': any,
}; };
export const $rootScope = { export const $rootScope = {