Telegram Web K with changes to work inside I2P https://web.telegram.i2p/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

330 lines
12 KiB

/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import MEDIA_MIME_TYPES_SUPPORTED from '../../environment/mediaMimeTypesSupport';
import {CancellablePromise} from '../../helpers/cancellablePromise';
import {clearBadCharsAndTrim} from '../../helpers/cleanSearchText';
import {formatFullSentTime} from '../../helpers/date';
import {simulateClickEvent, attachClickEvent} from '../../helpers/dom/clickEvent';
import replaceContent from '../../helpers/dom/replaceContent';
import formatBytes from '../../helpers/formatBytes';
import {MediaSizeType} from '../../helpers/mediaSizes';
import noop from '../../helpers/noop';
import {Message, MessageMedia, WebPage} from '../../layer';
import {MyDocument} from '../../lib/appManagers/appDocsManager';
import appDownloadManager, {Progress} from '../../lib/appManagers/appDownloadManager';
import appImManager from '../../lib/appManagers/appImManager';
import {AppManagers} from '../../lib/appManagers/managers';
import getDownloadMediaDetails from '../../lib/appManagers/utils/download/getDownloadMediaDetails';
import choosePhotoSize from '../../lib/appManagers/utils/photos/choosePhotoSize';
import {joinElementsWith} from '../../lib/langPack';
import {MAX_FILE_SAVE_SIZE} from '../../lib/mtproto/mtproto_config';
import wrapPlainText from '../../lib/richTextProcessor/wrapPlainText';
import rootScope from '../../lib/rootScope';
import type {ThumbCache} from '../../lib/storages/thumbs';
import {MediaSearchContext} from '../appMediaPlaybackController';
import AudioElement from '../audio';
import LazyLoadQueue from '../lazyLoadQueue';
import {MiddleEllipsisElement} from '../middleEllipsis';
import ProgressivePreloader from '../preloader';
import wrapPhoto from './photo';
import wrapSenderToPeer from './senderToPeer';
import wrapSentTime from './sentTime';
rootScope.addEventListener('document_downloading', (docId) => {
const elements = Array.from(document.querySelectorAll(`.document[data-doc-id="${docId}"]`)) as HTMLElement[];
elements.forEach((element) => {
if(element.querySelector('.preloader-container.manual')) {
simulateClickEvent(element);
}
});
});
export default async function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showSender, searchContext, loadPromises, autoDownloadSize, lazyLoadQueue, sizeType, managers = rootScope.managers, cacheContext}: {
message: Message.message,
withTime?: boolean,
fontWeight?: number,
voiceAsMusic?: boolean,
showSender?: boolean,
searchContext?: MediaSearchContext,
loadPromises?: Promise<any>[],
autoDownloadSize?: number,
lazyLoadQueue?: LazyLoadQueue,
sizeType?: MediaSizeType,
managers?: AppManagers,
cacheContext?: ThumbCache
}): Promise<HTMLElement> {
if(!fontWeight) fontWeight = 500;
if(!sizeType) sizeType = '' as any;
const noAutoDownload = autoDownloadSize === 0;
const doc = ((message.media as MessageMedia.messageMediaDocument).document || ((message.media as MessageMedia.messageMediaWebPage).webpage as WebPage.webPage).document) as MyDocument;
const uploadFileName = message?.uploadingFileName;
if(doc.type === 'audio' || doc.type === 'voice' || doc.type === 'round') {
const audioElement = new AudioElement();
audioElement.withTime = withTime;
audioElement.message = message;
audioElement.noAutoDownload = noAutoDownload;
audioElement.lazyLoadQueue = lazyLoadQueue;
audioElement.loadPromises = loadPromises;
if(voiceAsMusic) audioElement.voiceAsMusic = voiceAsMusic;
if(searchContext) audioElement.searchContext = searchContext;
if(showSender) audioElement.showSender = showSender;
audioElement.dataset.fontWeight = '' + fontWeight;
audioElement.dataset.sizeType = sizeType;
await audioElement.render();
return audioElement;
}
const extSplitted = doc.file_name ? doc.file_name.split('.') : '';
let ext = '';
ext = extSplitted.length > 1 && Array.isArray(extSplitted) ?
clearBadCharsAndTrim(extSplitted.pop().split(' ', 1)[0].toLowerCase()) :
'file';
const docDiv = document.createElement('div');
docDiv.classList.add('document', `ext-${ext}`);
docDiv.dataset.docId = '' + doc.id;
(docDiv as any).doc = doc;
// return docDiv;
const icoDiv = document.createElement('div');
icoDiv.classList.add('document-ico');
let icoTextEl: HTMLElement;
const hadContext = !!cacheContext;
const getCacheContext = () => {
return hadContext ? cacheContext : managers.thumbsStorage.getCacheContext(doc);
};
cacheContext = await getCacheContext();
let hasThumb = false;
if((doc.thumbs?.length || (message.pFlags.is_outgoing && cacheContext.url && doc.type === 'photo'))/* && doc.mime_type !== 'image/gif' */) {
docDiv.classList.add('document-with-thumb');
hasThumb = true;
const imgs: (HTMLImageElement | HTMLCanvasElement)[] = [];
// ! WARNING, use thumbs for check when thumb will be generated for media
if(message.pFlags.is_outgoing && ['photo', 'video'].includes(doc.type)) {
icoDiv.innerHTML = `<img src="${cacheContext.url}">`;
imgs.push(icoDiv.firstElementChild as HTMLImageElement);
} else {
const perf = performance.now();
const wrapped = await wrapPhoto({
photo: doc,
message: null,
container: icoDiv,
boxWidth: 54,
boxHeight: 54,
loadPromises,
withoutPreloader: true,
lazyLoadQueue,
size: choosePhotoSize(doc, 54, 54, true),
managers
});
// console.log('was wrapping photo', performance.now() - perf);
icoDiv.style.width = icoDiv.style.height = '';
if(wrapped.images.thumb) imgs.push(wrapped.images.thumb);
if(wrapped.images.full) imgs.push(wrapped.images.full);
}
imgs.forEach((img) => img.classList.add('document-thumb'));
} else {
icoTextEl = document.createElement('span');
icoTextEl.classList.add('document-ico-text');
icoTextEl.innerText = ext;
icoDiv.append(icoTextEl);
}
// let fileName = stringMiddleOverflow(doc.file_name || 'Unknown.file', 26);
const fileName = doc.file_name ? wrapPlainText(doc.file_name) : 'Unknown.file';
const descriptionEl = document.createElement('div');
descriptionEl.classList.add('document-description');
const bytesContainer = document.createElement('span');
const bytesEl = formatBytes(doc.size);
const bytesJoiner = ' / ';
const descriptionParts: (HTMLElement | string | DocumentFragment)[] = [bytesEl];
if(withTime) {
descriptionParts.push(formatFullSentTime(message.date));
}
if(showSender) {
descriptionParts.push(await wrapSenderToPeer(message));
}
if(!withTime && !showSender) {
const b = document.createElement('span');
const bytesMaxEl = formatBytes(doc.size);
b.append(bytesJoiner, bytesMaxEl);
b.style.visibility = 'hidden';
descriptionParts.push(b);
}
docDiv.innerHTML = `
${(cacheContext.downloaded && !uploadFileName) || !message.mid || !hasThumb ? '' : `<div class="document-download"></div>`}
<div class="document-name"></div>
<div class="document-size"></div>
`;
const nameDiv = docDiv.querySelector('.document-name') as HTMLElement;
const middleEllipsisEl = new MiddleEllipsisElement();
middleEllipsisEl.dataset.fontWeight = '' + fontWeight;
middleEllipsisEl.dataset.sizeType = sizeType;
middleEllipsisEl.textContent = fileName;
// setInnerHTML(middleEllipsisEl, fileName);
nameDiv.append(middleEllipsisEl);
if(showSender) {
nameDiv.append(wrapSentTime(message));
}
const sizeDiv = docDiv.querySelector('.document-size') as HTMLElement;
bytesContainer.append(...joinElementsWith(descriptionParts, ' · '));
sizeDiv.append(bytesContainer);
docDiv.prepend(icoDiv);
if(!uploadFileName && message.pFlags.is_outgoing && !message.mid) {
return docDiv;
}
let downloadDiv: HTMLElement, preloader: ProgressivePreloader = null;
const onLoad = () => {
if(doc.size <= MAX_FILE_SAVE_SIZE) {
docDiv.classList.add('downloaded');
}
docDiv.classList.remove('downloading');
if(downloadDiv) {
if(downloadDiv !== icoDiv) {
const _downloadDiv = downloadDiv;
setTimeout(() => {
_downloadDiv.remove();
}, 200);
}
downloadDiv = null;
}
if(preloader) {
preloader = null;
}
};
const addByteProgress = (promise: CancellablePromise<any>) => {
docDiv.classList.add('downloading');
const sizeContainer = document.createElement('span');
const _bytesContainer = formatBytes(doc.size);
sizeContainer.style.position = 'absolute';
sizeContainer.style.left = '0';
promise.then(onLoad, noop).finally(() => {
// sizeContainer.replaceWith(bytesContainer);
bytesContainer.style.visibility = '';
sizeContainer.remove();
// b && b.classList.remove('hide');
});
// b && b.classList.add('hide');
const format = (bytes: number) => formatBytes(bytes);
let d = format(0);
bytesContainer.style.visibility = 'hidden';
// bytesContainer.replaceWith(sizeContainer);
sizeContainer.append(d, bytesJoiner, _bytesContainer);
bytesContainer.parentElement.append(sizeContainer);
promise.addNotifyListener((progress: Progress) => {
const _d = format(progress.done);
d.replaceWith(_d);
d = _d;
});
};
// ! DO NOT USE ASYNC/AWAIT HERE ! SAFARI WON'T LET DOWNLOAD THE FILE BECAUSE OF ASYNC
const load = (e?: Event) => {
const save = !e || e.isTrusted;
const doc = (docDiv as any).doc;
// const doc = await managers.appDocsManager.getDoc(docDiv.dataset.docId);
let download: CancellablePromise<any>;
const queueId = appImManager.chat.bubbles ? appImManager.chat.bubbles.lazyLoadQueue.queueId : undefined;
if(!save) {
download = appDownloadManager.downloadToDisc({media: doc, queueId}, true);
} else if(doc.type === 'pdf') {
const canOpenAfter = /* managers.appDocsManager.downloading.has(doc.id) || */!preloader || preloader.detached;
download = appDownloadManager.downloadMediaURL({media: doc, queueId});
if(canOpenAfter) {
download.then(() => {
setTimeout(async() => { // wait for preloader animation end
const url = (await getCacheContext()).url;
window.open(url);
}, rootScope.settings.animationsEnabled ? 250 : 0);
});
}
} else if(MEDIA_MIME_TYPES_SUPPORTED.has(doc.mime_type) && doc.thumbs?.length) {
download = appDownloadManager.downloadMediaURL({media: doc, queueId});
} else {
download = appDownloadManager.downloadToDisc({media: doc, queueId});
}
download.catch(() => {
docDiv.classList.remove('downloading');
});
if(downloadDiv) {
preloader.attach(downloadDiv, true, download);
addByteProgress(download);
}
};
const {fileName: downloadFileName} = getDownloadMediaDetails({media: doc, downloadId: '1'});
if(await managers.apiFileManager.isDownloading(downloadFileName)) {
downloadDiv = docDiv.querySelector('.document-download') || icoDiv;
const promise = appDownloadManager.downloadToDisc({media: doc}, true);
preloader = new ProgressivePreloader();
preloader.attach(downloadDiv, false, promise);
preloader.setDownloadFunction(load);
addByteProgress(promise);
} else if(!cacheContext.downloaded || uploadFileName) {
downloadDiv = docDiv.querySelector('.document-download') || icoDiv;
preloader = new ProgressivePreloader({
isUpload: !!uploadFileName
});
if(!uploadFileName) {
preloader.construct();
preloader.setManual();
preloader.attach(downloadDiv);
preloader.setDownloadFunction(load);
if(autoDownloadSize !== undefined && autoDownloadSize >= doc.size) {
simulateClickEvent(preloader.preloader);
}
} else {
const uploadPromise = appDownloadManager.getUpload(uploadFileName);
preloader.attachPromise(uploadPromise);
preloader.attach(downloadDiv);
addByteProgress(uploadPromise);
}
}
attachClickEvent(docDiv, (e) => {
if(preloader) {
preloader.onClick(e);
} else {
load(e);
}
});
return docDiv;
}