@ -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 HTMLDivElemen t ) ;
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 HTMLDivE lement,
div : e lement,
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 ) ) ;
} ) ;
/ * l e t t i m e o u t = w i n d o w . s e t T i m e o u t ( ( ) = > {
@ -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' ) ;
le t menuWrapper = this . content . previousElementSibling as HTMLDivElement ;
const menuWrapper = this . content . previousElementSibling as HTMLDivElement ;
this . menu = menuWrapper . firstElementChild as HTMLUListElement ;
le t menuScroll = new ScrollableX ( menuWrapper ) ;
cons t 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 ( ) ;
} ;
/ * s t i c k e r s D i v . a d d E v e n t L i s t e n e r ( ' m o u s e o v e r ' , ( 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 ) ;
/ * s e t I n t e r v a l ( ( ) = > {
// @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 ) ;
} , . 25 e3 ) ; * /
// 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 ( ) ) ;
}
}