2020-10-11 22:12:17 +03:00
import { readBlobAsText } from '../helpers/blob' ;
import { deferredPromise } from '../helpers/cancellablePromise' ;
import { months } from '../helpers/date' ;
import mediaSizes from '../helpers/mediaSizes' ;
2020-10-31 00:39:36 +02:00
import { isAppleMobile , isSafari } from '../helpers/userAgent' ;
2020-10-11 22:12:17 +03:00
import { PhotoSize } from '../layer' ;
2020-09-17 22:33:23 +03:00
import appDocsManager , { MyDocument } from "../lib/appManagers/appDocsManager" ;
2020-10-11 22:12:17 +03:00
import { DownloadBlob } from '../lib/appManagers/appDownloadManager' ;
import appMessagesManager from '../lib/appManagers/appMessagesManager' ;
import appPhotosManager , { MyPhoto } from '../lib/appManagers/appPhotosManager' ;
import LottieLoader from '../lib/lottieLoader' ;
import VideoPlayer from '../lib/mediaPlayer' ;
2020-10-21 02:25:36 +03:00
import { cancelEvent , formatBytes , getEmojiToneIndex , isInDOM } from "../lib/utils" ;
2020-10-11 22:12:17 +03:00
import webpWorkerController from '../lib/webp/webpWorkerController' ;
import animationIntersector from './animationIntersector' ;
import appMediaPlaybackController from './appMediaPlaybackController' ;
import AudioElement from './audio' ;
import ReplyContainer from './chat/replyContainer' ;
import { Layouter , RectPart } from './groupedLayout' ;
2020-02-11 22:35:57 +07:00
import LazyLoadQueue from './lazyLoadQueue' ;
2020-08-28 14:25:43 +03:00
import { renderImageFromUrl } from './misc' ;
2020-05-09 15:02:07 +03:00
import PollElement from './poll' ;
2020-10-11 22:12:17 +03:00
import ProgressivePreloader from './preloader' ;
2020-02-11 22:35:57 +07:00
2020-10-28 20:20:01 +02:00
const MAX_VIDEO_AUTOPLAY_SIZE = 50 * 1024 * 1024 ; // 50 MB
2020-08-24 17:09:31 +03:00
export function wrapVideo ( { doc , container , message , boxWidth , boxHeight , withTail , isOut , middleware , lazyLoadQueue , noInfo , group } : {
2020-09-17 22:33:23 +03:00
doc : MyDocument ,
2020-08-24 17:09:31 +03:00
container? : HTMLDivElement ,
message? : any ,
boxWidth? : number ,
boxHeight? : number ,
2020-04-20 00:40:00 +03:00
withTail? : boolean ,
isOut? : boolean ,
2020-08-24 17:09:31 +03:00
middleware ? : ( ) = > boolean ,
lazyLoadQueue? : LazyLoadQueue ,
noInfo? : true ,
group? : string ,
2020-04-20 00:40:00 +03:00
} ) {
2020-10-28 20:20:01 +02:00
const isAlbumItem = ! ( boxWidth && boxHeight ) ;
const canAutoplay = doc . type != 'video' || ( doc . size <= MAX_VIDEO_AUTOPLAY_SIZE && ! isAlbumItem ) ;
let spanTime : HTMLElement ;
2020-08-24 17:09:31 +03:00
if ( ! noInfo ) {
if ( doc . type != 'round' ) {
2020-10-28 20:20:01 +02:00
spanTime = document . createElement ( 'span' ) ;
spanTime . classList . add ( 'video-time' ) ;
container . append ( spanTime ) ;
2020-08-24 17:09:31 +03:00
if ( doc . type != 'gif' ) {
2020-10-28 20:20:01 +02:00
spanTime . innerText = ( doc . duration + '' ) . toHHMMSS ( false ) ;
if ( canAutoplay ) {
spanTime . classList . add ( 'tgico' , 'can-autoplay' ) ;
} else {
const spanPlay = document . createElement ( 'span' ) ;
spanPlay . classList . add ( 'video-play' , 'tgico-largeplay' , 'btn-circle' , 'position-center' ) ;
container . append ( spanPlay ) ;
}
2020-08-24 17:09:31 +03:00
} else {
2020-10-28 20:20:01 +02:00
spanTime . innerText = 'GIF' ;
2020-08-24 17:09:31 +03:00
}
2020-06-16 23:48:08 +03:00
}
}
2020-08-25 19:26:49 +03:00
if ( doc . mime_type == 'image/gif' ) {
return wrapPhoto ( doc , message , container , boxWidth , boxHeight , withTail , isOut , lazyLoadQueue , middleware ) ;
}
2020-08-29 14:45:37 +03:00
/ * c o n s t v i d e o = d o c . t y p e = = ' r o u n d ' ? a p p M e d i a P l a y b a c k C o n t r o l l e r . a d d M e d i a ( d o c , m e s s a g e . m i d ) a s H T M L V i d e o E l e m e n t : d o c u m e n t . c r e a t e E l e m e n t ( ' v i d e o ' ) ;
if ( video . parentElement ) {
video . remove ( ) ;
} * /
2020-08-24 17:09:31 +03:00
const video = document . createElement ( 'video' ) ;
2020-08-30 13:43:57 +03:00
video . muted = true ;
2020-10-31 00:39:36 +02:00
video . setAttribute ( 'playsinline' , 'true' ) ;
2020-08-29 14:45:37 +03:00
if ( doc . type == 'round' ) {
2020-08-30 13:43:57 +03:00
//video.muted = true;
2020-08-29 14:45:37 +03:00
const globalVideo = appMediaPlaybackController . addMedia ( doc , message . mid ) ;
video . addEventListener ( 'canplay' , ( ) = > {
if ( globalVideo . currentTime > 0 ) {
video . currentTime = globalVideo . currentTime ;
}
if ( ! globalVideo . paused ) {
// с закоментированными настройками - хром выключал видео при скролле, для этого нужно было включить видео - выйти из диалога, зайти заново и проскроллить вверх
/ * v i d e o . a u t o p l a y = t r u e ;
video . loop = false ; * /
video . play ( ) ;
}
} , { once : true } ) ;
const clear = ( ) = > {
//console.log('clearing video');
globalVideo . removeEventListener ( 'timeupdate' , onTimeUpdate ) ;
globalVideo . removeEventListener ( 'play' , onGlobalPlay ) ;
globalVideo . removeEventListener ( 'pause' , onGlobalPause ) ;
video . removeEventListener ( 'play' , onVideoPlay ) ;
video . removeEventListener ( 'pause' , onVideoPause ) ;
} ;
const onTimeUpdate = ( ) = > {
if ( ! isInDOM ( video ) ) {
clear ( ) ;
}
} ;
const onGlobalPlay = ( ) = > {
video . play ( ) ;
} ;
const onGlobalPause = ( ) = > {
video . pause ( ) ;
} ;
const onVideoPlay = ( ) = > {
globalVideo . play ( ) ;
} ;
const onVideoPause = ( ) = > {
//console.log('video pause event');
if ( isInDOM ( video ) ) {
globalVideo . pause ( ) ;
} else {
clear ( ) ;
}
} ;
globalVideo . addEventListener ( 'timeupdate' , onTimeUpdate ) ;
globalVideo . addEventListener ( 'play' , onGlobalPlay ) ;
globalVideo . addEventListener ( 'pause' , onGlobalPause ) ;
video . addEventListener ( 'play' , onVideoPlay ) ;
video . addEventListener ( 'pause' , onVideoPause ) ;
2020-08-30 13:43:57 +03:00
} else {
video . autoplay = true ; // для safari
2020-08-29 14:45:37 +03:00
}
2020-08-24 17:09:31 +03:00
2020-05-23 08:31:18 +03:00
let img : HTMLImageElement ;
2020-08-24 17:09:31 +03:00
if ( message ) {
2020-10-31 04:10:44 +02:00
if ( ! canAutoplay ) {
2020-08-24 17:09:31 +03:00
return wrapPhoto ( doc , message , container , boxWidth , boxHeight , withTail , isOut , lazyLoadQueue , middleware ) ;
}
if ( withTail ) {
img = wrapMediaWithTail ( doc , message , container , boxWidth , boxHeight , isOut ) ;
2020-05-23 08:31:18 +03:00
} else {
2020-08-25 19:26:49 +03:00
if ( boxWidth && boxHeight ) { // !album
appPhotosManager . setAttachmentSize ( doc , container , boxWidth , boxHeight , false , true ) ;
}
2020-09-17 22:33:23 +03:00
if ( doc . thumbs ? . length && 'bytes' in doc . thumbs [ 0 ] ) {
2020-08-25 19:26:49 +03:00
appPhotosManager . setAttachmentPreview ( doc . thumbs [ 0 ] . bytes , container , false ) ;
2020-08-24 17:09:31 +03:00
}
img = container . lastElementChild as HTMLImageElement ;
2020-08-25 19:26:49 +03:00
if ( img ? . tagName != 'IMG' ) {
2020-08-24 17:09:31 +03:00
container . append ( img = new Image ( ) ) ;
2020-05-23 08:31:18 +03:00
}
2020-04-08 18:46:43 +03:00
}
2020-08-24 17:09:31 +03:00
if ( img ) {
img . classList . add ( 'thumbnail' ) ;
}
2020-05-23 08:31:18 +03:00
2020-08-24 17:09:31 +03:00
if ( withTail ) {
const foreignObject = img . parentElement ;
video . width = + foreignObject . getAttributeNS ( null , 'width' ) ;
video . height = + foreignObject . getAttributeNS ( null , 'height' ) ;
foreignObject . append ( video ) ;
2020-04-08 18:46:43 +03:00
}
2020-06-06 23:38:48 +03:00
}
2020-04-08 18:46:43 +03:00
2020-08-24 17:09:31 +03:00
if ( ! img ? . parentElement ) {
2020-08-28 14:25:43 +03:00
const gotThumb = appDocsManager . getThumb ( doc , false ) ;
if ( gotThumb ) {
gotThumb . promise . then ( ( ) = > {
video . poster = gotThumb . thumb . url ;
} ) ;
2020-08-24 17:09:31 +03:00
}
2020-06-13 11:19:39 +03:00
}
2020-06-16 23:48:08 +03:00
2020-08-24 17:09:31 +03:00
if ( ! video . parentElement && container ) {
2020-05-23 08:31:18 +03:00
container . append ( video ) ;
2020-04-08 18:46:43 +03:00
}
2020-08-21 14:17:16 +03:00
const loadVideo = async ( ) = > {
2020-08-25 19:26:49 +03:00
if ( middleware && ! middleware ( ) ) {
return ;
}
let preloader : ProgressivePreloader ;
2020-08-24 17:09:31 +03:00
if ( message ? . media ? . preloader ) { // means upload
2020-08-25 19:26:49 +03:00
preloader = message . media . preloader as ProgressivePreloader ;
2020-10-16 13:08:28 +03:00
preloader . attach ( container , undefined , undefined ) ;
2020-08-25 19:26:49 +03:00
} else if ( ! doc . downloaded && ! doc . supportsStreaming ) {
const promise = appDocsManager . downloadDocNew ( doc ) ;
2020-10-16 13:08:28 +03:00
preloader = new ProgressivePreloader ( null , true , false , 'prepend' ) ;
preloader . attach ( container , true , promise ) ;
2020-08-25 19:26:49 +03:00
/ * v i d e o . a d d E v e n t L i s t e n e r ( ' c a n p l a y ' , ( ) = > {
if ( preloader ) {
preloader . detach ( ) ;
}
} , { once : true } ) ; * /
2020-06-16 23:48:08 +03:00
2020-08-25 19:26:49 +03:00
await promise ;
2020-10-28 20:20:01 +02:00
if ( middleware && ! middleware ( ) ) {
return ;
}
2020-08-26 19:14:23 +03:00
} else if ( doc . supportsStreaming ) {
2020-10-16 13:08:28 +03:00
preloader = new ProgressivePreloader ( null , false , false , 'prepend' ) ;
preloader . attach ( container , false , null ) ;
2020-10-28 20:20:01 +02:00
video . addEventListener ( isSafari ? 'timeupdate' : 'canplay' , ( ) = > {
2020-08-26 19:14:23 +03:00
preloader . detach ( ) ;
} , { once : true } ) ;
2020-04-28 20:30:54 +03:00
}
2020-10-28 20:20:01 +02:00
2020-06-06 23:38:48 +03:00
//console.log('loaded doc:', doc, doc.url, container);
2020-06-16 23:48:08 +03:00
2020-09-21 20:34:19 +03:00
const deferred = deferredPromise < void > ( ) ;
2020-08-25 19:26:49 +03:00
//if(doc.type == 'gif'/* || true */) {
2020-10-31 00:39:36 +02:00
video . addEventListener ( isAppleMobile ? 'loadeddata' : 'canplay' , ( ) = > {
2020-08-25 19:26:49 +03:00
if ( img ? . parentElement ) {
2020-06-16 23:48:08 +03:00
img . remove ( ) ;
}
/ * i f ( ! v i d e o . p a u s e d ) {
video . pause ( ) ;
} * /
2020-10-28 20:20:01 +02:00
if ( doc . type != 'round' && group ) {
2020-08-24 17:09:31 +03:00
animationIntersector . addAnimation ( video , group ) ;
}
2020-09-21 20:34:19 +03:00
// test lazyLoadQueue
//setTimeout(() => {
deferred . resolve ( ) ;
//}, 5000);
2020-06-16 23:48:08 +03:00
} , { once : true } ) ;
2020-08-25 19:26:49 +03:00
//}
2020-10-28 20:20:01 +02:00
if ( doc . type == 'video' ) {
video . addEventListener ( 'timeupdate' , ( ) = > {
spanTime . innerText = ( video . duration - video . currentTime + '' ) . toHHMMSS ( false ) ;
} ) ;
}
2020-10-04 02:10:57 +03:00
video . addEventListener ( 'error' , ( e ) = > {
deferred . resolve ( ) ;
/ * c o n s o l e . e r r o r ( ' v i d e o e r r o r ' , e , v i d e o . s r c ) ;
if ( video . src ) { // if wasn't cleaned
deferred . reject ( e ) ;
} else {
deferred . resolve ( ) ;
} * /
} ) ;
2020-09-21 20:34:19 +03:00
2020-08-29 14:45:37 +03:00
//if(doc.type != 'round') {
renderImageFromUrl ( video , doc . url ) ;
//}
2020-06-16 23:48:08 +03:00
/ * i f ( ! c o n t a i n e r . p a r e n t E l e m e n t ) {
container . append ( video ) ;
} * /
2020-08-30 13:43:57 +03:00
2020-10-28 20:20:01 +02:00
if ( doc . type == 'round' ) {
video . dataset . ckin = 'circle' ;
video . dataset . overlay = '1' ;
new VideoPlayer ( video ) ;
} else {
2020-06-06 23:38:48 +03:00
video . muted = true ;
2020-05-18 04:21:58 +03:00
video . loop = true ;
2020-06-13 11:19:39 +03:00
//video.play();
video . autoplay = true ;
2020-05-18 04:21:58 +03:00
}
2020-09-21 20:34:19 +03:00
return deferred ;
2020-02-11 22:35:57 +07:00
} ;
2020-04-28 20:30:54 +03:00
2020-06-16 23:48:08 +03:00
/ * i f ( d o c . s i z e > = 2 0 e 6 & & ! d o c . d o w n l o a d e d ) {
2020-04-28 20:30:54 +03:00
let downloadDiv = document . createElement ( 'div' ) ;
downloadDiv . classList . add ( 'download' ) ;
let span = document . createElement ( 'span' ) ;
2020-05-06 04:03:31 +03:00
span . classList . add ( 'btn-circle' , 'tgico-download' ) ;
2020-04-28 20:30:54 +03:00
downloadDiv . append ( span ) ;
downloadDiv . addEventListener ( 'click' , ( ) = > {
downloadDiv . remove ( ) ;
loadVideo ( ) ;
2020-02-11 22:35:57 +07:00
} ) ;
2020-04-28 20:30:54 +03:00
2020-05-06 04:03:31 +03:00
container . prepend ( downloadDiv ) ;
2020-04-28 20:30:54 +03:00
return ;
2020-06-16 23:48:08 +03:00
} * /
2020-08-24 17:09:31 +03:00
2020-09-21 20:34:19 +03:00
return /* doc.downloaded || */ ! lazyLoadQueue /* && false */ ? loadVideo ( ) : ( lazyLoadQueue . push ( { div : container , load : loadVideo /* , wasSeen: true */ } ) , Promise . resolve ( ) ) ;
2020-02-11 22:35:57 +07:00
}
2020-06-13 11:19:39 +03:00
export const formatDate = ( timestamp : number , monthShort = false , withYear = true ) = > {
2020-04-28 20:30:54 +03:00
const date = new Date ( timestamp * 1000 ) ;
2020-06-13 11:19:39 +03:00
let month = months [ date . getMonth ( ) ] ;
if ( monthShort ) month = month . slice ( 0 , 3 ) ;
let str = month + ' ' + date . getDate ( ) ;
if ( withYear ) {
str += ', ' + date . getFullYear ( ) ;
}
return str + ' at ' + date . getHours ( ) + ':' + ( '0' + date . getMinutes ( ) ) . slice ( - 2 ) ;
2020-04-28 20:30:54 +03:00
} ;
2020-09-17 22:33:23 +03:00
export function wrapDocument ( doc : MyDocument , withTime = false , uploading = false , mid? : number ) : HTMLElement {
2020-06-13 11:19:39 +03:00
if ( doc . type == 'audio' || doc . type == 'voice' ) {
return wrapAudio ( doc , withTime , mid ) ;
2020-02-15 22:20:38 +07:00
}
2020-04-28 20:30:54 +03:00
2020-02-11 22:35:57 +07:00
let extSplitted = doc . file_name ? doc . file_name . split ( '.' ) : '' ;
let ext = '' ;
ext = extSplitted . length > 1 && Array . isArray ( extSplitted ) ? extSplitted . pop ( ) . toLowerCase ( ) : 'file' ;
2020-04-28 20:30:54 +03:00
let docDiv = document . createElement ( 'div' ) ;
docDiv . classList . add ( 'document' , ` ext- ${ ext } ` ) ;
2020-09-19 14:37:19 +03:00
const icoDiv = document . createElement ( 'div' ) ;
icoDiv . classList . add ( 'document-ico' ) ;
2020-10-07 16:57:33 +03:00
if ( doc . thumbs ? . length || ( uploading && doc . url ) ) {
2020-10-16 15:35:44 +03:00
docDiv . classList . add ( 'document-with-thumb' ) ;
2020-09-19 14:37:19 +03:00
if ( uploading ) {
icoDiv . innerHTML = ` <img src=" ${ doc . url } "> ` ;
} else {
wrapPhoto ( doc , null , icoDiv , 54 , 54 , false , null , null , null ) ;
icoDiv . style . width = icoDiv . style . height = '' ;
}
const img = icoDiv . querySelector ( 'img' ) ;
if ( img ) img . classList . add ( 'document-thumb' ) ;
} else {
icoDiv . innerText = ext ;
2020-02-11 22:35:57 +07:00
}
2020-09-19 14:37:19 +03:00
2020-02-11 22:35:57 +07:00
let fileName = doc . file_name || 'Unknown.file' ;
let size = formatBytes ( doc . size ) ;
if ( withTime ) {
2020-04-28 20:30:54 +03:00
size += ' · ' + formatDate ( doc . date ) ;
2020-02-11 22:35:57 +07:00
}
docDiv . innerHTML = `
2020-02-16 02:03:27 +07:00
$ { ! uploading ? ` <div class="document-download"><div class="tgico-download"></div></div> ` : '' }
2020-02-11 22:35:57 +07:00
< div class = "document-name" > $ { fileName } < / div >
< div class = "document-size" > $ { size } < / div >
` ;
2020-09-19 14:37:19 +03:00
docDiv . prepend ( icoDiv ) ;
2020-02-16 02:03:27 +07:00
if ( ! uploading ) {
let downloadDiv = docDiv . querySelector ( '.document-download' ) as HTMLDivElement ;
2020-08-25 12:39:39 +03:00
let preloader : ProgressivePreloader ;
2020-08-25 19:26:49 +03:00
let download : DownloadBlob ;
2020-02-16 02:03:27 +07:00
2020-10-21 02:25:36 +03:00
docDiv . addEventListener ( 'click' , ( e ) = > {
2020-08-22 19:53:59 +03:00
if ( ! download ) {
2020-02-16 02:03:27 +07:00
if ( downloadDiv . classList . contains ( 'downloading' ) ) {
return ; // means not ready yet
}
2020-02-16 01:08:26 +07:00
2020-02-16 02:03:27 +07:00
if ( ! preloader ) {
2020-08-25 12:39:39 +03:00
preloader = new ProgressivePreloader ( null , true ) ;
2020-02-16 02:03:27 +07:00
}
2020-08-22 19:53:59 +03:00
download = appDocsManager . saveDocFile ( doc ) ;
2020-08-25 12:39:39 +03:00
preloader . attach ( downloadDiv , true , download ) ;
2020-02-16 01:08:26 +07:00
2020-08-25 12:39:39 +03:00
download . then ( ( ) = > {
2020-08-22 19:53:59 +03:00
downloadDiv . remove ( ) ;
} ) . catch ( err = > {
if ( err . name === 'AbortError' ) {
download = null ;
}
} ) . finally ( ( ) = > {
downloadDiv . classList . remove ( 'downloading' ) ;
} ) ;
2020-02-16 02:03:27 +07:00
downloadDiv . classList . add ( 'downloading' ) ;
} else {
2020-08-25 12:39:39 +03:00
download . cancel ( ) ;
2020-02-16 02:03:27 +07:00
}
} ) ;
}
2020-02-11 22:35:57 +07:00
return docDiv ;
}
2020-09-17 22:33:23 +03:00
export function wrapAudio ( doc : MyDocument , withTime = false , mid? : number ) : HTMLElement {
2020-06-13 11:19:39 +03:00
let elem = new AudioElement ( ) ;
elem . setAttribute ( 'doc-id' , doc . id ) ;
elem . setAttribute ( 'with-time' , '' + + withTime ) ;
elem . setAttribute ( 'message-id' , '' + mid ) ;
return elem ;
2020-02-15 22:20:38 +07:00
}
2020-09-17 22:33:23 +03:00
function wrapMediaWithTail ( photo : MyPhoto | MyDocument , message : { mid : number , message : string } , container : HTMLDivElement , boxWidth : number , boxHeight : number , isOut : boolean ) {
2020-08-25 19:26:49 +03:00
const svg = document . createElementNS ( "http://www.w3.org/2000/svg" , "svg" ) ;
2020-04-08 18:46:43 +03:00
svg . classList . add ( 'bubble__media-container' , isOut ? 'is-out' : 'is-in' ) ;
2020-02-11 22:35:57 +07:00
2020-08-25 19:26:49 +03:00
const foreignObject = document . createElementNS ( "http://www.w3.org/2000/svg" , 'foreignObject' ) ;
2020-04-08 18:46:43 +03:00
2020-08-25 19:26:49 +03:00
appPhotosManager . setAttachmentSize ( photo , foreignObject , boxWidth , boxHeight /* , false, true */ ) ;
2020-05-23 08:31:18 +03:00
2020-08-25 19:26:49 +03:00
const width = + foreignObject . getAttributeNS ( null , 'width' ) ;
const height = + foreignObject . getAttributeNS ( null , 'height' ) ;
2020-05-23 08:31:18 +03:00
svg . setAttributeNS ( null , 'width' , '' + width ) ;
svg . setAttributeNS ( null , 'height' , '' + height ) ;
2020-04-14 18:46:31 +03:00
2020-06-05 19:01:06 +03:00
svg . setAttributeNS ( null , 'viewBox' , '0 0 ' + width + ' ' + height ) ;
svg . setAttributeNS ( null , 'preserveAspectRatio' , 'none' ) ;
2020-08-25 19:26:49 +03:00
const clipID = 'clip' + message . mid ;
2020-04-08 18:46:43 +03:00
svg . dataset . clipID = clipID ;
2020-02-11 22:35:57 +07:00
2020-08-25 19:26:49 +03:00
const defs = document . createElementNS ( "http://www.w3.org/2000/svg" , 'defs' ) ;
2020-04-14 18:46:31 +03:00
let clipPathHTML : string = '' ;
2020-04-08 18:46:43 +03:00
if ( message . message ) {
//clipPathHTML += `<rect width="${width}" height="${height}"></rect>`;
} else {
if ( isOut ) {
clipPathHTML += `
< use href = "#message-tail" transform = "translate(${width - 2}, ${height}) scale(-1, -1)" > < / use >
< path / >
` ;
} else {
clipPathHTML += `
< use href = "#message-tail" transform = "translate(2, ${height}) scale(1, -1)" > < / use >
< path / >
` ;
}
}
2020-04-14 18:46:31 +03:00
2020-04-08 18:46:43 +03:00
defs . innerHTML = ` <clipPath id=" ${ clipID } "> ${ clipPathHTML } </clipPath> ` ;
2020-05-23 08:31:18 +03:00
container . style . width = parseInt ( container . style . width ) - 9 + 'px' ;
2020-06-13 11:19:39 +03:00
container . classList . add ( 'with-tail' ) ;
2020-05-23 08:31:18 +03:00
svg . append ( defs , foreignObject ) ;
container . append ( svg ) ;
let img = foreignObject . firstElementChild as HTMLImageElement ;
if ( ! img ) {
foreignObject . append ( img = new Image ( ) ) ;
}
2020-04-08 18:46:43 +03:00
2020-05-23 08:31:18 +03:00
return img ;
2020-04-08 18:46:43 +03:00
}
2020-09-17 22:33:23 +03:00
export function wrapPhoto ( photo : MyPhoto | MyDocument , message : any , container : HTMLDivElement , boxWidth = mediaSizes . active . regular . width , boxHeight = mediaSizes . active . regular . height , withTail : boolean , isOut : boolean , lazyLoadQueue : LazyLoadQueue , middleware : ( ) = > boolean , size : PhotoSize = null ) {
2020-05-23 08:31:18 +03:00
let image : HTMLImageElement ;
2020-04-08 18:46:43 +03:00
if ( withTail ) {
image = wrapMediaWithTail ( photo , message , container , boxWidth , boxHeight , isOut ) ;
2020-05-23 08:31:18 +03:00
} else {
2020-08-25 19:26:49 +03:00
if ( boxWidth && boxHeight ) { // !album
size = appPhotosManager . setAttachmentSize ( photo , container , boxWidth , boxHeight , false , true ) ;
}
if ( photo . _ == 'document' || ! photo . downloaded ) {
2020-09-17 22:33:23 +03:00
const thumbs = ( photo as MyPhoto ) . sizes || ( photo as MyDocument ) . thumbs ;
if ( thumbs ? . length && 'bytes' in thumbs [ 0 ] ) {
2020-08-25 19:26:49 +03:00
appPhotosManager . setAttachmentPreview ( thumbs [ 0 ] . bytes , container , false ) ;
2020-05-23 08:31:18 +03:00
}
2020-05-02 00:28:40 +03:00
}
2020-05-23 08:31:18 +03:00
image = container . lastElementChild as HTMLImageElement ;
if ( ! image || image . tagName != 'IMG' ) {
container . append ( image = new Image ( ) ) ;
2020-04-08 18:46:43 +03:00
}
}
2020-10-31 04:10:44 +02:00
if ( ! ( ( photo as MyPhoto ) . sizes || ( photo as MyDocument ) . thumbs ) ) {
return Promise . resolve ( ) ;
}
2020-04-14 18:46:31 +03:00
2020-05-06 04:03:31 +03:00
//console.log('wrapPhoto downloaded:', photo, photo.downloaded, container);
2020-04-14 18:46:31 +03:00
2020-08-25 12:39:39 +03:00
const cacheContext = appPhotosManager . getCacheContext ( photo ) ;
2020-04-26 04:45:07 +03:00
2020-04-14 18:46:31 +03:00
let preloader : ProgressivePreloader ;
2020-09-19 14:37:19 +03:00
if ( message ? . media ? . preloader ) { // means upload
2020-05-02 00:28:40 +03:00
message . media . preloader . attach ( container ) ;
2020-08-25 12:39:39 +03:00
} else if ( ! cacheContext . downloaded ) {
2020-10-16 13:08:28 +03:00
preloader = new ProgressivePreloader ( container , false , false , photo . _ == 'document' ? 'prepend' : 'append' ) ;
2020-05-02 00:28:40 +03:00
}
2020-08-25 12:39:39 +03:00
const load = ( ) = > {
2020-08-25 19:26:49 +03:00
const promise = photo . _ == 'document' && photo . animated ? appDocsManager . downloadDocNew ( photo ) : appPhotosManager . preloadPhoto ( photo , size ) ;
2020-08-25 12:39:39 +03:00
2020-04-14 18:46:31 +03:00
if ( preloader ) {
preloader . attach ( container , true , promise ) ;
}
2020-08-28 14:25:43 +03:00
2020-04-15 23:55:08 +03:00
return promise . then ( ( ) = > {
2020-04-26 04:45:07 +03:00
if ( middleware && ! middleware ( ) ) return ;
2020-04-15 23:55:08 +03:00
2020-08-25 19:26:49 +03:00
renderImageFromUrl ( image || container , cacheContext . url || photo . url ) ;
2020-02-14 18:04:39 +07:00
} ) ;
} ;
2020-08-25 12:39:39 +03:00
2020-09-21 20:34:19 +03:00
return cacheContext . downloaded || ! lazyLoadQueue ? load ( ) : ( lazyLoadQueue . push ( { div : container , load : load , wasSeen : true } ) , Promise . resolve ( ) ) ;
2020-02-11 22:35:57 +07:00
}
2020-06-13 11:19:39 +03:00
export function wrapSticker ( { doc , div , middleware , lazyLoadQueue , group , play , onlyThumb , emoji , width , height , withThumb , loop } : {
2020-09-17 22:33:23 +03:00
doc : MyDocument ,
2020-05-26 18:04:06 +03:00
div : HTMLDivElement ,
middleware ? : ( ) = > boolean ,
lazyLoadQueue? : LazyLoadQueue ,
group? : string ,
play? : boolean ,
onlyThumb? : boolean ,
2020-06-13 11:19:39 +03:00
emoji? : string ,
width? : number ,
height? : number ,
withThumb? : boolean ,
loop? : boolean
2020-05-26 18:04:06 +03:00
} ) {
2020-08-28 14:25:43 +03:00
const stickerType = doc . sticker ;
2020-04-14 18:46:31 +03:00
2020-06-13 11:19:39 +03:00
if ( ! width ) {
width = ! emoji ? 200 : undefined ;
}
if ( ! height ) {
height = ! emoji ? 200 : undefined ;
}
2020-04-14 18:46:31 +03:00
if ( stickerType == 2 && ! LottieLoader . loaded ) {
2020-05-30 09:44:54 +03:00
//LottieLoader.loadLottie();
LottieLoader . loadLottieWorkers ( ) ;
2020-04-14 18:46:31 +03:00
}
2020-02-11 22:35:57 +07:00
if ( ! stickerType ) {
2020-05-09 15:02:07 +03:00
console . error ( 'wrong doc for wrapSticker!' , doc ) ;
2020-05-23 08:31:18 +03:00
throw new Error ( 'wrong doc for wrapSticker!' ) ;
2020-02-11 22:35:57 +07:00
}
2020-05-23 08:31:18 +03:00
div . dataset . docID = doc . id ;
2020-02-11 22:35:57 +07:00
2020-05-09 15:02:07 +03:00
//console.log('wrap sticker', doc, div, onlyThumb);
2020-05-26 18:04:06 +03:00
const toneIndex = emoji ? getEmojiToneIndex ( emoji ) : - 1 ;
2020-02-11 22:35:57 +07:00
2020-09-17 22:33:23 +03:00
if ( doc . thumbs ? . length && ! div . firstElementChild && ( ! doc . downloaded || stickerType == 2 || onlyThumb ) && toneIndex <= 0 /* && doc.thumbs[0]._ != 'photoSizeEmpty' */ ) {
2020-08-28 14:25:43 +03:00
const thumb = doc . thumbs [ 0 ] ;
2020-04-08 18:46:43 +03:00
2020-04-19 00:55:20 +03:00
//console.log('wrap sticker', thumb, div);
2020-08-23 15:12:08 +03:00
let img : HTMLImageElement ;
const afterRender = ( ) = > {
if ( ! div . childElementCount ) {
div . append ( img ) ;
}
} ;
2020-09-17 22:33:23 +03:00
if ( 'url' in thumb ) {
img = new Image ( ) ;
renderImageFromUrl ( img , thumb . url , afterRender ) ;
} else if ( 'bytes' in thumb ) {
2020-08-23 15:12:08 +03:00
img = new Image ( ) ;
2020-05-23 08:31:18 +03:00
2020-09-17 22:33:23 +03:00
if ( ( ! isSafari || doc . pFlags . stickerThumbConverted || thumb . url ) /* && false */ ) {
2020-08-25 12:39:39 +03:00
renderImageFromUrl ( img , appPhotosManager . getPreviewURLFromThumb ( thumb , true ) , afterRender ) ;
2020-05-23 08:31:18 +03:00
} else {
2020-09-17 22:33:23 +03:00
webpWorkerController . convert ( doc . id , thumb . bytes as Uint8Array ) . then ( bytes = > {
2020-05-23 08:31:18 +03:00
thumb . bytes = bytes ;
2020-09-17 22:33:23 +03:00
doc . pFlags . stickerThumbConverted = true ;
2020-08-28 14:25:43 +03:00
if ( middleware && ! middleware ( ) ) return ;
2020-05-23 08:31:18 +03:00
if ( ! div . childElementCount ) {
2020-08-25 12:39:39 +03:00
renderImageFromUrl ( img , appPhotosManager . getPreviewURLFromThumb ( thumb , true ) , afterRender ) ;
2020-05-23 08:31:18 +03:00
}
2020-08-28 14:25:43 +03:00
} ) . catch ( ( ) = > { } ) ;
2020-05-23 08:31:18 +03:00
}
2020-08-28 14:25:43 +03:00
} else if ( stickerType == 2 && ( withThumb || onlyThumb ) ) {
2020-08-23 15:12:08 +03:00
img = new Image ( ) ;
2020-08-28 14:25:43 +03:00
2020-08-24 17:09:31 +03:00
const load = ( ) = > {
2020-06-16 23:48:08 +03:00
if ( div . childElementCount || ( middleware && ! middleware ( ) ) ) return ;
2020-08-28 14:25:43 +03:00
const r = ( ) = > {
if ( div . childElementCount || ( middleware && ! middleware ( ) ) ) return ;
renderImageFromUrl ( img , thumb . url , afterRender ) ;
} ;
if ( thumb . url ) {
r ( ) ;
return Promise . resolve ( ) ;
} else {
return appDocsManager . getThumbURL ( doc , thumb ) . promise . then ( r ) ;
}
2020-08-24 17:09:31 +03:00
} ;
2020-04-08 18:46:43 +03:00
2020-08-28 14:25:43 +03:00
if ( lazyLoadQueue && onlyThumb ) {
lazyLoadQueue . push ( { div , load } ) ;
return Promise . resolve ( ) ;
} else {
load ( ) ;
}
2020-02-11 22:35:57 +07:00
}
}
2020-05-23 08:31:18 +03:00
2020-08-28 14:25:43 +03:00
if ( onlyThumb ) { // for sticker panel
return Promise . resolve ( ) ;
2020-02-27 00:52:59 +07:00
}
2020-02-11 22:35:57 +07:00
2020-05-06 04:03:31 +03:00
let downloaded = doc . downloaded ;
2020-08-23 15:12:08 +03:00
let load = async ( ) = > {
2020-02-11 22:35:57 +07:00
if ( middleware && ! middleware ( ) ) return ;
2020-05-23 08:31:18 +03:00
2020-02-11 22:35:57 +07:00
if ( stickerType == 2 ) {
2020-08-23 15:12:08 +03:00
/ * i f ( d o c . i d = = ' 1 8 6 0 7 4 9 7 6 3 0 0 8 2 6 6 3 0 1 ' ) {
console . log ( 'loaded sticker:' , doc , div ) ;
} * /
2020-04-14 18:46:31 +03:00
2020-08-23 15:12:08 +03:00
//console.time('download sticker' + doc.id);
2020-05-26 18:04:06 +03:00
2020-08-23 15:12:08 +03:00
//appDocsManager.downloadDocNew(doc.id).promise.then(res => res.json()).then(async(json) => {
2020-08-25 12:39:39 +03:00
//fetch(doc.url).then(res => res.json()).then(async(json) => {
2020-09-20 01:38:00 +03:00
/* return */ await appDocsManager . downloadDocNew ( doc )
2020-08-28 14:25:43 +03:00
. then ( readBlobAsText )
2020-09-25 17:50:57 +03:00
//.then(JSON.parse)
2020-08-28 14:25:43 +03:00
. then ( async ( json ) = > {
2020-08-23 15:12:08 +03:00
//console.timeEnd('download sticker' + doc.id);
2020-09-20 01:38:00 +03:00
//console.log('loaded sticker:', doc, div/* , blob */);
2020-08-23 15:12:08 +03:00
if ( middleware && ! middleware ( ) ) return ;
2020-05-30 09:44:54 +03:00
let animation = await LottieLoader . loadAnimationWorker /* loadAnimation */ ( {
2020-02-11 22:35:57 +07:00
container : div ,
2020-06-13 11:19:39 +03:00
loop : loop && ! emoji ,
autoplay : play ,
2020-08-23 15:12:08 +03:00
animationData : json ,
2020-06-13 11:19:39 +03:00
width ,
height
2020-05-26 18:04:06 +03:00
} , group , toneIndex ) ;
2020-09-20 01:38:00 +03:00
//const deferred = deferredPromise<void>();
2020-08-28 14:25:43 +03:00
2020-06-13 11:19:39 +03:00
animation . addListener ( 'firstFrame' , ( ) = > {
2020-05-30 09:44:54 +03:00
if ( div . firstElementChild && div . firstElementChild . tagName == 'IMG' ) {
div . firstElementChild . remove ( ) ;
2020-06-13 11:19:39 +03:00
} else {
animation . canvas . classList . add ( 'fade-in' ) ;
2020-05-30 09:44:54 +03:00
}
2020-09-20 01:38:00 +03:00
//deferred.resolve();
2020-06-13 11:19:39 +03:00
} , true ) ;
2020-08-28 14:25:43 +03:00
2020-06-13 11:19:39 +03:00
if ( emoji ) {
div . addEventListener ( 'click' , ( ) = > {
let animation = LottieLoader . getAnimation ( div ) ;
2020-08-28 14:25:43 +03:00
2020-06-13 11:19:39 +03:00
if ( animation . paused ) {
animation . restart ( ) ;
2020-02-11 22:35:57 +07:00
}
2020-06-13 11:19:39 +03:00
} ) ;
}
2020-09-20 01:38:00 +03:00
//return deferred;
2020-08-28 14:25:43 +03:00
//await new Promise((resolve) => setTimeout(resolve, 5e3));
2020-02-11 22:35:57 +07:00
} ) ;
2020-08-23 15:12:08 +03:00
//console.timeEnd('render sticker' + doc.id);
2020-02-11 22:35:57 +07:00
} else if ( stickerType == 1 ) {
let img = new Image ( ) ;
2020-04-16 03:48:41 +03:00
2020-05-06 04:03:31 +03:00
if ( ! downloaded && ( ! div . firstElementChild || div . firstElementChild . tagName != 'IMG' ) ) {
2020-06-13 11:19:39 +03:00
img . classList . add ( 'fade-in-transition' ) ;
img . style . opacity = '0' ;
2020-05-06 04:03:31 +03:00
2020-05-18 04:21:58 +03:00
img . addEventListener ( 'load' , ( ) = > {
2020-08-23 15:12:08 +03:00
doc . downloaded = true ;
2020-05-06 04:03:31 +03:00
window . requestAnimationFrame ( ( ) = > {
img . style . opacity = '' ;
} ) ;
2020-05-18 04:21:58 +03:00
} ) ;
2020-05-06 04:03:31 +03:00
}
2020-08-28 14:25:43 +03:00
const r = ( ) = > {
if ( middleware && ! middleware ( ) ) return ;
2020-04-16 03:48:41 +03:00
2020-08-28 14:25:43 +03:00
renderImageFromUrl ( img , doc . url , ( ) = > {
if ( div . firstElementChild && div . firstElementChild != img ) {
div . firstElementChild . remove ( ) ;
}
div . append ( img ) ;
} ) ;
} ;
if ( doc . url ) r ( ) ;
else {
appDocsManager . downloadDocNew ( doc ) . then ( r ) ;
}
2020-02-11 22:35:57 +07:00
}
2020-08-23 15:12:08 +03:00
} ;
2020-02-11 22:35:57 +07:00
2020-05-30 09:44:54 +03:00
return lazyLoadQueue && ( ! doc . downloaded || stickerType == 2 ) ? ( lazyLoadQueue . push ( { div , load , wasSeen : group == 'chat' && stickerType != 2 } ) , Promise . resolve ( ) ) : load ( ) ;
2020-02-11 22:35:57 +07:00
}
2020-02-16 01:08:26 +07:00
2020-10-11 22:12:17 +03:00
export function wrapReply ( title : string , subtitle : string , message? : any ) {
const replyContainer = new ReplyContainer ( 'reply' ) ;
replyContainer . fill ( title , subtitle , message ) ;
2020-02-17 19:18:06 +07:00
/////////console.log('wrapReply', title, subtitle, media);
2020-10-11 22:12:17 +03:00
return replyContainer . container ;
2020-02-16 01:08:26 +07:00
}
2020-05-02 00:28:40 +03:00
export function wrapAlbum ( { groupID , attachmentDiv , middleware , uploading , lazyLoadQueue , isOut } : {
groupID : string ,
attachmentDiv : HTMLElement ,
middleware ? : ( ) = > boolean ,
lazyLoadQueue? : LazyLoadQueue ,
uploading? : boolean ,
isOut : boolean
} ) {
2020-09-17 22:33:23 +03:00
const items : { size : PhotoSize.photoSize , media : any , message : any } [ ] = [ ] ;
2020-05-02 00:28:40 +03:00
2020-09-01 22:59:49 +03:00
// !higher msgID will be the FIRST in album
2020-10-18 02:19:56 +03:00
const storage = appMessagesManager . getMidsByAlbum ( groupID ) ;
2020-09-01 22:59:49 +03:00
for ( const mid of storage ) {
const m = appMessagesManager . getMessage ( mid ) ;
const media = m . media . photo || m . media . document ;
2020-05-02 00:28:40 +03:00
2020-09-01 22:59:49 +03:00
const size : any = media . _ == 'photo' ? appPhotosManager . choosePhotoSize ( media , 480 , 480 ) : { w : media.w , h : media.h } ;
2020-05-02 00:28:40 +03:00
items . push ( { size , media , message : m } ) ;
}
2020-09-01 22:59:49 +03:00
const spacing = 2 ;
const layouter = new Layouter ( items . map ( i = > ( { w : i.size.w , h : i.size.h } ) ) , mediaSizes . active . album . width , 100 , spacing ) ;
const layout = layouter . layout ( ) ;
2020-06-13 11:19:39 +03:00
//console.log('layout:', layout, items.map(i => ({w: i.size.w, h: i.size.h})));
2020-05-02 00:28:40 +03:00
/ * l e t b o r d e r R a d i u s = w i n d o w . g e t C o m p u t e d S t y l e ( r e a l P a r e n t ) . g e t P r o p e r t y V a l u e ( ' b o r d e r - r a d i u s ' ) ;
let brSplitted = fillPropertyValue ( borderRadius ) ; * /
2020-09-01 22:59:49 +03:00
for ( const { geometry , sides } of layout ) {
const item = items . shift ( ) ;
2020-05-09 15:02:07 +03:00
if ( ! item ) {
console . error ( 'no item for layout!' ) ;
continue ;
}
2020-09-01 22:59:49 +03:00
const { size , media , message } = item ;
const div = document . createElement ( 'div' ) ;
2020-05-02 00:28:40 +03:00
div . classList . add ( 'album-item' ) ;
div . dataset . mid = message . mid ;
div . style . width = geometry . width + 'px' ;
div . style . height = geometry . height + 'px' ;
div . style . top = geometry . y + 'px' ;
div . style . left = geometry . x + 'px' ;
if ( sides & RectPart . Right ) {
attachmentDiv . style . width = geometry . width + geometry . x + 'px' ;
}
if ( sides & RectPart . Bottom ) {
attachmentDiv . style . height = geometry . height + geometry . y + 'px' ;
}
if ( sides & RectPart . Left && sides & RectPart . Top ) {
div . style . borderTopLeftRadius = 'inherit' ;
}
if ( sides & RectPart . Left && sides & RectPart . Bottom ) {
div . style . borderBottomLeftRadius = 'inherit' ;
}
if ( sides & RectPart . Right && sides & RectPart . Top ) {
div . style . borderTopRightRadius = 'inherit' ;
}
if ( sides & RectPart . Right && sides & RectPart . Bottom ) {
div . style . borderBottomRightRadius = 'inherit' ;
}
if ( media . _ == 'photo' ) {
wrapPhoto (
2020-08-25 19:26:49 +03:00
media ,
2020-05-02 00:28:40 +03:00
message ,
div ,
0 ,
0 ,
false ,
isOut ,
lazyLoadQueue ,
middleware ,
size
) ;
} else {
wrapVideo ( {
doc : message.media.document ,
container : div ,
message ,
boxWidth : 0 ,
boxHeight : 0 ,
withTail : false ,
isOut ,
lazyLoadQueue ,
middleware
} ) ;
}
// @ts-ignore
//div.style.backgroundColor = '#' + Math.floor(Math.random() * (2 ** 24 - 1)).toString(16).padStart(6, '0');
attachmentDiv . append ( div ) ;
}
}
2020-05-09 15:02:07 +03:00
2020-05-10 04:23:21 +03:00
export function wrapPoll ( pollID : string , mid : number ) {
2020-06-13 11:19:39 +03:00
const elem = new PollElement ( ) ;
2020-05-10 04:23:21 +03:00
elem . setAttribute ( 'poll-id' , pollID ) ;
elem . setAttribute ( 'message-id' , '' + mid ) ;
2020-05-09 15:02:07 +03:00
return elem ;
}