Open documents in media viewer
This commit is contained in:
parent
ab454a5fa2
commit
728c4932ae
@ -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) {
|
||||||
@ -1072,6 +1079,16 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
|||||||
video.pause();
|
video.pause();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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
|
||||||
@ -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};
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -955,50 +955,75 @@ 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) {
|
||||||
if(withTail) {
|
selector = '.document-container';
|
||||||
str += '.bubble__media-container';
|
|
||||||
} else {
|
} else {
|
||||||
str += '.attachment video, .attachment img';
|
const withTail = this.bubbles[id].classList.contains('with-media-tail');
|
||||||
|
selector = '.album-item video, .album-item img, .preview video, .preview img, ';
|
||||||
|
if(withTail) {
|
||||||
|
selector += '.bubble__media-container';
|
||||||
|
} else {
|
||||||
|
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) {
|
||||||
if(hasAspecter && !findUpClassName(element, 'media-container-aspecter')) return;
|
elements.forEach((element) => {
|
||||||
let albumItem = findUpClassName(element, 'album-item');
|
targets.push({
|
||||||
const parent = albumItem || element.parentElement;
|
element: element.querySelector('.document-ico'),
|
||||||
if(parents.has(parent)) return;
|
mid: +element.dataset.mid,
|
||||||
parents.add(parent);
|
peerId: this.peerId
|
||||||
targets.push({
|
});
|
||||||
element,
|
|
||||||
mid: +albumItem?.dataset.mid || id,
|
|
||||||
peerId: this.peerId
|
|
||||||
});
|
});
|
||||||
});
|
} else {
|
||||||
|
const hasAspecter = !!this.bubbles[id].querySelector('.media-container-aspecter');
|
||||||
|
elements.forEach((element) => {
|
||||||
|
if(hasAspecter && !findUpClassName(element, 'media-container-aspecter')) return;
|
||||||
|
let albumItem = findUpClassName(element, 'album-item');
|
||||||
|
const parent = albumItem || element.parentElement;
|
||||||
|
if(parents.has(parent)) return;
|
||||||
|
parents.add(parent);
|
||||||
|
targets.push({
|
||||||
|
element,
|
||||||
|
mid: albumItem ? +albumItem.dataset.mid : id,
|
||||||
|
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;
|
||||||
|
|
||||||
|
@ -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',
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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) => {
|
||||||
|
@ -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();
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -90,41 +90,43 @@ 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},
|
||||||
|
|
||||||
'event-heavy-animation-start': void,
|
'event-heavy-animation-start': void,
|
||||||
'event-heavy-animation-end': void,
|
'event-heavy-animation-end': void,
|
||||||
|
|
||||||
'im_mount': void,
|
'im_mount': void,
|
||||||
'im_tab_change': number,
|
'im_tab_change': number,
|
||||||
|
|
||||||
'idle': boolean,
|
'idle': boolean,
|
||||||
|
|
||||||
'overlay_toggle': boolean,
|
'overlay_toggle': boolean,
|
||||||
|
|
||||||
'background_change': void,
|
'background_change': void,
|
||||||
|
|
||||||
'privacy_update': Update.updatePrivacy,
|
'privacy_update': Update.updatePrivacy,
|
||||||
|
|
||||||
'notify_settings': Update.updateNotifySettings,
|
'notify_settings': Update.updateNotifySettings,
|
||||||
'notify_peer_type_settings': {key: Exclude<NotifyPeer['_'], 'notifyPeer'>, settings: PeerNotifySettings},
|
'notify_peer_type_settings': {key: Exclude<NotifyPeer['_'], 'notifyPeer'>, settings: PeerNotifySettings},
|
||||||
|
|
||||||
'language_change': string,
|
'language_change': string,
|
||||||
|
|
||||||
'theme_change': void,
|
'theme_change': void,
|
||||||
|
|
||||||
'instance_activated': void,
|
'instance_activated': void,
|
||||||
'instance_deactivated': void,
|
'instance_deactivated': void,
|
||||||
|
|
||||||
'push_notification_click': PushNotificationObject,
|
'push_notification_click': PushNotificationObject,
|
||||||
'push_init': PushSubscriptionNotify,
|
'push_init': PushSubscriptionNotify,
|
||||||
'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…
x
Reference in New Issue
Block a user