Fix stickers panel performance & styles
This commit is contained in:
parent
8f6165164f
commit
9b6fe590fa
@ -203,8 +203,8 @@ export default class InlineHelper extends AutocompleteHelper {
|
||||
} else if(media.type === 'sticker') {
|
||||
container.classList.add('super-sticker');
|
||||
this.superStickerRenderer.renderSticker(media, container, loadPromises);
|
||||
if(media.sticker === 2) {
|
||||
this.superStickerRenderer.observeAnimatedDiv(container);
|
||||
if(media.animated) {
|
||||
this.superStickerRenderer.observeAnimated(container);
|
||||
}
|
||||
}
|
||||
} else if(media) {
|
||||
|
@ -187,6 +187,10 @@ export class EmoticonsDropdown extends DropdownHover {
|
||||
return super.init();
|
||||
}
|
||||
|
||||
public getElement() {
|
||||
return this.element;
|
||||
}
|
||||
|
||||
private onSelectTabClick = (id: number) => {
|
||||
if(this.tabId === id) {
|
||||
return;
|
||||
@ -248,11 +252,11 @@ export class EmoticonsDropdown extends DropdownHover {
|
||||
setActive(which);
|
||||
|
||||
if(menuScroll) {
|
||||
if(which < menu.childElementCount - 4) {
|
||||
menuScroll.container.scrollLeft = (which - 3) * 47;
|
||||
} else {
|
||||
menuScroll.container.scrollLeft = which * 47;
|
||||
}
|
||||
menuScroll.scrollIntoViewNew({
|
||||
element: menu.children[which] as HTMLElement,
|
||||
position: 'center',
|
||||
axis: 'x'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -5,7 +5,6 @@
|
||||
*/
|
||||
|
||||
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";
|
||||
@ -22,62 +21,72 @@ import PopupStickers from "../../popups/stickers";
|
||||
import Scrollable, { ScrollableX } from "../../scrollable";
|
||||
import StickyIntersector from "../../stickyIntersector";
|
||||
import { wrapSticker, wrapStickerSetThumb } from "../../wrappers";
|
||||
import ButtonIcon from "../../buttonIcon";
|
||||
import positionElementByIndex from "../../../helpers/dom/positionElementByIndex";
|
||||
import VisibilityIntersector, { OnVisibilityChange } from "../../visibilityIntersector";
|
||||
import findAndSplice from "../../../helpers/array/findAndSplice";
|
||||
|
||||
export class SuperStickerRenderer {
|
||||
public lazyLoadQueue: LazyLoadQueueRepeat;
|
||||
private animatedDivs: Set<HTMLDivElement> = new Set();
|
||||
private animated: Set<HTMLElement> = new Set();
|
||||
|
||||
constructor(
|
||||
private regularLazyLoadQueue: LazyLoadQueue,
|
||||
private group: string,
|
||||
private managers: AppManagers
|
||||
private managers: AppManagers,
|
||||
private options?: IntersectionObserverInit
|
||||
) {
|
||||
this.lazyLoadQueue = new LazyLoadQueueRepeat(undefined, (target, visible) => {
|
||||
this.lazyLoadQueue = new LazyLoadQueueRepeat(undefined, ({target, visible}) => {
|
||||
if(!visible) {
|
||||
this.processInvisibleDiv(target as HTMLDivElement);
|
||||
this.processInvisible(target);
|
||||
}
|
||||
});
|
||||
}, options);
|
||||
}
|
||||
|
||||
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');
|
||||
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.observeAnimatedDiv(div);
|
||||
this.observeAnimated(element);
|
||||
}
|
||||
}
|
||||
|
||||
// * This will wrap only a thumb
|
||||
wrapSticker({
|
||||
/* !doc.animated && */wrapSticker({
|
||||
doc,
|
||||
div,
|
||||
div: element,
|
||||
lazyLoadQueue: this.regularLazyLoadQueue,
|
||||
group: this.group,
|
||||
onlyThumb: doc.animated,
|
||||
loadPromises
|
||||
});
|
||||
|
||||
return div;
|
||||
return element;
|
||||
}
|
||||
|
||||
public observeAnimatedDiv(div: HTMLDivElement) {
|
||||
this.animatedDivs.add(div);
|
||||
|
||||
public observeAnimated(element: HTMLElement) {
|
||||
this.animated.add(element);
|
||||
this.lazyLoadQueue.observe({
|
||||
div,
|
||||
load: this.processVisibleDiv
|
||||
div: element,
|
||||
load: this.processVisible
|
||||
});
|
||||
}
|
||||
|
||||
private checkAnimationContainer = (div: HTMLElement, visible: boolean) => {
|
||||
public unobserveAnimated(element: HTMLElement) {
|
||||
this.animated.delete(element);
|
||||
this.lazyLoadQueue.unobserve(element);
|
||||
}
|
||||
|
||||
private checkAnimationContainer = (element: HTMLElement, visible: boolean) => {
|
||||
//console.error('checkAnimationContainer', div, visible);
|
||||
const players = animationIntersector.getAnimations(div);
|
||||
const players = animationIntersector.getAnimations(element);
|
||||
players.forEach((player) => {
|
||||
if(!visible) {
|
||||
animationIntersector.checkAnimation(player, true, true);
|
||||
@ -87,8 +96,8 @@ export class SuperStickerRenderer {
|
||||
});
|
||||
};
|
||||
|
||||
private processVisibleDiv = async(div: HTMLElement) => {
|
||||
const docId = div.dataset.docId;
|
||||
private processVisible = async(element: HTMLElement) => {
|
||||
const docId = element.dataset.docId;
|
||||
const doc = await this.managers.appDocsManager.getDoc(docId);
|
||||
|
||||
const size = mediaSizes.active.esgSticker.width;
|
||||
@ -97,7 +106,7 @@ export class SuperStickerRenderer {
|
||||
|
||||
const promise = wrapSticker({
|
||||
doc,
|
||||
div: div as HTMLDivElement,
|
||||
div: element,
|
||||
width: size,
|
||||
height: size,
|
||||
lazyLoadQueue: null,
|
||||
@ -109,7 +118,7 @@ export class SuperStickerRenderer {
|
||||
|
||||
promise.then(() => {
|
||||
//clearTimeout(timeout);
|
||||
this.checkAnimationContainer(div, this.lazyLoadQueue.intersector.isVisible(div));
|
||||
this.checkAnimationContainer(element, this.lazyLoadQueue.intersector.isVisible(element));
|
||||
});
|
||||
|
||||
/* let timeout = window.setTimeout(() => {
|
||||
@ -119,123 +128,140 @@ export class SuperStickerRenderer {
|
||||
return promise;
|
||||
};
|
||||
|
||||
public processInvisibleDiv = async(div: HTMLElement) => {
|
||||
const docId = div.dataset.docId;
|
||||
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(div, false);
|
||||
this.checkAnimationContainer(element, false);
|
||||
|
||||
div.innerHTML = '';
|
||||
this.renderSticker(doc, div as HTMLDivElement);
|
||||
element.textContent = '';
|
||||
this.renderSticker(doc, element as HTMLDivElement);
|
||||
};
|
||||
}
|
||||
|
||||
type StickersTabCategory = {
|
||||
elements: {
|
||||
container: HTMLElement,
|
||||
title: HTMLElement,
|
||||
items: HTMLElement,
|
||||
menuTab: HTMLElement,
|
||||
menuTabPadding: HTMLElement
|
||||
},
|
||||
set: StickerSet.stickerSet,
|
||||
items: {
|
||||
document: MyDocument,
|
||||
element: HTMLElement
|
||||
}[]
|
||||
};
|
||||
|
||||
const RECENT_STICKERS_COUNT = 20;
|
||||
|
||||
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 categories: {[id: string]: StickersTabCategory};
|
||||
private categoriesMap: Map<HTMLElement, StickersTabCategory>;
|
||||
private categoriesIntersector: VisibilityIntersector;
|
||||
|
||||
private scroll: Scrollable;
|
||||
|
||||
private menu: HTMLElement;
|
||||
|
||||
private mounted = false;
|
||||
|
||||
private queueCategoryPush: {element: HTMLElement, prepend: boolean}[] = [];
|
||||
|
||||
private stickyIntersector: StickyIntersector;
|
||||
|
||||
private superStickerRenderer: SuperStickerRenderer;
|
||||
|
||||
constructor(private managers: AppManagers) {
|
||||
|
||||
this.categories = {};
|
||||
this.categoriesMap = new Map();
|
||||
}
|
||||
|
||||
categoryPush(categoryDiv: HTMLElement, categoryTitle: DocumentFragment | string = '', promise: Promise<MyDocument[]>, prepend?: boolean) {
|
||||
//if((docs.length % 5) !== 0) categoryDiv.classList.add('not-full');
|
||||
private createCategory(stickerSet: StickerSet.stickerSet, _title: HTMLElement | DocumentFragment) {
|
||||
const container = document.createElement('div');
|
||||
container.classList.add('emoji-category', 'hide');
|
||||
|
||||
const itemsDiv = document.createElement('div');
|
||||
itemsDiv.classList.add('category-items', 'super-stickers');
|
||||
const items = document.createElement('div');
|
||||
items.classList.add('category-items', 'super-stickers');
|
||||
|
||||
const titleDiv = document.createElement('div');
|
||||
titleDiv.classList.add('category-title');
|
||||
const title = document.createElement('div');
|
||||
title.classList.add('category-title');
|
||||
title.append(_title);
|
||||
|
||||
if(categoryTitle) {
|
||||
if(typeof(categoryTitle) === 'string') titleDiv.innerHTML = categoryTitle;
|
||||
else titleDiv.append(categoryTitle);
|
||||
}
|
||||
const menuTab = ButtonIcon(undefined, {noRipple: true});
|
||||
menuTab.classList.add('menu-horizontal-div-item');
|
||||
|
||||
categoryDiv.append(titleDiv, itemsDiv);
|
||||
const menuTabPadding = document.createElement('div');
|
||||
menuTabPadding.classList.add('menu-horizontal-div-item-padding');
|
||||
|
||||
this.stickyIntersector.observeStickyHeaderChanges(categoryDiv);
|
||||
menuTab.append(menuTabPadding);
|
||||
|
||||
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
|
||||
const category: StickersTabCategory = {
|
||||
elements: {
|
||||
container,
|
||||
title,
|
||||
items,
|
||||
menuTab,
|
||||
menuTabPadding
|
||||
},
|
||||
set: stickerSet,
|
||||
items: []
|
||||
};
|
||||
|
||||
if(prepend) {
|
||||
this.menu.insertBefore(button, this.menu.firstElementChild.nextSibling);
|
||||
} else {
|
||||
this.menu.append(button);
|
||||
}
|
||||
container.append(title, items);
|
||||
|
||||
//stickersScroll.append(categoryDiv);
|
||||
this.categories[stickerSet.id] = category;
|
||||
this.categoriesMap.set(container, category);
|
||||
|
||||
this.categoriesIntersector.observe(container);
|
||||
|
||||
return category;
|
||||
}
|
||||
|
||||
private categoryPush(
|
||||
category: StickersTabCategory,
|
||||
promise: Promise<MyDocument[]>
|
||||
) {
|
||||
const {container, items} = category.elements;
|
||||
this.stickyIntersector.observeStickyHeaderChanges(container);
|
||||
|
||||
promise.then((documents) => {
|
||||
documents.forEach((document) => {
|
||||
const element = this.superStickerRenderer.renderSticker(document);
|
||||
category.items.push({document, element});
|
||||
// items.append(element);
|
||||
});
|
||||
|
||||
const containerWidth = 410;
|
||||
const stickerSize = mediaSizes.active.esgSticker.width;
|
||||
|
||||
const itemsPerRow = Math.floor(containerWidth / stickerSize);
|
||||
const rows = Math.ceil(documents.length / itemsPerRow);
|
||||
const height = rows * stickerSize;
|
||||
|
||||
items.style.height = height + 'px';
|
||||
|
||||
container.classList.remove('hide');
|
||||
});
|
||||
}
|
||||
|
||||
private async renderStickerSet(set: StickerSet.stickerSet, prepend = false) {
|
||||
const category = this.createCategory(set, wrapEmojiText(set.title));
|
||||
const {menuTab, menuTabPadding, container} = category.elements;
|
||||
|
||||
positionElementByIndex(menuTab, this.menu, prepend ? 1 : 0xFFFF);
|
||||
|
||||
const promise = this.managers.appStickersManager.getStickerSet(set);
|
||||
this.categoryPush(categoryDiv, wrapEmojiText(set.title), promise.then((stickerSet) => stickerSet.documents as MyDocument[]), prepend);
|
||||
const stickerSet = await promise;
|
||||
this.categoryPush(
|
||||
category,
|
||||
promise.then((stickerSet) => stickerSet.documents as MyDocument[])
|
||||
);
|
||||
// const stickerSet = await promise;
|
||||
|
||||
positionElementByIndex(container, this.scroll.container, prepend ? 1 : 0xFFFF, -1);
|
||||
|
||||
//console.log('got stickerSet', stickerSet, li);
|
||||
|
||||
wrapStickerSetThumb({
|
||||
set,
|
||||
container: button,
|
||||
container: menuTabPadding,
|
||||
group: EMOTICONSSTICKERGROUP,
|
||||
lazyLoadQueue: EmoticonsDropdown.lazyLoadQueue,
|
||||
width: 32,
|
||||
@ -244,21 +270,18 @@ export default class StickersTab implements EmoticonsTab {
|
||||
});
|
||||
}
|
||||
|
||||
init() {
|
||||
public 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;
|
||||
const menuWrapper = this.content.previousElementSibling as HTMLDivElement;
|
||||
this.menu = menuWrapper.firstElementChild as HTMLUListElement;
|
||||
|
||||
let menuScroll = new ScrollableX(menuWrapper);
|
||||
const menuScroll = new ScrollableX(menuWrapper);
|
||||
|
||||
this.stickersDiv = document.createElement('div');
|
||||
this.stickersDiv.classList.add('stickers-categories');
|
||||
this.content.append(this.stickersDiv);
|
||||
this.scroll = new Scrollable(this.content, 'STICKERS');
|
||||
this.scroll.onAdditionalScroll = () => {
|
||||
setTyping();
|
||||
};
|
||||
|
||||
/* stickersDiv.addEventListener('mouseover', (e) => {
|
||||
let target = e.target as HTMLElement;
|
||||
@ -277,30 +300,43 @@ export default class StickersTab implements EmoticonsTab {
|
||||
}
|
||||
}); */
|
||||
|
||||
rootScope.addEventListener('stickers_installed', (e) => {
|
||||
const set: StickerSet.stickerSet = e;
|
||||
|
||||
if(!this.stickerSets[set.id] && this.mounted) {
|
||||
const onCategoryVisibility: OnVisibilityChange = ({target, visible, entry}) => {
|
||||
const category = this.categoriesMap.get(target);
|
||||
// console.log('roll the windows up', category, target, visible, entry);
|
||||
if(!visible) {
|
||||
category.elements.items.textContent = '';
|
||||
} else {
|
||||
category.elements.items.append(...category.items.map(({element}) => element));
|
||||
}
|
||||
};
|
||||
|
||||
const intersectionOptions: IntersectionObserverInit = {root: emoticonsDropdown.getElement()};
|
||||
this.categoriesIntersector = new VisibilityIntersector(onCategoryVisibility, intersectionOptions);
|
||||
|
||||
rootScope.addEventListener('stickers_installed', (set) => {
|
||||
if(!this.categories[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];
|
||||
rootScope.addEventListener('stickers_deleted', ({id}) => {
|
||||
const set = this.categories[id];
|
||||
if(set && this.mounted) {
|
||||
set.elements.container.remove();
|
||||
set.elements.menuTab.remove();
|
||||
this.categoriesIntersector.unobserve(set.elements.container);
|
||||
set.items.forEach(({element}) => this.superStickerRenderer.unobserveAnimated(element));
|
||||
delete this.categories[id];
|
||||
this.categoriesMap.delete(set.elements.container);
|
||||
}
|
||||
});
|
||||
|
||||
this.stickersDiv.addEventListener('click', (e) => {
|
||||
this.scroll.container.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();
|
||||
const container = findUpClassName(target, 'emoji-category');
|
||||
const category = this.categoriesMap.get(container);
|
||||
new PopupStickers({id: category.set.id, access_hash: category.set.access_hash}).show();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -311,12 +347,6 @@ export default class StickersTab implements EmoticonsTab {
|
||||
rootScope.dispatchEvent('choosing_sticker', !cancel);
|
||||
};
|
||||
|
||||
this.scroll = new Scrollable(this.content, 'STICKERS');
|
||||
this.scroll.setVirtualContainer(this.stickersDiv);
|
||||
this.scroll.onAdditionalScroll = () => {
|
||||
setTyping();
|
||||
};
|
||||
|
||||
emoticonsDropdown.addEventListener('closed', () => {
|
||||
setTyping(true);
|
||||
});
|
||||
@ -329,20 +359,19 @@ export default class StickersTab implements EmoticonsTab {
|
||||
|
||||
const preloader = putPreloader(this.content, true);
|
||||
|
||||
const recentCategory = this.createCategory({id: 'recent'} as any, i18n('Stickers.Recent'));
|
||||
recentCategory.elements.title.classList.add('disable-hover');
|
||||
recentCategory.elements.menuTab.classList.add('tgico-recent', 'active');
|
||||
recentCategory.elements.menuTabPadding.remove();
|
||||
positionElementByIndex(recentCategory.elements.container, this.scroll.container, 0);
|
||||
positionElementByIndex(recentCategory.elements.menuTab, this.menu, 0);
|
||||
|
||||
Promise.all([
|
||||
this.managers.appStickersManager.getRecentStickers().then((stickers) => {
|
||||
this.recentStickers = stickers.stickers.slice(0, 20) as MyDocument[];
|
||||
const sliced = stickers.stickers.slice(0, RECENT_STICKERS_COUNT) 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'));
|
||||
this.categoryPush(recentCategory, Promise.resolve(sliced));
|
||||
}),
|
||||
|
||||
this.managers.appStickersManager.getAllStickers().then((res) => {
|
||||
@ -357,38 +386,61 @@ export default class StickersTab implements EmoticonsTab {
|
||||
setTyping();
|
||||
});
|
||||
|
||||
this.superStickerRenderer = new SuperStickerRenderer(EmoticonsDropdown.lazyLoadQueue, EMOTICONSSTICKERGROUP, this.managers);
|
||||
this.superStickerRenderer = new SuperStickerRenderer(EmoticonsDropdown.lazyLoadQueue, EMOTICONSSTICKERGROUP, this.managers, intersectionOptions);
|
||||
|
||||
emoticonsDropdown.addLazyLoadQueueRepeat(this.superStickerRenderer.lazyLoadQueue, this.superStickerRenderer.processInvisibleDiv);
|
||||
const rendererLazyLoadQueue = this.superStickerRenderer.lazyLoadQueue;
|
||||
emoticonsDropdown.addLazyLoadQueueRepeat(rendererLazyLoadQueue, this.superStickerRenderer.processInvisible);
|
||||
|
||||
/* setInterval(() => {
|
||||
// @ts-ignore
|
||||
const players = Object.values(lottieLoader.players).filter((p) => p.width === 80);
|
||||
// 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, this.superStickerRenderer.lazyLoadQueue.intersector.getVisible().length);
|
||||
}, .25e3); */
|
||||
// console.log(
|
||||
// 'STICKERS RENDERED IN PANEL:',
|
||||
// players.length,
|
||||
// players.filter((p) => !p.paused).length,
|
||||
// rendererLazyLoadQueue.intersector.getVisible().length
|
||||
// );
|
||||
// }, .25e3);
|
||||
|
||||
|
||||
this.init = null;
|
||||
}
|
||||
|
||||
pushRecentSticker(doc: MyDocument) {
|
||||
this.managers.appStickersManager.pushRecentSticker(doc);
|
||||
public pushRecentSticker(doc: MyDocument) {
|
||||
this.managers.appStickersManager.pushRecentSticker(doc.id);
|
||||
|
||||
if(!this.recentDiv?.parentElement) {
|
||||
const set = this.categories['recent'];
|
||||
if(!set) {
|
||||
return;
|
||||
}
|
||||
|
||||
let div = this.recentDiv.querySelector(`[data-doc-id="${doc.id}"]`);
|
||||
if(!div) {
|
||||
div = this.superStickerRenderer.renderSticker(doc);
|
||||
const items = set.elements.items;
|
||||
let item = findAndSplice(set.items, (item) => item.document.id === doc.id);
|
||||
if(!item) {
|
||||
item = {
|
||||
element: this.superStickerRenderer.renderSticker(doc),
|
||||
document: 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());
|
||||
set.items.unshift(item);
|
||||
if(items.childElementCount) items.prepend(item.element);
|
||||
if(items.childElementCount > RECENT_STICKERS_COUNT) {
|
||||
(Array.from(items.children) as HTMLElement[]).slice(RECENT_STICKERS_COUNT).forEach((el) => el.remove());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ export default class GifsMasonry {
|
||||
) {
|
||||
this.managers = rootScope.managers;
|
||||
|
||||
this.lazyLoadQueue = new LazyLoadQueueRepeat2(undefined, (target, visible) => {
|
||||
this.lazyLoadQueue = new LazyLoadQueueRepeat2(undefined, ({target, visible}) => {
|
||||
if(visible) {
|
||||
this.processVisibleDiv(target);
|
||||
} else {
|
||||
|
@ -4,7 +4,7 @@
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import VisibilityIntersector from "./visibilityIntersector";
|
||||
import VisibilityIntersector, { OnVisibilityChangeItem } from "./visibilityIntersector";
|
||||
import findAndSpliceAll from "../helpers/array/findAndSpliceAll";
|
||||
import findAndSplice from "../helpers/array/findAndSplice";
|
||||
import LazyLoadQueueIntersector, { LazyLoadElement } from "./lazyLoadQueueIntersector";
|
||||
@ -16,7 +16,7 @@ export default class LazyLoadQueue extends LazyLoadQueueIntersector {
|
||||
this.intersector = new VisibilityIntersector(this.onVisibilityChange);
|
||||
}
|
||||
|
||||
private onVisibilityChange = (target: HTMLElement, visible: boolean) => {
|
||||
private onVisibilityChange = ({target, visible}: OnVisibilityChangeItem) => {
|
||||
if(visible) {
|
||||
/* if(DEBUG) {
|
||||
this.log('isIntersecting', target);
|
||||
|
@ -11,10 +11,11 @@ import VisibilityIntersector, { OnVisibilityChange } from "./visibilityIntersect
|
||||
export default class LazyLoadQueueRepeat extends LazyLoadQueueIntersector {
|
||||
private _queue: Map<HTMLElement, LazyLoadElement> = new Map();
|
||||
|
||||
constructor(parallelLimit?: number, protected onVisibilityChange?: OnVisibilityChange) {
|
||||
constructor(parallelLimit?: number, protected onVisibilityChange?: OnVisibilityChange, options?: IntersectionObserverInit) {
|
||||
super(parallelLimit);
|
||||
|
||||
this.intersector = new VisibilityIntersector((target, visible) => {
|
||||
this.intersector = new VisibilityIntersector((item) => {
|
||||
const {target, visible} = item;
|
||||
const spliced = findAndSpliceAll(this.queue, (i) => i.div === target);
|
||||
if(visible) {
|
||||
const items = spliced.length ? spliced : [this._queue.get(target)];
|
||||
@ -23,9 +24,9 @@ export default class LazyLoadQueueRepeat extends LazyLoadQueueIntersector {
|
||||
});
|
||||
}
|
||||
|
||||
this.onVisibilityChange && this.onVisibilityChange(target, visible);
|
||||
this.onVisibilityChange && this.onVisibilityChange(item);
|
||||
this.setProcessQueueTimeout();
|
||||
});
|
||||
}, options);
|
||||
}
|
||||
|
||||
public clear() {
|
||||
|
@ -12,7 +12,8 @@ export default class LazyLoadQueueRepeat2 extends LazyLoadQueueIntersector {
|
||||
constructor(parallelLimit?: number, protected onVisibilityChange?: OnVisibilityChange) {
|
||||
super(parallelLimit);
|
||||
|
||||
this.intersector = new VisibilityIntersector((target, visible) => {
|
||||
this.intersector = new VisibilityIntersector((item) => {
|
||||
const {target, visible} = item;
|
||||
const spliced = findAndSpliceAll(this.queue, (i) => i.div === target);
|
||||
if(visible && spliced.length) {
|
||||
spliced.forEach((item) => {
|
||||
@ -20,7 +21,7 @@ export default class LazyLoadQueueRepeat2 extends LazyLoadQueueIntersector {
|
||||
});
|
||||
}
|
||||
|
||||
this.onVisibilityChange && this.onVisibilityChange(target, visible);
|
||||
this.onVisibilityChange && this.onVisibilityChange(item);
|
||||
this.setProcessQueueTimeout();
|
||||
});
|
||||
}
|
||||
|
@ -5,20 +5,21 @@
|
||||
*/
|
||||
|
||||
type TargetType = HTMLElement;
|
||||
export type OnVisibilityChange = (target: TargetType, visible: boolean) => void;
|
||||
export type OnVisibilityChangeItem = {target: TargetType, visible: boolean, entry: IntersectionObserverEntry};
|
||||
export type OnVisibilityChange = (item: OnVisibilityChangeItem) => void;
|
||||
|
||||
export default class VisibilityIntersector {
|
||||
private observer: IntersectionObserver;
|
||||
private items: Map<TargetType, boolean> = new Map();
|
||||
private locked = false;
|
||||
|
||||
constructor(onVisibilityChange: OnVisibilityChange) {
|
||||
constructor(onVisibilityChange: OnVisibilityChange, options?: IntersectionObserverInit) {
|
||||
this.observer = new IntersectionObserver((entries) => {
|
||||
if(this.locked) {
|
||||
return;
|
||||
}
|
||||
|
||||
const changed: {target: TargetType, visible: boolean}[] = [];
|
||||
const changed: OnVisibilityChangeItem[] = [];
|
||||
|
||||
entries.forEach((entry) => {
|
||||
const target = entry.target as TargetType;
|
||||
@ -37,15 +38,19 @@ export default class VisibilityIntersector {
|
||||
return;
|
||||
} */
|
||||
|
||||
changed[entry.isIntersecting ? 'unshift' : 'push']({target, visible: entry.isIntersecting});
|
||||
const change: typeof changed[0] = {target, visible: entry.isIntersecting, entry};
|
||||
|
||||
// ! order will be incorrect so can't use it
|
||||
// changed[entry.isIntersecting ? 'unshift' : 'push'](change);
|
||||
changed.push(change);
|
||||
|
||||
//onVisibilityChange(target, entry.isIntersecting);
|
||||
});
|
||||
|
||||
changed.forEach((smth) => {
|
||||
onVisibilityChange(smth.target, smth.visible);
|
||||
changed.forEach((item) => {
|
||||
onVisibilityChange(item);
|
||||
});
|
||||
});
|
||||
}, options);
|
||||
}
|
||||
|
||||
public getVisible() {
|
||||
|
@ -11,6 +11,7 @@ import appDownloadManager from "../../lib/appManagers/appDownloadManager";
|
||||
import { AppManagers } from "../../lib/appManagers/managers";
|
||||
import lottieLoader from "../../lib/rlottie/lottieLoader";
|
||||
import rootScope from "../../lib/rootScope";
|
||||
import animationIntersector from "../animationIntersector";
|
||||
import LazyLoadQueue from "../lazyLoadQueue";
|
||||
import wrapSticker from "./sticker";
|
||||
|
||||
@ -62,6 +63,7 @@ export default async function wrapStickerSetThumb({set, lazyLoadQueue, container
|
||||
return promise.then((blob) => {
|
||||
renderImageFromUrl(media, URL.createObjectURL(blob), () => {
|
||||
container.append(media);
|
||||
animationIntersector.addAnimation(media as HTMLVideoElement, group);
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -79,7 +81,9 @@ export default async function wrapStickerSetThumb({set, lazyLoadQueue, container
|
||||
div: container,
|
||||
group: group,
|
||||
lazyLoadQueue,
|
||||
managers
|
||||
managers,
|
||||
width,
|
||||
height
|
||||
}); // kostil
|
||||
}
|
||||
}
|
||||
|
@ -61,9 +61,8 @@ export default class DropdownHover extends EventListenerBase<{
|
||||
}
|
||||
|
||||
private onMouseOut = (e: MouseEvent) => {
|
||||
if(KEEP_OPEN) return;
|
||||
if(KEEP_OPEN || !this.isActive()) return;
|
||||
clearTimeout(this.displayTimeout);
|
||||
if(!this.isActive()) return;
|
||||
|
||||
const toElement = (e as any).toElement as Element;
|
||||
if(toElement && findUpAsChild(toElement, this.element)) {
|
||||
|
@ -158,9 +158,7 @@
|
||||
</div>
|
||||
<div class="tabs-tab stickers-padding">
|
||||
<div class="menu-wrapper">
|
||||
<nav class="menu-horizontal-div no-stripe justify-start">
|
||||
<button class="menu-horizontal-div-item btn-icon tgico-recent active"></button>
|
||||
</nav>
|
||||
<nav class="menu-horizontal-div no-stripe justify-start"></nav>
|
||||
</div>
|
||||
<div class="emoticons-content" id="content-stickers"></div>
|
||||
</div>
|
||||
|
@ -103,7 +103,7 @@ export class AppStickersManager extends AppManager {
|
||||
});
|
||||
}
|
||||
|
||||
public saveStickers(docs: Document[]) {
|
||||
private saveStickers(docs: Document[]) {
|
||||
forEachReverse(docs, (doc, idx) => {
|
||||
doc = this.appDocsManager.saveDoc(doc);
|
||||
|
||||
@ -295,7 +295,7 @@ export class AppStickersManager extends AppManager {
|
||||
});
|
||||
}
|
||||
|
||||
public saveStickerSet(res: Omit<MessagesStickerSet.messagesStickerSet, '_'>, id: DocId) {
|
||||
private saveStickerSet(res: Omit<MessagesStickerSet.messagesStickerSet, '_'>, id: DocId) {
|
||||
//console.log('stickers save set', res);w
|
||||
|
||||
const newSet: MessagesStickerSet = {
|
||||
@ -401,6 +401,8 @@ export class AppStickersManager extends AppManager {
|
||||
}
|
||||
|
||||
public async toggleStickerSet(set: StickerSet.stickerSet) {
|
||||
set = this.storage.getFromCache(set.id).set;
|
||||
|
||||
if(set.installed_date) {
|
||||
const res = await this.apiManager.invokeApi('messages.uninstallStickerSet', {
|
||||
stickerset: this.getStickerSetInput(set)
|
||||
@ -559,7 +561,8 @@ export class AppStickersManager extends AppManager {
|
||||
});
|
||||
}
|
||||
|
||||
public pushRecentSticker(doc: MyDocument) {
|
||||
public pushRecentSticker(docId: DocId) {
|
||||
const doc = this.appDocsManager.getDoc(docId);
|
||||
const docEmoticon = fixEmoji(doc.stickerEmojiRaw);
|
||||
for(const emoticon in this.getStickersByEmoticonsPromises) {
|
||||
const promise = this.getStickersByEmoticonsPromises[emoticon];
|
||||
|
@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
.emoji-dropdown {
|
||||
--menu-height: 3.0625rem;
|
||||
display: flex/* !important */;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
@ -41,16 +42,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* @include respond-to(handhelds) {
|
||||
width: calc(100% + 1rem);
|
||||
margin-left: -.5rem;
|
||||
} */
|
||||
|
||||
.emoji-container {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
|
||||
.menu-horizontal-div {
|
||||
z-index: 4;
|
||||
background-color: var(--surface-color);
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-tabs {
|
||||
@ -62,34 +63,32 @@
|
||||
&-search {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
margin-left: 4px !important;
|
||||
margin-left: .5rem !important;
|
||||
}
|
||||
|
||||
&-delete {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
margin-right: 4px !important;
|
||||
margin-right: .5rem !important;
|
||||
}
|
||||
|
||||
.menu-horizontal-div-item {
|
||||
margin: 0 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.category-title {
|
||||
font-size: var(--font-size-14);
|
||||
font-weight: var(--font-weight-bold);
|
||||
line-height: var(--line-height-14);
|
||||
color: var(--secondary-text-color);
|
||||
padding: .8125rem .875rem .6875rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tabs-container {
|
||||
/* width: 300%; */
|
||||
height: 100%;
|
||||
|
||||
.category-title {
|
||||
//position: sticky;
|
||||
top: 0;
|
||||
//font-size: .85rem;
|
||||
font-size: 14px;
|
||||
font-weight: var(--font-weight-bold);
|
||||
color: var(--secondary-text-color);
|
||||
//background: linear-gradient(to bottom,#fff 0,rgba(255,255,255,.9) 60%,rgba(255,255,255,0) 100%);
|
||||
z-index: 2;
|
||||
//padding: .53333rem 6PX .66667rem;
|
||||
padding: 12px 6px 6px 6px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sticky_sentinel {
|
||||
&--top {
|
||||
top: 0;
|
||||
@ -110,173 +109,83 @@
|
||||
position: relative;
|
||||
background-color: var(--surface-color);
|
||||
}
|
||||
|
||||
.scrollable {
|
||||
padding: 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-padding.active {
|
||||
.emoji-padding {
|
||||
.super-emojis {
|
||||
padding: 0 .5rem;
|
||||
}
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
.menu-horizontal-div .menu-horizontal-div-item {
|
||||
.menu-horizontal-div-item {
|
||||
flex: unset;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.category-items {
|
||||
grid-template-columns: repeat(auto-fill, 40px);
|
||||
|
||||
> span {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
justify-self: center;
|
||||
}
|
||||
}
|
||||
|
||||
.category-title {
|
||||
padding: 12px 6px 6px 10px;
|
||||
}
|
||||
|
||||
.scrollable {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.emoji-category .category-items {
|
||||
grid-column-gap: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-padding,
|
||||
.stickers-padding {
|
||||
.menu-horizontal-div {
|
||||
// padding: 2px;
|
||||
z-index: 4;
|
||||
background-color: var(--surface-color);
|
||||
|
||||
.menu-horizontal-div-item {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-category {
|
||||
//padding-top: 1px;
|
||||
position: relative;
|
||||
margin: 0 -.125rem;
|
||||
|
||||
/* &:first-child {
|
||||
//padding-top: 5px;
|
||||
} */
|
||||
|
||||
/* &::after {
|
||||
content: "";
|
||||
flex: auto;
|
||||
} */
|
||||
}
|
||||
|
||||
.sticker-category {
|
||||
position: relative;
|
||||
.menu-horizontal-div {
|
||||
width: 100%;
|
||||
height: var(--menu-height);
|
||||
min-height: var(--menu-height);
|
||||
align-items: center;
|
||||
|
||||
.category-title {
|
||||
&-item {
|
||||
font-size: 1.5rem;
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
line-height: 2.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 0 0 auto;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.stickers-padding {
|
||||
.category-title {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.stickers-recent {
|
||||
.category-title {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* &::after {
|
||||
content: "";
|
||||
flex: auto;
|
||||
} */
|
||||
|
||||
/* &.not-full::after {
|
||||
content: "";
|
||||
flex: auto;
|
||||
} */
|
||||
|
||||
.category-items {
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, var(--esg-sticker-size)); // 64px
|
||||
grid-column-gap: 1px;
|
||||
justify-content: space-between;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#content-stickers {
|
||||
.scrollable {
|
||||
padding: 0px 5px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-horizontal-div {
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
|
||||
.menu-horizontal-div-item {
|
||||
font-size: 1.5rem;
|
||||
margin: 0 12px;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.stickers-padding {
|
||||
&.active {
|
||||
.scrollable {
|
||||
padding: 0;
|
||||
//box-shadow: 0px 1px 5px -1px rgba(0, 0, 0, .21);
|
||||
}
|
||||
|
||||
.menu-horizontal-div {
|
||||
.menu-horizontal-div-item {
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
padding: 0;
|
||||
margin-right: 1px;
|
||||
margin-left: 1px;
|
||||
}
|
||||
}
|
||||
padding: 0 .3125rem;
|
||||
}
|
||||
|
||||
.menu-wrapper {
|
||||
padding: 0;
|
||||
height: 48px;
|
||||
height: var(--menu-height);
|
||||
max-width: 100%;
|
||||
position: relative;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
background-color: var(--surface-color);
|
||||
}
|
||||
|
||||
.menu-horizontal-div-item {
|
||||
/* width: calc(100% / 7); */
|
||||
flex: 0 0 auto;
|
||||
|
||||
&.active {
|
||||
&:not(.tgico-recent) {
|
||||
background-color: var(--light-secondary-text-color);
|
||||
}
|
||||
}
|
||||
|
||||
> canvas, > img {
|
||||
//padding: .75rem;
|
||||
padding: 8px;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
> canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.menu-horizontal-div {
|
||||
&-item {
|
||||
flex: 0 0 auto;
|
||||
padding: .25rem;
|
||||
margin: 0 .3125rem;
|
||||
|
||||
&-padding {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&.active {
|
||||
&:not(.tgico-recent) {
|
||||
background-color: var(--light-secondary-text-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -968,13 +968,13 @@ img.emoji {
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, var(--esg-sticker-size)); // 64px
|
||||
grid-column-gap: 1px;
|
||||
// grid-column-gap: 1px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.super-sticker {
|
||||
@include hover-background-effect() {
|
||||
border-radius: 10px;
|
||||
border-radius: $border-radius-medium;
|
||||
}
|
||||
|
||||
/* &:nth-child(5n+5) {
|
||||
|
Loading…
Reference in New Issue
Block a user