2020-10-14 15:51:21 +03:00
import { deferredPromise } from "../helpers/cancellablePromise" ;
import mediaSizes from "../helpers/mediaSizes" ;
import { isTouchSupported } from "../helpers/touchSupport" ;
import { isSafari } from "../helpers/userAgent" ;
import appDocsManager , { MyDocument } from "../lib/appManagers/appDocsManager" ;
import appImManager from "../lib/appManagers/appImManager" ;
import appMessagesManager from "../lib/appManagers/appMessagesManager" ;
import appPeersManager from "../lib/appManagers/appPeersManager" ;
import appPhotosManager from "../lib/appManagers/appPhotosManager" ;
import { logger } from "../lib/logger" ;
import VideoPlayer from "../lib/mediaPlayer" ;
import { RichTextProcessor } from "../lib/richtextprocessor" ;
2020-11-15 05:33:47 +02:00
import rootScope from "../lib/rootScope" ;
2020-11-07 05:48:07 +02:00
import { cancelEvent , fillPropertyValue , findUpClassName , generatePathData } from "../helpers/dom" ;
2020-10-31 04:10:44 +02:00
import animationIntersector from "./animationIntersector" ;
2020-10-14 15:51:21 +03:00
import appMediaPlaybackController from "./appMediaPlaybackController" ;
import AvatarElement from "./avatar" ;
import ButtonIcon from "./buttonIcon" ;
import { ButtonMenuItemOptions } from "./buttonMenu" ;
import ButtonMenuToggle from "./buttonMenuToggle" ;
import { LazyLoadQueueBase } from "./lazyLoadQueue" ;
import { renderImageFromUrl } from "./misc" ;
2020-12-18 05:07:32 +02:00
import PopupForward from "./popups/forward" ;
2020-10-14 15:51:21 +03:00
import ProgressivePreloader from "./preloader" ;
2020-10-31 20:45:49 +02:00
import Scrollable from "./scrollable" ;
2020-12-24 11:13:21 +02:00
import appSidebarRight , { AppSidebarRight } from "./sidebarRight" ;
2020-10-14 15:51:21 +03:00
import SwipeHandler from "./swipeHandler" ;
2020-11-14 22:03:59 +02:00
import { months , ONE_DAY } from "../helpers/date" ;
2020-12-25 14:53:20 +02:00
import { SearchSuperContext } from "./appSearchSuper." ;
2021-02-13 19:32:10 +04:00
import DEBUG from "../config/debug" ;
2020-08-27 21:25:47 +03:00
// TODO: масштабирование картинок (не SVG) при ресайзе, и правильный возврат на исходную позицию
// TODO: картинки "обрезаются" если возвращаются или появляются с места, где есть их перекрытие (топбар, поле ввода)
2020-09-01 15:53:46 +03:00
// TODO: видео в мобильной вёрстке, если показываются элементы управления: если свайпнуть в сторону, то элементы вернутся на место, т.е . прыгнут - это не ок, надо бы замаскировать
2020-02-06 22:43:07 +07:00
2020-10-14 15:51:21 +03:00
const MEDIA_VIEWER_CLASSNAME = 'media-viewer' ;
2020-08-28 22:53:10 +03:00
2020-10-14 15:51:21 +03:00
class AppMediaViewerBase < ContentAdditionType extends string , ButtonsAdditionType extends string , TargetType extends object > {
public wholeDiv : HTMLElement ;
protected overlaysDiv : HTMLElement ;
protected author : { [ k in 'container' | 'avatarEl' | 'nameEl' | 'date' ] : HTMLElement } = { } as any ;
protected content : { [ k in 'main' | 'container' | 'media' | 'mover' | ContentAdditionType ] : HTMLElement } = { } as any ;
public buttons : { [ k in 'download' | 'close' | 'prev' | 'next' | 'mobile-close' | ButtonsAdditionType ] : HTMLElement } = { } as any ;
2020-12-11 04:06:16 +02:00
protected tempId = 0 ;
2020-10-14 15:51:21 +03:00
protected preloader : ProgressivePreloader = null ;
protected preloaderStreamable : ProgressivePreloader = null ;
2020-08-28 22:53:10 +03:00
2020-10-14 15:51:21 +03:00
protected lastTarget : HTMLElement = null ;
protected prevTargets : TargetType [ ] = [ ] ;
protected nextTargets : TargetType [ ] = [ ] ;
//protected targetContainer: HTMLElement = null;
//protected loadMore: () => void = null;
2020-09-01 19:44:53 +03:00
2020-10-14 15:51:21 +03:00
public log : ReturnType < typeof logger > ;
2020-08-28 22:53:10 +03:00
2020-10-15 01:46:29 +03:00
protected isFirstOpen = true ;
2020-10-14 15:51:21 +03:00
protected loadMediaPromiseUp : Promise < void > = null ;
protected loadMediaPromiseDown : Promise < void > = null ;
protected loadedAllMediaUp = false ;
protected loadedAllMediaDown = false ;
2020-08-28 22:53:10 +03:00
2020-10-14 15:51:21 +03:00
protected reverse = false ; // reverse means next = higher msgid
protected needLoadMore = true ;
2020-08-28 22:53:10 +03:00
2020-10-14 15:51:21 +03:00
protected pageEl = document . getElementById ( 'page-chats' ) as HTMLDivElement ;
2020-08-28 22:53:10 +03:00
2020-10-14 15:51:21 +03:00
protected setMoverPromise : Promise < void > ;
protected setMoverAnimationPromise : Promise < void > ;
2020-08-28 22:53:10 +03:00
2020-10-14 15:51:21 +03:00
protected lazyLoadQueue : LazyLoadQueueBase ;
2020-08-28 22:53:10 +03:00
2020-10-14 15:51:21 +03:00
protected highlightSwitchersTimeout : number ;
2020-08-28 22:53:10 +03:00
2020-10-14 15:51:21 +03:00
protected onDownloadClick : ( e : MouseEvent ) = > void ;
protected onPrevClick : ( target : TargetType ) = > void ;
protected onNextClick : ( target : TargetType ) = > void ;
protected loadMoreMedia : ( older : boolean ) = > Promise < void > ;
2020-02-06 22:43:07 +07:00
2020-10-14 15:51:21 +03:00
constructor ( topButtons : Array < keyof AppMediaViewerBase < ContentAdditionType , ButtonsAdditionType , TargetType > [ 'buttons' ] > ) {
this . log = logger ( 'AMV' ) ;
this . preloader = new ProgressivePreloader ( ) ;
2021-01-18 22:34:41 +04:00
this . preloaderStreamable = new ProgressivePreloader ( {
cancelable : false ,
streamable : true
} ) ;
this . preloader . construct ( ) ;
this . preloaderStreamable . construct ( ) ;
2020-10-14 15:51:21 +03:00
this . lazyLoadQueue = new LazyLoadQueueBase ( ) ;
2020-02-15 12:54:55 +07:00
2020-10-14 15:51:21 +03:00
this . wholeDiv = document . createElement ( 'div' ) ;
this . wholeDiv . classList . add ( MEDIA_VIEWER_CLASSNAME + '-whole' ) ;
2020-04-08 18:46:43 +03:00
2020-10-14 15:51:21 +03:00
this . overlaysDiv = document . createElement ( 'div' ) ;
this . overlaysDiv . classList . add ( 'overlays' ) ;
2020-04-08 18:46:43 +03:00
2020-10-14 15:51:21 +03:00
const mainDiv = document . createElement ( 'div' ) ;
mainDiv . classList . add ( MEDIA_VIEWER_CLASSNAME ) ;
2020-05-23 08:31:18 +03:00
2020-10-14 15:51:21 +03:00
// * author
this . author . container = document . createElement ( 'div' ) ;
this . author . container . classList . add ( MEDIA_VIEWER_CLASSNAME + '-author' , 'no-select' ) ;
2020-05-27 09:21:16 +03:00
2020-10-14 15:51:21 +03:00
this . author . avatarEl = new AvatarElement ( ) ;
2020-12-14 02:19:36 +02:00
this . author . avatarEl . classList . add ( MEDIA_VIEWER_CLASSNAME + '-userpic' , 'avatar-44' ) ;
2020-05-27 09:21:16 +03:00
2020-10-14 15:51:21 +03:00
this . author . nameEl = document . createElement ( 'div' ) ;
this . author . nameEl . classList . add ( MEDIA_VIEWER_CLASSNAME + '-name' ) ;
2020-08-28 22:53:10 +03:00
2020-10-14 15:51:21 +03:00
this . author . date = document . createElement ( 'div' ) ;
this . author . date . classList . add ( MEDIA_VIEWER_CLASSNAME + '-date' ) ;
2020-08-27 21:25:47 +03:00
2020-10-14 15:51:21 +03:00
this . author . container . append ( this . author . avatarEl , this . author . nameEl , this . author . date ) ;
2020-08-27 21:25:47 +03:00
2020-10-14 15:51:21 +03:00
// * buttons
const buttonsDiv = document . createElement ( 'div' ) ;
buttonsDiv . classList . add ( MEDIA_VIEWER_CLASSNAME + '-buttons' ) ;
topButtons . concat ( [ 'download' , 'close' ] ) . forEach ( name = > {
const button = ButtonIcon ( name ) ;
this . buttons [ name ] = button ;
buttonsDiv . append ( button ) ;
} ) ;
// * content
this . content . main = document . createElement ( 'div' ) ;
this . content . main . classList . add ( MEDIA_VIEWER_CLASSNAME + '-content' ) ;
this . content . container = document . createElement ( 'div' ) ;
this . content . container . classList . add ( MEDIA_VIEWER_CLASSNAME + '-container' ) ;
this . content . media = document . createElement ( 'div' ) ;
this . content . media . classList . add ( MEDIA_VIEWER_CLASSNAME + '-media' ) ;
this . content . container . append ( this . content . media ) ;
this . content . main . append ( this . content . container ) ;
mainDiv . append ( this . author . container , buttonsDiv , this . content . main ) ;
this . overlaysDiv . append ( mainDiv ) ;
// * overlays end
2020-02-15 18:14:58 +07:00
2020-10-14 15:51:21 +03:00
this . buttons [ "mobile-close" ] = ButtonIcon ( 'close' , { onlyMobile : true } ) ;
this . buttons . prev = document . createElement ( 'div' ) ;
this . buttons . prev . className = ` ${ MEDIA_VIEWER_CLASSNAME } -switcher ${ MEDIA_VIEWER_CLASSNAME } -switcher-left ` ;
this . buttons . prev . innerHTML = ` <span class="tgico-down ${ MEDIA_VIEWER_CLASSNAME } -prev-button"></span> ` ;
this . buttons . next = document . createElement ( 'div' ) ;
this . buttons . next . className = ` ${ MEDIA_VIEWER_CLASSNAME } -switcher ${ MEDIA_VIEWER_CLASSNAME } -switcher-right ` ;
this . buttons . next . innerHTML = ` <span class="tgico-down ${ MEDIA_VIEWER_CLASSNAME } -next-button"></span> ` ;
this . wholeDiv . append ( this . overlaysDiv , this . buttons [ 'mobile-close' ] , this . buttons . prev , this . buttons . next ) ;
// * constructing html end
2020-06-05 19:01:06 +03:00
2020-10-14 15:51:21 +03:00
this . setNewMover ( ) ;
}
protected setListeners() {
this . buttons . download . addEventListener ( 'click' , this . onDownloadClick ) ;
2020-08-27 21:25:47 +03:00
[ this . buttons . close , this . buttons [ "mobile-close" ] , this . preloaderStreamable . preloader ] . forEach ( el = > {
2020-10-14 15:51:21 +03:00
el . addEventListener ( 'click' , this . close . bind ( this ) ) ;
2020-02-06 22:43:07 +07:00
} ) ;
2020-10-14 15:51:21 +03:00
2020-05-30 09:44:54 +03:00
this . buttons . prev . addEventListener ( 'click' , ( e ) = > {
cancelEvent ( e ) ;
2020-05-27 09:21:16 +03:00
if ( this . setMoverPromise ) return ;
2020-10-14 15:51:21 +03:00
const target = this . prevTargets . pop ( ) ;
2020-02-15 12:54:55 +07:00
if ( target ) {
2020-10-14 15:51:21 +03:00
this . onPrevClick ( target ) ;
2020-02-15 12:54:55 +07:00
} else {
this . buttons . prev . style . display = 'none' ;
}
} ) ;
2020-05-30 09:44:54 +03:00
this . buttons . next . addEventListener ( 'click' , ( e ) = > {
cancelEvent ( e ) ;
2020-05-27 09:21:16 +03:00
if ( this . setMoverPromise ) return ;
2020-04-08 18:46:43 +03:00
let target = this . nextTargets . shift ( ) ;
2020-02-15 12:54:55 +07:00
if ( target ) {
2020-10-14 15:51:21 +03:00
this . onNextClick ( target ) ;
2020-02-15 12:54:55 +07:00
} else {
this . buttons . next . style . display = 'none' ;
}
2020-02-06 22:43:07 +07:00
} ) ;
2020-10-11 22:50:40 +03:00
2020-08-27 21:25:47 +03:00
this . wholeDiv . addEventListener ( 'click' , this . onClick ) ;
2020-08-28 22:53:10 +03:00
2020-10-02 23:33:32 +03:00
if ( isTouchSupported ) {
2020-08-28 22:53:10 +03:00
const swipeHandler = new SwipeHandler ( this . wholeDiv , ( xDiff , yDiff ) = > {
2020-09-01 15:53:46 +03:00
if ( VideoPlayer . isFullScreen ( ) ) {
return ;
}
2020-08-28 22:53:10 +03:00
//console.log(xDiff, yDiff);
const percents = Math . abs ( xDiff ) / appPhotosManager . windowW ;
if ( percents > . 2 || xDiff > 125 ) {
//console.log('will swipe', xDiff);
if ( xDiff < 0 ) {
this . buttons . prev . click ( ) ;
} else {
this . buttons . next . click ( ) ;
}
return true ;
}
const percentsY = Math . abs ( yDiff ) / appPhotosManager . windowH ;
if ( percentsY > . 2 || yDiff > 125 ) {
this . buttons . close . click ( ) ;
return true ;
}
return false ;
2020-10-31 20:45:49 +02:00
} , ( evt ) = > {
// * Fix for seek input
2021-02-04 02:30:23 +02:00
if ( ( evt . target as HTMLElement ) . tagName === 'INPUT' || findUpClassName ( evt . target , 'media-viewer-caption' ) ) {
2020-10-31 20:45:49 +02:00
return false ;
}
return true ;
2020-08-28 22:53:10 +03:00
} ) ;
}
2020-02-06 22:43:07 +07:00
}
2020-02-15 12:54:55 +07:00
2020-10-14 15:51:21 +03:00
protected setBtnMenuToggle ( buttons : ButtonMenuItemOptions [ ] ) {
const btnMenuToggle = ButtonMenuToggle ( { onlyMobile : true } , 'bottom-left' , buttons ) ;
this . wholeDiv . append ( btnMenuToggle ) ;
}
public close ( e? : MouseEvent ) {
2020-10-11 22:50:40 +03:00
if ( e ) {
cancelEvent ( e ) ;
}
if ( this . setMoverAnimationPromise ) return ;
this . lazyLoadQueue . clear ( ) ;
const promise = this . setMoverToTarget ( this . lastTarget , true ) . then ( ( { onAnimationEnd } ) = > onAnimationEnd ) ;
this . lastTarget = null ;
this . prevTargets = [ ] ;
this . nextTargets = [ ] ;
this . loadedAllMediaUp = this . loadedAllMediaDown = false ;
this . loadMediaPromiseUp = this . loadMediaPromiseDown = null ;
this . setMoverPromise = null ;
2020-12-11 04:06:16 +02:00
this . tempId = - 1 ;
2020-10-11 22:50:40 +03:00
2021-02-04 02:30:23 +02:00
/ * i f ( a p p S i d e b a r R i g h t . h i s t o r y T a b I D s . s l i c e ( - 1 ) [ 0 ] = = = A p p S i d e b a r R i g h t . S L I D E R I T E M S I D S . f o r w a r d ) {
2020-10-11 22:50:40 +03:00
promise . then ( ( ) = > {
appSidebarRight . forwardTab . closeBtn . click ( ) ;
} ) ;
2020-10-20 02:39:36 +03:00
} * /
2020-10-11 22:50:40 +03:00
window . removeEventListener ( 'keydown' , this . onKeyDown ) ;
2020-10-14 15:51:21 +03:00
promise . finally ( ( ) = > {
this . wholeDiv . remove ( ) ;
2020-11-15 05:33:47 +02:00
rootScope . overlayIsActive = false ;
2020-10-31 04:10:44 +02:00
animationIntersector . checkAnimations ( false ) ;
2020-10-11 22:50:40 +03:00
} ) ;
2020-10-14 15:51:21 +03:00
return promise ;
}
2020-08-27 21:25:47 +03:00
onClick = ( e : MouseEvent ) = > {
if ( this . setMoverAnimationPromise ) return ;
const target = e . target as HTMLElement ;
2021-02-04 02:30:23 +02:00
if ( target . tagName === 'A' ) return ;
2020-08-27 21:25:47 +03:00
cancelEvent ( e ) ;
2020-10-02 23:33:32 +03:00
if ( isTouchSupported ) {
2020-08-28 22:53:10 +03:00
if ( this . highlightSwitchersTimeout ) {
clearTimeout ( this . highlightSwitchersTimeout ) ;
} else {
this . wholeDiv . classList . add ( 'highlight-switchers' ) ;
}
2020-09-20 01:38:00 +03:00
this . highlightSwitchersTimeout = window . setTimeout ( ( ) = > {
2020-08-28 22:53:10 +03:00
this . wholeDiv . classList . remove ( 'highlight-switchers' ) ;
this . highlightSwitchersTimeout = 0 ;
} , 3 e3 ) ;
return ;
}
2020-08-27 21:25:47 +03:00
let mover : HTMLElement = null ;
2020-10-31 20:45:49 +02:00
[ 'media-viewer-mover' , 'media-viewer-buttons' , 'media-viewer-author' , 'media-viewer-caption' ] . find ( s = > {
2020-08-27 21:25:47 +03:00
try {
mover = findUpClassName ( target , s ) ;
if ( mover ) return true ;
} catch ( err ) { return false ; }
} ) ;
2021-02-04 02:30:23 +02:00
if ( /* target === this.mediaViewerDiv */ ! mover || target . tagName === 'IMG' || target . tagName === 'image' ) {
2020-08-27 21:25:47 +03:00
this . buttons . close . click ( ) ;
}
} ;
onKeyDown = ( e : KeyboardEvent ) = > {
2020-02-15 18:14:58 +07:00
//this.log('onKeyDown', e);
2021-02-04 02:30:23 +02:00
if ( e . key === 'Escape' ) {
2020-10-14 15:51:21 +03:00
this . close ( ) ;
2021-02-04 02:30:23 +02:00
} else if ( e . key === 'ArrowRight' ) {
2020-02-15 18:14:58 +07:00
this . buttons . next . click ( ) ;
2021-02-04 02:30:23 +02:00
} else if ( e . key === 'ArrowLeft' ) {
2020-02-15 18:14:58 +07:00
this . buttons . prev . click ( ) ;
}
2020-08-27 21:25:47 +03:00
} ;
2020-02-15 18:14:58 +07:00
2020-10-14 15:51:21 +03:00
protected async setMoverToTarget ( target : HTMLElement , closing = false , fromRight = 0 ) {
2020-08-26 23:31:07 +03:00
const mover = this . content . mover ;
2020-02-15 12:54:55 +07:00
2020-08-30 22:30:11 +03:00
if ( ! target ) {
2020-10-14 15:51:21 +03:00
target = this . content . media ;
2020-08-30 22:30:11 +03:00
}
2020-02-15 12:54:55 +07:00
if ( ! closing ) {
mover . innerHTML = '' ;
2020-05-30 09:44:54 +03:00
//mover.append(this.buttons.prev, this.buttons.next);
2020-02-15 12:54:55 +07:00
}
2020-08-27 21:25:47 +03:00
this . removeCenterFromMover ( mover ) ;
2020-02-15 12:54:55 +07:00
2020-08-26 23:31:07 +03:00
const wasActive = fromRight !== 0 ;
2020-02-15 18:14:58 +07:00
2021-01-03 19:59:13 +04:00
const delay = rootScope . settings . animationsEnabled ? ( wasActive ? 350 : 200 ) : 0 ;
2020-04-08 18:46:43 +03:00
//let delay = wasActive ? 350 : 10000;
2020-02-15 18:14:58 +07:00
/ * i f ( w a s A c t i v e ) {
this . moveTheMover ( mover ) ;
mover = this . setNewMover ( ) ;
} * /
2021-01-13 14:48:35 +04:00
/ * i f ( D E B U G ) {
this . log ( 'setMoverToTarget' , target , closing , wasActive , fromRight ) ;
} * /
2020-02-15 18:14:58 +07:00
2020-08-19 17:11:25 +03:00
let realParent : HTMLElement ;
2020-04-08 18:46:43 +03:00
let rect : DOMRect ;
if ( target ) {
2020-10-14 15:51:21 +03:00
if ( target instanceof AvatarElement ) {
realParent = target ;
rect = target . getBoundingClientRect ( ) ;
} else if ( target instanceof SVGImageElement || target . parentElement instanceof SVGForeignObjectElement ) {
2020-04-08 18:46:43 +03:00
realParent = findUpClassName ( target , 'attachment' ) ;
rect = realParent . getBoundingClientRect ( ) ;
} else {
2020-08-19 17:11:25 +03:00
realParent = target . parentElement as HTMLElement ;
2020-04-08 18:46:43 +03:00
rect = target . getBoundingClientRect ( ) ;
}
}
2020-10-14 15:51:21 +03:00
const containerRect = this . content . media . getBoundingClientRect ( ) ;
2020-02-15 18:14:58 +07:00
let transform = '' ;
let left : number ;
let top : number ;
if ( wasActive ) {
left = fromRight === 1 ? appPhotosManager . windowW : - containerRect . width ;
top = containerRect . top ;
} else {
left = rect . left ;
top = rect . top ;
}
2020-09-23 23:29:53 +03:00
transform += ` translate3d( ${ left } px, ${ top } px,0) ` ;
2020-08-27 21:25:47 +03:00
/ * i f ( w a s A c t i v e ) {
left = fromRight === 1 ? appPhotosManager . windowW / 2 : - ( containerRect . width + appPhotosManager . windowW / 2 ) ;
transform += ` translate( ${ left } px,-50%) ` ;
} else {
left = rect . left - ( appPhotosManager . windowW / 2 ) ;
top = rect . top - ( appPhotosManager . windowH / 2 ) ;
transform += ` translate( ${ left } px, ${ top } px) ` ;
} * /
2020-05-18 04:21:58 +03:00
let aspecter : HTMLDivElement ;
2021-02-04 02:30:23 +02:00
if ( target instanceof HTMLImageElement || target instanceof HTMLVideoElement || target . tagName === 'DIV' ) {
2020-05-18 04:21:58 +03:00
if ( mover . firstElementChild && mover . firstElementChild . classList . contains ( 'media-viewer-aspecter' ) ) {
aspecter = mover . firstElementChild as HTMLDivElement ;
2020-08-26 23:31:07 +03:00
const player = aspecter . querySelector ( '.ckin__player' ) ;
2020-05-18 04:21:58 +03:00
if ( player ) {
2020-08-26 23:31:07 +03:00
const video = player . firstElementChild as HTMLVideoElement ;
2020-05-18 04:21:58 +03:00
aspecter . append ( video ) ;
player . remove ( ) ;
}
if ( ! aspecter . style . cssText ) { // всё из-за видео, элементы управления скейлятся, так бы можно было этого не делать
mover . classList . remove ( 'active' ) ;
this . setFullAspect ( aspecter , containerRect , rect ) ;
void mover . offsetLeft ; // reflow
mover . classList . add ( 'active' ) ;
}
} else {
aspecter = document . createElement ( 'div' ) ;
2020-08-27 21:25:47 +03:00
aspecter . classList . add ( 'media-viewer-aspecter' /* , 'disable-hover' */ ) ;
2020-05-18 04:21:58 +03:00
mover . prepend ( aspecter ) ;
}
2020-10-07 16:57:33 +03:00
aspecter . style . cssText = ` width: ${ rect . width } px; height: ${ rect . height } px; transform: scale3d( ${ containerRect . width / rect . width } , ${ containerRect . height / rect . height } , 1); ` ;
2020-05-18 04:21:58 +03:00
}
2020-02-15 12:54:55 +07:00
mover . style . width = containerRect . width + 'px' ;
mover . style . height = containerRect . height + 'px' ;
2020-08-26 23:31:07 +03:00
const scaleX = rect . width / containerRect . width ;
const scaleY = rect . height / containerRect . height ;
2020-02-15 18:14:58 +07:00
if ( ! wasActive ) {
2020-10-07 16:57:33 +03:00
transform += ` scale3d( ${ scaleX } , ${ scaleY } ,1) ` ;
2020-04-08 18:46:43 +03:00
}
2020-02-15 18:14:58 +07:00
2020-04-08 18:46:43 +03:00
let borderRadius = window . getComputedStyle ( realParent ) . getPropertyValue ( 'border-radius' ) ;
2020-08-26 23:31:07 +03:00
const brSplitted = fillPropertyValue ( borderRadius ) as string [ ] ;
2020-04-08 18:46:43 +03:00
borderRadius = brSplitted . map ( r = > ( parseInt ( r ) / scaleX ) + 'px' ) . join ( ' ' ) ;
if ( ! wasActive ) {
2020-02-15 18:14:58 +07:00
mover . style . borderRadius = borderRadius ;
}
2020-10-07 16:57:33 +03:00
//let borderRadius = '0px 0px 0px 0px';
2020-02-15 18:14:58 +07:00
mover . style . transform = transform ;
2020-02-17 19:18:06 +07:00
/ * i f ( w a s A c t i v e ) {
2020-02-15 18:14:58 +07:00
this . log ( 'setMoverToTarget' , mover . style . transform ) ;
2020-02-17 19:18:06 +07:00
} * /
2020-02-15 15:22:33 +07:00
2020-04-08 18:46:43 +03:00
let path : SVGPathElement ;
2020-08-26 23:31:07 +03:00
const isOut = target . classList . contains ( 'is-out' ) ;
2020-04-08 18:46:43 +03:00
2020-08-27 21:25:47 +03:00
const deferred = this . setMoverAnimationPromise = deferredPromise < void > ( ) ;
const ret = { onAnimationEnd : deferred } ;
this . setMoverAnimationPromise . then ( ( ) = > {
this . setMoverAnimationPromise = null ;
} ) ;
2020-02-15 12:54:55 +07:00
if ( ! closing ) {
2020-05-27 09:21:16 +03:00
let mediaElement : HTMLImageElement | HTMLVideoElement ;
let src : string ;
2021-01-20 14:01:18 +04:00
if ( target . tagName === 'DIV' || target . tagName === 'AVATAR-ELEMENT' ) { // useContainerAsTarget
2020-05-27 09:21:16 +03:00
if ( target . firstElementChild ) {
mediaElement = new Image ( ) ;
src = ( target . firstElementChild as HTMLImageElement ) . src ;
mover . append ( mediaElement ) ;
}
/ * m e d i a E l e m e n t = n e w I m a g e ( ) ;
src = target . style . backgroundImage . slice ( 5 , - 2 ) ; * /
2020-05-18 04:21:58 +03:00
} else if ( target instanceof HTMLImageElement ) {
2020-05-27 09:21:16 +03:00
mediaElement = new Image ( ) ;
src = target . src ;
2020-05-18 04:21:58 +03:00
} else if ( target instanceof HTMLVideoElement ) {
2020-08-26 23:31:07 +03:00
const video = mediaElement = document . createElement ( 'video' ) ;
2020-08-24 17:09:31 +03:00
video . src = target ? . src ;
2020-04-08 18:46:43 +03:00
} else if ( target instanceof SVGSVGElement ) {
2020-12-11 04:06:16 +02:00
const clipId = target . dataset . clipId ;
const newClipId = clipId + '-mv' ;
2020-04-08 18:46:43 +03:00
2020-08-26 23:31:07 +03:00
const { width , height } = containerRect ;
2020-04-08 18:46:43 +03:00
2020-08-26 23:31:07 +03:00
const newSvg = document . createElementNS ( "http://www.w3.org/2000/svg" , "svg" ) ;
2020-04-08 18:46:43 +03:00
newSvg . setAttributeNS ( null , 'width' , '' + width ) ;
newSvg . setAttributeNS ( null , 'height' , '' + height ) ;
2020-08-27 21:25:47 +03:00
// нижние два свойства для масштабирования
newSvg . setAttributeNS ( null , 'viewBox' , ` 0 0 ${ width } ${ height } ` ) ;
newSvg . setAttributeNS ( null , 'preserveAspectRatio' , 'xMidYMid meet' ) ;
2020-12-11 04:06:16 +02:00
newSvg . insertAdjacentHTML ( 'beforeend' , target . firstElementChild . outerHTML . replace ( clipId , newClipId ) ) ;
newSvg . insertAdjacentHTML ( 'beforeend' , target . lastElementChild . outerHTML . replace ( clipId , newClipId ) ) ;
2020-04-08 18:46:43 +03:00
// теперь надо выставить новую позицию для хвостика
2020-08-26 23:31:07 +03:00
const defs = newSvg . firstElementChild ;
const use = defs . firstElementChild . firstElementChild as SVGUseElement ;
2020-04-08 18:46:43 +03:00
if ( use instanceof SVGUseElement ) {
let transform = use . getAttributeNS ( null , 'transform' ) ;
transform = transform . replace ( /translate\((.+?), (.+?)\) scale\((.+?), (.+?)\)/ , ( match , x , y , sX , sY ) = > {
x = + x ;
2021-02-04 02:30:23 +02:00
if ( x !== 2 ) {
2020-04-08 18:46:43 +03:00
x = width - ( 2 / scaleX ) ;
} else {
x = 2 / scaleX ;
}
y = height ;
return ` translate( ${ x } , ${ y } ) scale( ${ + sX / scaleX } , ${ + sY / scaleY } ) ` ;
} ) ;
use . setAttributeNS ( null , 'transform' , transform ) ;
// и новый RECT
path = defs . firstElementChild . lastElementChild as SVGPathElement ;
// код ниже нужен только чтобы скрыть моргание до момента как сработает таймаут
let d : string ;
2020-08-26 23:31:07 +03:00
const br : [ number , number , number , number ] = borderRadius . split ( ' ' ) . map ( v = > parseInt ( v ) ) as any ;
2020-04-08 18:46:43 +03:00
if ( isOut ) d = generatePathData ( 0 , 0 , width - 9 / scaleX , height , . . . br ) ;
else d = generatePathData ( 9 / scaleX , 0 , width - 9 / scaleX , height , . . . br ) ;
path . setAttributeNS ( null , 'd' , d ) ;
}
2020-08-26 23:31:07 +03:00
const foreignObject = newSvg . lastElementChild ;
2020-05-27 09:21:16 +03:00
foreignObject . setAttributeNS ( null , 'width' , '' + containerRect . width ) ;
foreignObject . setAttributeNS ( null , 'height' , '' + containerRect . height ) ;
2020-04-08 18:46:43 +03:00
mover . prepend ( newSvg ) ;
2020-02-15 12:54:55 +07:00
}
2020-05-18 04:21:58 +03:00
if ( aspecter ) {
aspecter . style . borderRadius = borderRadius ;
2020-09-01 15:53:46 +03:00
if ( mediaElement ) {
aspecter . append ( mediaElement ) ;
}
2020-05-27 09:21:16 +03:00
}
mediaElement = mover . querySelector ( 'video, img' ) ;
2020-09-01 15:53:46 +03:00
if ( mediaElement instanceof HTMLImageElement ) {
mediaElement . classList . add ( 'thumbnail' ) ;
if ( ! aspecter ) {
mediaElement . style . width = containerRect . width + 'px' ;
mediaElement . style . height = containerRect . height + 'px' ;
}
2020-05-27 09:21:16 +03:00
2020-09-01 15:53:46 +03:00
if ( src ) {
await new Promise ( ( resolve , reject ) = > {
mediaElement . addEventListener ( 'load' , resolve ) ;
if ( src ) {
mediaElement . src = src ;
}
} ) ;
}
2020-06-16 23:48:08 +03:00
} / * else if ( mediaElement instanceof HTMLVideoElement && mediaElement . firstElementChild && ( ( mediaElement . firstElementChild as HTMLSourceElement ) . src || src ) ) {
2020-05-27 09:21:16 +03:00
await new Promise ( ( resolve , reject ) = > {
mediaElement . addEventListener ( 'loadeddata' , resolve ) ;
if ( src ) {
( mediaElement . firstElementChild as HTMLSourceElement ) . src = src ;
}
} ) ;
2020-06-16 23:48:08 +03:00
} * /
2020-02-15 12:54:55 +07:00
mover . style . display = '' ;
2020-02-15 18:14:58 +07:00
2020-05-27 09:21:16 +03:00
window . requestAnimationFrame ( ( ) = > {
2020-02-15 18:14:58 +07:00
mover . classList . add ( wasActive ? 'moving' : 'active' ) ;
2020-05-27 09:21:16 +03:00
} ) ;
2020-02-15 12:54:55 +07:00
} else {
2020-08-27 21:25:47 +03:00
/ * i f ( m o v e r . c l a s s L i s t . c o n t a i n s ( ' c e n t e r ' ) ) {
mover . classList . remove ( 'center' ) ;
void mover . offsetLeft ; // reflow
} * /
2020-04-08 18:46:43 +03:00
if ( target instanceof SVGSVGElement ) {
path = mover . querySelector ( 'path' ) ;
if ( path ) {
this . sizeTailPath ( path , containerRect , scaleX , delay , false , isOut , borderRadius ) ;
}
}
2020-05-27 09:21:16 +03:00
if ( target . classList . contains ( 'media-viewer-media' ) ) {
mover . classList . add ( 'hiding' ) ;
}
2020-02-15 12:54:55 +07:00
setTimeout ( ( ) = > {
2020-05-30 09:44:54 +03:00
this . wholeDiv . classList . remove ( 'active' ) ;
2020-02-15 18:14:58 +07:00
} , 0 ) ;
setTimeout ( ( ) = > {
mover . style . borderRadius = borderRadius ;
if ( mover . firstElementChild ) {
( mover . firstElementChild as HTMLElement ) . style . borderRadius = borderRadius ;
}
2020-02-15 22:20:38 +07:00
} , delay / 2 ) ;
2020-02-15 18:14:58 +07:00
2020-02-15 12:54:55 +07:00
setTimeout ( ( ) = > {
mover . innerHTML = '' ;
2020-05-27 09:21:16 +03:00
mover . classList . remove ( 'moving' , 'active' , 'hiding' ) ;
2020-09-01 15:53:46 +03:00
mover . style . cssText = 'display: none;' ;
2020-08-27 21:25:47 +03:00
deferred . resolve ( ) ;
2020-02-15 18:14:58 +07:00
} , delay ) ;
2020-02-15 12:54:55 +07:00
2020-08-27 21:25:47 +03:00
return ret ;
2020-05-27 09:21:16 +03:00
}
2020-02-15 15:22:33 +07:00
2020-05-27 09:21:16 +03:00
//await new Promise((resolve) => setTimeout(resolve, 0));
2020-10-07 16:57:33 +03:00
//await new Promise((resolve) => window.requestAnimationFrame(resolve));
// * одного RAF'а недостаточно, иногда анимация с одним не срабатывает (преимущественно на мобильных)
await new Promise ( ( resolve ) = > window . requestAnimationFrame ( ( ) = > window . requestAnimationFrame ( resolve ) ) ) ;
2020-05-18 04:21:58 +03:00
2020-08-27 21:25:47 +03:00
// чтобы проверить установленную позицию - раскомментировать
2020-11-03 10:46:28 +02:00
// throw '';
2020-05-30 09:44:54 +03:00
2020-10-07 16:57:33 +03:00
//await new Promise((resolve) => setTimeout(resolve, 5e3));
mover . style . transform = ` translate3d( ${ containerRect . left } px, ${ containerRect . top } px,0) scale3d(1,1,1) ` ;
2020-08-27 21:25:47 +03:00
//mover.style.transform = `translate(-50%,-50%) scale(1,1)`;
2020-02-15 18:14:58 +07:00
2020-05-27 09:21:16 +03:00
if ( aspecter ) {
this . setFullAspect ( aspecter , containerRect , rect ) ;
}
2020-04-08 18:46:43 +03:00
2020-10-07 16:57:33 +03:00
//throw '';
2020-05-27 09:21:16 +03:00
setTimeout ( ( ) = > {
mover . style . borderRadius = '' ;
2020-05-18 04:21:58 +03:00
2020-05-27 09:21:16 +03:00
if ( mover . firstElementChild ) {
( mover . firstElementChild as HTMLElement ) . style . borderRadius = '' ;
}
2020-10-14 15:51:21 +03:00
} , 0 /* delay / 2 */ ) ;
2020-05-20 17:25:23 +03:00
2020-05-27 09:21:16 +03:00
mover . dataset . timeout = '' + setTimeout ( ( ) = > {
mover . classList . remove ( 'moving' ) ;
2020-05-18 04:21:58 +03:00
2020-05-27 09:21:16 +03:00
if ( aspecter ) { // всё из-за видео, элементы управления скейлятся, так бы можно было этого не делать
2020-08-27 21:25:47 +03:00
if ( mover . querySelector ( 'video' ) || true ) {
2020-05-30 09:44:54 +03:00
mover . classList . remove ( 'active' ) ;
aspecter . style . cssText = '' ;
void mover . offsetLeft ; // reflow
}
2020-08-27 21:25:47 +03:00
//aspecter.classList.remove('disable-hover');
2020-04-08 18:46:43 +03:00
}
2020-05-27 09:21:16 +03:00
2020-08-27 21:25:47 +03:00
// эти строки нужны для установки центральной позиции, в случае ресайза это будет нужно
mover . classList . add ( 'center' , 'no-transition' ) ;
/ * m o v e r . s t y l e . l e f t = m o v e r . s t y l e . t o p = ' 5 0 % ' ;
mover . style . transform = 'translate(-50%, -50%)' ;
void mover . offsetLeft ; // reflow */
// это уже нужно для будущих анимаций
2020-05-27 09:21:16 +03:00
mover . classList . add ( 'active' ) ;
delete mover . dataset . timeout ;
2020-08-27 21:25:47 +03:00
deferred . resolve ( ) ;
2020-05-27 09:21:16 +03:00
} , delay ) ;
if ( path ) {
this . sizeTailPath ( path , containerRect , scaleX , delay , true , isOut , borderRadius ) ;
}
2020-08-27 21:25:47 +03:00
return ret ;
2020-02-15 12:54:55 +07:00
}
2020-02-15 18:14:58 +07:00
2020-10-14 15:51:21 +03:00
protected setFullAspect ( aspecter : HTMLDivElement , containerRect : DOMRect , rect : DOMRect ) {
2020-06-20 04:11:24 +03:00
/ * l e t m e d i a = a s p e c t e r . f i r s t E l e m e n t C h i l d ;
2020-05-18 04:21:58 +03:00
let proportion : number ;
if ( media instanceof HTMLImageElement ) {
proportion = media . naturalWidth / media . naturalHeight ;
} else if ( media instanceof HTMLVideoElement ) {
proportion = media . videoWidth / media . videoHeight ;
2020-06-20 04:11:24 +03:00
} * /
const proportion = containerRect . width / containerRect . height ;
2020-05-18 04:21:58 +03:00
let { width , height } = rect ;
2021-02-04 02:30:23 +02:00
/ * i f ( p r o p o r t i o n = = = 1 ) {
2020-05-18 04:21:58 +03:00
aspecter . style . cssText = '' ;
2020-05-30 09:44:54 +03:00
} else { * /
2020-05-18 04:21:58 +03:00
if ( proportion > 0 ) {
width = height * proportion ;
} else {
height = width * proportion ;
}
2020-05-27 09:21:16 +03:00
//this.log('will set style aspecter:', `width: ${width}px; height: ${height}px; transform: scale(${containerRect.width / width}, ${containerRect.height / height});`);
2020-10-07 16:57:33 +03:00
aspecter . style . cssText = ` width: ${ width } px; height: ${ height } px; transform: scale3d( ${ containerRect . width / width } , ${ containerRect . height / height } , 1); ` ;
2020-05-30 09:44:54 +03:00
//}
2020-05-18 04:21:58 +03:00
}
2020-10-14 15:51:21 +03:00
protected sizeTailPath ( path : SVGPathElement , rect : DOMRect , scaleX : number , delay : number , upscale : boolean , isOut : boolean , borderRadius : string ) {
2020-08-26 23:31:07 +03:00
const start = Date . now ( ) ;
const { width , height } = rect ;
2020-04-08 18:46:43 +03:00
delay = delay / 2 ;
2020-08-26 23:31:07 +03:00
const br = borderRadius . split ( ' ' ) . map ( v = > parseInt ( v ) ) ;
2020-04-08 18:46:43 +03:00
2020-08-26 23:31:07 +03:00
const step = ( ) = > {
const diff = Date . now ( ) - start ;
2020-04-08 18:46:43 +03:00
2021-01-03 19:59:13 +04:00
let progress = delay ? diff / delay : 1 ;
2020-04-08 18:46:43 +03:00
if ( progress > 1 ) progress = 1 ;
if ( upscale ) progress = 1 - progress ;
2020-08-26 23:31:07 +03:00
const _br : [ number , number , number , number ] = br . map ( v = > v * progress ) as any ;
2020-04-08 18:46:43 +03:00
let d : string ;
if ( isOut ) d = generatePathData ( 0 , 0 , width - ( 9 / scaleX * progress ) , height , . . . _br ) ;
else d = generatePathData ( 9 / scaleX * progress , 0 , width /* width - (9 / scaleX * progress) */ , height , . . . _br ) ;
path . setAttributeNS ( null , 'd' , d ) ;
if ( diff < delay ) window . requestAnimationFrame ( step ) ;
} ;
//window.requestAnimationFrame(step);
step ( ) ;
}
2020-10-14 15:51:21 +03:00
protected removeCenterFromMover ( mover : HTMLElement ) {
2020-08-27 21:25:47 +03:00
if ( mover . classList . contains ( 'center' ) ) {
2020-09-01 15:53:46 +03:00
//const rect = mover.getBoundingClientRect();
2020-10-14 15:51:21 +03:00
const rect = this . content . media . getBoundingClientRect ( ) ;
2020-09-23 23:29:53 +03:00
mover . style . transform = ` translate3d( ${ rect . left } px, ${ rect . top } px,0) ` ;
2020-08-27 21:25:47 +03:00
mover . classList . remove ( 'center' ) ;
void mover . offsetLeft ; // reflow
mover . classList . remove ( 'no-transition' ) ;
}
}
2020-10-14 15:51:21 +03:00
protected moveTheMover ( mover : HTMLElement , toLeft = true ) {
2020-08-26 23:31:07 +03:00
const windowW = appPhotosManager . windowW ;
2020-02-15 18:14:58 +07:00
2020-08-27 21:25:47 +03:00
this . removeCenterFromMover ( mover ) ;
2020-05-18 04:21:58 +03:00
//mover.classList.remove('active');
2020-02-15 18:14:58 +07:00
mover . classList . add ( 'moving' ) ;
2020-05-18 04:21:58 +03:00
if ( mover . dataset . timeout ) { // и это тоже всё из-за скейла видео, так бы это не нужно было
clearTimeout ( + mover . dataset . timeout ) ;
}
2020-08-26 23:31:07 +03:00
const rect = mover . getBoundingClientRect ( ) ;
2020-02-15 18:14:58 +07:00
2020-09-23 23:29:53 +03:00
const newTransform = mover . style . transform . replace ( /translate3d\((.+?),/ , ( match , p1 ) = > {
2020-08-27 21:25:47 +03:00
const x = toLeft ? - rect.width : windowW ;
//const x = toLeft ? -(rect.right + (rect.width / 2)) : windowW / 2;
2020-02-15 18:14:58 +07:00
return match . replace ( p1 , x + 'px' ) ;
} ) ;
2020-02-17 19:18:06 +07:00
////////this.log('set newTransform:', newTransform, mover.style.transform, toLeft);
2020-02-15 18:14:58 +07:00
mover . style . transform = newTransform ;
setTimeout ( ( ) = > {
mover . remove ( ) ;
} , 350 ) ;
}
2020-10-14 15:51:21 +03:00
protected setNewMover() {
2020-08-26 23:31:07 +03:00
const newMover = document . createElement ( 'div' ) ;
2020-02-15 18:14:58 +07:00
newMover . classList . add ( 'media-viewer-mover' ) ;
2020-05-30 09:44:54 +03:00
if ( this . content . mover ) {
2020-08-26 23:31:07 +03:00
const oldMover = this . content . mover ;
2020-05-30 09:44:54 +03:00
oldMover . parentElement . append ( newMover ) ;
} else {
this . wholeDiv . append ( newMover ) ;
}
2020-02-15 18:14:58 +07:00
return this . content . mover = newMover ;
}
2020-04-08 18:46:43 +03:00
2020-08-26 23:31:07 +03:00
/ * p u b l i c i s E l e m e n t V i s i b l e ( c o n t a i n e r : H T M L E l e m e n t , t a r g e t : H T M L E l e m e n t ) {
const rect = container . getBoundingClientRect ( ) ;
const targetRect = target . getBoundingClientRect ( ) ;
2020-04-08 18:46:43 +03:00
return targetRect . bottom > rect . top && targetRect . top < rect . bottom ;
2020-08-26 23:31:07 +03:00
} * /
2020-04-08 18:46:43 +03:00
2020-10-14 15:51:21 +03:00
protected updateMediaSource ( target : HTMLElement , url : string , tagName : 'video' | 'img' ) {
2020-04-08 18:46:43 +03:00
//if(target instanceof SVGSVGElement) {
2021-01-26 00:47:04 +02:00
const el = target . tagName . toLowerCase ( ) === tagName ? target : target.querySelector ( tagName ) as HTMLElement ;
if ( el ) {
renderImageFromUrl ( el , url ) ;
}
2020-04-08 18:46:43 +03:00
/ * } e l s e {
} * /
}
2020-10-14 15:51:21 +03:00
2020-12-11 04:06:16 +02:00
protected setAuthorInfo ( fromId : number , timestamp : number ) {
2020-11-14 22:03:59 +02:00
const date = new Date ( ) ;
const time = new Date ( timestamp * 1000 ) ;
const now = date . getTime ( ) / 1000 ;
const timeStr = time . getHours ( ) + ':' + ( '0' + time . getMinutes ( ) ) . slice ( - 2 ) ;
let dateStr : string ;
2021-02-04 02:30:23 +02:00
if ( ( now - timestamp ) < ONE_DAY && date . getDate ( ) === time . getDate ( ) ) { // if the same day
2020-11-14 22:03:59 +02:00
dateStr = 'Today' ;
2021-02-04 02:30:23 +02:00
} else if ( ( now - timestamp ) < ( ONE_DAY * 2 ) && ( date . getDate ( ) - 1 ) === time . getDate ( ) ) { // yesterday
2020-11-14 22:03:59 +02:00
dateStr = 'Yesterday' ;
2021-02-04 02:30:23 +02:00
} else if ( date . getFullYear ( ) !== time . getFullYear ( ) ) { // different year
2020-11-14 22:03:59 +02:00
dateStr = months [ time . getMonth ( ) ] . slice ( 0 , 3 ) + ' ' + time . getDate ( ) + ', ' + time . getFullYear ( ) ;
} else {
dateStr = months [ time . getMonth ( ) ] . slice ( 0 , 3 ) + ' ' + time . getDate ( ) ;
}
this . author . date . innerText = dateStr + ' at ' + timeStr ;
2020-10-14 15:51:21 +03:00
2020-12-11 04:06:16 +02:00
const name = appPeersManager . getPeerTitle ( fromId ) ;
2020-10-14 15:51:21 +03:00
this . author . nameEl . innerHTML = name ;
let oldAvatar = this . author . avatarEl ;
this . author . avatarEl = ( this . author . avatarEl . cloneNode ( ) as AvatarElement ) ;
2020-12-11 04:06:16 +02:00
this . author . avatarEl . setAttribute ( 'peer' , '' + fromId ) ;
2020-10-14 15:51:21 +03:00
oldAvatar . parentElement . replaceChild ( this . author . avatarEl , oldAvatar ) ;
}
2020-04-08 18:46:43 +03:00
2020-12-11 04:06:16 +02:00
protected async _openMedia ( media : any , timestamp : number , fromId : number , fromRight : number , target? : HTMLElement , reverse = false ,
2020-10-14 15:51:21 +03:00
prevTargets : TargetType [ ] = [ ] , nextTargets : TargetType [ ] = [ ] , needLoadMore = true ) {
2020-05-27 09:21:16 +03:00
if ( this . setMoverPromise ) return this . setMoverPromise ;
2020-10-14 15:51:21 +03:00
2021-01-13 14:48:35 +04:00
/ * i f ( D E B U G ) {
this . log ( 'openMedia:' , media , fromId , prevTargets , nextTargets ) ;
} * /
2020-10-14 15:51:21 +03:00
2020-12-11 04:06:16 +02:00
this . setAuthorInfo ( fromId , timestamp ) ;
2020-02-06 22:43:07 +07:00
2021-02-04 02:30:23 +02:00
const isVideo = ( media as MyDocument ) . type === 'video' || ( media as MyDocument ) . type === 'gif' ;
2020-04-08 18:46:43 +03:00
2020-10-15 01:46:29 +03:00
if ( this . isFirstOpen ) {
2020-06-05 19:01:06 +03:00
//this.targetContainer = targetContainer;
2020-04-08 18:46:43 +03:00
this . prevTargets = prevTargets ;
this . nextTargets = nextTargets ;
this . reverse = reverse ;
2020-05-02 00:28:40 +03:00
this . needLoadMore = needLoadMore ;
2020-10-15 01:46:29 +03:00
this . isFirstOpen = false ;
2020-04-08 18:46:43 +03:00
//this.loadMore = loadMore;
2020-06-05 19:01:06 +03:00
2021-02-04 02:30:23 +02:00
/ * i f ( a p p S i d e b a r R i g h t . h i s t o r y T a b I D s . s l i c e ( - 1 ) [ 0 ] = = = A p p S i d e b a r R i g h t . S L I D E R I T E M S I D S . f o r w a r d ) {
2020-09-24 02:37:22 +03:00
appSidebarRight . forwardTab . closeBtn . click ( ) ;
2020-06-05 19:01:06 +03:00
await new Promise ( ( resolve ) = > setTimeout ( resolve , 200 ) ) ;
2020-10-20 02:39:36 +03:00
} * /
2020-04-08 18:46:43 +03:00
}
/ * i f ( t h i s . n e x t T a r g e t s . l e n g t h < 1 0 & & t h i s . l o a d M o r e ) {
this . loadMore ( ) ;
} * /
2020-02-15 18:14:58 +07:00
2020-04-08 18:46:43 +03:00
//if(prevTarget && (!prevTarget.parentElement || !this.isElementVisible(this.targetContainer, prevTarget))) prevTarget = null;
//if(nextTarget && (!nextTarget.parentElement || !this.isElementVisible(this.targetContainer, nextTarget))) nextTarget = null;
2020-10-14 15:51:21 +03:00
this . buttons . prev . classList . toggle ( 'hide' , ! this . prevTargets . length ) ;
this . buttons . next . classList . toggle ( 'hide' , ! this . nextTargets . length ) ;
2020-02-06 22:43:07 +07:00
2020-10-14 15:51:21 +03:00
const container = this . content . media ;
2021-01-26 00:47:04 +02:00
const useContainerAsTarget = ! target || target === container ;
2020-04-08 18:46:43 +03:00
if ( useContainerAsTarget ) target = container ;
2020-02-15 18:14:58 +07:00
this . lastTarget = target ;
2020-12-11 04:06:16 +02:00
const tempId = ++ this . tempId ;
2020-04-08 18:46:43 +03:00
2020-05-02 00:28:40 +03:00
if ( this . needLoadMore ) {
if ( this . nextTargets . length < 20 ) {
this . loadMoreMedia ( ! this . reverse ) ;
}
if ( this . prevTargets . length < 20 ) {
this . loadMoreMedia ( this . reverse ) ;
}
2020-04-08 18:46:43 +03:00
}
2020-02-06 22:43:07 +07:00
if ( container . firstElementChild ) {
2020-02-07 13:38:55 +07:00
container . innerHTML = '' ;
2020-02-06 22:43:07 +07:00
}
2020-02-15 12:54:55 +07:00
// ok set
2020-02-15 18:14:58 +07:00
2020-05-26 18:04:06 +03:00
const wasActive = fromRight !== 0 ;
2020-02-15 18:14:58 +07:00
if ( wasActive ) {
this . moveTheMover ( this . content . mover , fromRight === 1 ) ;
this . setNewMover ( ) ;
} else {
2020-08-27 21:25:47 +03:00
window . addEventListener ( 'keydown' , this . onKeyDown ) ;
2020-10-14 15:51:21 +03:00
const mainColumns = this . pageEl . querySelector ( '#main-columns' ) ;
this . pageEl . insertBefore ( this . wholeDiv , mainColumns ) ;
void this . wholeDiv . offsetLeft ; // reflow
2020-05-30 09:44:54 +03:00
this . wholeDiv . classList . add ( 'active' ) ;
2020-11-15 05:33:47 +02:00
rootScope . overlayIsActive = true ;
2020-10-31 04:10:44 +02:00
animationIntersector . checkAnimations ( true ) ;
2020-02-15 18:14:58 +07:00
}
2020-02-17 19:18:06 +07:00
////////this.log('wasActive:', wasActive);
2020-02-15 18:14:58 +07:00
2020-05-26 18:04:06 +03:00
const mover = this . content . mover ;
//const maxWidth = appPhotosManager.windowW - 16;
2020-06-21 15:25:17 +03:00
const maxWidth = mediaSizes . isMobile ? this . pageEl.scrollWidth : this.pageEl.scrollWidth - 16 ;
2020-05-26 18:04:06 +03:00
const maxHeight = appPhotosManager . windowH - 100 ;
2021-01-26 00:47:04 +02:00
let thumbPromise : Promise < any > = Promise . resolve ( ) ;
if ( useContainerAsTarget ) {
const cacheContext = appPhotosManager . getCacheContext ( media ) ;
2021-01-26 03:26:28 +02:00
let img : HTMLImageElement ;
2021-01-26 00:47:04 +02:00
if ( cacheContext . downloaded ) {
2021-01-26 03:26:28 +02:00
img = new Image ( ) ;
2021-01-26 00:47:04 +02:00
img . src = cacheContext . url ;
} else {
const gotThumb = appPhotosManager . getStrippedThumbIfNeeded ( media ) ;
if ( gotThumb ) {
thumbPromise = gotThumb . loadPromise ;
2021-01-26 03:26:28 +02:00
img = gotThumb . image ;
2021-01-26 00:47:04 +02:00
}
}
2021-01-26 03:26:28 +02:00
if ( img ) {
img . classList . add ( 'thumbnail' ) ;
container . append ( img ) ;
}
2021-01-10 20:10:02 +04:00
}
2021-01-26 00:47:04 +02:00
const size = appPhotosManager . setAttachmentSize ( media , container , maxWidth , maxHeight , mediaSizes . isMobile ? false : true ) ;
2020-05-26 18:04:06 +03:00
// need after setAttachmentSize
2020-05-27 09:21:16 +03:00
/ * i f ( u s e C o n t a i n e r A s T a r g e t ) {
2020-05-23 08:31:18 +03:00
target = target . querySelector ( 'img, video' ) || target ;
2020-05-27 09:21:16 +03:00
} * /
2020-05-23 08:31:18 +03:00
2020-08-27 21:25:47 +03:00
const preloader = media . supportsStreaming ? this . preloaderStreamable : this.preloader ;
2020-05-27 09:21:16 +03:00
let setMoverPromise : Promise < void > ;
2020-02-06 22:43:07 +07:00
if ( isVideo ) {
2020-02-17 19:18:06 +07:00
////////this.log('will wrap video', media, size);
2020-02-15 12:54:55 +07:00
2020-08-30 13:43:57 +03:00
// потому что для safari нужно создать элемент из event'а
const video = document . createElement ( 'video' ) ;
2021-01-26 00:47:04 +02:00
const set = ( ) = > this . setMoverToTarget ( target , false , fromRight ) . then ( ( { onAnimationEnd } ) = > {
2020-05-18 04:21:58 +03:00
//return; // set and don't move
2020-02-15 18:14:58 +07:00
//if(wasActive) return;
2020-04-08 18:46:43 +03:00
//return;
2021-01-26 00:47:04 +02:00
2020-06-16 23:48:08 +03:00
const div = mover . firstElementChild && mover . firstElementChild . classList . contains ( 'media-viewer-aspecter' ) ? mover.firstElementChild : mover ;
2020-08-30 13:43:57 +03:00
//const video = mover.querySelector('video') || document.createElement('video');
2021-01-26 00:47:04 +02:00
2020-08-30 13:43:57 +03:00
const moverVideo = mover . querySelector ( 'video' ) ;
if ( moverVideo ) {
moverVideo . remove ( ) ;
}
2021-01-26 00:47:04 +02:00
2020-08-27 21:25:47 +03:00
//video.src = '';
2021-01-26 00:47:04 +02:00
2020-10-31 00:39:36 +02:00
video . setAttribute ( 'playsinline' , 'true' ) ;
2021-01-26 00:47:04 +02:00
2020-10-21 20:02:36 +03:00
// * fix for playing video if viewer is closed (https://contest.com/javascript-web-bonus/entry1425#issue11629)
video . addEventListener ( 'timeupdate' , ( ) = > {
2021-02-04 02:30:23 +02:00
if ( this . tempId !== tempId ) {
2020-10-21 20:02:36 +03:00
video . pause ( ) ;
}
} ) ;
2021-01-26 00:47:04 +02:00
2020-09-01 15:53:46 +03:00
if ( isSafari ) {
2020-10-31 00:39:36 +02:00
// test stream
// video.controls = true;
2020-09-01 15:53:46 +03:00
video . autoplay = true ;
}
2021-01-26 00:47:04 +02:00
2021-02-04 02:30:23 +02:00
if ( media . type === 'gif' ) {
2020-06-06 23:38:48 +03:00
video . muted = true ;
2020-05-13 18:26:40 +03:00
video . autoplay = true ;
2020-05-27 09:21:16 +03:00
video . loop = true ;
2020-05-13 18:26:40 +03:00
}
2021-01-26 00:47:04 +02:00
2020-08-27 21:25:47 +03:00
if ( ! video . parentElement ) {
div . append ( video ) ;
}
2021-01-26 00:47:04 +02:00
2020-08-27 21:25:47 +03:00
const canPlayThrough = new Promise ( ( resolve ) = > {
2020-09-01 15:53:46 +03:00
video . addEventListener ( 'canplay' , resolve , { once : true } ) ;
2020-08-27 21:25:47 +03:00
} ) ;
2021-01-26 00:47:04 +02:00
2020-08-22 19:53:59 +03:00
const createPlayer = ( ) = > {
2021-02-04 02:30:23 +02:00
if ( media . type !== 'gif' ) {
2020-05-18 04:21:58 +03:00
video . dataset . ckin = 'default' ;
video . dataset . overlay = '1' ;
2021-01-26 00:47:04 +02:00
2020-08-26 14:59:32 +03:00
// fix for simultaneous play
2020-08-29 14:45:37 +03:00
appMediaPlaybackController . pause ( ) ;
appMediaPlaybackController . willBePlayedMedia = null ;
2020-08-27 21:25:47 +03:00
Promise . all ( [ canPlayThrough , onAnimationEnd ] ) . then ( ( ) = > {
2021-02-04 02:30:23 +02:00
if ( this . tempId !== tempId ) {
2020-10-21 20:02:36 +03:00
return ;
}
2021-01-26 00:47:04 +02:00
2020-08-27 21:25:47 +03:00
const player = new VideoPlayer ( video , true , media . supportsStreaming ) ;
/ * d i v . a p p e n d ( v i d e o ) ;
mover . append ( player . wrapper ) ; * /
} ) ;
2020-08-26 23:31:07 +03:00
}
2020-05-18 04:21:58 +03:00
} ;
2021-01-26 00:47:04 +02:00
2020-08-27 21:25:47 +03:00
if ( media . supportsStreaming ) {
onAnimationEnd . then ( ( ) = > {
if ( video . readyState < video . HAVE_FUTURE_DATA ) {
preloader . attach ( mover , true ) ;
}
2021-01-26 00:47:04 +02:00
2020-08-27 21:25:47 +03:00
/ * c a n P l a y T h r o u g h . t h e n ( ( ) = > {
preloader . detach ( ) ;
} ) ; * /
} ) ;
2021-01-26 00:47:04 +02:00
2020-08-27 21:25:47 +03:00
const attachCanPlay = ( ) = > {
video . addEventListener ( 'canplay' , ( ) = > {
//this.log('video waited and progress loaded');
preloader . detach ( ) ;
video . parentElement . classList . remove ( 'is-buffering' ) ;
} , { once : true } ) ;
} ;
2021-01-26 00:47:04 +02:00
2020-08-27 21:25:47 +03:00
video . addEventListener ( 'waiting' , ( e ) = > {
const loading = video . networkState === video . NETWORK_LOADING ;
const isntEnoughData = video . readyState < video . HAVE_FUTURE_DATA ;
2021-01-26 00:47:04 +02:00
2020-08-27 21:25:47 +03:00
//this.log('video waiting for progress', loading, isntEnoughData);
if ( loading && isntEnoughData ) {
attachCanPlay ( ) ;
2021-01-26 00:47:04 +02:00
2020-08-27 21:25:47 +03:00
preloader . attach ( mover , true ) ;
2021-01-26 00:47:04 +02:00
2020-08-27 21:25:47 +03:00
// поставлю класс для плеера, чтобы убрать большую иконку пока прелоадер на месте
video . parentElement . classList . add ( 'is-buffering' ) ;
}
} ) ;
2021-01-26 00:47:04 +02:00
2020-08-27 21:25:47 +03:00
attachCanPlay ( ) ;
}
2020-04-08 18:46:43 +03:00
2021-02-04 02:30:23 +02:00
//if(!video.src || media.url !== video.src) {
2020-06-16 23:48:08 +03:00
const load = ( ) = > {
2020-12-11 04:06:16 +02:00
const promise = media . supportsStreaming ? Promise . resolve ( ) : appDocsManager . downloadDoc ( media ) ;
2020-08-22 19:53:59 +03:00
2020-08-27 21:25:47 +03:00
if ( ! media . supportsStreaming ) {
onAnimationEnd . then ( ( ) = > {
2021-01-20 14:01:18 +04:00
if ( ! media . url ) {
preloader . attach ( mover , true , promise ) ;
}
2020-08-27 21:25:47 +03:00
} ) ;
}
2021-01-26 00:47:04 +02:00
2020-08-27 21:25:47 +03:00
( promise as Promise < any > ) . then ( async ( ) = > {
2021-02-04 02:30:23 +02:00
if ( this . tempId !== tempId ) {
2020-05-27 09:21:16 +03:00
this . log . warn ( 'media viewer changed video' ) ;
return ;
2020-04-08 18:46:43 +03:00
}
2021-01-26 00:47:04 +02:00
2020-08-22 19:53:59 +03:00
const url = media . url ;
2020-08-27 21:25:47 +03:00
if ( target instanceof SVGSVGElement /* && (video.parentElement || !isSafari) */ ) { // if video exists
//if(!video.parentElement) {
2020-06-16 23:48:08 +03:00
div . firstElementChild . lastElementChild . append ( video ) ;
2020-08-27 21:25:47 +03:00
//}
2020-06-16 23:48:08 +03:00
2020-08-24 17:09:31 +03:00
this . updateMediaSource ( mover , url , 'video' ) ;
2020-05-27 09:21:16 +03:00
} else {
2020-08-24 17:09:31 +03:00
renderImageFromUrl ( video , url ) ;
2020-05-18 04:21:58 +03:00
}
2021-01-26 00:47:04 +02:00
2020-08-26 23:31:07 +03:00
createPlayer ( ) ;
2020-05-27 09:21:16 +03:00
} ) ;
2021-01-26 00:47:04 +02:00
2020-05-27 09:21:16 +03:00
return promise ;
} ;
2021-01-26 00:47:04 +02:00
2020-09-21 20:34:19 +03:00
this . lazyLoadQueue . unshift ( { load } ) ;
2020-08-27 21:25:47 +03:00
//} else createPlayer();
2020-05-27 09:21:16 +03:00
} ) ;
2021-01-26 00:47:04 +02:00
setMoverPromise = thumbPromise . then ( set ) ;
2020-02-06 22:43:07 +07:00
} else {
2021-01-26 00:47:04 +02:00
const set = ( ) = > this . setMoverToTarget ( target , false , fromRight ) . then ( ( { onAnimationEnd } ) = > {
2020-05-18 04:21:58 +03:00
//return; // set and don't move
2020-02-15 18:14:58 +07:00
//if(wasActive) return;
2020-04-08 18:46:43 +03:00
//return;
2020-05-27 09:21:16 +03:00
2020-09-01 15:53:46 +03:00
const load = ( ) = > {
const cancellablePromise = appPhotosManager . preloadPhoto ( media . id , size ) ;
2021-01-26 00:47:04 +02:00
2020-08-27 21:25:47 +03:00
onAnimationEnd . then ( ( ) = > {
2021-01-20 14:01:18 +04:00
if ( ! media . url ) {
this . preloader . attach ( mover , true , cancellablePromise ) ;
}
2020-08-27 21:25:47 +03:00
} ) ;
2021-01-20 14:01:18 +04:00
Promise . all ( [ onAnimationEnd , cancellablePromise ] ) . then ( ( ) = > {
if ( this . tempId !== tempId ) {
2020-05-27 09:21:16 +03:00
this . log . warn ( 'media viewer changed photo' ) ;
return ;
2020-05-18 04:21:58 +03:00
}
2020-05-27 09:21:16 +03:00
///////this.log('indochina', blob);
2021-01-26 00:47:04 +02:00
2020-09-01 15:53:46 +03:00
const url = media . url ;
2020-05-27 09:21:16 +03:00
if ( target instanceof SVGSVGElement ) {
this . updateMediaSource ( target , url , 'img' ) ;
this . updateMediaSource ( mover , url , 'img' ) ;
2021-01-26 00:47:04 +02:00
2020-09-02 00:16:04 +03:00
if ( mediaSizes . isMobile ) {
const imgs = mover . querySelectorAll ( 'img' ) ;
if ( imgs && imgs . length ) {
imgs . forEach ( img = > {
img . classList . remove ( 'thumbnail' ) ; // может здесь это вообще не нужно
} ) ;
}
}
2020-05-27 09:21:16 +03:00
} else {
2020-09-01 15:53:46 +03:00
const div = mover . firstElementChild && mover . firstElementChild . classList . contains ( 'media-viewer-aspecter' ) ? mover.firstElementChild : mover ;
2021-01-26 00:47:04 +02:00
const haveImage = div . firstElementChild ? . tagName === 'IMG' ? div . firstElementChild as HTMLImageElement : null ;
if ( ! haveImage || haveImage . src !== url ) {
let image = new Image ( ) ;
2021-01-26 03:26:28 +02:00
image . classList . add ( 'thumbnail' ) ;
2021-01-26 00:47:04 +02:00
//this.log('will renderImageFromUrl:', image, div, target);
renderImageFromUrl ( image , url , ( ) = > {
this . updateMediaSource ( target , url , 'img' ) ;
2020-05-27 09:21:16 +03:00
2021-01-26 00:47:04 +02:00
if ( haveImage ) {
window . requestAnimationFrame ( ( ) = > {
haveImage . remove ( ) ;
} ) ;
}
div . append ( image ) ;
} ) ;
}
2020-05-27 09:21:16 +03:00
}
2021-01-26 00:47:04 +02:00
2020-05-27 09:21:16 +03:00
this . preloader . detach ( ) ;
} ) . catch ( err = > {
this . log . error ( err ) ;
} ) ;
2021-01-26 00:47:04 +02:00
2020-05-27 09:21:16 +03:00
return cancellablePromise ;
} ;
2021-01-26 00:47:04 +02:00
2021-01-21 16:27:14 +02:00
this . lazyLoadQueue . unshift ( { load } ) ;
2020-05-27 09:21:16 +03:00
} ) ;
2021-01-26 00:47:04 +02:00
setMoverPromise = thumbPromise . then ( set ) ;
2020-02-06 22:43:07 +07:00
}
2020-05-27 09:21:16 +03:00
2020-08-27 21:25:47 +03:00
return this . setMoverPromise = setMoverPromise . catch ( ( ) = > {
this . setMoverAnimationPromise = null ;
} ) . finally ( ( ) = > {
2020-05-27 09:21:16 +03:00
this . setMoverPromise = null ;
} ) ;
2020-02-06 22:43:07 +07:00
}
}
2020-10-14 15:51:21 +03:00
type AppMediaViewerTargetType = {
element : HTMLElement ,
2020-12-25 14:53:20 +02:00
mid : number ,
peerId : number
2020-10-14 15:51:21 +03:00
} ;
export default class AppMediaViewer extends AppMediaViewerBase < 'caption' , 'delete' | 'forward' , AppMediaViewerTargetType > {
2020-12-11 04:06:16 +02:00
public currentMessageId = 0 ;
2020-12-25 14:53:20 +02:00
public currentPeerId = 0 ;
public searchContext : SearchSuperContext ;
2020-10-14 15:51:21 +03:00
2020-12-25 14:53:20 +02:00
constructor ( ) {
2020-10-14 15:51:21 +03:00
super ( [ 'delete' , 'forward' ] ) ;
2020-10-31 20:45:49 +02:00
/ * c o n s t s t u b = d o c u m e n t . c r e a t e E l e m e n t ( ' d i v ' ) ;
2020-10-14 15:51:21 +03:00
stub . classList . add ( MEDIA_VIEWER_CLASSNAME + '-stub' ) ;
2020-10-31 20:45:49 +02:00
this . content . main . prepend ( stub ) ; * /
2020-10-14 15:51:21 +03:00
this . content . caption = document . createElement ( 'div' ) ;
2020-10-31 20:45:49 +02:00
this . content . caption . classList . add ( MEDIA_VIEWER_CLASSNAME + '-caption' /* , 'media-viewer-stub' */ ) ;
new Scrollable ( this . content . caption ) ;
//this.content.main.append(this.content.caption);
this . wholeDiv . append ( this . content . caption ) ;
2020-10-14 15:51:21 +03:00
this . setBtnMenuToggle ( [ {
icon : 'forward' ,
text : 'Forward' ,
onClick : this.onForwardClick
} , {
icon : 'download' ,
text : 'Download' ,
onClick : this.onDownloadClick
} , {
2020-10-15 21:05:15 +03:00
icon : 'delete danger btn-disabled' ,
2020-10-14 15:51:21 +03:00
text : 'Delete' ,
onClick : ( ) = > { }
} ] ) ;
// * constructing html end
this . setListeners ( ) ;
}
protected setListeners() {
super . setListeners ( ) ;
this . buttons . forward . addEventListener ( 'click' , this . onForwardClick ) ;
this . author . container . addEventListener ( 'click' , this . onAuthorClick ) ;
}
2020-10-15 01:46:29 +03:00
/ * p u b l i c c l o s e ( e ? : M o u s e E v e n t ) {
2020-10-14 15:51:21 +03:00
const good = ! this . setMoverAnimationPromise ;
const promise = super . close ( e ) ;
if ( good ) { // clear
2020-12-11 04:06:16 +02:00
this . currentMessageId = 0 ;
this . peerId = 0 ;
2020-10-14 15:51:21 +03:00
}
return promise ;
2020-10-15 01:46:29 +03:00
} * /
2020-10-14 15:51:21 +03:00
onPrevClick = ( target : AppMediaViewerTargetType ) = > {
2020-12-25 14:53:20 +02:00
this . nextTargets . unshift ( { element : this.lastTarget , mid : this.currentMessageId , peerId : this.currentPeerId } ) ;
this . openMedia ( appMessagesManager . getMessageByPeer ( target . peerId , target . mid ) , target . element , - 1 ) ;
2020-10-14 15:51:21 +03:00
} ;
onNextClick = ( target : AppMediaViewerTargetType ) = > {
2020-12-25 14:53:20 +02:00
this . prevTargets . push ( { element : this.lastTarget , mid : this.currentMessageId , peerId : this.currentPeerId } ) ;
this . openMedia ( appMessagesManager . getMessageByPeer ( target . peerId , target . mid ) , target . element , 1 ) ;
2020-10-14 15:51:21 +03:00
} ;
2020-11-21 15:13:23 +02:00
onForwardClick = ( ) = > {
2020-12-11 04:06:16 +02:00
if ( this . currentMessageId ) {
//appSidebarRight.forwardTab.open([this.currentMessageId]);
2020-12-25 14:53:20 +02:00
new PopupForward ( this . currentPeerId , [ this . currentMessageId ] , ( ) = > {
2020-10-20 02:39:36 +03:00
return this . close ( ) ;
} ) ;
2020-10-14 15:51:21 +03:00
}
} ;
onAuthorClick = ( e : MouseEvent ) = > {
2020-12-25 14:53:20 +02:00
if ( this . currentMessageId && this . currentMessageId !== Number . MAX_SAFE_INTEGER ) {
2020-12-11 04:06:16 +02:00
const mid = this . currentMessageId ;
2020-12-25 14:53:20 +02:00
const peerId = this . currentPeerId ;
const threadId = this . searchContext . threadId ;
2020-10-16 18:16:05 +03:00
this . close ( e )
//.then(() => mediaSizes.isMobile ? appSidebarRight.sharedMediaTab.closeBtn.click() : Promise.resolve())
. then ( ( ) = > {
if ( mediaSizes . isMobile ) {
2020-12-24 11:13:21 +02:00
appSidebarRight . closeTab ( AppSidebarRight . SLIDERITEMSIDS . sharedMedia ) ;
2020-10-16 18:16:05 +03:00
}
2020-12-25 14:53:20 +02:00
const message = appMessagesManager . getMessageByPeer ( peerId , mid ) ;
appImManager . setInnerPeer ( message . peerId , mid , threadId ? 'discussion' : undefined , threadId ) ;
2020-10-14 15:51:21 +03:00
} ) ;
}
} ;
2020-11-21 15:13:23 +02:00
onDownloadClick = ( ) = > {
2020-12-25 14:53:20 +02:00
const message = appMessagesManager . getMessageByPeer ( this . currentPeerId , this . currentMessageId ) ;
2020-10-14 15:51:21 +03:00
if ( message . media . photo ) {
2020-12-11 04:06:16 +02:00
appPhotosManager . savePhotoFile ( message . media . photo , appImManager . chat . bubbles . lazyLoadQueue . queueId ) ;
2020-10-14 15:51:21 +03:00
} else {
let document : MyDocument = null ;
if ( message . media . webpage ) document = message . media . webpage . document ;
else document = message . media . document ;
if ( document ) {
//console.log('will save document:', document);
2020-12-11 04:06:16 +02:00
appDocsManager . saveDocFile ( document , appImManager . chat . bubbles . lazyLoadQueue . queueId ) ;
2020-10-14 15:51:21 +03:00
}
}
} ;
// нет смысла делать проверку для reverse и loadMediaPromise
protected loadMoreMedia = ( older = true ) = > {
//if(!older && this.reverse) return;
2020-10-15 01:46:29 +03:00
if ( older && this . loadedAllMediaDown ) return Promise . resolve ( ) ;
else if ( ! older && this . loadedAllMediaUp ) return Promise . resolve ( ) ;
2020-10-14 15:51:21 +03:00
if ( older && this . loadMediaPromiseDown ) return this . loadMediaPromiseDown ;
else if ( ! older && this . loadMediaPromiseUp ) return this . loadMediaPromiseUp ;
const loadCount = 50 ;
const backLimit = older ? 0 : loadCount ;
2020-12-11 04:06:16 +02:00
let maxId = this . currentMessageId ;
2020-10-14 15:51:21 +03:00
2020-12-25 14:53:20 +02:00
let anchor : AppMediaViewerTargetType ;
2020-10-14 15:51:21 +03:00
if ( older ) {
anchor = this . reverse ? this . prevTargets [ 0 ] : this . nextTargets [ this . nextTargets . length - 1 ] ;
} else {
anchor = this . reverse ? this . nextTargets [ this . nextTargets . length - 1 ] : this . prevTargets [ 0 ] ;
}
2020-12-11 04:06:16 +02:00
if ( anchor ) maxId = anchor . mid ;
2020-12-23 04:18:24 +02:00
if ( ! older ) maxId = appMessagesManager . incrementMessageId ( maxId , 1 ) ;
2020-10-14 15:51:21 +03:00
2020-12-25 19:38:32 +02:00
const promise = appMessagesManager . getSearch ( {
2020-12-25 14:53:20 +02:00
peerId : this.searchContext.peerId ,
query : this.searchContext.query ,
inputFilter : {
_ : this.searchContext.inputFilter
} ,
maxId ,
limit : backLimit ? 0 : loadCount ,
backLimit ,
threadId : this.searchContext.threadId ,
folderId : this.searchContext.folderId ,
2020-12-25 20:20:43 +02:00
nextRate : this.searchContext.nextRate ,
minDate : this.searchContext.minDate ,
maxDate : this.searchContext.maxDate
2020-12-25 14:53:20 +02:00
} ) . then ( value = > {
2021-01-26 03:26:28 +02:00
if ( DEBUG ) {
2021-01-13 14:48:35 +04:00
this . log ( 'loaded more media by maxId:' , maxId , value , older , this . reverse ) ;
2021-01-26 03:26:28 +02:00
}
2020-10-14 15:51:21 +03:00
2020-12-25 14:53:20 +02:00
if ( value . next_rate ) {
this . searchContext . nextRate = value . next_rate ;
2020-10-14 15:51:21 +03:00
}
if ( value . history . length < loadCount ) {
/ * i f ( t h i s . r e v e r s e ) {
if ( older ) this . loadedAllMediaUp = true ;
else this . loadedAllMediaDown = true ;
} else { * /
if ( older ) this . loadedAllMediaDown = true ;
else this . loadedAllMediaUp = true ;
//}
}
const method = older ? value.history.forEach : value.history.forEachReverse ;
2020-12-18 05:07:32 +02:00
method . call ( value . history , message = > {
2020-12-25 14:53:20 +02:00
const { mid , peerId } = message ;
2020-10-15 01:46:29 +03:00
const media = this . getMediaFromMessage ( message ) ;
2020-10-14 15:51:21 +03:00
2020-10-15 01:46:29 +03:00
if ( ! media ) return ;
2021-02-04 02:30:23 +02:00
//if(media._ === 'document' && media.type !== 'video') return;
2020-10-14 15:51:21 +03:00
2020-12-25 14:53:20 +02:00
const t = { element : null as HTMLElement , mid , peerId } ;
2020-10-14 15:51:21 +03:00
if ( older ) {
if ( this . reverse ) this . prevTargets . unshift ( t ) ;
else this . nextTargets . push ( t ) ;
} else {
if ( this . reverse ) this . nextTargets . push ( t ) ;
else this . prevTargets . unshift ( t ) ;
}
} ) ;
2020-10-15 01:46:29 +03:00
this . buttons . prev . classList . toggle ( 'hide' , ! this . prevTargets . length ) ;
this . buttons . next . classList . toggle ( 'hide' , ! this . nextTargets . length ) ;
2020-10-14 15:51:21 +03:00
} , ( ) = > { } ) . then ( ( ) = > {
if ( older ) this . loadMediaPromiseDown = null ;
else this . loadMediaPromiseUp = null ;
} ) ;
if ( older ) this . loadMediaPromiseDown = promise ;
else this . loadMediaPromiseUp = promise ;
return promise ;
} ;
2020-10-15 01:46:29 +03:00
private getMediaFromMessage ( message : any ) {
return message . action ? message.action.photo : message.media.photo
|| message . media . document
|| ( message . media . webpage && ( message . media . webpage . document || message . media . webpage . photo ) ) ;
}
2020-10-14 15:51:21 +03:00
private setCaption ( message : any ) {
const caption = message . message ;
2020-10-31 20:45:49 +02:00
this . content . caption . classList . toggle ( 'hide' , ! caption ) ;
2020-10-14 15:51:21 +03:00
if ( caption ) {
2020-10-31 20:45:49 +02:00
this . content . caption . firstElementChild . innerHTML = RichTextProcessor . wrapRichText ( caption , {
2020-10-14 15:51:21 +03:00
entities : message.totalEntities
} ) ;
} else {
2020-10-31 20:45:49 +02:00
this . content . caption . firstElementChild . innerHTML = '' ;
2020-10-14 15:51:21 +03:00
}
}
2020-12-25 14:53:20 +02:00
public setSearchContext ( context : SearchSuperContext ) {
this . searchContext = context ;
if ( this . searchContext . folderId !== undefined ) {
this . loadedAllMediaUp = true ;
if ( this . searchContext . nextRate === undefined ) {
this . loadedAllMediaDown = true ;
}
}
return this ;
}
public async openMedia ( message : any , target? : HTMLElement , fromRight = 0 , reverse = false ,
prevTargets : AppMediaViewer [ 'prevTargets' ] = [ ] , nextTargets : AppMediaViewer [ 'prevTargets' ] = [ ] , needLoadMore = true ) {
2020-10-14 15:51:21 +03:00
if ( this . setMoverPromise ) return this . setMoverPromise ;
2020-10-15 01:46:29 +03:00
const mid = message . mid ;
2020-12-11 04:06:16 +02:00
const fromId = message . fromId ;
2020-10-15 01:46:29 +03:00
const media = this . getMediaFromMessage ( message ) ;
2020-10-14 15:51:21 +03:00
2020-12-11 04:06:16 +02:00
this . currentMessageId = mid ;
2020-12-25 14:53:20 +02:00
this . currentPeerId = message . peerId ;
2020-12-11 04:06:16 +02:00
const promise = super . _openMedia ( media , message . date , fromId , fromRight , target , reverse , prevTargets , nextTargets , needLoadMore ) ;
2020-10-14 15:51:21 +03:00
this . setCaption ( message ) ;
return promise ;
}
}
2020-12-11 04:06:16 +02:00
type AppMediaViewerAvatarTargetType = { element : HTMLElement , photoId : string } ;
2020-10-15 01:46:29 +03:00
export class AppMediaViewerAvatar extends AppMediaViewerBase < '' , 'delete' , AppMediaViewerAvatarTargetType > {
2020-12-11 04:06:16 +02:00
public currentPhotoId : string ;
public peerId : number ;
2020-10-15 01:46:29 +03:00
2020-12-11 04:06:16 +02:00
constructor ( peerId : number ) {
2020-10-14 15:51:21 +03:00
super ( [ 'delete' ] ) ;
2020-12-11 04:06:16 +02:00
this . peerId = peerId ;
2020-10-15 01:46:29 +03:00
2020-10-14 15:51:21 +03:00
this . setBtnMenuToggle ( [ {
icon : 'download' ,
text : 'Download' ,
onClick : this.onDownloadClick
} , {
2020-10-15 21:05:15 +03:00
icon : 'delete danger btn-disabled' ,
2020-10-14 15:51:21 +03:00
text : 'Delete' ,
onClick : ( ) = > { }
} ] ) ;
// * constructing html end
this . setListeners ( ) ;
}
2020-10-15 01:46:29 +03:00
onPrevClick = ( target : AppMediaViewerAvatarTargetType ) = > {
2020-12-11 04:06:16 +02:00
this . nextTargets . unshift ( { element : this.lastTarget , photoId : this.currentPhotoId } ) ;
this . openMedia ( target . photoId , target . element , - 1 ) ;
2020-10-15 01:46:29 +03:00
} ;
onNextClick = ( target : AppMediaViewerAvatarTargetType ) = > {
2020-12-11 04:06:16 +02:00
this . prevTargets . push ( { element : this.lastTarget , photoId : this.currentPhotoId } ) ;
this . openMedia ( target . photoId , target . element , 1 ) ;
2020-10-15 01:46:29 +03:00
} ;
2020-11-21 15:13:23 +02:00
onDownloadClick = ( ) = > {
2020-12-11 04:06:16 +02:00
appPhotosManager . savePhotoFile ( appPhotosManager . getPhoto ( this . currentPhotoId ) , appImManager . chat . bubbles . lazyLoadQueue . queueId ) ;
2020-10-14 15:51:21 +03:00
} ;
protected loadMoreMedia = ( older = true ) = > {
2020-12-11 04:06:16 +02:00
if ( this . peerId < 0 ) return Promise . resolve ( ) ; // ! это значит, что открыло аватар чата, но следующих фотографий нет.
2020-10-15 01:46:29 +03:00
if ( this . loadedAllMediaDown ) return Promise . resolve ( ) ;
if ( this . loadMediaPromiseDown ) return this . loadMediaPromiseDown ;
2020-12-11 04:06:16 +02:00
const peerId = this . peerId ;
2020-10-15 01:46:29 +03:00
const loadCount = 50 ;
2020-12-11 04:06:16 +02:00
const maxId = this . nextTargets . length ? this . nextTargets [ this . nextTargets . length - 1 ] . photoId : this.currentPhotoId ;
2020-10-15 01:46:29 +03:00
2020-12-11 04:06:16 +02:00
const promise = appPhotosManager . getUserPhotos ( peerId , maxId , loadCount ) . then ( value = > {
2021-02-04 02:30:23 +02:00
if ( this . peerId !== peerId ) {
2020-10-15 01:46:29 +03:00
this . log . warn ( 'peer changed' ) ;
return ;
}
2021-01-13 14:48:35 +04:00
// if(DEBUG) {
// this.log('loaded more media by maxId:', /* maxId, */value, older, this.reverse);
// }
2020-10-15 01:46:29 +03:00
if ( value . photos . length < loadCount ) {
this . loadedAllMediaDown = true ;
}
2020-12-11 04:06:16 +02:00
value . photos . forEach ( photoId = > {
2021-02-04 02:30:23 +02:00
if ( this . currentPhotoId === photoId ) return ;
2020-12-11 04:06:16 +02:00
this . nextTargets . push ( { element : null as HTMLElement , photoId : photoId } ) ;
2020-10-15 01:46:29 +03:00
} ) ;
this . buttons . prev . classList . toggle ( 'hide' , ! this . prevTargets . length ) ;
this . buttons . next . classList . toggle ( 'hide' , ! this . nextTargets . length ) ;
} , ( ) = > { } ) . then ( ( ) = > {
this . loadMediaPromiseDown = null ;
} ) ;
return this . loadMediaPromiseDown = promise ;
2020-10-14 15:51:21 +03:00
} ;
2020-12-11 04:06:16 +02:00
public async openMedia ( photoId : string , target? : HTMLElement , fromRight = 0 ) {
2020-10-14 15:51:21 +03:00
if ( this . setMoverPromise ) return this . setMoverPromise ;
2020-12-11 04:06:16 +02:00
const photo = appPhotosManager . getPhoto ( photoId ) ;
2020-10-14 15:51:21 +03:00
2020-12-11 04:06:16 +02:00
this . currentPhotoId = photo . id ;
2020-10-14 15:51:21 +03:00
2020-12-11 04:06:16 +02:00
return super . _openMedia ( photo , photo . date , this . peerId , fromRight , target , false ) ;
2020-10-14 15:51:21 +03:00
}
}