/* * https://github.com/morethanwords/tweb * Copyright (C) 2019-2021 Eduard Kuzmenko * https://github.com/morethanwords/tweb/blob/master/LICENSE */ import renderImageWithFadeIn from "../../helpers/dom/renderImageWithFadeIn"; import mediaSizes from "../../helpers/mediaSizes"; import { Message, PhotoSize } from "../../layer"; import { MyDocument } from "../../lib/appManagers/appDocsManager"; import { MyPhoto } from "../../lib/appManagers/appPhotosManager"; import rootScope from "../../lib/rootScope"; import LazyLoadQueue from "../lazyLoadQueue"; import ProgressivePreloader from "../preloader"; import blur from '../../helpers/blur'; import { AppManagers } from "../../lib/appManagers/managers"; import getStrippedThumbIfNeeded from "../../helpers/getStrippedThumbIfNeeded"; import setAttachmentSize from "../../helpers/setAttachmentSize"; import choosePhotoSize from "../../lib/appManagers/utils/photos/choosePhotoSize"; import type { ThumbCache } from "../../lib/storages/thumbs"; import appDownloadManager from "../../lib/appManagers/appDownloadManager"; export default async function wrapPhoto({photo, message, container, boxWidth, boxHeight, withTail, isOut, lazyLoadQueue, middleware, size, withoutPreloader, loadPromises, autoDownloadSize, noBlur, noThumb, noFadeIn, blurAfter, managers = rootScope.managers}: { photo: MyPhoto | MyDocument, message?: Message.message | Message.messageService, container: HTMLElement, boxWidth?: number, boxHeight?: number, withTail?: boolean, isOut?: boolean, lazyLoadQueue?: LazyLoadQueue, middleware?: () => boolean, size?: PhotoSize, withoutPreloader?: boolean, loadPromises?: Promise[], autoDownloadSize?: number, noBlur?: boolean, noThumb?: boolean, noFadeIn?: boolean, blurAfter?: boolean, managers?: AppManagers, }) { if(!((photo as MyPhoto).sizes || (photo as MyDocument).thumbs)) { if(boxWidth && boxHeight && !size && photo._ === 'document') { setAttachmentSize(photo, container, boxWidth, boxHeight, undefined, message); } return { loadPromises: { thumb: Promise.resolve(), full: Promise.resolve() }, images: { thumb: null, full: null }, preloader: null, aspecter: null }; } let noAutoDownload = autoDownloadSize === 0; if(!size) { if(boxWidth === undefined) boxWidth = mediaSizes.active.regular.width; if(boxHeight === undefined) boxHeight = mediaSizes.active.regular.height; } container.classList.add('media-container'); let aspecter = container; let isFit = true; let loadThumbPromise: Promise = Promise.resolve(); let thumbImage: HTMLImageElement | HTMLCanvasElement; let image: HTMLImageElement; let cacheContext: ThumbCache; const isGif = photo._ === 'document' && photo.mime_type === 'image/gif' && !size; // if(withTail) { // image = wrapMediaWithTail(photo, message, container, boxWidth, boxHeight, isOut); // } else { image = new Image(); if(boxWidth && boxHeight && !size) { // !album const set = setAttachmentSize(photo, container, boxWidth, boxHeight, undefined, message, undefined, isGif ? { _: 'photoSize', w: photo.w, h: photo.h, size: photo.size, type: 'full' } : undefined); size = set.photoSize; isFit = set.isFit; cacheContext = await managers.thumbsStorage.getCacheContext(photo, size.type); if(!isFit) { aspecter = document.createElement('div'); aspecter.classList.add('media-container-aspecter'); aspecter.style.width = set.size.width + 'px'; aspecter.style.height = set.size.height + 'px'; const gotThumb = getStrippedThumbIfNeeded(photo, cacheContext, !noBlur, true); if(gotThumb) { loadThumbPromise = gotThumb.loadPromise; const thumbImage = gotThumb.image; // local scope thumbImage.classList.add('media-photo'); container.append(thumbImage); } else { const res = await wrapPhoto({ container, message, photo, boxWidth: 0, boxHeight: 0, size, lazyLoadQueue, isOut, loadPromises, middleware, withoutPreloader, withTail, autoDownloadSize, noBlur, noThumb: true, blurAfter: true, managers //noFadeIn: true }); const thumbImage = res.images.full; thumbImage.classList.add('media-photo', 'thumbnail'); //container.append(thumbImage); } container.classList.add('media-container-fitted'); container.append(aspecter); } } else { if(!size) { size = choosePhotoSize(photo, boxWidth, boxHeight, true); } cacheContext = await managers.thumbsStorage.getCacheContext(photo, size?.type); } if(!noThumb) { const gotThumb = getStrippedThumbIfNeeded(photo, cacheContext, !noBlur); if(gotThumb) { loadThumbPromise = Promise.all([loadThumbPromise, gotThumb.loadPromise]); thumbImage = gotThumb.image; thumbImage.classList.add('media-photo'); aspecter.append(thumbImage); } } // } image.classList.add('media-photo'); //console.log('wrapPhoto downloaded:', photo, photo.downloaded, container); const needFadeIn = (thumbImage || !cacheContext.downloaded) && rootScope.settings.animationsEnabled && !noFadeIn; let preloader: ProgressivePreloader; const uploadingFileName = (message as Message.message)?.uploadingFileName; if(!withoutPreloader) { if(!cacheContext.downloaded || uploadingFileName) { preloader = new ProgressivePreloader({ attachMethod: 'prepend', isUpload: !!uploadingFileName }); } if(uploadingFileName) { // means upload preloader.attachPromise(appDownloadManager.getUpload(uploadingFileName)); preloader.attach(container); noAutoDownload = undefined; } } const getDownloadPromise = () => { // const promise = isGif && !size ? // managers.appDocsManager.downloadDoc(photo, /* undefined, */lazyLoadQueue?.queueId) : // managers.appPhotosManager.preloadPhoto(photo, size, lazyLoadQueue?.queueId, noAutoDownload); const haveToDownload = isGif && !size; const promise = appDownloadManager.downloadMediaURL({ media: photo, thumb: size, queueId: lazyLoadQueue?.queueId, onlyCache: haveToDownload ? undefined : noAutoDownload }); return promise; }; const renderOnLoad = (url: string) => { return renderImageWithFadeIn(container, image, url, needFadeIn, aspecter, thumbImage); }; const onLoad = async(url: string) => { if(middleware && !middleware()) return; if(blurAfter) { const result = blur(url, 12); return result.promise.then(() => { // image = result.canvas; return renderOnLoad(result.canvas.toDataURL()); }); } return renderOnLoad(url); }; let loadPromise: Promise; const canAttachPreloader = ( (size as PhotoSize.photoSize).w >= 150 && (size as PhotoSize.photoSize).h >= 150 ) || noAutoDownload; const load = async() => { if(noAutoDownload && !withoutPreloader && preloader) { preloader.construct(); preloader.setManual(); } const promise = getDownloadPromise(); const cacheContext = await managers.thumbsStorage.getCacheContext(photo, size?.type); if( preloader && !cacheContext.downloaded && !withoutPreloader && canAttachPreloader ) { preloader.attach(container, false, promise); } noAutoDownload = undefined; const renderPromise = promise.then(onLoad); renderPromise.catch(() => {}); return {download: promise, render: renderPromise}; }; if(preloader) { preloader.setDownloadFunction(load); } if(cacheContext.downloaded) { loadThumbPromise = loadPromise = (await load()).render; } else { if(!lazyLoadQueue) loadPromise = (await load()).render; /* else if(noAutoDownload) { preloader.construct(); preloader.setManual(); preloader.attach(container); } */ else lazyLoadQueue.push({div: container, load: () => load().then(({download}) => download)}); } if(loadPromises && loadThumbPromise) { loadPromises.push(loadThumbPromise); } const perf = performance.now(); await loadThumbPromise; const elapsedTime = performance.now() - perf; if(elapsedTime > 4) { console.log('wrapping photo thumb time', elapsedTime, photo, size); } return { loadPromises: { thumb: loadThumbPromise, full: loadPromise || Promise.resolve() }, images: { thumb: thumbImage, full: image }, preloader, aspecter }; }