tweb/src/components/emoticonsDropdown/tabs/stickers.ts

371 lines
12 KiB
TypeScript

/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import emoticonsDropdown, { EmoticonsDropdown, EMOTICONSSTICKERGROUP, EmoticonsTab } from "..";
import findUpAttribute from "../../../helpers/dom/findUpAttribute";
import findUpClassName from "../../../helpers/dom/findUpClassName";
import mediaSizes from "../../../helpers/mediaSizes";
import { MessagesAllStickers, StickerSet } from "../../../layer";
import appDocsManager, { MyDocument } from "../../../lib/appManagers/appDocsManager";
import appStickersManager from "../../../lib/appManagers/appStickersManager";
import { i18n } from "../../../lib/langPack";
import { RichTextProcessor } from "../../../lib/richtextprocessor";
import rootScope from "../../../lib/rootScope";
import animationIntersector from "../../animationIntersector";
import LazyLoadQueue, { LazyLoadQueueRepeat } from "../../lazyLoadQueue";
import { putPreloader } from "../../misc";
import PopupStickers from "../../popups/stickers";
import Scrollable, { ScrollableX } from "../../scrollable";
import StickyIntersector from "../../stickyIntersector";
import { wrapSticker, wrapStickerSetThumb } from "../../wrappers";
export class SuperStickerRenderer {
public lazyLoadQueue: LazyLoadQueueRepeat;
private animatedDivs: Set<HTMLDivElement> = new Set();
constructor(private regularLazyLoadQueue: LazyLoadQueue, private group: string) {
this.lazyLoadQueue = new LazyLoadQueueRepeat(undefined, (target, visible) => {
if(!visible) {
this.processInvisibleDiv(target as HTMLDivElement);
}
});
}
public clear() {
this.lazyLoadQueue.clear();
}
public renderSticker(doc: MyDocument, div?: HTMLDivElement, loadPromises?: Promise<any>[]) {
if(!div) {
div = document.createElement('div');
div.classList.add('grid-item', 'super-sticker');
if(doc.sticker === 2) {
this.observeAnimatedDiv(div);
}
}
// * This will wrap only a thumb
wrapSticker({
doc,
div,
lazyLoadQueue: this.regularLazyLoadQueue,
group: this.group,
onlyThumb: doc.sticker === 2,
loadPromises
});
return div;
}
public observeAnimatedDiv(div: HTMLDivElement) {
this.animatedDivs.add(div);
this.lazyLoadQueue.observe({
div,
load: this.processVisibleDiv
});
}
private 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);
}
});
};
private processVisibleDiv = (div: HTMLElement) => {
const docId = div.dataset.docId;
const doc = appDocsManager.getDoc(docId);
const size = mediaSizes.active.esgSticker.width;
//console.log('processVisibleDiv:', div);
const promise = wrapSticker({
doc,
div: div as HTMLDivElement,
width: size,
height: size,
lazyLoadQueue: null,
group: this.group,
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;
};
public 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);
};
}
export default class StickersTab implements EmoticonsTab {
private content: HTMLElement;
private stickersDiv: HTMLElement;
private stickerSets: {[id: string]: {
stickers: HTMLElement,
tab: HTMLElement
}} = {};
private recentDiv: HTMLElement;
private recentStickers: MyDocument[] = [];
private scroll: Scrollable;
private menu: HTMLElement;
private mounted = false;
private queueCategoryPush: {element: HTMLElement, prepend: boolean}[] = [];
private stickyIntersector: StickyIntersector;
private superStickerRenderer: SuperStickerRenderer;
categoryPush(categoryDiv: HTMLElement, categoryTitle: string = '', promise: Promise<MyDocument[]>, prepend?: boolean) {
//if((docs.length % 5) !== 0) categoryDiv.classList.add('not-full');
const itemsDiv = document.createElement('div');
itemsDiv.classList.add('category-items', 'super-stickers');
const titleDiv = document.createElement('div');
titleDiv.classList.add('category-title');
if(categoryTitle) {
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.superStickerRenderer.renderSticker(doc));
});
if(this.queueCategoryPush.length) {
this.queueCategoryPush.forEach(({element, prepend}) => {
if(prepend) {
if(this.recentDiv.parentElement) {
this.stickersDiv.prepend(element);
this.stickersDiv.prepend(this.recentDiv);
} else {
this.stickersDiv.prepend(element);
}
} else this.stickersDiv.append(element);
});
this.queueCategoryPush.length = 0;
}
});
return {titleDiv};
}
async renderStickerSet(set: StickerSet.stickerSet, prepend = false) {
const categoryDiv = document.createElement('div');
categoryDiv.classList.add('sticker-category');
categoryDiv.dataset.id = '' + set.id;
categoryDiv.dataset.access_hash = '' + set.access_hash;
const button = document.createElement('button');
button.classList.add('btn-icon', 'menu-horizontal-div-item');
this.stickerSets[set.id] = {
stickers: categoryDiv,
tab: button
};
if(prepend) {
this.menu.insertBefore(button, this.menu.firstElementChild.nextSibling);
} else {
this.menu.append(button);
}
//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);
wrapStickerSetThumb({
set,
container: button,
group: EMOTICONSSTICKERGROUP,
lazyLoadQueue: EmoticonsDropdown.lazyLoadQueue,
width: 32,
height: 32,
autoplay: false
});
}
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', 'stickers-recent');
let menuWrapper = this.content.previousElementSibling as HTMLDivElement;
this.menu = menuWrapper.firstElementChild as HTMLUListElement;
let menuScroll = new ScrollableX(menuWrapper);
this.stickersDiv = document.createElement('div');
this.stickersDiv.classList.add('stickers-categories');
this.content.append(this.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.addEventListener('stickers_installed', (e) => {
const set: StickerSet.stickerSet = e;
if(!this.stickerSets[set.id] && this.mounted) {
this.renderStickerSet(set, true);
}
});
rootScope.addEventListener('stickers_deleted', (e) => {
const set: StickerSet.stickerSet = e;
if(this.stickerSets[set.id] && this.mounted) {
const elements = this.stickerSets[set.id];
elements.stickers.remove();
elements.tab.remove();
delete this.stickerSets[set.id];
}
});
this.stickersDiv.addEventListener('click', (e) => {
const target = e.target as HTMLElement;
if(findUpClassName(target, 'category-title')) {
const el = findUpAttribute(target, 'data-id');
new PopupStickers({id: el.dataset.id, access_hash: el.dataset.access_hash}).show();
return;
}
EmoticonsDropdown.onMediaClick(e);
});
this.scroll = new Scrollable(this.content, 'STICKERS');
this.scroll.setVirtualContainer(this.stickersDiv);
this.stickyIntersector = EmoticonsDropdown.menuOnClick(this.menu, this.scroll, menuScroll).stickyIntersector;
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();
const {titleDiv} = this.categoryPush(this.recentDiv, '', Promise.resolve(this.recentStickers), true);
titleDiv.append(i18n('Stickers.Recent'));
}),
appStickersManager.getAllStickers().then((res) => {
preloader.remove();
for(let set of (res as MessagesAllStickers.messagesAllStickers).sets) {
this.renderStickerSet(set);
}
})
]).finally(() => {
this.mounted = true;
});
this.superStickerRenderer = new SuperStickerRenderer(EmoticonsDropdown.lazyLoadQueue, EMOTICONSSTICKERGROUP);
emoticonsDropdown.addLazyLoadQueueRepeat(this.superStickerRenderer.lazyLoadQueue, this.superStickerRenderer.processInvisibleDiv);
/* 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.superStickerRenderer.lazyLoadQueue.intersector.getVisible().length);
}, .25e3); */
this.init = null;
}
pushRecentSticker(doc: MyDocument) {
if(!this.recentDiv?.parentElement) {
return;
}
let div = this.recentDiv.querySelector(`[data-doc-id="${doc.id}"]`);
if(!div) {
div = this.superStickerRenderer.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() {
}
}