Fix gif thumbs
Fix dead streaming & downloading through SW Possible fix for stuck sticker viewer
This commit is contained in:
parent
b3dad1a9ca
commit
5f429acfe2
@ -787,15 +787,33 @@ export default class AppMediaViewerBase<
|
||||
});
|
||||
|
||||
if(!closing) {
|
||||
let mediaElement: HTMLImageElement | HTMLVideoElement;
|
||||
let mediaElement: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement;
|
||||
let src: string;
|
||||
|
||||
if(target instanceof HTMLVideoElement) {
|
||||
const elements = Array.from(target.parentElement.querySelectorAll('img')) as HTMLImageElement[];
|
||||
if(elements.length) {
|
||||
target = elements.pop();
|
||||
// if(target instanceof HTMLVideoElement) {
|
||||
const selector = 'video, img, .canvas-thumbnail';
|
||||
const queryFrom = target.matches(selector) ? target.parentElement : target;
|
||||
const elements = Array.from(queryFrom.querySelectorAll(selector)) as HTMLImageElement[];
|
||||
if(elements.length) {
|
||||
target = elements.pop();
|
||||
const canvas = document.createElement('canvas');
|
||||
const context = canvas.getContext('2d');
|
||||
if(target instanceof HTMLImageElement) {
|
||||
canvas.width = target.naturalWidth;
|
||||
canvas.height = target.naturalHeight;
|
||||
} else if(target instanceof HTMLVideoElement) {
|
||||
canvas.width = target.videoWidth;
|
||||
canvas.height = target.videoHeight;
|
||||
} else if(target instanceof HTMLCanvasElement) {
|
||||
canvas.width = target.width;
|
||||
canvas.height = target.height;
|
||||
}
|
||||
|
||||
canvas.className = 'canvas-thumbnail thumbnail media-photo';
|
||||
context.drawImage(target as HTMLImageElement | HTMLCanvasElement, 0, 0);
|
||||
target = canvas;
|
||||
}
|
||||
// }
|
||||
|
||||
if(target.tagName === 'DIV' || target.tagName === 'AVATAR-ELEMENT') { // useContainerAsTarget
|
||||
const images = Array.from(target.querySelectorAll('img')) as HTMLImageElement[];
|
||||
@ -865,6 +883,8 @@ export default class AppMediaViewerBase<
|
||||
foreignObject.setAttributeNS(null, 'height', '' + containerRect.height);
|
||||
|
||||
mover.prepend(newSvg);
|
||||
} else if(target instanceof HTMLCanvasElement) {
|
||||
mediaElement = target;
|
||||
}
|
||||
|
||||
if(aspecter) {
|
||||
|
@ -647,7 +647,7 @@ export default class AppSearchSuper {
|
||||
onlyPreview: true,
|
||||
withoutPreloader: true,
|
||||
noPlayButton: true,
|
||||
size
|
||||
photoSize: size
|
||||
})).thumb;
|
||||
} else {
|
||||
wrapped = await wrapPhoto({
|
||||
|
@ -22,7 +22,7 @@ import {SuperStickerRenderer} from '../emoticonsDropdown/tabs/stickers';
|
||||
import mediaSizes from '../../helpers/mediaSizes';
|
||||
import readBlobAsDataURL from '../../helpers/blob/readBlobAsDataURL';
|
||||
import setInnerHTML from '../../helpers/dom/setInnerHTML';
|
||||
import renderImageWithFadeIn from '../../helpers/dom/renderImageWithFadeIn';
|
||||
import renderMediaWithFadeIn from '../../helpers/dom/renderMediaWithFadeIn';
|
||||
import {AppManagers} from '../../lib/appManagers/managers';
|
||||
import wrapEmojiText from '../../lib/richTextProcessor/wrapEmojiText';
|
||||
import wrapRichText from '../../lib/richTextProcessor/wrapRichText';
|
||||
@ -188,7 +188,7 @@ export default class InlineHelper extends AutocompleteHelper {
|
||||
const image = new Image();
|
||||
image.classList.add('media-photo');
|
||||
readBlobAsDataURL(blob).then((dataURL) => {
|
||||
renderImageWithFadeIn(mediaContainer, image, dataURL, true);
|
||||
renderMediaWithFadeIn(mediaContainer, image, dataURL, true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -6,11 +6,12 @@
|
||||
|
||||
import replaceContent from '../../helpers/dom/replaceContent';
|
||||
import limitSymbols from '../../helpers/string/limitSymbols';
|
||||
import {Document, MessageMedia, Photo, WebPage} from '../../layer';
|
||||
import appImManager, {CHAT_ANIMATION_GROUP} from '../../lib/appManagers/appImManager';
|
||||
import choosePhotoSize from '../../lib/appManagers/utils/photos/choosePhotoSize';
|
||||
import wrapEmojiText from '../../lib/richTextProcessor/wrapEmojiText';
|
||||
import DivAndCaption from '../divAndCaption';
|
||||
import {wrapPhoto, wrapSticker} from '../wrappers';
|
||||
import {wrapPhoto, wrapSticker, wrapVideo} from '../wrappers';
|
||||
import wrapMessageForReply from '../wrappers/messageForReply';
|
||||
|
||||
const MEDIA_SIZE = 32;
|
||||
@ -38,49 +39,61 @@ export async function wrapReplyDivAndCaption(options: {
|
||||
loadPromises = [];
|
||||
}
|
||||
|
||||
let media = message && message.media;
|
||||
let messageMedia: MessageMedia | WebPage.webPage = message?.media;
|
||||
let setMedia = false, isRound = false;
|
||||
const mediaChildren = mediaEl ? Array.from(mediaEl.children).slice() : [];
|
||||
let middleware: () => boolean;
|
||||
if(media && mediaEl) {
|
||||
if(messageMedia && mediaEl) {
|
||||
subtitleEl.textContent = '';
|
||||
subtitleEl.append(await wrapMessageForReply(message, undefined, undefined, undefined, undefined, true));
|
||||
|
||||
// console.log('wrap reply', media);
|
||||
|
||||
if(media.webpage) {
|
||||
media = media.webpage;
|
||||
}
|
||||
|
||||
if(media.photo || (media.document && media.document.thumbs?.length)/* ['video', 'sticker', 'gif', 'round', 'photo', 'audio'].indexOf(media.document.type) !== -1) */) {
|
||||
messageMedia = (messageMedia as MessageMedia.messageMediaWebPage).webpage as WebPage.webPage || messageMedia;
|
||||
const photo = (messageMedia as MessageMedia.messageMediaPhoto).photo as Photo.photo;
|
||||
const document = (messageMedia as MessageMedia.messageMediaDocument).document as Document.document;
|
||||
if(photo || (document && document.thumbs?.length)/* ['video', 'sticker', 'gif', 'round', 'photo', 'audio'].indexOf(document.type) !== -1) */) {
|
||||
middleware = appImManager.chat.bubbles.getMiddleware();
|
||||
const lazyLoadQueue = appImManager.chat.bubbles.lazyLoadQueue;
|
||||
|
||||
if(media.document?.type === 'sticker') {
|
||||
setMedia = true;
|
||||
if(document?.type === 'sticker') {
|
||||
await wrapSticker({
|
||||
doc: media.document,
|
||||
doc: document,
|
||||
div: mediaEl,
|
||||
lazyLoadQueue,
|
||||
group: CHAT_ANIMATION_GROUP,
|
||||
// onlyThumb: media.document.sticker === 2,
|
||||
// onlyThumb: document.sticker === 2,
|
||||
width: MEDIA_SIZE,
|
||||
height: MEDIA_SIZE,
|
||||
middleware,
|
||||
loadPromises
|
||||
});
|
||||
setMedia = true;
|
||||
} else if(document?.type === 'gif' && document.video_thumbs) {
|
||||
setMedia = true;
|
||||
await wrapVideo({
|
||||
doc: document,
|
||||
container: mediaEl,
|
||||
boxWidth: MEDIA_SIZE,
|
||||
boxHeight: MEDIA_SIZE,
|
||||
lazyLoadQueue,
|
||||
noPlayButton: true,
|
||||
noInfo: true,
|
||||
middleware,
|
||||
loadPromises,
|
||||
withoutPreloader: true,
|
||||
videoSize: document.video_thumbs[0],
|
||||
group: CHAT_ANIMATION_GROUP
|
||||
});
|
||||
} else {
|
||||
const photo = media.photo || media.document;
|
||||
|
||||
isRound = photo.type === 'round';
|
||||
const m = photo || document;
|
||||
isRound = document?.type === 'round';
|
||||
|
||||
try {
|
||||
await wrapPhoto({
|
||||
photo,
|
||||
photo: m,
|
||||
container: mediaEl,
|
||||
boxWidth: MEDIA_SIZE,
|
||||
boxHeight: MEDIA_SIZE,
|
||||
size: choosePhotoSize(photo, MEDIA_SIZE, MEDIA_SIZE),
|
||||
size: choosePhotoSize(m, MEDIA_SIZE, MEDIA_SIZE),
|
||||
middleware,
|
||||
lazyLoadQueue,
|
||||
noBlur: true,
|
||||
|
@ -27,6 +27,7 @@ import {MediaSize} from '../../helpers/mediaSize';
|
||||
import {ThumbCache} from '../../lib/storages/thumbs';
|
||||
import onMediaLoad from '../../helpers/onMediaLoad';
|
||||
import apiManagerProxy from '../../lib/mtproto/mtprotoworker';
|
||||
import {THUMB_TYPE_FULL} from '../../lib/mtproto/mtproto_config';
|
||||
|
||||
type SendFileParams = Partial<{
|
||||
file: File,
|
||||
@ -377,7 +378,7 @@ export default class PopupNewMedia extends PopupElement {
|
||||
cacheContext = {
|
||||
url: params.objectURL,
|
||||
downloaded: file.size,
|
||||
type: 'full'
|
||||
type: THUMB_TYPE_FULL
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -208,7 +208,7 @@ export default class AppBackgroundTab extends SliderSuperTab {
|
||||
const media = document.createElement('div');
|
||||
media.classList.add('grid-item-media');
|
||||
|
||||
let wrapped: ReturnType<typeof wrapPhoto>, size: PhotoSize;
|
||||
let wrapped: ReturnType<typeof wrapPhoto>, size: ReturnType<typeof choosePhotoSize>;
|
||||
if(hasFile) {
|
||||
size = choosePhotoSize(doc, 200, 200);
|
||||
wrapped = wrapPhoto({
|
||||
|
@ -302,9 +302,10 @@ export default function attachStickerViewerListeners({listenTo, listenerSetter,
|
||||
}
|
||||
|
||||
document.removeEventListener('mousemove', onMouseMove);
|
||||
document.removeEventListener('mouseup', onMouseUp, {capture: true});
|
||||
};
|
||||
|
||||
document.addEventListener('mousemove', onMousePreMove);
|
||||
document.addEventListener('mouseup', onMouseUp, {once: true});
|
||||
document.addEventListener('mouseup', onMouseUp, {once: true, capture: true});
|
||||
});
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ export default async function wrapDocument({message, withTime, fontWeight, voice
|
||||
docDiv.classList.add('document-with-thumb');
|
||||
hasThumb = true;
|
||||
|
||||
const imgs: (HTMLImageElement | HTMLCanvasElement)[] = [];
|
||||
const imgs: (HTMLImageElement | HTMLCanvasElement | HTMLVideoElement)[] = [];
|
||||
// ! WARNING, use thumbs for check when thumb will be generated for media
|
||||
if(message.pFlags.is_outgoing && ['photo', 'video'].includes(doc.type)) {
|
||||
icoDiv.innerHTML = `<img src="${cacheContext.url}">`;
|
||||
|
@ -4,9 +4,9 @@
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import renderImageWithFadeIn from '../../helpers/dom/renderImageWithFadeIn';
|
||||
import renderMediaWithFadeIn from '../../helpers/dom/renderMediaWithFadeIn';
|
||||
import mediaSizes from '../../helpers/mediaSizes';
|
||||
import {Message, PhotoSize, WebDocument} from '../../layer';
|
||||
import {Message, PhotoSize, VideoSize, WebDocument} from '../../layer';
|
||||
import {MyDocument} from '../../lib/appManagers/appDocsManager';
|
||||
import {MyPhoto} from '../../lib/appManagers/appPhotosManager';
|
||||
import rootScope from '../../lib/rootScope';
|
||||
@ -20,6 +20,9 @@ import choosePhotoSize from '../../lib/appManagers/utils/photos/choosePhotoSize'
|
||||
import type {ThumbCache} from '../../lib/storages/thumbs';
|
||||
import appDownloadManager from '../../lib/appManagers/appDownloadManager';
|
||||
import isWebDocument from '../../lib/appManagers/utils/webDocs/isWebDocument';
|
||||
import createVideo from '../../helpers/dom/createVideo';
|
||||
import noop from '../../helpers/noop';
|
||||
import {THUMB_TYPE_FULL} from '../../lib/mtproto/mtproto_config';
|
||||
|
||||
export default async function wrapPhoto({photo, message, container, boxWidth, boxHeight, withTail, isOut, lazyLoadQueue, middleware, size, withoutPreloader, loadPromises, autoDownloadSize, noBlur, noThumb, noFadeIn, blurAfter, managers = rootScope.managers}: {
|
||||
photo: MyPhoto | MyDocument | WebDocument,
|
||||
@ -31,7 +34,7 @@ export default async function wrapPhoto({photo, message, container, boxWidth, bo
|
||||
isOut?: boolean,
|
||||
lazyLoadQueue?: LazyLoadQueue,
|
||||
middleware?: () => boolean,
|
||||
size?: PhotoSize,
|
||||
size?: PhotoSize | VideoSize,
|
||||
withoutPreloader?: boolean,
|
||||
loadPromises?: Promise<any>[],
|
||||
autoDownloadSize?: number,
|
||||
@ -41,24 +44,27 @@ export default async function wrapPhoto({photo, message, container, boxWidth, bo
|
||||
blurAfter?: boolean,
|
||||
managers?: AppManagers,
|
||||
}) {
|
||||
const ret = {
|
||||
loadPromises: {
|
||||
thumb: Promise.resolve() as Promise<any>,
|
||||
full: Promise.resolve() as Promise<any>
|
||||
},
|
||||
images: {
|
||||
thumb: null as HTMLImageElement | HTMLCanvasElement,
|
||||
full: null as HTMLVideoElement | HTMLImageElement
|
||||
},
|
||||
preloader: null as ProgressivePreloader,
|
||||
aspecter: null as HTMLElement
|
||||
};
|
||||
|
||||
const isDocument = photo._ === 'document';
|
||||
const isWebDoc = isWebDocument(photo);
|
||||
if(!((photo as MyPhoto).sizes || (photo as MyDocument).thumbs) && !isWebDoc) {
|
||||
if(boxWidth && boxHeight && !size && photo._ === 'document') {
|
||||
if(boxWidth && boxHeight && !size && isDocument) {
|
||||
setAttachmentSize(photo, container, boxWidth, boxHeight, undefined, message);
|
||||
}
|
||||
|
||||
return {
|
||||
loadPromises: {
|
||||
thumb: Promise.resolve(),
|
||||
full: Promise.resolve()
|
||||
},
|
||||
images: {
|
||||
thumb: null,
|
||||
full: null
|
||||
},
|
||||
preloader: null,
|
||||
aspecter: null
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
let noAutoDownload = autoDownloadSize === 0;
|
||||
@ -76,11 +82,10 @@ export default async function wrapPhoto({photo, message, container, boxWidth, bo
|
||||
let thumbImage: HTMLImageElement | HTMLCanvasElement;
|
||||
// let image: HTMLImageElement;
|
||||
let cacheContext: ThumbCache;
|
||||
const isGif = photo._ === 'document' && photo.mime_type === 'image/gif' && !size;
|
||||
const isGif = isDocument && photo.mime_type === 'image/gif' && !size;
|
||||
// if(withTail) {
|
||||
// image = wrapMediaWithTail(photo, message, container, boxWidth, boxHeight, isOut);
|
||||
// } else {
|
||||
const image = new Image();
|
||||
|
||||
if(boxWidth && boxHeight && !size) { // !album
|
||||
const set = setAttachmentSize(photo, container, boxWidth, boxHeight, undefined, message, undefined, isGif ? {
|
||||
@ -88,7 +93,7 @@ export default async function wrapPhoto({photo, message, container, boxWidth, bo
|
||||
w: photo.w,
|
||||
h: photo.h,
|
||||
size: photo.size,
|
||||
type: 'full'
|
||||
type: THUMB_TYPE_FULL
|
||||
} : undefined);
|
||||
size = set.photoSize;
|
||||
isFit = set.isFit;
|
||||
@ -147,14 +152,29 @@ export default async function wrapPhoto({photo, message, container, boxWidth, bo
|
||||
const gotThumb = getStrippedThumbIfNeeded(photo, cacheContext, !noBlur);
|
||||
if(gotThumb) {
|
||||
loadThumbPromise = Promise.all([loadThumbPromise, gotThumb.loadPromise]);
|
||||
thumbImage = gotThumb.image;
|
||||
ret.loadPromises.thumb = ret.loadPromises.full = loadThumbPromise;
|
||||
thumbImage = ret.images.thumb = gotThumb.image;
|
||||
thumbImage.classList.add('media-photo');
|
||||
aspecter.append(thumbImage);
|
||||
}
|
||||
}
|
||||
// }
|
||||
|
||||
image.classList.add('media-photo');
|
||||
if(size?._ === 'photoSizeEmpty' && isDocument) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
let media: HTMLVideoElement | HTMLImageElement;
|
||||
if(size?._ === 'videoSize') {
|
||||
media = ret.images.full = createVideo();
|
||||
media.autoplay = true;
|
||||
media.loop = true;
|
||||
media.muted = true;
|
||||
media.classList.add('media-photo');
|
||||
} else {
|
||||
media = ret.images.full = new Image();
|
||||
media.classList.add('media-photo');
|
||||
}
|
||||
|
||||
// console.log('wrapPhoto downloaded:', photo, photo.downloaded, container);
|
||||
|
||||
@ -194,7 +214,7 @@ export default async function wrapPhoto({photo, message, container, boxWidth, bo
|
||||
};
|
||||
|
||||
const renderOnLoad = (url: string) => {
|
||||
return renderImageWithFadeIn(container, image, url, needFadeIn, aspecter, thumbImage);
|
||||
return renderMediaWithFadeIn(container, media, url, needFadeIn, aspecter, thumbImage);
|
||||
};
|
||||
|
||||
const onLoad = async(url: string) => {
|
||||
@ -236,7 +256,7 @@ export default async function wrapPhoto({photo, message, container, boxWidth, bo
|
||||
noAutoDownload = undefined;
|
||||
|
||||
const renderPromise = promise.then(onLoad);
|
||||
renderPromise.catch(() => {});
|
||||
renderPromise.catch(noop);
|
||||
return {download: promise, render: renderPromise};
|
||||
};
|
||||
|
||||
@ -261,22 +281,15 @@ export default async function wrapPhoto({photo, message, container, boxWidth, bo
|
||||
|
||||
// const perf = performance.now();
|
||||
await loadThumbPromise;
|
||||
ret.loadPromises.thumb = loadThumbPromise;
|
||||
ret.loadPromises.full = loadPromise || Promise.resolve();
|
||||
ret.preloader = preloader;
|
||||
ret.aspecter = aspecter;
|
||||
|
||||
// const elapsedTime = performance.now() - perf;
|
||||
// if(elapsedTime > 4) {
|
||||
// console.log('wrapping photo thumb time', elapsedTime, photo, size);
|
||||
// }
|
||||
|
||||
return {
|
||||
loadPromises: {
|
||||
thumb: loadThumbPromise,
|
||||
full: loadPromise || Promise.resolve()
|
||||
},
|
||||
images: {
|
||||
thumb: thumbImage,
|
||||
full: image
|
||||
},
|
||||
preloader,
|
||||
aspecter
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
@ -14,12 +14,13 @@ import createVideo from '../../helpers/dom/createVideo';
|
||||
import isInDOM from '../../helpers/dom/isInDOM';
|
||||
import renderImageFromUrl from '../../helpers/dom/renderImageFromUrl';
|
||||
import mediaSizes, {ScreenSize} from '../../helpers/mediaSizes';
|
||||
import noop from '../../helpers/noop';
|
||||
import onMediaLoad from '../../helpers/onMediaLoad';
|
||||
import {fastRaf} from '../../helpers/schedulers';
|
||||
import throttle from '../../helpers/schedulers/throttle';
|
||||
import sequentialDom from '../../helpers/sequentialDom';
|
||||
import toHHMMSS from '../../helpers/string/toHHMMSS';
|
||||
import {Message, PhotoSize} from '../../layer';
|
||||
import {Message, PhotoSize, VideoSize} from '../../layer';
|
||||
import {MyDocument} from '../../lib/appManagers/appDocsManager';
|
||||
import appDownloadManager from '../../lib/appManagers/appDownloadManager';
|
||||
import appImManager from '../../lib/appManagers/appImManager';
|
||||
@ -59,7 +60,7 @@ mediaSizes.addEventListener('changeScreen', (from, to) => {
|
||||
}
|
||||
});
|
||||
|
||||
export default async function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue, noInfo, group, onlyPreview, withoutPreloader, loadPromises, noPlayButton, size, searchContext, autoDownload, managers = rootScope.managers}: {
|
||||
export default async function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue, noInfo, group, onlyPreview, withoutPreloader, loadPromises, noPlayButton, photoSize, videoSize, searchContext, autoDownload, managers = rootScope.managers}: {
|
||||
doc: MyDocument,
|
||||
container?: HTMLElement,
|
||||
message?: Message.message,
|
||||
@ -76,7 +77,8 @@ export default async function wrapVideo({doc, container, message, boxWidth, boxH
|
||||
withoutPreloader?: boolean,
|
||||
loadPromises?: Promise<any>[],
|
||||
autoDownload?: ChatAutoDownloadSettings,
|
||||
size?: PhotoSize,
|
||||
photoSize?: PhotoSize,
|
||||
videoSize?: VideoSize,
|
||||
searchContext?: MediaSearchContext,
|
||||
managers?: AppManagers
|
||||
}) {
|
||||
@ -144,7 +146,7 @@ export default async function wrapVideo({doc, container, message, boxWidth, boxH
|
||||
withoutPreloader,
|
||||
loadPromises,
|
||||
autoDownloadSize,
|
||||
size,
|
||||
size: photoSize,
|
||||
managers
|
||||
});
|
||||
|
||||
@ -354,7 +356,7 @@ export default async function wrapVideo({doc, container, message, boxWidth, boxH
|
||||
withoutPreloader: true,
|
||||
loadPromises,
|
||||
autoDownloadSize: autoDownload?.photo,
|
||||
size,
|
||||
size: photoSize,
|
||||
managers
|
||||
});
|
||||
|
||||
@ -386,7 +388,7 @@ export default async function wrapVideo({doc, container, message, boxWidth, boxH
|
||||
|
||||
let cacheContext: ThumbCache;
|
||||
const getCacheContext = async() => {
|
||||
return cacheContext = await managers.thumbsStorage.getCacheContext(doc);
|
||||
return cacheContext = await managers.thumbsStorage.getCacheContext(doc, videoSize?.type);
|
||||
};
|
||||
|
||||
await getCacheContext();
|
||||
@ -426,18 +428,6 @@ export default async function wrapVideo({doc, container, message, boxWidth, boxH
|
||||
}
|
||||
}, {once: true});
|
||||
|
||||
onMediaLoad(video).then(() => {
|
||||
if(group) {
|
||||
animationIntersector.addAnimation(video, group);
|
||||
}
|
||||
|
||||
if(preloader && !uploadFileName) {
|
||||
preloader.detach();
|
||||
}
|
||||
|
||||
renderDeferred.resolve();
|
||||
});
|
||||
|
||||
if(doc.type === 'video') {
|
||||
const onTimeUpdate = () => {
|
||||
if(!video.duration) {
|
||||
@ -478,7 +468,13 @@ export default async function wrapVideo({doc, container, message, boxWidth, boxH
|
||||
let loadPromise: Promise<any> = Promise.resolve();
|
||||
if((preloader && !uploadFileName) || withoutPreloader) {
|
||||
if(!cacheContext.downloaded && !doc.supportsStreaming) {
|
||||
const promise = loadPromise = managers.apiFileManager.downloadMediaURL({media: doc, queueId: lazyLoadQueue?.queueId, onlyCache: noAutoDownload});
|
||||
const promise = loadPromise = appDownloadManager.downloadMediaURL({
|
||||
media: doc,
|
||||
queueId: lazyLoadQueue?.queueId,
|
||||
onlyCache: noAutoDownload,
|
||||
thumb: videoSize
|
||||
});
|
||||
|
||||
if(preloader) {
|
||||
preloader.attach(container, false, promise);
|
||||
}
|
||||
@ -512,8 +508,21 @@ export default async function wrapVideo({doc, container, message, boxWidth, boxH
|
||||
}
|
||||
|
||||
await getCacheContext();
|
||||
|
||||
onMediaLoad(video).then(() => {
|
||||
if(group) {
|
||||
animationIntersector.addAnimation(video, group);
|
||||
}
|
||||
|
||||
if(preloader && !uploadFileName) {
|
||||
preloader.detach();
|
||||
}
|
||||
|
||||
renderDeferred.resolve();
|
||||
});
|
||||
|
||||
renderImageFromUrl(video, cacheContext.url);
|
||||
}, () => {});
|
||||
}, noop);
|
||||
|
||||
return {download: loadPromise, render: renderDeferred};
|
||||
};
|
||||
|
@ -4,6 +4,8 @@
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import onMediaLoad from '../onMediaLoad';
|
||||
|
||||
// import { getHeavyAnimationPromise } from "../../hooks/useHeavyAnimationCheck";
|
||||
|
||||
export const loadedURLs: {[url: string]: boolean} = {};
|
||||
@ -22,17 +24,24 @@ export default function renderImageFromUrl(
|
||||
) {
|
||||
if(!url) {
|
||||
console.error('renderImageFromUrl: no url?', elem, url);
|
||||
callback && callback();
|
||||
callback?.();
|
||||
return;
|
||||
}
|
||||
|
||||
if(((loadedURLs[url]/* && false */) && useCache) || elem instanceof HTMLVideoElement) {
|
||||
const isVideo = elem instanceof HTMLVideoElement;
|
||||
if(((loadedURLs[url]/* && false */) && useCache) || isVideo) {
|
||||
if(elem) {
|
||||
set(elem, url);
|
||||
}
|
||||
|
||||
callback && callback();
|
||||
// callback && getHeavyAnimationPromise().then(() => callback());
|
||||
if(callback) {
|
||||
if(isVideo) {
|
||||
onMediaLoad(elem).then(callback);
|
||||
} else {
|
||||
callback?.();
|
||||
}
|
||||
// callback && getHeavyAnimationPromise().then(() => callback());
|
||||
}
|
||||
} else {
|
||||
const isImage = elem instanceof HTMLImageElement;
|
||||
const loader = isImage ? elem as HTMLImageElement : new Image();
|
||||
@ -48,7 +57,7 @@ export default function renderImageFromUrl(
|
||||
// console.log('onload:', url, performance.now() - perf);
|
||||
// TODO: переделать прогрузки аватаров до начала анимации, иначе с этим ожиданием они неприятно появляются
|
||||
// callback && getHeavyAnimationPromise().then(() => callback());
|
||||
callback && callback();
|
||||
callback?.();
|
||||
}, {once: true});
|
||||
|
||||
if(callback) {
|
||||
|
@ -1,54 +0,0 @@
|
||||
/*
|
||||
* https://github.com/morethanwords/tweb
|
||||
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import sequentialDom from '../sequentialDom';
|
||||
import renderImageFromUrl from './renderImageFromUrl';
|
||||
|
||||
export default function renderImageWithFadeIn(
|
||||
container: HTMLElement,
|
||||
image: HTMLImageElement,
|
||||
url: string,
|
||||
needFadeIn: boolean,
|
||||
aspecter = container,
|
||||
thumbImage?: HTMLElement
|
||||
) {
|
||||
if(needFadeIn) {
|
||||
image.classList.add('fade-in');
|
||||
}
|
||||
|
||||
const promise = new Promise<void>((resolve) => {
|
||||
/* if(photo._ === 'document') {
|
||||
console.error('wrapPhoto: will render document', photo, size, cacheContext);
|
||||
return resolve();
|
||||
} */
|
||||
|
||||
renderImageFromUrl(image, url, () => {
|
||||
sequentialDom.mutateElement(container, () => {
|
||||
aspecter.append(image);
|
||||
|
||||
resolve();
|
||||
/* fastRaf(() => {
|
||||
resolve();
|
||||
}); */
|
||||
|
||||
if(needFadeIn) {
|
||||
image.addEventListener('animationend', () => {
|
||||
sequentialDom.mutate(() => {
|
||||
image.classList.remove('fade-in');
|
||||
thumbImage?.remove();
|
||||
});
|
||||
}, {once: true});
|
||||
} else {
|
||||
thumbImage?.remove();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// recordPromise(promise, 'renderImageWithFadeIn');
|
||||
|
||||
return promise;
|
||||
}
|
42
src/helpers/dom/renderMediaWithFadeIn.ts
Normal file
42
src/helpers/dom/renderMediaWithFadeIn.ts
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* https://github.com/morethanwords/tweb
|
||||
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import sequentialDom from '../sequentialDom';
|
||||
import {renderImageFromUrlPromise} from './renderImageFromUrl';
|
||||
|
||||
export default function renderMediaWithFadeIn(
|
||||
container: HTMLElement,
|
||||
media: Parameters<typeof renderImageFromUrlPromise>[0],
|
||||
url: string,
|
||||
needFadeIn: boolean,
|
||||
aspecter = container,
|
||||
thumbImage?: HTMLElement
|
||||
) {
|
||||
if(needFadeIn) {
|
||||
media.classList.add('fade-in');
|
||||
}
|
||||
|
||||
const promise = renderImageFromUrlPromise(media, url).then(() => {
|
||||
return sequentialDom.mutateElement(container, () => {
|
||||
aspecter.append(media);
|
||||
|
||||
if(needFadeIn) {
|
||||
media.addEventListener('animationend', () => {
|
||||
sequentialDom.mutate(() => {
|
||||
media.classList.remove('fade-in');
|
||||
thumbImage?.remove();
|
||||
});
|
||||
}, {once: true});
|
||||
} else {
|
||||
thumbImage?.remove();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// recordPromise(promise, 'renderImageWithFadeIn');
|
||||
|
||||
return promise;
|
||||
}
|
@ -10,8 +10,9 @@ import type {ThumbCache} from '../lib/storages/thumbs';
|
||||
import getImageFromStrippedThumb from './getImageFromStrippedThumb';
|
||||
|
||||
export default function getStrippedThumbIfNeeded(photo: MyPhoto | MyDocument, cacheContext: ThumbCache, useBlur: boolean, ignoreCache = false) {
|
||||
if(!cacheContext.downloaded || (['video', 'gif'] as MyDocument['type'][]).includes((photo as MyDocument).type) || ignoreCache) {
|
||||
if(photo._ === 'document' && cacheContext.downloaded && !ignoreCache) {
|
||||
const isVideo = (['video', 'gif'] as MyDocument['type'][]).includes((photo as MyDocument).type);
|
||||
if(!cacheContext.downloaded || isVideo || ignoreCache) {
|
||||
if(photo._ === 'document' && cacheContext.downloaded && !ignoreCache && !isVideo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ import getDocumentURL from './utils/docs/getDocumentURL';
|
||||
import type {ThumbCache} from '../storages/thumbs';
|
||||
import makeError from '../../helpers/makeError';
|
||||
import {EXTENSION_MIME_TYPE_MAP} from '../../environment/mimeTypeMap';
|
||||
import {THUMB_TYPE_FULL} from '../mtproto/mtproto_config';
|
||||
|
||||
export type MyDocument = Document.document;
|
||||
|
||||
@ -326,7 +327,7 @@ export class AppDocsManager extends AppManager {
|
||||
w: 0,
|
||||
location: {} as any,
|
||||
size: file.size,
|
||||
type: 'full'
|
||||
type: THUMB_TYPE_FULL
|
||||
} as PhotoSize.photoSize;
|
||||
let document: MyDocument = {
|
||||
_: 'document',
|
||||
|
@ -25,7 +25,7 @@ import {MyPhoto} from './appPhotosManager';
|
||||
import {getFileNameByLocation} from '../../helpers/fileName';
|
||||
import DEBUG from '../../config/debug';
|
||||
import SlicedArray, {Slice, SliceEnd} from '../../helpers/slicedArray';
|
||||
import {FOLDER_ID_ALL, MUTE_UNTIL, NULL_PEER_ID, REAL_FOLDER_ID, REPLIES_HIDDEN_CHANNEL_ID, REPLIES_PEER_ID, SERVICE_PEER_ID} from '../mtproto/mtproto_config';
|
||||
import {FOLDER_ID_ALL, MUTE_UNTIL, NULL_PEER_ID, REAL_FOLDER_ID, REPLIES_HIDDEN_CHANNEL_ID, REPLIES_PEER_ID, SERVICE_PEER_ID, THUMB_TYPE_FULL} from '../mtproto/mtproto_config';
|
||||
import telegramMeWebManager from '../mtproto/telegramMeWebManager';
|
||||
import {getMiddleware} from '../../helpers/middleware';
|
||||
import assumeType from '../../helpers/assumeType';
|
||||
@ -758,7 +758,7 @@ export class AppMessagesManager extends AppManager {
|
||||
_: 'photoSize',
|
||||
w: options.width,
|
||||
h: options.height,
|
||||
type: 'full',
|
||||
type: THUMB_TYPE_FULL,
|
||||
location: null,
|
||||
size: file.size
|
||||
} as PhotoSize.photoSize;
|
||||
@ -842,7 +842,7 @@ export class AppMessagesManager extends AppManager {
|
||||
_: 'photoSize',
|
||||
w: options.width,
|
||||
h: options.height,
|
||||
type: 'full',
|
||||
type: THUMB_TYPE_FULL,
|
||||
size: file.size
|
||||
};
|
||||
} else if(attachType === 'video') {
|
||||
@ -4947,7 +4947,7 @@ export class AppMessagesManager extends AppManager {
|
||||
if(/* photo._ !== 'photoEmpty' */photo) {
|
||||
const newPhotoSize = newPhoto.sizes[newPhoto.sizes.length - 1];
|
||||
const cacheContext = this.thumbsStorage.getCacheContext(newPhoto, newPhotoSize.type);
|
||||
const oldCacheContext = this.thumbsStorage.getCacheContext(photo, 'full');
|
||||
const oldCacheContext = this.thumbsStorage.getCacheContext(photo, THUMB_TYPE_FULL);
|
||||
Object.assign(cacheContext, oldCacheContext);
|
||||
|
||||
const photoSize = newPhoto.sizes[newPhoto.sizes.length - 1] as PhotoSize.photoSize;
|
||||
|
@ -6,8 +6,9 @@
|
||||
|
||||
import type {MyDocument} from '../../appDocsManager';
|
||||
import type {MyPhoto} from '../../appPhotosManager';
|
||||
import type {PhotoSize, WebDocument} from '../../../../layer';
|
||||
import type {PhotoSize, VideoSize, WebDocument} from '../../../../layer';
|
||||
import calcImageInBox from '../../../../helpers/calcImageInBox';
|
||||
import {THUMB_TYPE_FULL} from '../../../mtproto/mtproto_config';
|
||||
|
||||
export default function choosePhotoSize(
|
||||
photo: MyPhoto | MyDocument | WebDocument,
|
||||
@ -32,15 +33,15 @@ export default function choosePhotoSize(
|
||||
c crop 640x640
|
||||
d crop 1280x1280 */
|
||||
|
||||
let bestPhotoSize: PhotoSize = {_: 'photoSizeEmpty', type: ''};
|
||||
let sizes = (photo as MyPhoto).sizes || (photo as MyDocument).thumbs as PhotoSize[];
|
||||
let sizes: PhotoSize[] = (photo as MyPhoto).sizes || (photo as MyDocument).thumbs as PhotoSize[];
|
||||
let bestPhotoSize: typeof sizes[0] = {_: 'photoSizeEmpty', type: THUMB_TYPE_FULL};
|
||||
if(pushDocumentSize && sizes && photo._ !== 'photo') {
|
||||
sizes = sizes.concat({
|
||||
_: 'photoSize',
|
||||
w: photo.w,
|
||||
h: photo.h,
|
||||
size: photo.size,
|
||||
type: undefined
|
||||
type: THUMB_TYPE_FULL
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ export const SERVICE_PEER_ID: PeerId = 777000;
|
||||
export const MUTE_UNTIL = 0x7FFFFFFF;
|
||||
export const BOT_START_PARAM = '';
|
||||
export const MAX_FILE_SAVE_SIZE = 20 * 1024 * 1024;
|
||||
export const THUMB_TYPE_FULL = '';
|
||||
|
||||
export const FOLDER_ID_ALL: REAL_FOLDER_ID = 0;
|
||||
export const FOLDER_ID_ARCHIVE: REAL_FOLDER_ID = 1;
|
||||
|
@ -304,6 +304,10 @@ class ApiManagerProxy extends MTProtoMessagePort {
|
||||
this.serviceMessagePort.addMultipleEventsListeners({
|
||||
port: (payload, source, event) => {
|
||||
this.invokeVoid('serviceWorkerPort', undefined, undefined, [event.ports[0]]);
|
||||
},
|
||||
|
||||
hello: (payload, source) => {
|
||||
this.serviceMessagePort.resendLockTask(source);
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
|
@ -128,7 +128,8 @@ export default class SuperMessagePort<
|
||||
protected onPortDisconnect: (source: MessageEventSource) => void;
|
||||
// protected onPortConnect: (source: MessageEventSource) => void;
|
||||
|
||||
protected resolveLocks: Map<SendPort, () => void>;
|
||||
protected heldLocks: Map<SendPort, {resolve: () => void, id: string}>;
|
||||
protected requestedLocks: Map<string, SendPort>;
|
||||
|
||||
constructor(protected logSuffix?: string) {
|
||||
super(false);
|
||||
@ -141,7 +142,8 @@ export default class SuperMessagePort<
|
||||
this.pending = new Map();
|
||||
this.log = logger('MP' + (logSuffix ? '-' + logSuffix : ''));
|
||||
this.debug = DEBUG;
|
||||
this.resolveLocks = new Map();
|
||||
this.heldLocks = new Map();
|
||||
this.requestedLocks = new Map();
|
||||
|
||||
this.processTaskMap = {
|
||||
result: this.processResultTask,
|
||||
@ -188,10 +190,10 @@ export default class SuperMessagePort<
|
||||
if('locks' in navigator) {
|
||||
const id = ['lock', tabId, this.logSuffix || '', Math.random() * 0x7FFFFFFF | 0].join('-');
|
||||
this.log.warn('created lock', id);
|
||||
const promise = new Promise<void>((resolve) => this.resolveLocks.set(port, resolve))
|
||||
.then(() => this.resolveLocks.delete(port));
|
||||
const promise = new Promise<void>((resolve) => this.heldLocks.set(port, {resolve, id}))
|
||||
.then(() => this.heldLocks.delete(port));
|
||||
navigator.locks.request(id, () => promise);
|
||||
this.pushTask(this.createTask('lock', id));
|
||||
this.resendLockTask(port);
|
||||
} else {
|
||||
window.addEventListener('beforeunload', () => {
|
||||
const task = this.createTask('close', undefined);
|
||||
@ -203,6 +205,15 @@ export default class SuperMessagePort<
|
||||
this.releasePending();
|
||||
}
|
||||
|
||||
public resendLockTask(port: SendPort) {
|
||||
const lock = this.heldLocks.get(port);
|
||||
if(!lock) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.pushTask(this.createTask('lock', lock.id), port);
|
||||
}
|
||||
|
||||
// ! Can't rely on ping because timers can be suspended
|
||||
// protected sendPing(port: SendPort, loop = IS_WORKER) {
|
||||
// let timeout: number;
|
||||
@ -251,8 +262,8 @@ export default class SuperMessagePort<
|
||||
|
||||
this.onPortDisconnect?.(port as any);
|
||||
|
||||
const resolveLock = this.resolveLocks.get(port as SendPort);
|
||||
resolveLock?.();
|
||||
const heldLock = this.heldLocks.get(port as SendPort);
|
||||
heldLock?.resolve();
|
||||
|
||||
const error = makeError('PORT_DISCONNECTED');
|
||||
for(const id in this.awaiting) {
|
||||
@ -430,8 +441,15 @@ export default class SuperMessagePort<
|
||||
// };
|
||||
|
||||
protected processLockTask = (task: LockTask, source: MessageEventSource, event: MessageEvent) => {
|
||||
navigator.locks.request(task.payload, () => {
|
||||
const id = task.payload;
|
||||
if(this.requestedLocks.has(id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.requestedLocks.set(id, source);
|
||||
navigator.locks.request(id, () => {
|
||||
this.processCloseTask(undefined, source, undefined);
|
||||
this.requestedLocks.delete(id);
|
||||
});
|
||||
};
|
||||
|
||||
@ -556,7 +574,7 @@ export default class SuperMessagePort<
|
||||
|
||||
const interval = ctx.setInterval(() => {
|
||||
this.log.error('task still has no result', task, port);
|
||||
}, 5e3);
|
||||
}, 60e3);
|
||||
} else if(false) {
|
||||
// let timedOut = false;
|
||||
const startTime = Date.now();
|
||||
|
@ -49,8 +49,9 @@ const onWindowConnected = (source: WindowClient) => {
|
||||
}
|
||||
|
||||
log('windows', Array.from(connectedWindows));
|
||||
serviceMessagePort.invokeVoid('hello', undefined, source);
|
||||
sendMessagePortIfNeeded(source);
|
||||
connectedWindows.add(source.id);
|
||||
connectedWindows.set(source.id, source);
|
||||
};
|
||||
|
||||
export const serviceMessagePort = new ServiceMessagePort<false>();
|
||||
@ -83,7 +84,7 @@ getWindowClients().then((windowClients) => {
|
||||
});
|
||||
});
|
||||
|
||||
const connectedWindows: Set<string> = new Set();
|
||||
const connectedWindows: Map<string, WindowClient> = new Map();
|
||||
(self as any).connectedWindows = connectedWindows;
|
||||
listenMessagePort(serviceMessagePort, undefined, (source) => {
|
||||
log('something has disconnected', source);
|
||||
|
@ -51,6 +51,7 @@ export default class ServiceMessagePort<Master extends boolean = false> extends
|
||||
}, {
|
||||
// to main thread
|
||||
pushClick: (payload: PushNotificationObject) => void,
|
||||
hello: (payload: void, source: MessageEventSource) => void,
|
||||
|
||||
// to mtproto worker
|
||||
requestFilePart: (payload: ServiceRequestFilePartTaskPayload) => Promise<MyUploadFile> | MyUploadFile
|
||||
|
@ -7,6 +7,7 @@
|
||||
import type {WebDocument} from '../../layer';
|
||||
import type {MyDocument} from '../appManagers/appDocsManager';
|
||||
import type {MyPhoto} from '../appManagers/appPhotosManager';
|
||||
import {THUMB_TYPE_FULL} from '../mtproto/mtproto_config';
|
||||
|
||||
export type ThumbCache = {
|
||||
downloaded: number,
|
||||
@ -20,7 +21,7 @@ export type ThumbsCache = {
|
||||
}
|
||||
};
|
||||
|
||||
const thumbFullSize = 'full';
|
||||
const thumbFullSize = THUMB_TYPE_FULL;
|
||||
|
||||
export type ThumbStorageMedia = MyPhoto | MyDocument | WebDocument;
|
||||
|
||||
|
@ -175,7 +175,8 @@
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
} */
|
||||
> img {
|
||||
> img,
|
||||
> video {
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
@ -227,7 +227,8 @@ $inactive-opacity: .4;
|
||||
}
|
||||
}
|
||||
|
||||
&-prev-button, &-next-button {
|
||||
&-prev-button,
|
||||
&-next-button {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
color: #fff;
|
||||
@ -302,7 +303,9 @@ $inactive-opacity: .4;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
img, video {
|
||||
img,
|
||||
video,
|
||||
.canvas-thumbnail {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 100%;
|
||||
@ -324,7 +327,8 @@ $inactive-opacity: .4;
|
||||
|
||||
// !SAFARI
|
||||
svg {
|
||||
img, video {
|
||||
img,
|
||||
video {
|
||||
position: unset;
|
||||
}
|
||||
}
|
||||
@ -395,7 +399,8 @@ $inactive-opacity: .4;
|
||||
} */
|
||||
|
||||
img:not(.thumbnail),
|
||||
video {
|
||||
video,
|
||||
.canvas-thumbnail {
|
||||
/* height: auto;
|
||||
width: auto; */
|
||||
object-fit: contain;
|
||||
@ -410,7 +415,8 @@ $inactive-opacity: .4;
|
||||
}
|
||||
|
||||
&.hiding {
|
||||
img, video {
|
||||
img,
|
||||
video {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@ -437,7 +443,8 @@ $inactive-opacity: .4;
|
||||
z-index: 5;
|
||||
padding: 0 1.25rem;
|
||||
|
||||
.btn-icon, .media-viewer-author {
|
||||
.btn-icon,
|
||||
.media-viewer-author {
|
||||
color: #fff;
|
||||
opacity: $inactive-opacity;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user