@ -1,21 +1,45 @@
@@ -1,21 +1,45 @@
import { limitSymbols } from "../helpers/string" ;
import appMessagesManager , { MyInputMessagesFilter } from "../lib/appManagers/appMessagesManager" ;
import { formatDateAccordingToToday , months } from "../helpers/date" ;
import { findUpClassName , positionElementByIndex } from "../helpers/dom" ;
import { copy , getObjectKeysAndSort } from "../helpers/object" ;
import { escapeRegExp , limitSymbols } from "../helpers/string" ;
import appChatsManager from "../lib/appManagers/appChatsManager" ;
import appDialogsManager from "../lib/appManagers/appDialogsManager" ;
import appMessagesManager , { MyInputMessagesFilter , MyMessage } from "../lib/appManagers/appMessagesManager" ;
import appPeersManager from "../lib/appManagers/appPeersManager" ;
import appPhotosManager from "../lib/appManagers/appPhotosManager" ;
import appStateManager from "../lib/appManagers/appStateManager" ;
import appUsersManager from "../lib/appManagers/appUsersManager" ;
import { logger } from "../lib/logger" ;
import RichTextProcessor from "../lib/richtextprocessor" ;
import rootScope from "../lib/rootScope" ;
import searchIndexManager from "../lib/searchIndexManager" ;
import AppMediaViewer from "./appMediaViewer" ;
import { SearchGroup , SearchGroupType } from "./appSearch" ;
import { horizontalMenu } from "./horizontalMenu" ;
import LazyLoadQueue from "./lazyLoadQueue" ;
import { renderImageFromUrl , putPreloader } from "./misc" ;
import { renderImageFromUrl , putPreloader , formatPhoneNumber } from "./misc" ;
import { ripple } from "./ripple" ;
import Scrollable from "./scrollable" ;
import { wrapDocument } from "./wrappers" ;
const testScroll = false ;
export type SearchSuperType = MyInputMessagesFilter /* | 'chats' */ ;
export type SearchSuperContext = {
peerId : number ,
inputFilter : MyInputMessagesFilter ,
query? : string ,
maxId? : number ,
folderId? : number ,
threadId? : number ,
date? : number ,
nextRate? : number
} ;
export default class AppSearchSuper {
public tabs : { [ t in MyInputMessagesFilter ] : HTMLDivElement } = { } as any ;
public tabs : { [ t in SearchSuperType ] : HTMLDivElement } = { } as any ;
public type : MyInputMessagesFilter ;
public type : SearchSuperType ;
public tabSelected : HTMLElement ;
public container : HTMLElement ;
@ -23,23 +47,34 @@ export default class AppSearchSuper {
@@ -23,23 +47,34 @@ export default class AppSearchSuper {
private tabsMenu : HTMLUListElement ;
private prevTabId = - 1 ;
public mediaDivsByIds : { [ mid : number ] : HTMLDivElement } = { } ;
private lazyLoadQueue = new LazyLoadQueue ( ) ;
private cleanupObj = { cleaned : false } ;
public historyStorage : Partial < { [ type in MyInputMessagesFilter ] : number [ ] } > = { } ;
public usedFromHistory : Partial < { [ type in MyInputMessagesFilter ] : number } > = { } ;
public historyStorage : Partial < { [ type in SearchSuperType ] : { mid : number , peerId : number } [ ] } > = { } ;
public usedFromHistory : Partial < { [ type in SearchSuperType ] : number } > = { } ;
public urlsToRevoke : string [ ] = [ ] ;
private peerId = 0 ;
private threadId = 0 ;
private query = '' ;
private searchContext : SearchSuperContext ;
public loadMutex : Promise < any > = Promise . resolve ( ) ;
private loadPromises : Partial < { [ type in MyInputMessagesFilter ] : Promise < void > } > = { } ;
private loaded : Partial < { [ type in MyInputMessagesFilter ] : boolean } > = { } ;
private nextRates : Partial < { [ type in SearchSuperType ] : number } > = { } ;
private loadPromises : Partial < { [ type in SearchSuperType ] : Promise < void > } > = { } ;
private loaded : Partial < { [ type in SearchSuperType ] : boolean } > = { } ;
private loadedChats = false ;
constructor ( public types : { inputFilter : MyInputMessagesFilter , name : string } [ ] , public scrollable : Scrollable ) {
private log = logger ( 'SEARCH-SUPER' ) ;
public selectTab : ReturnType < typeof horizontalMenu > ;
private monthContainers : Partial < {
[ type in SearchSuperType ] : {
[ timestamp : number ] : {
container : HTMLElement ,
items : HTMLElement
}
}
} > = { } ;
constructor ( public types : { inputFilter : SearchSuperType , name : string } [ ] , public scrollable : Scrollable , public searchGroups ? : { [ group in SearchGroupType ] : SearchGroup } , public asChatList = false ) {
this . container = document . createElement ( 'div' ) ;
this . container . classList . add ( 'search-super' ) ;
@ -92,24 +127,24 @@ export default class AppSearchSuper {
@@ -92,24 +127,24 @@ export default class AppSearchSuper {
} ;
//this.scroll.attachSentinels(undefined, 400);
horizontalMenu ( this . tabsMenu , this . tabsContainer , ( id , tabContent ) = > {
if ( this . prevTabId == id ) return ;
this . selectTab = horizontalMenu ( this . tabsMenu , this . tabsContainer , ( id , tabContent ) = > {
if ( this . prevTabId === id ) return ;
if ( this . prevTabId != - 1 ) {
if ( this . prevTabId !== - 1 ) {
this . onTransitionStart ( ) ;
}
this . type = this . types [ id ] . inputFilter ;
this . tabSelected = tabContent . firstElementChild as HTMLDivElement ;
if ( this . prevTabId != - 1 && nav . offsetTop ) {
if ( this . prevTabId !== - 1 && nav . offsetTop ) {
this . scrollable . scrollTop -= nav . offsetTop ;
}
/ * t h i s . l o g ( ' s e t V i r t u a l C o n t a i n e r ' , i d , t h i s . s h a r e d M e d i a S e l e c t e d , t h i s . s h a r e d M e d i a S e l e c t e d . c h i l d E l e m e n t C o u n t ) ;
this . scroll . setVirtualContainer ( this . sharedMediaSelected ) ; * /
if ( this . prevTabId != - 1 && ! this . tabSelected . childElementCount ) { // quick brown fix
if ( this . prevTabId !== - 1 && ! this . tabSelected . childElementCount ) { // quick brown fix
//this.contentContainer.classList.remove('loaded');
this . load ( true ) ;
}
@ -121,27 +156,30 @@ export default class AppSearchSuper {
@@ -121,27 +156,30 @@ export default class AppSearchSuper {
} ) ;
this . tabs . inputMessagesFilterPhotoVideo . addEventListener ( 'click' , ( e ) = > {
const target = e . target as HTMLDivElement ;
const target = findUpClassName ( e . target as HTMLDivElement , 'grid-item' ) ;
const messageI d = + target . dataset . mid ;
if ( ! messageI d ) {
console . warn ( 'no messageId by click on target:' , target ) ;
const mi d = + target . dataset . mid ;
if ( ! mi d ) {
this . log . warn ( 'no messageId by click on target:' , target ) ;
return ;
}
const message = appMessagesManager . getMessageByPeer ( this . peerId , messageId ) ;
const ids = Object . keys ( this . mediaDivsByIds ) . map ( k = > + k ) . sort ( ( a , b ) = > a - b ) ;
const idx = ids . findIndex ( i = > i == messageId ) ;
const targets = ids . map ( id = > {
const element = this . mediaDivsByIds [ id ] as HTMLElement ;
//element = element.querySelector('img') || element;
return { element , mid : id } ;
const peerId = + target . dataset . peerId ;
const targets = ( Array . from ( this . tabs . inputMessagesFilterPhotoVideo . querySelectorAll ( '.grid-item' ) ) as HTMLElement [ ] ) . map ( el = > {
return { element : el , mid : + el . dataset . mid , peerId : + el . dataset . peerId } ;
} ) ;
//const ids = Object.keys(this.mediaDivsByIds).map(k => +k).sort((a, b) => a - b);
const idx = targets . findIndex ( item = > item . mid === mid && item . peerId === peerId ) ;
new AppMediaViewer ( ) . openMedia ( message , target , false , targets . slice ( idx + 1 ) . reverse ( ) , targets . slice ( 0 , idx ) . reverse ( ) , true /* , this.threadId */ ) ;
const message = appMessagesManager . getMessageByPeer ( peerId , mid ) ;
new AppMediaViewer ( )
. setSearchContext ( this . copySearchContext ( this . type ) )
. openMedia ( message , target , 0 , false , targets . slice ( 0 , idx ) , targets . slice ( idx + 1 ) ) ;
} ) ;
this . type = this . types [ 0 ] . inputFilter ;
}
private onTransitionStart = ( ) = > {
@ -163,18 +201,24 @@ export default class AppSearchSuper {
@@ -163,18 +201,24 @@ export default class AppSearchSuper {
this . container . classList . remove ( 'sliding' ) ;
} ;
public filterMessagesByType ( ids : number [ ] , type : MyInputMessagesFilter ) {
let messages : any [ ] = [ ] ;
public filterMessagesByType ( messages : any [ ] , type : SearchSuperType ) : MyMessage [ ] {
if ( type === 'inputMessagesFilterEmpty' ) return messages ;
if ( type != 'inputMessagesFilterUrl' ) {
for ( let mid of ids ) {
let message = appMessagesManager . getMessageByPeer ( this . peerId , mid ) ;
if ( message . media ) messages . push ( message ) ;
}
} else {
messages = ids . slice ( ) . map ( mid = > appMessagesManager . getMessageByPeer ( this . peerId , mid ) ) ;
if ( type !== 'inputMessagesFilterUrl' ) {
messages = messages . filter ( message = > ! ! message . media ) ;
}
/ * i f ( ! t h i s . p e e r I d ) {
messages = messages . filter ( message = > {
if ( message . peerId === rootScope . myId ) {
return true ;
}
const dialog = appMessagesManager . getDialogByPeerId ( message . fromId ) [ 0 ] ;
return dialog && dialog . folder_id === 0 ;
} ) ;
} * /
let filtered : any [ ] = [ ] ;
switch ( type ) {
@ -186,7 +230,7 @@ export default class AppSearchSuper {
@@ -186,7 +230,7 @@ export default class AppSearchSuper {
continue ;
}
if ( media . _ == 'document' && media . type != 'video' /* && media.type != 'gif' */ ) {
if ( media . _ === 'document' && media . type !== 'video' /* && media.type != = 'gif' */ ) {
//this.log('broken video', media);
continue ;
}
@ -209,9 +253,9 @@ export default class AppSearchSuper {
@@ -209,9 +253,9 @@ export default class AppSearchSuper {
}
case 'inputMessagesFilterUrl' : {
console . log ( 'inputMessagesFilterUrl' , messages ) ;
//this.log('inputMessagesFilterUrl', messages);
for ( let message of messages ) {
//if((message.media.webpage && message.media.webpage._ != 'webPageEmpty')) {
//if((message.media.webpage && message.media.webpage._ !== 'webPageEmpty')) {
filtered . push ( message ) ;
//}
}
@ -221,7 +265,19 @@ export default class AppSearchSuper {
@@ -221,7 +265,19 @@ export default class AppSearchSuper {
case 'inputMessagesFilterMusic' : {
for ( let message of messages ) {
if ( ! message . media . document || message . media . document . type != 'audio' ) {
if ( ! message . media . document || message . media . document . type !== 'audio' ) {
continue ;
}
filtered . push ( message ) ;
}
break ;
}
case 'inputMessagesFilterVoice' : {
for ( let message of messages ) {
if ( ! message . media . document || message . media . document . type !== 'voice' ) {
continue ;
}
@ -238,31 +294,39 @@ export default class AppSearchSuper {
@@ -238,31 +294,39 @@ export default class AppSearchSuper {
return filtered ;
}
public async performSearchResult ( messages : any [ ] , type : MyInputMessagesFilter , append = true ) {
const peerId = this . peerId ;
const threadId = this . threadId ;
const elemsToAppend : HTMLElement [ ] = [ ] ;
public async performSearchResult ( messages : any [ ] , type : SearchSuperType , append = true ) {
const elemsToAppend : { element : HTMLElement , message : any } [ ] = [ ] ;
const promises : Promise < any > [ ] = [ ] ;
const sharedMediaDiv = this . tabs [ type ] ;
const sharedMediaDiv : HTMLElement = type === 'inputMessagesFilterEmpty' ? this . searchGroups.messages.list : this.tabs [ type ] ;
/ * f o r ( l e t c o n t e n t T y p e i n c o n t e n t T o S h a r e d M a p ) {
if ( contentToSharedMap [ contentType as ContentType ] == type ) {
sharedMediaDiv = this . sharedMedia [ contentType as ContentType ] ;
}
} * /
const middleware = this . getMiddleware ( ) ;
// https://core.telegram.org/type/MessagesFilter
switch ( type ) {
case 'inputMessagesFilterEmpty' : {
for ( const message of messages ) {
const { dialog , dom } = appDialogsManager . addDialogNew ( {
dialog : message.peerId ,
container : sharedMediaDiv as HTMLUListElement /* searchGroup.list */ ,
drawStatus : false ,
avatarSize : 54
} ) ;
appDialogsManager . setLastMessage ( dialog , message , dom , this . searchContext . query ) ;
}
this . searchGroups . messages . setActive ( ) ;
break ;
}
case 'inputMessagesFilterPhotoVideo' : {
for ( const message of messages ) {
const media = message . media . photo || message . media . document || ( message . media . webpage && message . media . webpage . document ) ;
const div = document . createElement ( 'div' ) ;
div . classList . add ( 'grid-item' ) ;
div . dataset . mid = '' + message . mid ;
//console.log(message, photo);
//this.log(message, photo);
const isPhoto = media . _ == 'photo' ;
const isPhoto = media . _ === 'photo' ;
const photo = isPhoto ? appPhotosManager . getPhoto ( media . id ) : null ;
let isDownloaded : boolean ;
@ -280,7 +344,7 @@ export default class AppSearchSuper {
@@ -280,7 +344,7 @@ export default class AppSearchSuper {
span . classList . add ( 'video-time' ) ;
div . append ( span ) ;
if ( media . type != 'gif' ) {
if ( media . type !== 'gif' ) {
span . innerText = ( media . duration + '' ) . toHHMMSS ( false ) ;
/ * c o n s t s p a n P l a y = d o c u m e n t . c r e a t e E l e m e n t ( ' s p a n ' ) ;
@ -293,8 +357,8 @@ export default class AppSearchSuper {
@@ -293,8 +357,8 @@ export default class AppSearchSuper {
const load = ( ) = > appPhotosManager . preloadPhoto ( isPhoto ? media.id : media , appPhotosManager . choosePhotoSize ( media , 200 , 200 ) )
. then ( ( ) = > {
if ( this . peerId != peerId || this . threadId != threadId ) {
console . warn ( 'peer changed' ) ;
if ( ! middleware ( ) ) {
//this.log.warn('peer changed');
return ;
}
@ -343,7 +407,7 @@ export default class AppSearchSuper {
@@ -343,7 +407,7 @@ export default class AppSearchSuper {
} ) ;
const timeout = setTimeout ( ( ) = > {
//console .log('didn\'t load', thumb, media, isDownloaded, sizes);
//this .log('didn\'t load', thumb, media, isDownloaded, sizes);
reject ( ) ;
} , 1 e3 ) ;
} ) ;
@ -356,26 +420,28 @@ export default class AppSearchSuper {
@@ -356,26 +420,28 @@ export default class AppSearchSuper {
else this . lazyLoadQueue . push ( { div , load } ) ;
}
elemsToAppend . push ( div ) ;
this . mediaDivsByIds [ message . mid ] = div ;
elemsToAppend . push ( { element : div , message } ) ;
}
break ;
}
case 'inputMessagesFilterVoice' :
case 'inputMessagesFilterMusic' :
case 'inputMessagesFilterDocument' : {
for ( const message of messages ) {
const div = wrapDocument ( {
message ,
withTime : true ,
fontWeight : 400
withTime : ! this . asChatList ,
fontWeight : 400 ,
voiceAsMusic : true ,
showSender : this.asChatList ,
searchContext : this.copySearchContext ( type )
} ) ;
if ( message . media . document . type === 'audio' ) {
div . classList . add ( 'audio-48' ) ;
}
div . dataset . mid = '' + message . mid ;
elemsToAppend . push ( div ) ;
elemsToAppend . push ( { element : div , message } ) ;
}
break ;
}
@ -432,7 +498,6 @@ export default class AppSearchSuper {
@@ -432,7 +498,6 @@ export default class AppSearchSuper {
}
let div = document . createElement ( 'div' ) ;
div . dataset . mid = '' + message . mid ;
let previewDiv = document . createElement ( 'div' ) ;
previewDiv . classList . add ( 'preview' ) ;
@ -444,8 +509,8 @@ export default class AppSearchSuper {
@@ -444,8 +509,8 @@ export default class AppSearchSuper {
if ( webpage . photo ) {
let load = ( ) = > appPhotosManager . preloadPhoto ( webpage . photo . id , appPhotosManager . choosePhotoSize ( webpage . photo , 60 , 60 ) )
. then ( ( ) = > {
if ( this . peerId != peerId || this . threadId != threadId ) {
console . warn ( 'peer changed' ) ;
if ( ! middleware ( ) ) {
//this.log.warn('peer changed');
return ;
}
@ -467,19 +532,23 @@ export default class AppSearchSuper {
@@ -467,19 +532,23 @@ export default class AppSearchSuper {
title = RichTextProcessor . wrapPlainText ( webpage . display_url . split ( '/' , 1 ) [ 0 ] ) ;
}
/ * i f ( w e b p a g e . d e s c r i p t i o n ? . i n c l u d e s ( ' Е щ е в н а ч а л е ' ) ) {
console . error ( 'FROM THE START' , webpage ) ;
} * /
let sender = this . asChatList ? ` <div class="subtitle sender"> ${ appMessagesManager . getSenderToPeerText ( message ) } </div> ` : '' ;
let titleAdditionHTML = '' ;
if ( this . asChatList ) {
titleAdditionHTML = ` <div class="sent-time"> ${ formatDateAccordingToToday ( new Date ( message . date * 1000 ) ) } </div> ` ;
}
div . append ( previewDiv ) ;
div . insertAdjacentHTML ( 'beforeend' , `
< div class = "title" > $ { title } < / button >
< div class = "title" > $ { title } $ { titleAdditionHTML } < / div >
< div class = "subtitle" > $ { subtitle } < / div >
< div class = "url" > $ { url } < / div >
$ { sender }
` );
if ( div . innerText . trim ( ) . length ) {
elemsToAppend . push ( div ) ;
elemsToAppend . push ( { element : div , message } ) ;
}
}
@ -488,7 +557,7 @@ export default class AppSearchSuper {
@@ -488,7 +557,7 @@ export default class AppSearchSuper {
}
default :
//console .warn('death is my friend', messages);
//this.log .warn('death is my friend', messages);
break ;
}
@ -498,25 +567,39 @@ export default class AppSearchSuper {
@@ -498,25 +567,39 @@ export default class AppSearchSuper {
if ( promises . length ) {
await Promise . all ( promises ) ;
if ( this . peerId !== peerId || this . threadId !== threadId ) {
console . warn ( 'peer changed' ) ;
if ( ! middleware ( ) ) {
//this.log.warn('peer changed');
return ;
}
}
if ( elemsToAppend . length ) {
sharedMediaDiv [ append ? 'append' : 'prepend' ] ( . . . elemsToAppend ) ;
const method = append ? 'append' : 'prepend' ;
elemsToAppend . forEach ( details = > {
const { element , message } = details ;
const monthContainer = this . getMonthContainerByTimestamp ( message . date , type ) ;
element . classList . add ( 'search-super-item' ) ;
element . dataset . mid = '' + message . mid ;
element . dataset . peerId = '' + message . peerId ;
monthContainer . items [ method ] ( element ) ;
} ) ;
}
if ( sharedMediaDiv ) {
const parent = sharedMediaDiv . parentElement ;
if ( type !== 'inputMessagesFilterEmpty' ) {
this . afterPerforming ( messages . length , sharedMediaDiv ) ;
}
}
private afterPerforming ( length : number , tab : HTMLElement ) {
if ( tab ) {
const parent = tab . parentElement ;
Array . from ( parent . children ) . slice ( 1 ) . forEach ( child = > {
child . remove ( ) ;
} ) ;
//this.contentContainer.classList.add('loaded');
if ( ! messages . length && ! sharedMediaDiv . childElementCount ) {
if ( ! length && ! tab . childElementCount ) {
const div = document . createElement ( 'div' ) ;
div . innerText = 'Nothing interesting here yet...' ;
div . classList . add ( 'position-center' , 'text-center' , 'content-empty' , 'no-select' ) ;
@ -525,20 +608,166 @@ export default class AppSearchSuper {
@@ -525,20 +608,166 @@ export default class AppSearchSuper {
}
}
}
private loadChats() {
const renderedPeerIds : Set < number > = new Set ( ) ;
const middleware = this . getMiddleware ( ) ;
for ( let i in this . searchGroups ) {
const group = this . searchGroups [ i as SearchGroupType ] ;
this . tabs . inputMessagesFilterEmpty . append ( group . container ) ;
group . clear ( ) ;
}
const query = this . searchContext . query ;
if ( query ) {
const setResults = ( results : number [ ] , group : SearchGroup , showMembersCount = false ) = > {
results . forEach ( ( peerId ) = > {
if ( renderedPeerIds . has ( peerId ) ) {
return ;
}
renderedPeerIds . add ( peerId ) ;
const peer = appPeersManager . getPeer ( peerId ) ;
//////////this.log('contacts peer', peer);
const { dom } = appDialogsManager . addDialogNew ( {
dialog : peerId ,
container : group.list ,
drawStatus : false ,
avatarSize : 48 ,
autonomous : group.autonomous
} ) ;
if ( showMembersCount && ( peer . participants_count || peer . participants ) ) {
const regExp = new RegExp ( ` ( ${ escapeRegExp ( query ) } | ${ escapeRegExp ( searchIndexManager . cleanSearchText ( query ) ) } ) ` , 'gi' ) ;
dom . titleSpan . innerHTML = dom . titleSpan . innerHTML . replace ( regExp , '<i>$1</i>' ) ;
dom . lastMessageSpan . innerText = appChatsManager . getChatMembersString ( - peerId ) ;
} else if ( peerId === rootScope . myId ) {
dom . lastMessageSpan . innerHTML = 'chat with yourself' ;
} else {
let username = appPeersManager . getPeerUsername ( peerId ) ;
if ( ! username ) {
const user = appUsersManager . getUser ( peerId ) ;
if ( user && user . phone ) {
username = '+' + formatPhoneNumber ( user . phone ) . formatted ;
}
} else {
username = '@' + username ;
}
dom . lastMessageSpan . innerHTML = '<i>' + username + '</i>' ;
}
} ) ;
group . toggle ( ) ;
} ;
const onLoad = < T > ( arg : T ) = > {
if ( ! middleware ( ) ) {
return ;
}
//this.loadedContacts = true;
return arg ;
} ;
return Promise . all ( [
appUsersManager . getContacts ( query , true )
. then ( onLoad )
. then ( ( contacts ) = > {
if ( contacts ) {
setResults ( contacts , this . searchGroups . contacts , true ) ;
}
} ) ,
appUsersManager . searchContacts ( query , 20 )
. then ( onLoad )
. then ( ( contacts ) = > {
if ( contacts ) {
setResults ( contacts . my_results , this . searchGroups . contacts , true ) ;
setResults ( contacts . results , this . searchGroups . globalContacts ) ;
}
} ) ,
appMessagesManager . getConversations ( query , 0 , 20 , 0 )
. then ( onLoad )
. then ( value = > {
if ( value ) {
setResults ( value . dialogs . map ( d = > d . peerId ) , this . searchGroups . contacts , true ) ;
}
} )
] ) ;
} else {
const renderRecentSearch = ( setActive = true ) = > {
return appStateManager . getState ( ) . then ( state = > {
if ( ! middleware ( ) ) {
return ;
}
this . searchGroups . recent . list . innerHTML = '' ;
state . recentSearch . slice ( 0 , 20 ) . forEach ( peerId = > {
let { dialog , dom } = appDialogsManager . addDialogNew ( {
dialog : peerId ,
container : this.searchGroups.recent.list ,
drawStatus : false ,
meAsSaved : true ,
avatarSize : 48 ,
autonomous : false
} ) ;
dom . lastMessageSpan . innerText = peerId > 0 ? appUsersManager . getUserStatusString ( peerId ) : appChatsManager . getChatMembersString ( peerId ) ;
} ) ;
if ( ! state . recentSearch . length ) {
this . searchGroups . recent . clear ( ) ;
} else if ( setActive ) {
this . searchGroups . recent . setActive ( ) ;
}
} ) ;
} ;
return Promise . all ( [
appUsersManager . getTopPeers ( ) . then ( peers = > {
if ( ! middleware ( ) ) return ;
//console.log('got top categories:', categories);
if ( peers . length ) {
peers . forEach ( ( peerId ) = > {
appDialogsManager . addDialogNew ( {
dialog : peerId ,
container : this.searchGroups.people.list ,
drawStatus : false ,
onlyFirstName : true ,
avatarSize : 54 ,
autonomous : false
} ) ;
} ) ;
}
this . searchGroups . people . setActive ( ) ;
} ) ,
renderRecentSearch ( )
] ) ;
}
}
public load ( single = false , justLoad = false ) {
if ( testScroll /* || 1 == 1 */ ) {
return ;
}
console . log ( 'loadSidebarMedia' , single , this . peerId , this . loadPromises ) ;
const peerId = this . peerId ;
const threadId = this . threadId ;
const peerId = this . searchContext . peerId ;
this . log ( 'load' , single , peerId , this . loadPromises ) ;
let typesToLoad = single ? [ this . type ] : this . types . filter ( t = > t . inputFilter !== this . type && t . inputFilter !== 'inputMessagesFilterEmpty' ) . map ( t = > t . inputFilter ) ;
let typesToLoad = single ? [ this . type ] : this . types . filter ( t = > t . inputFilter !== this . type ) . map ( t = > t . inputFilter ) ;
typesToLoad = typesToLoad . filter ( type = > ! this . loaded [ type ]
|| this . usedFromHistory [ type ] < this . historyStorage [ type ] . length ) ;
|| ( this . historyStorage [ type ] && this . usedFromHistory [ type ] < this . historyStorage [ type ] . length ) ) ;
if ( ! typesToLoad . length ) return ;
@ -546,12 +775,26 @@ export default class AppSearchSuper {
@@ -546,12 +775,26 @@ export default class AppSearchSuper {
const historyStorage = this . historyStorage ? ? ( this . historyStorage = { } ) ;
const promises = typesToLoad . map ( type = > {
const middleware = this . getMiddleware ( ) ;
const promises : Promise < any > [ ] = typesToLoad . map ( type = > {
if ( this . loadPromises [ type ] ) return this . loadPromises [ type ] ;
const history = historyStorage [ type ] ? ? ( historyStorage [ type ] = [ ] ) ;
const logStr = 'loadSidebarMedia [' + type + ']: ' ;
if ( type === 'inputMessagesFilterEmpty' && ! history . length ) {
if ( ! this . loadedChats ) {
this . loadChats ( ) ;
this . loadedChats = true ;
}
if ( ! this . searchContext . query . trim ( ) ) {
this . loaded [ type ] = true ;
return Promise . resolve ( ) ;
}
}
const logStr = 'load [' + type + ']: ' ;
// render from cache
if ( history . length && this . usedFromHistory [ type ] < history . length && ! justLoad ) {
@ -561,11 +804,11 @@ export default class AppSearchSuper {
@@ -561,11 +804,11 @@ export default class AppSearchSuper {
do {
let ids = history . slice ( used , used + loadCount ) ;
console . log ( logStr + 'will render from cache' , used , history , ids , loadCount ) ;
//this.log(logStr + 'will render from cache', used, history, ids, loadCount);
used += ids . length ;
slicedLength += ids . length ;
messages . push ( . . . this . filterMessagesByType ( ids , type ) ) ;
messages . push ( . . . this . filterMessagesByType ( ids . map ( m = > appMessagesManager . getMessageByPeer ( m . peerId , m . mid ) ) , type ) ) ;
} while ( slicedLength < loadCount && used < history . length ) ;
// если перебор
@ -577,36 +820,49 @@ export default class AppSearchSuper {
@@ -577,36 +820,49 @@ export default class AppSearchSuper {
this . usedFromHistory [ type ] = used ;
//if(messages.length) {
return this . performSearchResult ( messages , type ) ;
return this . performSearchResult ( messages , type ) . finally ( ( ) = > {
setTimeout ( ( ) = > {
this . scrollable . checkForTriggers ( ) ;
} , 0 ) ;
} ) ;
//}
return Promise . resolve ( ) ;
}
let maxId = history [ history . length - 1 ] || 0 ;
let maxId = history . length ? history [ history . length - 1 ] . mid : 0 ;
console . log ( logStr + 'search house of glass pre' , type , maxId ) ;
//this.log(logStr + 'search house of glass pre', type, maxId);
//let loadCount = history.length ? 50 : 15;
return this . loadPromises [ type ] = appMessagesManager . getSearch ( peerId , '' , { _ : type } , maxId , loadCount , undefined , undefined /* , this.threadId */ )
. then ( value = > {
const mids = value . history . map ( message = > message . mid ) ;
history . push ( . . . mids ) ;
return this . loadPromises [ type ] = appMessagesManager . getSearchNew ( {
peerId ,
query : this.searchContext.query ,
inputFilter : { _ : type } ,
maxId ,
limit : loadCount ,
nextRate : this.nextRates [ type ] ? ? ( this . nextRates [ type ] = 0 ) ,
threadId : this.searchContext.threadId ,
folderId : this.searchContext.folderId
} ) . then ( value = > {
history . push ( . . . value . history . map ( m = > ( { mid : m.mid , peerId : m.peerId } ) ) ) ;
console . log ( logStr + 'search house of glass' , type , value ) ;
this . log ( logStr + 'search house of glass' , type , value ) ;
if ( this . peerId !== peerId || this . threadId !== threadId ) {
//console.warn('peer changed');
if ( ! middleware ( ) ) {
//this.log .warn('peer changed');
return ;
}
// ! Фикс случая, когда не загружаются документы при открытой панели разработчиков (происходит из-за того, что не совпадают критерии отбора документов в getSearch)
if ( value . history . length < loadCount ) {
//if((value.count || history.length == value.count) && history.length >= value.count) {
console . log ( logStr + 'loaded all media' , value , loadCount ) ;
//this.log(logStr + 'loaded all media', value, loadCount);
this . loaded [ type ] = true ;
}
this . nextRates [ type ] = value . next_rate ;
if ( justLoad ) {
return Promise . resolve ( ) ;
}
@ -616,46 +872,89 @@ export default class AppSearchSuper {
@@ -616,46 +872,89 @@ export default class AppSearchSuper {
if ( ! this . loaded [ type ] ) {
this . loadPromises [ type ] . then ( ( ) = > {
setTimeout ( ( ) = > {
console . log ( 'will preload more' ) ;
const promise = this . load ( true , true ) ;
if ( promise ) {
promise . then ( ( ) = > {
console . log ( 'preloaded more' ) ;
this . scrollable . checkForTriggers ( ) ;
} ) ;
//this.log('will preload more');
if ( this . type === type ) {
const promise = this . load ( true , true ) ;
if ( promise ) {
promise . then ( ( ) = > {
//this.log('preloaded more');
setTimeout ( ( ) = > {
this . scrollable . checkForTriggers ( ) ;
} , 0 ) ;
} ) ;
}
}
} , 0 ) ;
} ) ;
}
//if(value.history.length) {
return this . performSearchResult ( this . filterMessagesByType ( mids , type ) , type ) ;
return this . performSearchResult ( this . filterMessagesByType ( value . history , type ) , type ) ;
//}
} ) . catch ( err = > {
console . error ( 'load error:' , err ) ;
this . log . error ( 'load error:' , err ) ;
} ) . finally ( ( ) = > {
this . loadPromises [ type ] = null ;
} ) ;
} ) ;
return Promise . all ( promises ) . catch ( err = > {
console . error ( 'Load error all promises:' , err ) ;
this . log . error ( 'Load error all promises:' , err ) ;
} ) ;
}
public getMonthContainerByTimestamp ( timestamp : number , type : SearchSuperType ) {
const date = new Date ( timestamp * 1000 ) ;
date . setHours ( 0 , 0 , 0 ) ;
date . setDate ( 1 ) ;
const dateTimestamp = date . getTime ( ) ;
const containers = this . monthContainers [ type ] ? ? ( this . monthContainers [ type ] = { } ) ;
if ( ! ( dateTimestamp in containers ) ) {
const str = months [ date . getMonth ( ) ] + ' ' + date . getFullYear ( ) ;
const container = document . createElement ( 'div' ) ;
container . className = 'search-super-month' ;
const name = document . createElement ( 'div' ) ;
name . classList . add ( 'search-super-month-name' ) ;
name . innerText = str ;
container . append ( name ) ;
const items = document . createElement ( 'div' ) ;
items . classList . add ( 'search-super-month-items' ) ;
container . append ( name , items ) ;
const haveTimestamps = getObjectKeysAndSort ( containers , 'desc' ) ;
let i = 0 ;
for ( ; i < haveTimestamps . length ; ++ i ) {
const t = haveTimestamps [ i ] ;
if ( dateTimestamp > t ) {
break ;
}
}
containers [ dateTimestamp ] = { container , items } ;
positionElementByIndex ( container , this . tabs [ type ] , i ) ;
}
return containers [ dateTimestamp ] ;
}
public cleanup() {
this . loadPromises = { } ;
this . loaded = { } ;
this . loadedChats = false ;
this . nextRates = { } ;
this . prevTabId = - 1 ;
this . mediaDivsByIds = { } ;
this . lazyLoadQueue . clear ( ) ;
this . types . forEach ( type = > {
this . usedFromHistory [ type . inputFilter ] = - 1 ;
} ) ;
this . type = 'inputMessagesFilterPhotoVideo' ;
this . cleanupObj . cleaned = true ;
this . cleanupObj = { cleaned : false } ;
}
public cleanupHTML() {
@ -666,11 +965,15 @@ export default class AppSearchSuper {
@@ -666,11 +965,15 @@ export default class AppSearchSuper {
this . urlsToRevoke . length = 0 ;
}
( Object . keys ( this . tabs ) as MyInputMessagesFilter [ ] ) . forEach ( sharedMediaType = > {
this . tabs [ sharedMediaType ] . innerHTML = '' ;
( Object . keys ( this . tabs ) as SearchSuperType [ ] ) . forEach ( type = > {
this . tabs [ type ] . innerHTML = '' ;
if ( type === 'inputMessagesFilterEmpty' ) {
return ;
}
if ( ! this . historyStorage || ! this . historyStorage [ sharedMediaType ] ) {
const parent = this . tabs [ sharedMediaType ] . parentElement ;
if ( ! this . historyStorage || ! this . historyStorage [ t ype] ) {
const parent = this . tabs [ t ype] . parentElement ;
if ( ! testScroll ) {
if ( ! parent . querySelector ( '.preloader' ) ) {
putPreloader ( parent , true ) ;
@ -683,6 +986,8 @@ export default class AppSearchSuper {
@@ -683,6 +986,8 @@ export default class AppSearchSuper {
}
}
} ) ;
this . monthContainers = { } ;
if ( testScroll ) {
for ( let i = 0 ; i < 1500 ; ++ i ) {
@ -694,15 +999,39 @@ export default class AppSearchSuper {
@@ -694,15 +999,39 @@ export default class AppSearchSuper {
this . tabs . inputMessagesFilterPhotoVideo . append ( div ) ;
}
}
}
// * will change .cleaned in cleanup() and new instance will be created
public getMiddleware() {
const cleanupObj = this . cleanupObj ;
return ( ) = > {
return ! cleanupObj . cleaned ;
} ;
}
( this . tabsMenu . firstElementChild as HTMLLIElement ) . click ( ) ; // set media
private copySearchContext ( newInputFilter : MyInputMessagesFilter ) {
const context = copy ( this . searchContext ) ;
context . inputFilter = newInputFilter ;
context . nextRate = this . nextRates [ newInputFilter ] ;
return context ;
}
public setQuery ( peerId : number , query : string , threadId : number , historyStorage : AppSearchSuper [ 'historyStorage' ] = { } ) {
this . peerId = peerId ;
this . query = query ;
this . threadId = threadId ;
this . historyStorage = historyStorage ;
public setQuery ( { peerId , query , threadId , historyStorage , folderId } : {
peerId : number ,
query? : string ,
threadId? : number ,
historyStorage? : AppSearchSuper [ 'historyStorage' ] ,
folderId? : number
} ) {
this . searchContext = {
peerId : peerId || 0 ,
query : query || '' ,
inputFilter : this.type ,
threadId ,
folderId
} ;
this . historyStorage = historyStorage ? ? { } ;
this . cleanup ( ) ;
}