Browse Source

Open documents in media viewer

master
Eduard Kuzmenko 3 years ago
parent
commit
728c4932ae
  1. 91
      src/components/appMediaViewer.ts
  2. 1
      src/components/audio.ts
  3. 61
      src/components/chat/bubbles.ts
  4. 7
      src/components/chat/contextMenu.ts
  5. 22
      src/components/wrappers.ts
  6. 10
      src/lib/appManagers/appDocsManager.ts
  7. 14
      src/lib/appManagers/appMessagesManager.ts
  8. 35
      src/lib/appManagers/appPhotosManager.ts
  9. 6
      src/lib/rootScope.ts

91
src/components/appMediaViewer.ts

@ -11,7 +11,7 @@ import { isMobileSafari, isSafari } from "../helpers/userAgent";
import appDocsManager, { MyDocument } from "../lib/appManagers/appDocsManager"; import appDocsManager, { MyDocument } from "../lib/appManagers/appDocsManager";
import appImManager from "../lib/appManagers/appImManager"; import appImManager from "../lib/appManagers/appImManager";
import appMessagesManager from "../lib/appManagers/appMessagesManager"; import appMessagesManager from "../lib/appManagers/appMessagesManager";
import appPhotosManager from "../lib/appManagers/appPhotosManager"; import appPhotosManager, { MyPhoto } from "../lib/appManagers/appPhotosManager";
import { logger } from "../lib/logger"; import { logger } from "../lib/logger";
import VideoPlayer from "../lib/mediaPlayer"; import VideoPlayer from "../lib/mediaPlayer";
import { RichTextProcessor } from "../lib/richtextprocessor"; import { RichTextProcessor } from "../lib/richtextprocessor";
@ -36,7 +36,7 @@ import { Message } from "../layer";
import { forEachReverse } from "../helpers/array"; import { forEachReverse } from "../helpers/array";
import AppSharedMediaTab from "./sidebarRight/tabs/sharedMedia"; import AppSharedMediaTab from "./sidebarRight/tabs/sharedMedia";
import findUpClassName from "../helpers/dom/findUpClassName"; import findUpClassName from "../helpers/dom/findUpClassName";
import renderImageFromUrl from "../helpers/dom/renderImageFromUrl"; import renderImageFromUrl, { renderImageFromUrlPromise } from "../helpers/dom/renderImageFromUrl";
import getVisibleRect from "../helpers/dom/getVisibleRect"; import getVisibleRect from "../helpers/dom/getVisibleRect";
import appDownloadManager from "../lib/appManagers/appDownloadManager"; import appDownloadManager from "../lib/appManagers/appDownloadManager";
import { cancelEvent } from "../helpers/dom/cancelEvent"; import { cancelEvent } from "../helpers/dom/cancelEvent";
@ -48,6 +48,7 @@ import appMessagesIdsManager from "../lib/appManagers/appMessagesIdsManager";
import I18n, { i18n } from "../lib/langPack"; import I18n, { i18n } from "../lib/langPack";
import { capitalizeFirstLetter } from "../helpers/string"; import { capitalizeFirstLetter } from "../helpers/string";
import setInnerHTML from "../helpers/dom/setInnerHTML"; import setInnerHTML from "../helpers/dom/setInnerHTML";
import { doubleRaf, fastRaf } from "../helpers/schedulers";
// TODO: масштабирование картинок (не SVG) при ресайзе, и правильный возврат на исходную позицию // TODO: масштабирование картинок (не SVG) при ресайзе, и правильный возврат на исходную позицию
// TODO: картинки "обрезаются" если возвращаются или появляются с места, где есть их перекрытие (топбар, поле ввода) // TODO: картинки "обрезаются" если возвращаются или появляются с места, где есть их перекрытие (топбар, поле ввода)
@ -511,8 +512,15 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
const deferred = this.setMoverAnimationPromise = deferredPromise<void>(); const deferred = this.setMoverAnimationPromise = deferredPromise<void>();
const ret = {onAnimationEnd: deferred}; const ret = {onAnimationEnd: deferred};
this.setMoverAnimationPromise.then(() => { const timeout = setTimeout(() => {
if(!deferred.isFulfilled && !deferred.isRejected) {
deferred.resolve();
}
}, 1000);
this.setMoverAnimationPromise.finally(() => {
this.setMoverAnimationPromise = null; this.setMoverAnimationPromise = null;
clearTimeout(timeout);
}); });
if(!closing) { if(!closing) {
@ -520,9 +528,11 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
let src: string; let src: string;
if(target.tagName === 'DIV' || target.tagName === 'AVATAR-ELEMENT') { // useContainerAsTarget if(target.tagName === 'DIV' || target.tagName === 'AVATAR-ELEMENT') { // useContainerAsTarget
if(target.firstElementChild) { const images = Array.from(target.querySelectorAll('img')) as HTMLImageElement[];
const image = images.pop();
if(image) {
mediaElement = new Image(); mediaElement = new Image();
src = (target.firstElementChild as HTMLImageElement).src; src = image.src;
mover.append(mediaElement); mover.append(mediaElement);
} }
/* mediaElement = new Image(); /* mediaElement = new Image();
@ -532,8 +542,8 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
mediaElement = new Image(); mediaElement = new Image();
src = target.src; src = target.src;
} else if(target instanceof HTMLVideoElement) { } else if(target instanceof HTMLVideoElement) {
const video = mediaElement = document.createElement('video'); mediaElement = document.createElement('video');
video.src = target?.src; mediaElement.src = target.src;
} else if(target instanceof SVGSVGElement) { } else if(target instanceof SVGSVGElement) {
const clipId = target.dataset.clipId; const clipId = target.dataset.clipId;
const newClipId = clipId + '-mv'; const newClipId = clipId + '-mv';
@ -605,13 +615,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
} }
if(src) { if(src) {
await new Promise((resolve, reject) => { await renderImageFromUrlPromise(mediaElement, src);
mediaElement.addEventListener('load', resolve);
if(src) {
mediaElement.src = src;
}
});
} }
}/* else if(mediaElement instanceof HTMLVideoElement && mediaElement.firstElementChild && ((mediaElement.firstElementChild as HTMLSourceElement).src || src)) { }/* else if(mediaElement instanceof HTMLVideoElement && mediaElement.firstElementChild && ((mediaElement.firstElementChild as HTMLSourceElement).src || src)) {
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
@ -625,7 +629,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
mover.style.display = ''; mover.style.display = '';
window.requestAnimationFrame(() => { fastRaf(() => {
mover.classList.add(wasActive ? 'moving' : 'active'); mover.classList.add(wasActive ? 'moving' : 'active');
}); });
} else { } else {
@ -674,7 +678,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
//await new Promise((resolve) => setTimeout(resolve, 0)); //await new Promise((resolve) => setTimeout(resolve, 0));
//await new Promise((resolve) => window.requestAnimationFrame(resolve)); //await new Promise((resolve) => window.requestAnimationFrame(resolve));
// * одного RAF'а недостаточно, иногда анимация с одним не срабатывает (преимущественно на мобильных) // * одного RAF'а недостаточно, иногда анимация с одним не срабатывает (преимущественно на мобильных)
await new Promise((resolve) => window.requestAnimationFrame(() => window.requestAnimationFrame(resolve))); await doubleRaf();
// чтобы проверить установленную позицию - раскомментировать // чтобы проверить установленную позицию - раскомментировать
// throw ''; // throw '';
@ -779,7 +783,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
else d = generatePathData(9 / scaleX * progress, 0, width/* 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); path.setAttributeNS(null, 'd', d);
if(diff < delay) window.requestAnimationFrame(step); if(diff < delay) fastRaf(step);
}; };
//window.requestAnimationFrame(step); //window.requestAnimationFrame(step);
@ -829,6 +833,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
protected setNewMover() { protected setNewMover() {
const newMover = document.createElement('div'); const newMover = document.createElement('div');
newMover.classList.add('media-viewer-mover'); newMover.classList.add('media-viewer-mover');
newMover.style.display = 'none';
if(this.content.mover) { if(this.content.mover) {
const oldMover = this.content.mover; const oldMover = this.content.mover;
@ -923,7 +928,8 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
this.setAuthorInfo(fromId, timestamp); this.setAuthorInfo(fromId, timestamp);
const isVideo = (media as MyDocument).type === 'video' || (media as MyDocument).type === 'gif'; const isDocument = media._ === 'document';
const isVideo = media.mime_type && ((['video', 'gif'] as MyDocument['type'][]).includes((media as MyDocument).type) || (media as MyDocument).mime_type.indexOf('video/') === 0);
if(this.isFirstOpen) { if(this.isFirstOpen) {
//this.targetContainer = targetContainer; //this.targetContainer = targetContainer;
@ -1014,7 +1020,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
} }
const maxHeight = windowH - 120 - padding; const maxHeight = windowH - 120 - padding;
let thumbPromise: Promise<any> = Promise.resolve(); let thumbPromise: Promise<any> = Promise.resolve();
const size = appPhotosManager.setAttachmentSize(media, container, maxWidth, maxHeight, mediaSizes.isMobile ? false : true).photoSize; const size = appPhotosManager.setAttachmentSize(media, container, maxWidth, maxHeight, mediaSizes.isMobile ? false : true, undefined, media._ === 'document' && media.w && media.h).photoSize;
if(useContainerAsTarget) { if(useContainerAsTarget) {
const cacheContext = appDownloadManager.getCacheContext(media, size.type); const cacheContext = appDownloadManager.getCacheContext(media, size.type);
let img: HTMLImageElement; let img: HTMLImageElement;
@ -1040,7 +1046,8 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
target = target.querySelector('img, video') || target; target = target.querySelector('img, video') || target;
} */ } */
const preloader = media.supportsStreaming ? this.preloaderStreamable : this.preloader; const supportsStreaming = media.supportsStreaming;
const preloader = supportsStreaming ? this.preloaderStreamable : this.preloader;
let setMoverPromise: Promise<void>; let setMoverPromise: Promise<void>;
if(isVideo) { if(isVideo) {
@ -1073,6 +1080,16 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
} }
}); });
video.addEventListener('error', (err) => {
if(video.error.code !== 4) {
this.log.error("Error " + video.error.code + "; details: " + video.error.message);
}
if(preloader) {
preloader.detach();
}
}, {once: true});
if(isSafari) { if(isSafari) {
// test stream // test stream
// video.controls = true; // video.controls = true;
@ -1107,7 +1124,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
return; return;
} }
const player = new VideoPlayer(video, true, media.supportsStreaming); const player = new VideoPlayer(video, true, supportsStreaming);
player.addEventListener('toggleControls', (show) => { player.addEventListener('toggleControls', (show) => {
this.wholeDiv.classList.toggle('has-video-controls', show); this.wholeDiv.classList.toggle('has-video-controls', show);
}); });
@ -1118,7 +1135,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
} }
}; };
if(media.supportsStreaming) { if(supportsStreaming) {
onAnimationEnd.then(() => { onAnimationEnd.then(() => {
if(video.readyState < video.HAVE_FUTURE_DATA) { if(video.readyState < video.HAVE_FUTURE_DATA) {
preloader.attach(mover, true); preloader.attach(mover, true);
@ -1158,9 +1175,9 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
//if(!video.src || media.url !== video.src) { //if(!video.src || media.url !== video.src) {
const load = () => { const load = () => {
const cacheContext = appDownloadManager.getCacheContext(media); const cacheContext = appDownloadManager.getCacheContext(media);
const promise = media.supportsStreaming ? Promise.resolve() : appDocsManager.downloadDoc(media); const promise: Promise<any> = supportsStreaming ? Promise.resolve() : appDocsManager.downloadDoc(media);
if(!media.supportsStreaming) { if(!supportsStreaming) {
onAnimationEnd.then(() => { onAnimationEnd.then(() => {
if(!cacheContext.url) { if(!cacheContext.url) {
preloader.attach(mover, true, promise); preloader.attach(mover, true, promise);
@ -1168,7 +1185,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
}); });
} }
(promise as Promise<any>).then(async() => { Promise.all([promise, onAnimationEnd]).then(async() => {
if(this.tempId !== tempId) { if(this.tempId !== tempId) {
this.log.warn('media viewer changed video'); this.log.warn('media viewer changed video');
return; return;
@ -1204,7 +1221,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
const load = () => { const load = () => {
const cacheContext = appDownloadManager.getCacheContext(media, size.type); const cacheContext = appDownloadManager.getCacheContext(media, size.type);
const cancellablePromise = appPhotosManager.preloadPhoto(media.id, size); const cancellablePromise = isDocument ? appDocsManager.downloadDoc(media) : appPhotosManager.preloadPhoto(media, size);
onAnimationEnd.then(() => { onAnimationEnd.then(() => {
if(!cacheContext.url) { if(!cacheContext.url) {
@ -1247,7 +1264,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
this.updateMediaSource(target, url, 'img'); this.updateMediaSource(target, url, 'img');
if(haveImage) { if(haveImage) {
window.requestAnimationFrame(() => { fastRaf(() => {
haveImage.remove(); haveImage.remove();
}); });
} }
@ -1477,12 +1494,16 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
} }
const method: any = older ? value.history.forEach.bind(value.history) : forEachReverse.bind(null, value.history); const method: any = older ? value.history.forEach.bind(value.history) : forEachReverse.bind(null, value.history);
const isForDocument = this.searchContext.inputFilter === 'inputMessagesFilterDocument';
method((message: Message.message) => { method((message: Message.message) => {
const {mid, peerId} = message; const {mid, peerId} = message;
const media = this.getMediaFromMessage(message); const media: MyPhoto | MyDocument = appMessagesManager.getMediaFromMessage(message);
if(!media) return; if(!media) return;
//if(media._ === 'document' && media.type !== 'video') return;
if(isForDocument && !AppMediaViewer.isMediaCompatibleForDocumentViewer(media)) {
return;
}
const t = {element: null as HTMLElement, mid, peerId}; const t = {element: null as HTMLElement, mid, peerId};
if(older) { if(older) {
@ -1507,12 +1528,6 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
return promise; return promise;
}; };
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));
}
private setCaption(message: Message.message) { private setCaption(message: Message.message) {
const caption = message.message; const caption = message.message;
let html = ''; let html = '';
@ -1548,7 +1563,7 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
const mid = message.mid; const mid = message.mid;
const fromId = message.fromId; const fromId = message.fromId;
const media = this.getMediaFromMessage(message); const media = appMessagesManager.getMediaFromMessage(message);
this.buttons.forward.classList.toggle('hide', message._ === 'messageService'); this.buttons.forward.classList.toggle('hide', message._ === 'messageService');
@ -1559,6 +1574,10 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
return promise; return promise;
} }
public static isMediaCompatibleForDocumentViewer(media: MyPhoto | MyDocument) {
return media._ === 'photo' || (['photo', 'video', 'gif'].includes(media.type) || media.mime_type.indexOf('video/') === 0);
}
} }
type AppMediaViewerAvatarTargetType = {element: HTMLElement, photoId: string}; type AppMediaViewerAvatarTargetType = {element: HTMLElement, photoId: string};

1
src/components/audio.ts

@ -216,6 +216,7 @@ function wrapVoiceMessage(audioEl: AudioElement) {
}); });
progress.addEventListener('mousedown', (e) => { progress.addEventListener('mousedown', (e) => {
e.preventDefault(); e.preventDefault();
if(e.button !== 1) return;
if(!audio.paused) { if(!audio.paused) {
audio.pause(); audio.pause();
} }

61
src/components/chat/bubbles.ts

@ -955,39 +955,63 @@ export default class ChatBubbles {
return; return;
} }
const documentDiv = findUpClassName(target, 'document-with-thumb');
if((target.tagName === 'IMG' && !target.classList.contains('emoji') && !target.classList.contains('document-thumb')) if((target.tagName === 'IMG' && !target.classList.contains('emoji') && !target.classList.contains('document-thumb'))
|| target.classList.contains('album-item') || target.classList.contains('album-item')
|| isVideoComponentElement || isVideoComponentElement
|| (target.tagName === 'VIDEO' && !bubble.classList.contains('round'))) { || (target.tagName === 'VIDEO' && !bubble.classList.contains('round'))
let messageId = +findUpClassName(target, 'album-item')?.dataset.mid || +bubble.dataset.mid; || (documentDiv && !documentDiv.querySelector('.preloader-container'))) {
let message = this.chat.getMessage(messageId); const groupedItem = findUpClassName(target, 'album-item') || findUpClassName(target, 'document-container');
const messageId = +(groupedItem || bubble).dataset.mid;
const message = this.chat.getMessage(messageId);
if(!message) { if(!message) {
this.log.warn('no message by messageId:', messageId); this.log.warn('no message by messageId:', messageId);
return; return;
} }
let targets: {element: HTMLElement, mid: number, peerId: number}[] = []; const f = documentDiv ? (media: any) => {
let ids = Object.keys(this.bubbles).map(k => +k).filter(id => { return AppMediaViewer.isMediaCompatibleForDocumentViewer(media);
} : (media: any) => {
return media._ === 'photo' || ['video', 'gif'].includes(media.type);
};
const targets: {element: HTMLElement, mid: number, peerId: number}[] = [];
const ids = Object.keys(this.bubbles).map(k => +k).filter(id => {
//if(!this.scrollable.visibleElements.find(e => e.element === this.bubbles[id])) return false; //if(!this.scrollable.visibleElements.find(e => e.element === this.bubbles[id])) return false;
let message = this.chat.getMessage(id); const message = this.chat.getMessage(id);
const media = this.appMessagesManager.getMediaFromMessage(message);
return message.media && (message.media.photo || (message.media.document && (message.media.document.type === 'video' || message.media.document.type === 'gif')) || (message.media.webpage && (message.media.webpage.document || message.media.webpage.photo))); return media && f(media);
}).sort((a, b) => a - b); }).sort((a, b) => a - b);
ids.forEach(id => { ids.forEach(id => {
let withTail = this.bubbles[id].classList.contains('with-media-tail'); let selector: string;
let str = '.album-item video, .album-item img, .preview video, .preview img, '; if(documentDiv) {
selector = '.document-container';
} else {
const withTail = this.bubbles[id].classList.contains('with-media-tail');
selector = '.album-item video, .album-item img, .preview video, .preview img, ';
if(withTail) { if(withTail) {
str += '.bubble__media-container'; selector += '.bubble__media-container';
} else { } else {
str += '.attachment video, .attachment img'; selector += '.attachment video, .attachment img';
}
} }
const hasAspecter = !!this.bubbles[id].querySelector('.media-container-aspecter'); const elements = Array.from(this.bubbles[id].querySelectorAll(selector)) as HTMLElement[];
let elements = this.bubbles[id].querySelectorAll(str) as NodeListOf<HTMLElement>;
const parents: Set<HTMLElement> = new Set(); const parents: Set<HTMLElement> = new Set();
Array.from(elements).forEach((element: HTMLElement) => { if(documentDiv) {
elements.forEach((element) => {
targets.push({
element: element.querySelector('.document-ico'),
mid: +element.dataset.mid,
peerId: this.peerId
});
});
} else {
const hasAspecter = !!this.bubbles[id].querySelector('.media-container-aspecter');
elements.forEach((element) => {
if(hasAspecter && !findUpClassName(element, 'media-container-aspecter')) return; if(hasAspecter && !findUpClassName(element, 'media-container-aspecter')) return;
let albumItem = findUpClassName(element, 'album-item'); let albumItem = findUpClassName(element, 'album-item');
const parent = albumItem || element.parentElement; const parent = albumItem || element.parentElement;
@ -995,10 +1019,11 @@ export default class ChatBubbles {
parents.add(parent); parents.add(parent);
targets.push({ targets.push({
element, element,
mid: +albumItem?.dataset.mid || id, mid: albumItem ? +albumItem.dataset.mid : id,
peerId: this.peerId peerId: this.peerId
}); });
}); });
}
}); });
targets.sort((a, b) => a.mid - b.mid); targets.sort((a, b) => a.mid - b.mid);
@ -1018,7 +1043,7 @@ export default class ChatBubbles {
.setSearchContext({ .setSearchContext({
threadId: this.chat.threadId, threadId: this.chat.threadId,
peerId: this.peerId, peerId: this.peerId,
inputFilter: 'inputMessagesFilterPhotoVideo' inputFilter: documentDiv ? 'inputMessagesFilterDocument' : 'inputMessagesFilterPhotoVideo'
}) })
.openMedia(message, targets[idx].element, 0, true, targets.slice(0, idx), targets.slice(idx + 1)); .openMedia(message, targets[idx].element, 0, true, targets.slice(0, idx), targets.slice(idx + 1));
@ -2760,7 +2785,7 @@ export default class ChatBubbles {
</div>`; </div>`;
const avatarElem = new AvatarElement(); const avatarElem = new AvatarElement();
//avatarElem.lazyLoadQueue = this.lazyLoadQueue; avatarElem.lazyLoadQueue = this.lazyLoadQueue;
avatarElem.setAttribute('peer', '' + message.media.user_id); avatarElem.setAttribute('peer', '' + message.media.user_id);
avatarElem.classList.add('contact-avatar', 'avatar-54'); avatarElem.classList.add('contact-avatar', 'avatar-54');
@ -2901,7 +2926,7 @@ export default class ChatBubbles {
const needAvatar = this.chat.isAnyGroup() && !isOut; const needAvatar = this.chat.isAnyGroup() && !isOut;
if(needAvatar) { if(needAvatar) {
let avatarElem = new AvatarElement(); let avatarElem = new AvatarElement();
//avatarElem.lazyLoadQueue = this.lazyLoadQueue; avatarElem.lazyLoadQueue = this.lazyLoadQueue;
avatarElem.classList.add('user-avatar', 'avatar-40'); avatarElem.classList.add('user-avatar', 'avatar-40');
avatarElem.loadPromises = loadPromises; avatarElem.loadPromises = loadPromises;

7
src/components/chat/contextMenu.ts

@ -283,7 +283,12 @@ export default class ChatContextMenu {
} }
const doc: MyDocument = this.message.media?.document; const doc: MyDocument = this.message.media?.document;
return doc && doc.type && !(['gif', 'photo', 'video', 'sticker'] as MyDocument['type'][]).includes(doc.type); if(!doc) return false;
let hasTarget = !!isTouchSupported;
const isGoodType = !doc.type || !(['gif', 'video', 'sticker'] as MyDocument['type'][]).includes(doc.type);
if(isGoodType) hasTarget = hasTarget || !!findUpClassName(this.target, 'document') || !!findUpClassName(this.target, 'audio');
return isGoodType && hasTarget;
} }
}, { }, {
icon: 'checkretract', icon: 'checkretract',

22
src/components/wrappers.ts

@ -446,6 +446,15 @@ export const formatDate = (timestamp: number, monthShort = false, withYear = tru
return str + ' at ' + date.getHours() + ':' + ('0' + date.getMinutes()).slice(-2); return str + ' at ' + date.getHours() + ':' + ('0' + date.getMinutes()).slice(-2);
}; };
rootScope.addEventListener('download_start', (docId) => {
const elements = Array.from(document.querySelectorAll(`.document[data-doc-id="${docId}"]`)) as HTMLElement[];
elements.forEach(element => {
if(element.querySelector('.preloader-container.manual')) {
element.click();
}
});
});
export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showSender, searchContext, loadPromises, noAutoDownload, lazyLoadQueue}: { export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showSender, searchContext, loadPromises, noAutoDownload, lazyLoadQueue}: {
message: any, message: any,
withTime?: boolean, withTime?: boolean,
@ -569,14 +578,17 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
const load = () => { const load = () => {
const doc = appDocsManager.getDoc(docDiv.dataset.docId); const doc = appDocsManager.getDoc(docDiv.dataset.docId);
let download: DownloadBlob; let download: DownloadBlob;
const queueId = appImManager.chat.bubbles ? appImManager.chat.bubbles.lazyLoadQueue.queueId : undefined;
if(doc.type === 'pdf') { if(doc.type === 'pdf') {
download = appDocsManager.downloadDoc(doc, appImManager.chat.bubbles ? appImManager.chat.bubbles.lazyLoadQueue.queueId : 0); download = appDocsManager.downloadDoc(doc, queueId);
download.then(() => { download.then(() => {
const cacheContext = appDownloadManager.getCacheContext(doc); const cacheContext = appDownloadManager.getCacheContext(doc);
window.open(cacheContext.url); window.open(cacheContext.url);
}); });
} else if(doc.type === 'photo' || doc.type === 'video' || doc.mime_type.indexOf('video/') === 0) {
download = appDocsManager.downloadDoc(doc, queueId);
} else { } else {
download = appDocsManager.saveDocFile(doc, appImManager.chat.bubbles ? appImManager.chat.bubbles.lazyLoadQueue.queueId : 0); download = appDocsManager.saveDocFile(doc, queueId);
} }
if(downloadDiv) { if(downloadDiv) {
@ -587,7 +599,11 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
return {download}; return {download};
}; };
if(!(cacheContext.downloaded && !uploading)) { if(appDocsManager.downloading.has(doc.id)) {
downloadDiv = docDiv.querySelector('.document-download');
preloader = new ProgressivePreloader();
preloader.attach(downloadDiv, false, appDocsManager.downloading.get(doc.id));
} else if(!(cacheContext.downloaded && !uploading)) {
downloadDiv = docDiv.querySelector('.document-download'); downloadDiv = docDiv.querySelector('.document-download');
preloader = message.media.preloader as ProgressivePreloader; preloader = message.media.preloader as ProgressivePreloader;

10
src/lib/appManagers/appDocsManager.ts

@ -22,6 +22,7 @@ import blur from '../../helpers/blur';
import apiManager from '../mtproto/mtprotoworker'; import apiManager from '../mtproto/mtprotoworker';
import { MOUNT_CLASS_TO } from '../../config/debug'; import { MOUNT_CLASS_TO } from '../../config/debug';
import { getFullDate } from '../../helpers/date'; import { getFullDate } from '../../helpers/date';
import rootScope from '../rootScope';
export type MyDocument = Document.document; export type MyDocument = Document.document;
@ -30,6 +31,7 @@ export type MyDocument = Document.document;
export class AppDocsManager { export class AppDocsManager {
private docs: {[docId: string]: MyDocument} = {}; private docs: {[docId: string]: MyDocument} = {};
private savingLottiePreview: {[docId: string]: true} = {}; private savingLottiePreview: {[docId: string]: true} = {};
public downloading: Map<string, DownloadBlob> = new Map();
constructor() { constructor() {
apiManager.onServiceWorkerFail = this.onServiceWorkerFail; apiManager.onServiceWorkerFail = this.onServiceWorkerFail;
@ -187,7 +189,7 @@ export class AppDocsManager {
} }
if(apiManager.isServiceWorkerOnline()) { if(apiManager.isServiceWorkerOnline()) {
if((doc.type === 'gif' && doc.size > 8e6) || doc.type === 'audio' || doc.type === 'video') { if((doc.type === 'gif' && doc.size > 8e6) || doc.type === 'audio' || doc.type === 'video'/* || doc.mime_type.indexOf('video/') === 0 */) {
doc.supportsStreaming = true; doc.supportsStreaming = true;
const cacheContext = appDownloadManager.getCacheContext(doc); const cacheContext = appDownloadManager.getCacheContext(doc);
@ -323,13 +325,17 @@ export class AppDocsManager {
const downloadOptions = this.getFileDownloadOptions(doc, undefined, queueId, onlyCache); const downloadOptions = this.getFileDownloadOptions(doc, undefined, queueId, onlyCache);
download = appDownloadManager.download(downloadOptions); download = appDownloadManager.download(downloadOptions);
this.downloading.set(doc.id, download);
rootScope.dispatchEvent('download_start', doc.id);
const cacheContext = appDownloadManager.getCacheContext(doc); const cacheContext = appDownloadManager.getCacheContext(doc);
const originalPromise = download; const originalPromise = download;
originalPromise.then((blob) => { originalPromise.then((blob) => {
cacheContext.url = URL.createObjectURL(blob); cacheContext.url = URL.createObjectURL(blob);
cacheContext.downloaded = blob.size; cacheContext.downloaded = blob.size;
}, () => {}); }, () => {}).finally(() => {
this.downloading.delete(doc.id);
});
if(doc.type === 'voice' && !opusDecodeController.isPlaySupported()) { if(doc.type === 'voice' && !opusDecodeController.isPlaySupported()) {
download = originalPromise.then(async(blob) => { download = originalPromise.then(async(blob) => {

14
src/lib/appManagers/appMessagesManager.ts

@ -5219,6 +5219,20 @@ export class AppMessagesManager {
appWebPagesManager.deleteWebPageFromPending(oldMessage.media.webpage, oldMessage.mid); appWebPagesManager.deleteWebPageFromPending(oldMessage.media.webpage, oldMessage.mid);
} }
} }
public getMediaFromMessage(message: any) {
return message.action ?
message.action.photo :
message.media && (
message.media.photo ||
message.media.document || (
message.media.webpage && (
message.media.webpage.document ||
message.media.webpage.photo
)
)
);
}
} }
const appMessagesManager = new AppMessagesManager(); const appMessagesManager = new AppMessagesManager();

35
src/lib/appManagers/appPhotosManager.ts

@ -87,7 +87,7 @@ export class AppPhotosManager {
return this.photos[photo.id] = photo; return this.photos[photo.id] = photo;
} }
public choosePhotoSize(photo: MyPhoto | MyDocument, boxWidth = 0, boxHeight = 0, useBytes = false) { public choosePhotoSize(photo: MyPhoto | MyDocument, boxWidth = 0, boxHeight = 0, useBytes = false, pushDocumentSize = false) {
if(window.devicePixelRatio > 1) { if(window.devicePixelRatio > 1) {
boxWidth *= 2; boxWidth *= 2;
boxHeight *= 2; boxHeight *= 2;
@ -105,7 +105,17 @@ export class AppPhotosManager {
d crop 1280x1280 */ d crop 1280x1280 */
let bestPhotoSize: PhotoSize = {_: 'photoSizeEmpty', type: ''}; let bestPhotoSize: PhotoSize = {_: 'photoSizeEmpty', type: ''};
const sizes = ((photo as MyPhoto).sizes || (photo as MyDocument).thumbs) as PhotoSize[]; let sizes = (photo as MyPhoto).sizes || (photo as MyDocument).thumbs as PhotoSize[];
if(pushDocumentSize && sizes && photo._ === 'document') {
sizes = sizes.concat({
_: 'photoSize',
w: (photo as MyDocument).w,
h: (photo as MyDocument).h,
size: (photo as MyDocument).size,
type: undefined
});
}
if(sizes?.length) { if(sizes?.length) {
for(let i = 0, length = sizes.length; i < length; ++i) { for(let i = 0, length = sizes.length; i < length; ++i) {
const photoSize = sizes[i]; const photoSize = sizes[i];
@ -214,15 +224,22 @@ export class AppPhotosManager {
return {image, loadPromise}; return {image, loadPromise};
} }
public setAttachmentSize(photo: MyPhoto | MyDocument, element: HTMLElement | SVGForeignObjectElement, boxWidth: number, boxHeight: number, noZoom = true, message?: any) { public setAttachmentSize(photo: MyPhoto | MyDocument,
const photoSize = this.choosePhotoSize(photo, boxWidth, boxHeight); element: HTMLElement | SVGForeignObjectElement,
boxWidth: number,
boxHeight: number,
noZoom = true,
message?: any,
pushDocumentSize?: boolean) {
const photoSize = this.choosePhotoSize(photo, boxWidth, boxHeight, undefined, pushDocumentSize);
//console.log('setAttachmentSize', photo, photo.sizes[0].bytes, div); //console.log('setAttachmentSize', photo, photo.sizes[0].bytes, div);
let size: MediaSize; let size: MediaSize;
if(photo._ === 'document') { const isDocument = photo._ === 'document';
size = makeMediaSize(photo.w || 512, photo.h || 512); if(isDocument) {
size = makeMediaSize((photo as MyDocument).w || (photoSize as PhotoSize.photoSize).w || 512, (photo as MyDocument).h || (photoSize as PhotoSize.photoSize).h || 512);
} else { } else {
size = makeMediaSize('w' in photoSize ? photoSize.w : 100, 'h' in photoSize ? photoSize.h : 100); size = makeMediaSize((photoSize as PhotoSize.photoSize).w || 100, (photoSize as PhotoSize.photoSize).h || 100);
} }
let boxSize = makeMediaSize(boxWidth, boxHeight); let boxSize = makeMediaSize(boxWidth, boxHeight);
@ -231,7 +248,7 @@ export class AppPhotosManager {
let isFit = true; let isFit = true;
if(photo._ === 'photo' || ['video', 'gif'].includes(photo.type)) { if(!isDocument || ['video', 'gif'].includes((photo as MyDocument).type)) {
if(boxSize.width < 200 && boxSize.height < 200) { // make at least one side this big if(boxSize.width < 200 && boxSize.height < 200) { // make at least one side this big
boxSize = size = size.aspectCovered(makeMediaSize(200, 200)); boxSize = size = size.aspectCovered(makeMediaSize(200, 200));
} }
@ -249,7 +266,7 @@ export class AppPhotosManager {
} }
} }
if(isFit && boxSize.width < 120) { // if image is too narrow if(isFit && boxSize.width < 120 && message) { // if image is too narrow
boxSize = makeMediaSize(120, boxSize.height); boxSize = makeMediaSize(120, boxSize.height);
isFit = false; isFit = false;
} }

6
src/lib/rootScope.ts

@ -90,7 +90,6 @@ export type BroadcastEvents = {
//'channel_settings': {channelId: number}, //'channel_settings': {channelId: number},
'webpage_updated': {id: string, msgs: {peerId: number, mid: number, isScheduled: boolean}[]}, 'webpage_updated': {id: string, msgs: {peerId: number, mid: number, isScheduled: boolean}[]},
'download_progress': any,
'connection_status_change': ConnectionStatusChange, 'connection_status_change': ConnectionStatusChange,
'settings_updated': {key: string, value: any}, 'settings_updated': {key: string, value: any},
'draft_updated': {peerId: number, threadId: number, draft: MyDraftMessage | undefined, force?: boolean}, 'draft_updated': {peerId: number, threadId: number, draft: MyDraftMessage | undefined, force?: boolean},
@ -124,7 +123,10 @@ export type BroadcastEvents = {
'push_subscribe': PushSubscriptionNotify, 'push_subscribe': PushSubscriptionNotify,
'push_unsubscribe': PushSubscriptionNotify, 'push_unsubscribe': PushSubscriptionNotify,
'emoji_recent': string 'emoji_recent': string,
'download_start': string,
'download_progress': any,
}; };
export class RootScope extends EventListenerBase<{ export class RootScope extends EventListenerBase<{

Loading…
Cancel
Save