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

897 lines
28 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 findUpClassName from '../../../helpers/dom/findUpClassName';
import mediaSizes from '../../../helpers/mediaSizes';
import {Document, MessagesAllStickers, StickerSet} from '../../../layer';
import {MyDocument} from '../../../lib/appManagers/appDocsManager';
import {AppManagers} from '../../../lib/appManagers/managers';
import {i18n, LangPackKey} from '../../../lib/langPack';
import wrapEmojiText from '../../../lib/richTextProcessor/wrapEmojiText';
import rootScope from '../../../lib/rootScope';
import animationIntersector, {AnimationItemGroup} from '../../animationIntersector';
import LazyLoadQueue from '../../lazyLoadQueue';
import LazyLoadQueueRepeat from '../../lazyLoadQueueRepeat';
import {putPreloader} from '../../putPreloader';
import PopupStickers from '../../popups/stickers';
import Scrollable, {ScrollableX} from '../../scrollable';
import findAndSplice from '../../../helpers/array/findAndSplice';
import {attachClickEvent} from '../../../helpers/dom/clickEvent';
import positionElementByIndex from '../../../helpers/dom/positionElementByIndex';
import noop from '../../../helpers/noop';
import ButtonIcon from '../../buttonIcon';
import confirmationPopup from '../../confirmationPopup';
import VisibilityIntersector, {OnVisibilityChangeItem} from '../../visibilityIntersector';
import createStickersContextMenu from '../../../helpers/dom/createStickersContextMenu';
import findUpAsChild from '../../../helpers/dom/findUpAsChild';
import forEachReverse from '../../../helpers/array/forEachReverse';
import attachStickerViewerListeners from '../../stickerViewer';
import ListenerSetter from '../../../helpers/listenerSetter';
import wrapSticker from '../../wrappers/sticker';
import wrapStickerSetThumb from '../../wrappers/stickerSetThumb';
import {MediaSize} from '../../../helpers/mediaSize';
import {AnyFunction} from '../../../types';
import {IgnoreMouseOutType} from '../../../helpers/dropdownHover';
import customProperties from '../../../helpers/dom/customProperties';
import windowSize from '../../../helpers/windowSize';
export class SuperStickerRenderer {
public lazyLoadQueue: LazyLoadQueueRepeat;
private animated: Set<HTMLElement> = new Set();
constructor(
private regularLazyLoadQueue: LazyLoadQueue,
private group: AnimationItemGroup,
private managers: AppManagers,
private options?: IntersectionObserverInit
) {
this.lazyLoadQueue = new LazyLoadQueueRepeat(undefined, ({target, visible}) => {
if(!visible) {
this.processInvisible(target);
}
}, options);
}
public clear() {
this.lazyLoadQueue.clear();
}
public renderSticker(doc: MyDocument, element?: HTMLElement, loadPromises?: Promise<any>[]) {
if(!element) {
element = document.createElement('div');
element.classList.add('grid-item', 'super-sticker');
element.dataset.docId = '' + doc.id;
if(doc.animated) {
this.observeAnimated(element);
}
}
// * This will wrap only a thumb
/* !doc.animated && */wrapSticker({
doc,
div: element,
lazyLoadQueue: this.regularLazyLoadQueue,
group: this.group,
onlyThumb: doc.animated,
loadPromises
});
return element;
}
public observeAnimated(element: HTMLElement) {
this.animated.add(element);
this.lazyLoadQueue.observe({
div: element,
load: this.processVisible
});
}
public unobserveAnimated(element: HTMLElement) {
this.animated.delete(element);
this.lazyLoadQueue.delete({div: element});
}
private checkAnimationContainer = (element: HTMLElement, visible: boolean) => {
// console.error('checkAnimationContainer', div, visible);
const players = animationIntersector.getAnimations(element);
players.forEach((player) => {
if(!visible) {
animationIntersector.removeAnimation(player);
} else {
animationIntersector.checkAnimation(player, false);
}
});
};
private processVisible = async(element: HTMLElement) => {
const docId = element.dataset.docId;
const doc = await this.managers.appDocsManager.getDoc(docId);
const size = mediaSizes.active.esgSticker.width;
// console.log('processVisibleDiv:', element);
const promise = wrapSticker({
doc,
div: element,
width: size,
height: size,
lazyLoadQueue: null,
group: this.group,
onlyThumb: false,
play: true,
loop: true,
withLock: true
}).then(({render}) => render);
promise.then(() => {
// clearTimeout(timeout);
this.checkAnimationContainer(element, this.lazyLoadQueue.intersector.isVisible(element));
});
/* let timeout = window.setTimeout(() => {
console.error('processVisibleDiv timeout', div, doc);
}, 1e3); */
return promise;
};
public processInvisible = async(element: HTMLElement) => {
const docId = element.dataset.docId;
const doc = await this.managers.appDocsManager.getDoc(docId);
// console.log('STICKER INvisible:', /* div, */docId);
this.checkAnimationContainer(element, false);
element.textContent = '';
this.renderSticker(doc, element as HTMLDivElement);
};
}
export type StickersTabCategoryItem = {element: HTMLElement};
export class StickersTabCategory<Item extends StickersTabCategoryItem, AdditionalElements extends Record<string, HTMLElement> = {}> {
public elements: {
container: HTMLElement,
title: HTMLElement,
items: HTMLElement,
menuTab: HTMLElement,
menuTabPadding: HTMLElement
} & AdditionalElements;
public items: Item[];
public mounted: boolean;
public id: string;
public limit: number;
public getContainerSize: () => {width: number, height: number};
private getElementMediaSize: () => MediaSize;
private gapX: number;
private gapY: number;
public set?: StickerSet;
public local?: boolean;
public menuScroll?: ScrollableX;
constructor(options: {
id: string,
title: HTMLElement | DocumentFragment,
overflowElement: HTMLElement,
getContainerSize: StickersTabCategory<Item>['getContainerSize'],
getElementMediaSize: StickersTabCategory<Item>['getElementMediaSize'],
gapX: number,
gapY: number,
noMenuTab?: boolean
}) {
const container = document.createElement('div');
container.classList.add('emoji-category');
const items = document.createElement('div');
items.classList.add('category-items');
let title: HTMLElement;
if(options.title) {
title = document.createElement('div');
title.classList.add('category-title');
title.append(options.title);
}
let menuTab: HTMLElement, menuTabPadding: HTMLElement;
if(!options.noMenuTab) {
menuTab = ButtonIcon(undefined, {noRipple: true});
menuTab.classList.add('menu-horizontal-div-item');
menuTabPadding = document.createElement('div');
menuTabPadding.classList.add('menu-horizontal-div-item-padding');
menuTab.append(menuTabPadding);
}
if(title) container.append(title);
container.append(items);
this.elements = {
container,
title,
items,
menuTab,
menuTabPadding
} as any;
this.id = options.id;
this.items = [];
this.getContainerSize = options.getContainerSize;
this.getElementMediaSize = options.getElementMediaSize;
this.gapX = options.gapX ?? 0;
this.gapY = options.gapY ?? 0;
}
public setCategoryItemsHeight() {
const {width: containerWidth} = this.getContainerSize();
const elementSize = this.getElementMediaSize().width;
let itemsPerRow = containerWidth / elementSize;
if(this.gapX) itemsPerRow -= Math.floor(itemsPerRow - 1) * this.gapX / elementSize;
itemsPerRow = Math.floor(itemsPerRow);
const rows = Math.ceil(this.items.length / itemsPerRow);
let height = rows * elementSize;
if(this.gapY) height += (rows - 1) * this.gapY;
this.elements.items.style.minHeight = height + 'px';
}
}
export class EmoticonsTabC<Category extends StickersTabCategory<any, any>> implements EmoticonsTab {
public content: HTMLElement;
public menuScroll: ScrollableX;
public container: HTMLElement;
public menuWrapper: HTMLElement;
public menu: HTMLElement;
protected categories: {[id: string]: Category};
protected categoriesMap: Map<HTMLElement, Category>;
protected categoriesByMenuTabMap: Map<HTMLElement, Category>;
protected categoriesIntersector: VisibilityIntersector;
protected localCategories: Category[];
protected listenerSetter: ListenerSetter;
public scrollable: Scrollable;
protected mounted = false;
protected menuOnClickResult: ReturnType<typeof EmoticonsDropdown['menuOnClick']>;
public tabId: number;
protected postponedEvents: {cb: AnyFunction, args: any[]}[];
public getContainerSize: Category['getContainerSize'];
constructor(
protected managers: AppManagers,
protected categoryItemsClassName: string,
protected getElementMediaSize: () => MediaSize,
protected padding: number,
protected gapX: number,
protected gapY: number
) {
this.categories = {};
this.categoriesMap = new Map();
this.categoriesByMenuTabMap = new Map();
this.localCategories = [];
this.postponedEvents = [];
this.listenerSetter = new ListenerSetter();
this.container = document.createElement('div');
this.container.classList.add('tabs-tab', 'emoticons-container');
this.menuWrapper = document.createElement('div');
this.menuWrapper.classList.add('menu-wrapper', 'emoticons-menu-wrapper');
this.menu = document.createElement('nav');
this.menu.className = 'menu-horizontal-div no-stripe justify-start emoticons-menu';
this.menuWrapper.append(this.menu);
this.menuScroll = new ScrollableX(this.menuWrapper);
this.content = document.createElement('div');
this.content.classList.add('emoticons-content');
this.container.append(this.menuWrapper, this.content);
this.scrollable = new Scrollable(this.content, 'STICKERS');
}
public getCategoryByContainer(container: HTMLElement) {
return this.categoriesMap.get(container);
}
public getCategoryByMenuTab(menuTab: HTMLElement) {
return this.categoriesByMenuTabMap.get(menuTab);
}
protected createCategory(stickerSet: StickerSet, title: HTMLElement | DocumentFragment, isLocal?: boolean, noMenuTab?: boolean) {
const category: Category = new StickersTabCategory({
id: '' + stickerSet.id,
title,
overflowElement: this.content,
getContainerSize: () => {
let width: number, height: number;
if(this.getContainerSize) {
const size = this.getContainerSize();
width = size.width;
height = size.height;
} else {
const esgWidth = customProperties.getPropertyAsSize('esg-width');
width = esgWidth === undefined ? windowSize.width : esgWidth;
}
return {width: width - this.padding, height};
},
getElementMediaSize: this.getElementMediaSize,
gapX: this.gapX,
gapY: this.gapY,
noMenuTab
}) as any;
if(this.categoryItemsClassName) {
category.elements.items.classList.add(this.categoryItemsClassName);
}
const container = category.elements.container;
container.classList.add('hide');
category.set = stickerSet;
this.categories[stickerSet.id] = category;
this.categoriesMap.set(container, category);
!noMenuTab && this.categoriesByMenuTabMap.set(category.elements.menuTab, category);
this.categoriesIntersector.observe(container);
!noMenuTab && this.menuOnClickResult.stickyIntersector.observeStickyHeaderChanges(container);
if(!isLocal) {
!noMenuTab && category.elements.menuTab.classList.add('not-local');
}
return category;
}
protected positionCategory(category: Category, prepend?: boolean) {
const {menuTab, container} = category.elements;
const posItems = prepend ? this.localCategories.filter((category) => category.mounted).length : 0xFFFF;
let foundMenuScroll = false;
const posMenu = prepend ? this.localCategories.filter((category) => {
if(category.menuScroll && !foundMenuScroll) {
foundMenuScroll = true;
return true;
}
return category.mounted && !category.menuScroll && category.elements.menuTab;
}).length : 0xFFFF;
positionElementByIndex(container, this.scrollable.container, posItems);
positionElementByIndex(menuTab, this.menu, posMenu);
}
protected isCategoryVisible(category: Category) {
return this.categoriesIntersector.getVisible().includes(category.elements.container);
}
protected toggleLocalCategory(category: Category, visible: boolean) {
if(!visible) {
category.elements.menuTab?.remove();
category.elements.container.remove();
} else {
const idx = this.localCategories.indexOf(category);
const sliced = this.localCategories.slice(0, idx);
let notMountedItems = 0, notMountedMenus = 0;
sliced.forEach((category) => {
if(!category.mounted) {
++notMountedItems;
++notMountedMenus;
} else if(!category.elements.menuTab || category.menuScroll) {
++notMountedMenus;
}
});
const itemsIdx = idx - notMountedItems, menuIdx = idx - notMountedMenus;
category.elements.menuTab && positionElementByIndex(category.elements.menuTab, this.menu, menuIdx);
positionElementByIndex(category.elements.container, this.scrollable.container, itemsIdx);
}
category.mounted = visible;
// category.elements.container.classList.toggle('hide', !visible);
}
protected createLocalCategory(id: string, title: LangPackKey | '', icon?: string, noMenuTab?: boolean) {
const category = this.createCategory({id} as any, title && i18n(title), true, noMenuTab);
category.local = true;
this.localCategories.push(category);
if(category.elements.title) {
category.elements.title.classList.add('disable-hover');
}
if(!noMenuTab) {
if(icon) {
category.elements.menuTab.classList.add('tgico', 'tgico-' + icon);
}
category.elements.menuTabPadding.remove();
}
this.toggleLocalCategory(category, false);
return category;
}
protected onLocalCategoryUpdate(category: Category) {
category.setCategoryItemsHeight();
this.toggleLocalCategory(category, !!category.items.length);
}
protected resizeCategories = () => {
for(const [container, category] of this.categoriesMap) {
category.setCategoryItemsHeight();
}
};
protected deleteCategory(category: Category) {
if(category) {
category.elements.container.remove();
category.elements.menuTab.remove();
this.categoriesIntersector.unobserve(category.elements.container);
delete this.categories[category.id];
this.categoriesMap.delete(category.elements.container);
this.categoriesByMenuTabMap.delete(category.elements.menuTab);
return true;
}
return false;
}
protected spliceExceed(category: Category) {
if(category.limit === undefined) {
return false;
}
const {items, limit} = category;
items.splice(limit, items.length - limit).forEach(({element}) => {
element.remove();
});
this.onLocalCategoryUpdate(category);
return true;
}
public init() {
this.listenerSetter.add(emoticonsDropdown)('closed', () => {
this.postponedEvents.forEach(({cb, args}) => {
cb(...args);
});
this.postponedEvents.length = 0;
});
}
public destroy() {
this.getContainerSize = undefined;
this.postponedEvents.length = 0;
this.categoriesIntersector?.disconnect();
this.listenerSetter.removeAll();
this.scrollable.destroy();
this.menuScroll?.destroy();
this.menuOnClickResult?.stickyIntersector?.disconnect();
}
protected postponedEvent = <K>(cb: (...args: K[]) => void) => {
return (...args: K[]) => {
if(emoticonsDropdown.isActive()) {
this.postponedEvents.push({cb, args});
} else {
cb(...args);
}
};
};
}
type StickersTabItem = {element: HTMLElement, document: Document.document};
export default class StickersTab extends EmoticonsTabC<StickersTabCategory<StickersTabItem>> {
private superStickerRenderer: SuperStickerRenderer;
constructor(managers: AppManagers) {
super(
managers,
'super-stickers',
() => mediaSizes.active.esgSticker,
3 * 2,
4,
4
);
this.container.classList.add('stickers-padding');
this.content.id = 'content-stickers';
}
private setFavedLimit(limit: number) {
const category = this.categories['faved'];
category.limit = limit;
}
private categoryAppendStickers(
category: StickersTabCategory<StickersTabItem>,
promise: Promise<MyDocument[]>
) {
const {container} = category.elements;
promise.then((documents) => {
const isVisible = this.isCategoryVisible(category);
documents.forEach((document) => {
const element = this.superStickerRenderer.renderSticker(document);
category.items.push({document, element});
if(isVisible) {
category.elements.items.append(element);
}
});
category.setCategoryItemsHeight();
container.classList.remove('hide');
});
}
private async renderStickerSet(set: StickerSet.stickerSet, prepend = false) {
const category = this.createCategory(set, wrapEmojiText(set.title));
const {menuTabPadding} = category.elements;
const promise = this.managers.appStickersManager.getStickerSet(set);
this.categoryAppendStickers(
category,
promise.then((stickerSet) => stickerSet.documents as MyDocument[])
);
this.positionCategory(category, prepend);
wrapStickerSetThumb({
set,
container: menuTabPadding,
group: EMOTICONSSTICKERGROUP,
lazyLoadQueue: EmoticonsDropdown.lazyLoadQueue,
width: 32,
height: 32,
autoplay: false
});
}
private onCategoryVisibility = ({target, visible, entry}: OnVisibilityChangeItem) => {
const category = this.categoriesMap.get(target);
category.elements.items.replaceChildren(...(!visible ? [] : category.items.map(({element}) => element)));
};
public init() {
super.init();
this.scrollable.onAdditionalScroll = () => {
this.setTyping();
};
/* 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();
}
}
}
}); */
const intersectionOptions: IntersectionObserverInit = {root: emoticonsDropdown.getElement()};
this.categoriesIntersector = new VisibilityIntersector(this.onCategoryVisibility, intersectionOptions);
const clearCategoryItems = (category: StickersTabCategory<StickersTabItem>) => {
category.elements.items.replaceChildren();
category.items.forEach(({element}) => this.superStickerRenderer.unobserveAnimated(element));
category.items.length = 0;
};
this.scrollable.container.addEventListener('click', (e) => {
const target = e.target as HTMLElement;
if(findUpClassName(target, 'category-title')) {
const container = findUpClassName(target, 'emoji-category');
const category = this.categoriesMap.get(container);
if(category.local) {
return;
}
new PopupStickers({id: category.set.id, access_hash: category.set.access_hash}).show();
return;
}
EmoticonsDropdown.onMediaClick(e);
});
this.menuOnClickResult = EmoticonsDropdown.menuOnClick(this, this.menu, this.scrollable, this.menuScroll);
const preloader = putPreloader(this.content, true);
const onCategoryStickers = (category: StickersTabCategory<StickersTabItem>, stickers: MyDocument[]) => {
// if(category.id === 'faved' && category.limit && category.limit < stickers.length) {
// category.limit = stickers.length;
// }
if(category.limit) {
stickers = stickers.slice(0, category.limit);
}
const ids = new Set(stickers.map((doc) => doc.id));
forEachReverse(category.items, (item) => {
if(!ids.has(item.document.id)) {
this.deleteSticker(category, item.document, true);
}
});
this.toggleLocalCategory(category, !!stickers.length);
forEachReverse(stickers, (doc, idx) => {
this.unshiftSticker(category, doc, true, idx);
});
this.spliceExceed(category);
category.elements.container.classList.remove('hide');
};
const favedCategory = this.createLocalCategory('faved', 'FavoriteStickers', 'savedmessages');
// favedCategory.elements.menuTab.classList.add('active');
const recentCategory = this.createLocalCategory('recent', 'Stickers.Recent', 'recent');
recentCategory.limit = 20;
const clearButton = ButtonIcon('close', {noRipple: true});
recentCategory.elements.title.append(clearButton);
attachClickEvent(clearButton, () => {
confirmationPopup({
titleLangKey: 'ClearRecentStickersAlertTitle',
descriptionLangKey: 'ClearRecentStickersAlertMessage',
button: {
langKey: 'Clear'
}
}).then(() => {
this.managers.appStickersManager.clearRecentStickers();
}, noop);
});
const premiumCategory = this.createLocalCategory('premium', 'PremiumStickersShort');
const s = document.createElement('span');
s.classList.add('tgico-star', 'color-premium');
premiumCategory.elements.menuTab.append(s);
const promises = [
Promise.all([
this.managers.apiManager.getLimit('favedStickers'),
this.managers.appStickersManager.getFavedStickersStickers()
]).then(([limit, stickers]) => {
this.setFavedLimit(limit);
onCategoryStickers(favedCategory, stickers);
}),
this.managers.appStickersManager.getRecentStickersStickers().then((stickers) => {
onCategoryStickers(recentCategory, stickers);
}),
this.managers.appStickersManager.getAllStickers().then((res) => {
for(const set of (res as MessagesAllStickers.messagesAllStickers).sets) {
this.renderStickerSet(set);
}
}),
this.managers.appStickersManager.getPremiumStickers().then((stickers) => {
const length = stickers.length;
this.toggleLocalCategory(premiumCategory, rootScope.premium && !!length);
this.categoryAppendStickers(premiumCategory, Promise.resolve(stickers));
rootScope.addEventListener('premium_toggle', (isPremium) => {
this.toggleLocalCategory(this.categories['premium'], isPremium && !!length);
});
})
];
Promise.race(promises).finally(() => {
preloader.remove();
});
Promise.all(promises).finally(() => {
this.mounted = true;
this.setTyping();
const favedCategory = this.categories['faved'];
const recentCategory = this.categories['recent'];
this.menuOnClickResult.setActive(favedCategory.items.length ? favedCategory : recentCategory);
rootScope.addEventListener('stickers_installed', (set) => {
if(!this.categories[set.id]) {
this.renderStickerSet(set, true);
}
});
});
this.superStickerRenderer = new SuperStickerRenderer(EmoticonsDropdown.lazyLoadQueue, EMOTICONSSTICKERGROUP, this.managers, intersectionOptions);
const rendererLazyLoadQueue = this.superStickerRenderer.lazyLoadQueue;
emoticonsDropdown.addLazyLoadQueueRepeat(rendererLazyLoadQueue, this.superStickerRenderer.processInvisible);
// emoticonsDropdown.addEventListener('close', () => {
// this.categoriesIntersector.lock();
// });
// emoticonsDropdown.addEventListener('closed', () => {
// for(const [container] of this.categoriesMap) {
// onCategoryVisibility(container, false);
// }
// });
// emoticonsDropdown.addEventListener('opened', () => {
// this.categoriesIntersector.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,
// rendererLazyLoadQueue.intersector.getVisible().length
// );
// }, .25e3);
rootScope.addEventListener('sticker_updated', ({type, document, faved}) => {
// if(type === 'faved') {
// return;
// }
const category = this.categories[type === 'faved' ? 'faved' : 'recent'];
if(category) {
if(faved) {
this.unshiftSticker(category, document);
} else {
this.deleteSticker(category, document);
}
}
});
rootScope.addEventListener('stickers_deleted', ({id}) => {
const category = this.categories[id];
if(this.deleteCategory(category)) {
clearCategoryItems(category);
}
});
rootScope.addEventListener('stickers_top', this.postponedEvent((id) => {
const category = this.categories[id];
if(category) {
this.positionCategory(category, true);
emoticonsDropdown.addEventListener('openAfterLayout', () => {
this.menuOnClickResult.setActiveStatic(category);
}, {once: true});
}
}));
rootScope.addEventListener('stickers_order', ({type, order}) => {
if(type !== 'stickers') {
return;
}
order.forEach((id) => {
const category = this.categories[id];
if(category) {
this.positionCategory(category, false);
}
});
});
rootScope.addEventListener('stickers_updated', ({type, stickers}) => {
const category = this.categories[type === 'faved' ? 'faved' : 'recent'];
if(category) {
onCategoryStickers(category, stickers);
}
});
rootScope.addEventListener('app_config', () => {
this.managers.apiManager.getLimit('favedStickers').then((limit) => {
this.setFavedLimit(limit);
});
});
mediaSizes.addEventListener('resize', this.resizeCategories);
attachStickerViewerListeners({listenTo: this.content, listenerSetter: new ListenerSetter()});
const type: IgnoreMouseOutType = 'menu';
createStickersContextMenu({
listenTo: this.content,
verifyRecent: (target) => !!findUpAsChild(target, this.categories['recent'].elements.items),
onOpen: () => {
emoticonsDropdown.setIgnoreMouseOut(type, true);
},
onClose: () => {
emoticonsDropdown.setIgnoreMouseOut(type, false);
}
});
this.init = null;
}
public deleteSticker(category: StickersTabCategory<StickersTabItem>, doc: MyDocument, batch?: boolean) {
const item = findAndSplice(category.items, (item) => item.document.id === doc.id);
if(item) {
item.element.remove();
if(!batch) {
this.onLocalCategoryUpdate(category);
}
}
}
public unshiftSticker(category: StickersTabCategory<StickersTabItem>, doc: MyDocument, batch?: boolean, idx?: number) {
if(idx !== undefined) {
const i = category.items[idx];
if(i && i.document.id === doc.id) {
return;
}
}
let item = findAndSplice(category.items, (item) => item.document.id === doc.id);
if(!item) {
item = {
element: this.superStickerRenderer.renderSticker(doc),
document: doc
};
}
category.items.unshift(item);
category.elements.items.prepend(item.element);
if(!batch) {
this.spliceExceed(category);
}
}
public unshiftRecentSticker(doc: MyDocument) {
this.managers.appStickersManager.saveRecentSticker(doc.id);
}
public deleteRecentSticker(doc: MyDocument) {
this.managers.appStickersManager.saveRecentSticker(doc.id, true);
}
public setTyping = (cancel = false) => {
if(!cancel && (!emoticonsDropdown.isActive() || emoticonsDropdown.tab !== this)) {
return;
}
rootScope.dispatchEvent('choosing_sticker', !cancel);
};
public onClosed() {
this.setTyping(true);
}
public onOpened() {
this.setTyping();
this.resizeCategories();
}
}