Browse Source

Fix stickers panel performance & styles

master
Eduard Kuzmenko 2 years ago
parent
commit
9b6fe590fa
  1. 4
      src/components/chat/inlineHelper.ts
  2. 14
      src/components/emoticonsDropdown/index.ts
  3. 368
      src/components/emoticonsDropdown/tabs/stickers.ts
  4. 2
      src/components/gifsMasonry.ts
  5. 4
      src/components/lazyLoadQueue.ts
  6. 9
      src/components/lazyLoadQueueRepeat.ts
  7. 5
      src/components/lazyLoadQueueRepeat2.ts
  8. 19
      src/components/visibilityIntersector.ts
  9. 6
      src/components/wrappers/stickerSetThumb.ts
  10. 3
      src/helpers/dropdownHover.ts
  11. 4
      src/index.hbs
  12. 9
      src/lib/appManagers/appStickersManager.ts
  13. 223
      src/scss/partials/_emojiDropdown.scss
  14. 4
      src/scss/style.scss

4
src/components/chat/inlineHelper.ts

@ -203,8 +203,8 @@ export default class InlineHelper extends AutocompleteHelper { @@ -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) {

14
src/components/emoticonsDropdown/index.ts

@ -187,6 +187,10 @@ export class EmoticonsDropdown extends DropdownHover { @@ -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 { @@ -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'
});
}
});

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

