import emoticonsDropdown, { EmoticonsTab, EMOTICONSSTICKERGROUP, EmoticonsDropdown } from ".."; import { StickerSet } from "../../../layer"; import Scrollable, { ScrollableX } from "../../scrollable_new"; import { wrapSticker } from "../../wrappers"; import appStickersManager from "../../../lib/appManagers/appStickersManager"; import appDownloadManager from "../../../lib/appManagers/appDownloadManager"; import { readBlobAsText } from "../../../helpers/blob"; import lottieLoader from "../../../lib/lottieLoader"; import { renderImageFromUrl, putPreloader } from "../../misc"; import { RichTextProcessor } from "../../../lib/richtextprocessor"; import { $rootScope } from "../../../lib/utils"; 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; private stickerSets: {[id: string]: { stickers: HTMLElement, tab: HTMLElement }} = {}; private recentDiv: HTMLElement; private recentStickers: MyDocument[] = []; private scroll: Scrollable; private menu: HTMLUListElement; private mounted = false; private queueCategoryPush: {element: HTMLElement, prepend: boolean}[] = []; private stickyIntersector: StickyIntersector; private animatedDivs: Set = new Set(); private lazyLoadQueue: LazyLoadQueueRepeat; categoryPush(categoryDiv: HTMLElement, categoryTitle: string, promise: Promise, prepend?: boolean) { //if((docs.length % 5) != 0) categoryDiv.classList.add('not-full'); const itemsDiv = document.createElement('div'); itemsDiv.classList.add('category-items'); const titleDiv = document.createElement('div'); titleDiv.classList.add('category-title'); titleDiv.innerHTML = categoryTitle; categoryDiv.append(titleDiv, itemsDiv); this.stickyIntersector.observeStickyHeaderChanges(categoryDiv); this.queueCategoryPush.push({element: categoryDiv, prepend}); promise.then(documents => { documents.forEach(doc => { //if(doc._ == 'documentEmpty') return; itemsDiv.append(this.renderSticker(doc)); }); if(this.queueCategoryPush.length) { this.queueCategoryPush.forEach(({element, prepend}) => { if(prepend) { if(this.recentDiv.parentElement) { this.scroll.prepend(element); this.scroll.prepend(this.recentDiv); } else { this.scroll.prepend(element); } } else this.scroll.append(element); }); this.queueCategoryPush.length = 0; } }); } renderSticker(doc: MyDocument, div?: HTMLDivElement) { if(!div) { div = document.createElement('div'); if(doc.sticker == 2) { this.animatedDivs.add(div); this.lazyLoadQueue.observe({ div, load: this.processVisibleDiv }); } } wrapSticker({ doc, div, /* width: 80, height: 80, play: false, loop: false, */ lazyLoadQueue: EmoticonsDropdown.lazyLoadQueue, group: EMOTICONSSTICKERGROUP, onlyThumb: doc.sticker == 2 }); return div; } async renderStickerSet(set: StickerSet.stickerSet, prepend = false) { const categoryDiv = document.createElement('div'); categoryDiv.classList.add('sticker-category'); const li = document.createElement('li'); li.classList.add('btn-icon'); this.stickerSets[set.id] = { stickers: categoryDiv, tab: li }; if(prepend) { this.menu.insertBefore(li, this.menu.firstElementChild.nextSibling); } else { this.menu.append(li); } //stickersScroll.append(categoryDiv); const promise = appStickersManager.getStickerSet(set); this.categoryPush(categoryDiv, RichTextProcessor.wrapEmojiText(set.title), promise.then(stickerSet => stickerSet.documents as MyDocument[]), prepend); const stickerSet = await promise; //console.log('got stickerSet', stickerSet, li); if(stickerSet.set.thumb) { const downloadOptions = appStickersManager.getStickerSetThumbDownloadOptions(stickerSet.set); const promise = appDownloadManager.download(downloadOptions); if(stickerSet.set.pFlags.animated) { promise .then(readBlobAsText) .then(JSON.parse) .then(json => { lottieLoader.loadAnimationWorker({ container: li, loop: true, autoplay: false, animationData: json, width: 32, height: 32 }, EMOTICONSSTICKERGROUP); }); } else { const image = new Image(); promise.then(blob => { renderImageFromUrl(image, URL.createObjectURL(blob), () => { li.append(image); }); }); } } else if(stickerSet.documents[0]._ != 'documentEmpty') { // as thumb will be used first sticker wrapSticker({ doc: stickerSet.documents[0], div: li as any, group: EMOTICONSSTICKERGROUP }); // kostil } } 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; this.recentDiv = document.createElement('div'); this.recentDiv.classList.add('sticker-category'); let menuWrapper = this.content.previousElementSibling as HTMLDivElement; this.menu = menuWrapper.firstElementChild.firstElementChild as HTMLUListElement; let menuScroll = new ScrollableX(menuWrapper); let stickersDiv = document.createElement('div'); stickersDiv.classList.add('stickers-categories'); this.content.append(stickersDiv); /* stickersDiv.addEventListener('mouseover', (e) => { let target = e.target as HTMLElement; if(target.tagName == 'CANVAS') { // turn on sticker let animation = lottieLoader.getAnimation(target.parentElement, EMOTICONSSTICKERGROUP); if(animation) { // @ts-ignore if(animation.currentFrame == animation.totalFrames - 1) { animation.goToAndPlay(0, true); } else { animation.play(); } } } }); */ $rootScope.$on('stickers_installed', (e) => { const set: StickerSet.stickerSet = e.detail; if(!this.stickerSets[set.id] && this.mounted) { this.renderStickerSet(set, true); } }); $rootScope.$on('stickers_deleted', (e) => { const set: StickerSet.stickerSet = e.detail; if(this.stickerSets[set.id] && this.mounted) { const elements = this.stickerSets[set.id]; elements.stickers.remove(); elements.tab.remove(); delete this.stickerSets[set.id]; } }); stickersDiv.addEventListener('click', EmoticonsDropdown.onMediaClick); this.scroll = new Scrollable(this.content, 'STICKERS', undefined, undefined, 2); this.scroll.setVirtualContainer(stickersDiv); this.stickyIntersector = EmoticonsDropdown.menuOnClick(this.menu, this.scroll, menuScroll); const preloader = putPreloader(this.content, true); Promise.all([ appStickersManager.getRecentStickers().then(stickers => { this.recentStickers = stickers.stickers.slice(0, 20) as MyDocument[]; //stickersScroll.prepend(categoryDiv); this.stickerSets['recent'] = { stickers: this.recentDiv, tab: this.menu.firstElementChild as HTMLElement }; preloader.remove(); this.categoryPush(this.recentDiv, 'Recent', Promise.resolve(this.recentStickers), true); }), apiManager.invokeApi('messages.getAllStickers', {hash: 0}).then(async(res) => { let stickers: { _: 'messages.allStickers', hash: number, sets: Array } = res as any; preloader.remove(); for(let set of stickers.sets) { this.renderStickerSet(set); } }) ]).finally(() => { this.mounted = true; }); this.lazyLoadQueue = new LazyLoadQueueRepeat(undefined, (target, visible) => { if(!visible) { this.processInvisibleDiv(target as HTMLDivElement); } }); 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 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); */ this.init = null; } pushRecentSticker(doc: MyDocument) { if(!this.recentDiv.parentElement) { return; } let div = this.recentDiv.querySelector(`[data-doc-i-d="${doc.id}"]`); if(!div) { div = this.renderSticker(doc); } const items = this.recentDiv.querySelector('.category-items'); items.prepend(div); if(items.childElementCount > 20) { (Array.from(items.children) as HTMLElement[]).slice(20).forEach(el => el.remove()); } } onClose() { } }