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. 81
      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. 28
      src/lib/rootScope.ts

91
src/components/appMediaViewer.ts

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

1
src/components/audio.ts

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

81
src/components/chat/bubbles.ts

@ -955,50 +955,75 @@ export default class ChatBubbles { @@ -955,50 +955,75 @@ export default class ChatBubbles {
return;
}
const documentDiv = findUpClassName(target, 'document-with-thumb');
if((target.tagName === 'IMG' && !target.classList.contains('emoji') && !target.classList.contains('document-thumb'))
|| target.classList.contains('album-item')
|| isVideoComponentElement
|| (target.tagName === 'VIDEO' && !bubble.classList.contains('round'))) {
let messageId = +findUpClassName(target, 'album-item')?.dataset.mid || +bubble.dataset.mid;
let message = this.chat.getMessage(messageId);
|| (target.tagName === 'VIDEO' && !bubble.classList.contains('round'))
|| (documentDiv && !documentDiv.querySelector('.preloader-container'))) {
const groupedItem = findUpClassName(target, 'album-item') || findUpClassName(target, 'document-container');
const messageId = +(groupedItem || bubble).dataset.mid;
const message = this.chat.getMessage(messageId);
if(!message) {
this.log.warn('no message by messageId:', messageId);
return;
}
let targets: {element: HTMLElement, mid: number, peerId: number}[] = [];
let ids = Object.keys(this.bubbles).map(k => +k).filter(id => {
const f = documentDiv ? (media: any) => {
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;
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);
ids.forEach(id => {
let withTail = this.bubbles[id].classList.contains('with-media-tail');
let str = '.album-item video, .album-item img, .preview video, .preview img, ';
if(withTail) {
str += '.bubble__media-container';
let selector: string;
if(documentDiv) {
selector = '.document-container';
} 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');
let elements = this.bubbles[id].querySelectorAll(str) as NodeListOf<HTMLElement>;
const elements = Array.from(this.bubbles[id].querySelectorAll(selector)) as HTMLElement[];
const parents: Set<HTMLElement> = new Set();
Array.from(elements).forEach((element: HTMLElement) => {
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?.dataset.mid || id,
peerId: this.peerId
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;
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);
@ -1018,7 +1043,7 @@ export default class ChatBubbles { @@ -1018,7 +1043,7 @@ export default class ChatBubbles {
.setSearchContext({
threadId: this.chat.threadId,
peerId: this.peerId,
inputFilter: 'inputMessagesFilterPhotoVideo'
inputFilter: documentDiv ? 'inputMessagesFilterDocument' : 'inputMessagesFilterPhotoVideo'
})
.openMedia(message, targets[idx].element, 0, true, targets.slice(0, idx), targets.slice(idx + 1));
@ -2760,7 +2785,7 @@ export default class ChatBubbles { @@ -2760,7 +2785,7 @@ export default class ChatBubbles {
</div>`;
const avatarElem = new AvatarElement();
//avatarElem.lazyLoadQueue = this.lazyLoadQueue;
avatarElem.lazyLoadQueue = this.lazyLoadQueue;
avatarElem.setAttribute('peer', '' + message.media.user_id);
avatarElem.classList.add('contact-avatar', 'avatar-54');
@ -2901,7 +2926,7 @@ export default class ChatBubbles { @@ -2901,7 +2926,7 @@ export default class ChatBubbles {
const needAvatar = this.chat.isAnyGroup() && !isOut;
if(needAvatar) {
let avatarElem = new AvatarElement();
//avatarElem.lazyLoadQueue = this.lazyLoadQueue;
avatarElem.lazyLoadQueue = this.lazyLoadQueue;
avatarElem.classList.add('user-avatar', 'avatar-40');
avatarElem.loadPromises = loadPromises;

7
src/components/chat/contextMenu.ts

@ -283,7 +283,12 @@ export default class ChatContextMenu { @@ -283,7 +283,12 @@ export default class ChatContextMenu {
}
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',

22
src/components/wrappers.ts

@ -446,6 +446,15 @@ export const formatDate = (timestamp: number, monthShort = false, withYear = tru @@ -446,6 +446,15 @@ export const formatDate = (timestamp: number, monthShort = false, withYear = tru
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}: {
message: any,
withTime?: boolean,
@ -569,14 +578,17 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS @@ -569,14 +578,17 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
const load = () => {
const doc = appDocsManager.getDoc(docDiv.dataset.docId);
let download: DownloadBlob;
const queueId = appImManager.chat.bubbles ? appImManager.chat.bubbles.lazyLoadQueue.queueId : undefined;
if(doc.type === 'pdf') {
download = appDocsManager.downloadDoc(doc, appImManager.chat.bubbles ? appImManager.chat.bubbles.lazyLoadQueue.queueId : 0);
download = appDocsManager.downloadDoc(doc, queueId);
download.then(() => {
const cacheContext = appDownloadManager.getCacheContext(doc);
window.open(cacheContext.url);
});
} else if(doc.type === 'photo' || doc.type === 'video' || doc.mime_type.indexOf('video/') === 0) {
download = appDocsManager.downloadDoc(doc, queueId);
} else {
download = appDocsManager.saveDocFile(doc, appImManager.chat.bubbles ? appImManager.chat.bubbles.lazyLoadQueue.queueId : 0);
download = appDocsManager.saveDocFile(doc, queueId);
}
if(downloadDiv) {
@ -587,7 +599,11 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS @@ -587,7 +599,11 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
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');
preloader = message.media.preloader as ProgressivePreloader;

10
src/lib/appManagers/appDocsManager.ts

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

14
src/lib/appManagers/appMessagesManager.ts

@ -5219,6 +5219,20 @@ export class AppMessagesManager { @@ -5219,6 +5219,20 @@ export class AppMessagesManager {
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();

35
src/lib/appManagers/appPhotosManager.ts

@ -87,7 +87,7 @@ export class AppPhotosManager { @@ -87,7 +87,7 @@ export class AppPhotosManager {
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) {
boxWidth *= 2;
boxHeight *= 2;
@ -105,7 +105,17 @@ export class AppPhotosManager { @@ -105,7 +105,17 @@ export class AppPhotosManager {
d crop 1280x1280 */
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) {
for(let i = 0, length = sizes.length; i < length; ++i) {
const photoSize = sizes[i];
@ -214,15 +224,22 @@ export class AppPhotosManager { @@ -214,15 +224,22 @@ export class AppPhotosManager {
return {image, loadPromise};
}
public setAttachmentSize(photo: MyPhoto | MyDocument, element: HTMLElement | SVGForeignObjectElement, boxWidth: number, boxHeight: number, noZoom = true, message?: any) {
const photoSize = this.choosePhotoSize(photo, boxWidth, boxHeight);
public setAttachmentSize(photo: MyPhoto | MyDocument,
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);
let size: MediaSize;
if(photo._ === 'document') {
size = makeMediaSize(photo.w || 512, photo.h || 512);
const isDocument = photo._ === 'document';
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 {
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);
@ -231,7 +248,7 @@ export class AppPhotosManager { @@ -231,7 +248,7 @@ export class AppPhotosManager {
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
boxSize = size = size.aspectCovered(makeMediaSize(200, 200));
}
@ -249,7 +266,7 @@ export class AppPhotosManager { @@ -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);
isFit = false;
}

28
src/lib/rootScope.ts

@ -90,41 +90,43 @@ export type BroadcastEvents = { @@ -90,41 +90,43 @@ export type BroadcastEvents = {
//'channel_settings': {channelId: number},
'webpage_updated': {id: string, msgs: {peerId: number, mid: number, isScheduled: boolean}[]},
'download_progress': any,
'connection_status_change': ConnectionStatusChange,
'settings_updated': {key: string, value: any},
'draft_updated': {peerId: number, threadId: number, draft: MyDraftMessage | undefined, force?: boolean},
'event-heavy-animation-start': void,
'event-heavy-animation-end': void,
'im_mount': void,
'im_tab_change': number,
'idle': boolean,
'overlay_toggle': boolean,
'background_change': void,
'privacy_update': Update.updatePrivacy,
'notify_settings': Update.updateNotifySettings,
'notify_peer_type_settings': {key: Exclude<NotifyPeer['_'], 'notifyPeer'>, settings: PeerNotifySettings},
'language_change': string,
'theme_change': void,
'instance_activated': void,
'instance_deactivated': void,
'push_notification_click': PushNotificationObject,
'push_init': PushSubscriptionNotify,
'push_subscribe': PushSubscriptionNotify,
'push_unsubscribe': PushSubscriptionNotify,
'emoji_recent': string
'emoji_recent': string,
'download_start': string,
'download_progress': any,
};
export class RootScope extends EventListenerBase<{

Loading…
Cancel
Save