@ -5,7 +5,6 @@ @@ -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"; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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});
const category: StickersTabCategory = {
elements: {
container,
title,
items,
menuTab,
menuTabPadding
},
set: stickerSet,
items: []
};
promise.then((documents) => {
documents.forEach((doc) => {
//if(doc._ === 'documentEmpty') return;
itemsDiv.append(this.superStickerRenderer.renderSticker(doc));
});
container.append(title, items);
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;
}
});
this.categories[stickerSet.id] = category;
this.categoriesMap.set(container, category);
this.categoriesIntersector.observe(container);
return {titleDiv};
return category;
}
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;
private categoryPush(
category: StickersTabCategory,
promise: Promise<MyDocument[]>
) {
const {container, items} = category.elements;
this.stickyIntersector.observeStickyHeaderChanges(container);
const button = document.createElement('button');
button.classList.add('btn-icon', 'menu-horizontal-div-item');
promise.then((documents) => {
documents.forEach((document) => {
const element = this.superStickerRenderer.renderSticker(document);
category.items.push({document, element});
// items.append(element);
});
this.stickerSets[set.id] = {
stickers: categoryDiv,
tab: button
};
const containerWidth = 410;
const stickerSize = mediaSizes.active.esgSticker.width;
if(prepend) {
this.menu.insertBefore(button, this.menu.firstElementChild.nextSibling);
} else {
this.menu.append(button);
}
const itemsPerRow = Math.floor(containerWidth / stickerSize);
const rows = Math.ceil(documents.length / itemsPerRow);
const height = rows * stickerSize;
//stickersScroll.append(categoryDiv);
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;
//console.log('got stickerSet', stickerSet, li);
positionElementByIndex(container, this.scroll.container, prepend ? 1 : 0xFFFF, -1);
wrapStickerSetThumb({
set,
container: button,
container: menuTabPadding,
group: EMOTICONSSTICKERGROUP,
lazyLoadQueue: EmoticonsDropdown.lazyLoadQueue,
width: 32,
@ -244,21 +270,18 @@ export default class StickersTab implements EmoticonsTab { @@ -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 { @@ -277,30 +300,43 @@ export default class StickersTab implements EmoticonsTab {
}
}); */
rootScope.addEventListener('stickers_installed', (e) => {
const set: StickerSet.stickerSet = e;
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);
if(!this.stickerSets[set.id] && this.mounted) {
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 { @@ -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 { @@ -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[];
//stickersScroll.prepend(categoryDiv);
this.stickerSets['recent'] = {
stickers: this.recentDiv,
tab: this.menu.firstElementChild as HTMLElement
};
const sliced = stickers.stickers.slice(0, RECENT_STICKERS_COUNT) as MyDocument[];
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 { @@ -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();
// });
console.log('STICKERS RENDERED IN PANEL:', players.length, players.filter((p) => !p.paused).length, this.superStickerRenderer.lazyLoadQueue.intersector.getVisible().length);
}, .25e3); */
// 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);
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());
}
}

2
src/components/gifsMasonry.ts

@ -34,7 +34,7 @@ export default class GifsMasonry { @@ -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
src/components/lazyLoadQueue.ts

@ -4,7 +4,7 @@ @@ -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 { @@ -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);

9
src/components/lazyLoadQueueRepeat.ts

@ -11,10 +11,11 @@ import VisibilityIntersector, { OnVisibilityChange } from "./visibilityIntersect @@ -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 { @@ -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() {

5
src/components/lazyLoadQueueRepeat2.ts

@ -12,7 +12,8 @@ export default class LazyLoadQueueRepeat2 extends LazyLoadQueueIntersector { @@ -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 { @@ -20,7 +21,7 @@ export default class LazyLoadQueueRepeat2 extends LazyLoadQueueIntersector {
});
}
this.onVisibilityChange && this.onVisibilityChange(target, visible);
this.onVisibilityChange && this.onVisibilityChange(item);
this.setProcessQueueTimeout();
});
}

19
src/components/visibilityIntersector.ts

@ -5,20 +5,21 @@ @@ -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 { @@ -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() {

6
src/components/wrappers/stickerSetThumb.ts

@ -11,6 +11,7 @@ import appDownloadManager from "../../lib/appManagers/appDownloadManager"; @@ -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 @@ -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 @@ -79,7 +81,9 @@ export default async function wrapStickerSetThumb({set, lazyLoadQueue, container
div: container,
group: group,
lazyLoadQueue,
managers
managers,
width,
height
}); // kostil
}
}

3
src/helpers/dropdownHover.ts

@ -61,9 +61,8 @@ export default class DropdownHover extends EventListenerBase<{ @@ -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)) {

4
src/index.hbs

@ -158,9 +158,7 @@ @@ -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>

9
src/lib/appManagers/appStickersManager.ts

@ -103,7 +103,7 @@ export class AppStickersManager extends AppManager { @@ -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 { @@ -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 { @@ -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 { @@ -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];

223
src/scss/partials/_emojiDropdown.scss

@ -5,6 +5,7 @@ @@ -5,6 +5,7 @@
*/
.emoji-dropdown {
--menu-height: 3.0625rem;
display: flex/* !important */;
flex-direction: column;
width: 100%;
@ -41,16 +42,16 @@ @@ -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 @@ @@ -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 @@ @@ -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;
.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;
height: var(--menu-height);
min-height: var(--menu-height);
align-items: center;
.menu-horizontal-div-item {
&-item {
font-size: 1.5rem;
margin: 0 12px;
width: 48px;
height: 48px;
line-height: 48px;
width: 2.5rem;
height: 2.5rem;
line-height: 2.5rem;
display: flex;
align-items: center;
flex: 0 0 auto;
padding: 0;
}
}
.stickers-padding {
&.active {
.scrollable {
padding: 0;
//box-shadow: 0px 1px 5px -1px rgba(0, 0, 0, .21);
}
.category-title {
cursor: pointer;
}
.menu-horizontal-div {
.menu-horizontal-div-item {
height: 48px;
width: 48px;
padding: 0;
margin-right: 1px;
margin-left: 1px;
}
}
.category-items {
width: 100%;
display: grid;
grid-template-columns: repeat(auto-fill, var(--esg-sticker-size)); // 64px
justify-content: space-between;
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);
}
}
}
}
}
}

4
src/scss/style.scss

@ -968,13 +968,13 @@ img.emoji { @@ -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…
Cancel
Save