Avatar & images fallback to blob due to speed
This commit is contained in:
parent
82043e4c4a
commit
17410a7a59
@ -61,7 +61,7 @@ class AppAudio {
|
|||||||
|
|
||||||
audio.addEventListener('error', onError);
|
audio.addEventListener('error', onError);
|
||||||
|
|
||||||
const downloadPromise: Promise<any> = !doc.supportsStreaming ? appDocsManager.downloadDocNew(doc.id).promise : Promise.resolve();
|
const downloadPromise: Promise<any> = !doc.supportsStreaming ? appDocsManager.downloadDocNew(doc.id) : Promise.resolve();
|
||||||
|
|
||||||
downloadPromise.then(() => {
|
downloadPromise.then(() => {
|
||||||
this.container.append(audio);
|
this.container.append(audio);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import appDocsManager from "../lib/appManagers/appDocsManager";
|
import appDocsManager from "../lib/appManagers/appDocsManager";
|
||||||
import { RichTextProcessor } from "../lib/richtextprocessor";
|
import { RichTextProcessor } from "../lib/richtextprocessor";
|
||||||
import { formatDate } from "./wrappers";
|
import { formatDate } from "./wrappers";
|
||||||
import ProgressivePreloader from "./preloader_new";
|
import ProgressivePreloader from "./preloader";
|
||||||
import { MediaProgressLine } from "../lib/mediaPlayer";
|
import { MediaProgressLine } from "../lib/mediaPlayer";
|
||||||
import appAudio from "./appAudio";
|
import appAudio from "./appAudio";
|
||||||
import { MTDocument } from "../types";
|
import { MTDocument } from "../types";
|
||||||
@ -367,9 +367,9 @@ export default class AudioElement extends HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
download = appDocsManager.downloadDocNew(doc.id);
|
download = appDocsManager.downloadDocNew(doc.id);
|
||||||
preloader.attach(downloadDiv, true, appDocsManager.getInputFileName(doc));
|
preloader.attach(downloadDiv, true, download);
|
||||||
|
|
||||||
download.promise.then(() => {
|
download.then(() => {
|
||||||
downloadDiv.remove();
|
downloadDiv.remove();
|
||||||
this.removeEventListener('click', onClick);
|
this.removeEventListener('click', onClick);
|
||||||
onLoad();
|
onLoad();
|
||||||
@ -383,7 +383,7 @@ export default class AudioElement extends HTMLElement {
|
|||||||
|
|
||||||
downloadDiv.classList.add('downloading');
|
downloadDiv.classList.add('downloading');
|
||||||
} else {
|
} else {
|
||||||
download.controller.abort();
|
download.cancel();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -38,8 +38,13 @@ export default class AvatarElement extends HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
attributeChangedCallback(name: string, oldValue: string, newValue: string) {
|
attributeChangedCallback(name: string, oldValue: string, newValue: string) {
|
||||||
|
//console.log('avatar changed attribute:', name, oldValue, newValue);
|
||||||
// вызывается при изменении одного из перечисленных выше атрибутов
|
// вызывается при изменении одного из перечисленных выше атрибутов
|
||||||
if(name == 'peer') {
|
if(name == 'peer') {
|
||||||
|
if(this.peerID == +newValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.peerID = +newValue;
|
this.peerID = +newValue;
|
||||||
this.update();
|
this.update();
|
||||||
} else if(name == 'peer-title') {
|
} else if(name == 'peer-title') {
|
||||||
|
@ -11,7 +11,7 @@ import apiManager from '../lib/mtproto/mtprotoworker';
|
|||||||
import LazyLoadQueue from "./lazyLoadQueue";
|
import LazyLoadQueue from "./lazyLoadQueue";
|
||||||
import { wrapSticker, wrapVideo } from "./wrappers";
|
import { wrapSticker, wrapVideo } from "./wrappers";
|
||||||
import appDocsManager from "../lib/appManagers/appDocsManager";
|
import appDocsManager from "../lib/appManagers/appDocsManager";
|
||||||
import ProgressivePreloader from "./preloader_new";
|
import ProgressivePreloader from "./preloader";
|
||||||
import Config, { touchSupport } from "../lib/config";
|
import Config, { touchSupport } from "../lib/config";
|
||||||
import { MTDocument } from "../types";
|
import { MTDocument } from "../types";
|
||||||
import animationIntersector from "./animationIntersector";
|
import animationIntersector from "./animationIntersector";
|
||||||
@ -377,9 +377,9 @@ class StickersTab implements EmoticonsTab {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const image = new Image();
|
const image = new Image();
|
||||||
renderImageFromUrl(image, thumbURL).then(() => {
|
renderImageFromUrl(image, thumbURL, () => {
|
||||||
li.append(image);
|
li.append(image);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
} else { // as thumb will be used first sticker
|
} else { // as thumb will be used first sticker
|
||||||
wrapSticker({
|
wrapSticker({
|
||||||
@ -636,7 +636,7 @@ class GifsTab implements EmoticonsTab {
|
|||||||
}, {once: true});
|
}, {once: true});
|
||||||
};
|
};
|
||||||
|
|
||||||
((posterURL ? renderImageFromUrl(img, posterURL) : Promise.resolve()) as Promise<any>).then(() => {
|
const afterRender = () => {
|
||||||
if(img) {
|
if(img) {
|
||||||
div.append(img);
|
div.append(img);
|
||||||
div.style.opacity = '';
|
div.style.opacity = '';
|
||||||
@ -658,7 +658,9 @@ class GifsTab implements EmoticonsTab {
|
|||||||
div.append(img);
|
div.append(img);
|
||||||
div.addEventListener('mouseover', onMouseOver, {once: true});
|
div.addEventListener('mouseover', onMouseOver, {once: true});
|
||||||
});
|
});
|
||||||
});
|
};
|
||||||
|
|
||||||
|
(posterURL ? renderImageFromUrl(img, posterURL, afterRender) : afterRender());
|
||||||
|
|
||||||
/* wrapVideo({
|
/* wrapVideo({
|
||||||
doc,
|
doc,
|
||||||
@ -742,7 +744,7 @@ class EmoticonsDropdown {
|
|||||||
if(firstTime) {
|
if(firstTime) {
|
||||||
this.toggleEl.onmouseout = this.element.onmouseout = (e) => {
|
this.toggleEl.onmouseout = this.element.onmouseout = (e) => {
|
||||||
const toElement = (e as any).toElement as Element;
|
const toElement = (e as any).toElement as Element;
|
||||||
if(findUpClassName(toElement, 'emoji-dropdown')) {
|
if(toElement && findUpClassName(toElement, 'emoji-dropdown')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,36 +1,40 @@
|
|||||||
import Config, { touchSupport, isApple, mediaSizes } from "../lib/config";
|
import Config, { touchSupport, isApple, mediaSizes } from "../lib/config";
|
||||||
|
|
||||||
let loadedURLs: {[url: string]: boolean} = {};
|
export const loadedURLs: {[url: string]: boolean} = {};
|
||||||
let set = (elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, url: string) => {
|
const set = (elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, url: string) => {
|
||||||
if(elem instanceof HTMLImageElement || elem instanceof HTMLVideoElement) elem.src = url;
|
if(elem instanceof HTMLImageElement || elem instanceof HTMLVideoElement) elem.src = url;
|
||||||
else if(elem instanceof SVGImageElement) elem.setAttributeNS(null, 'href', url);
|
else if(elem instanceof SVGImageElement) elem.setAttributeNS(null, 'href', url);
|
||||||
else elem.style.backgroundImage = 'url(' + url + ')';
|
else elem.style.backgroundImage = 'url(' + url + ')';
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function renderImageFromUrl(elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, url: string): Promise<boolean> {
|
// проблема функции в том, что она не подходит для ссылок, пригодна только для blob'ов, потому что обычным ссылкам нужен 'load' каждый раз.
|
||||||
if(loadedURLs[url]) {
|
export function renderImageFromUrl(elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, url: string, callback?: (err?: Event) => void): boolean {
|
||||||
|
if((loadedURLs[url]/* && false */) || elem instanceof HTMLVideoElement) {
|
||||||
set(elem, url);
|
set(elem, url);
|
||||||
|
callback && callback();
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
if(elem instanceof HTMLVideoElement) {
|
const isImage = elem instanceof HTMLImageElement;
|
||||||
set(elem, url);
|
const loader = isImage ? elem as HTMLImageElement : new Image();
|
||||||
} else {
|
//const loader = new Image();
|
||||||
await new Promise((resolve, reject) => {
|
loader.src = url;
|
||||||
let loader = new Image();
|
//let perf = performance.now();
|
||||||
loader.src = url;
|
loader.addEventListener('load', () => {
|
||||||
//let perf = performance.now();
|
if(!isImage) {
|
||||||
loader.addEventListener('load', () => {
|
set(elem, url);
|
||||||
set(elem, url);
|
}
|
||||||
loadedURLs[url] = true;
|
|
||||||
//console.log('onload:', url, performance.now() - perf);
|
|
||||||
resolve(false);
|
|
||||||
});
|
|
||||||
loader.addEventListener('error', reject);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return !!loadedURLs[url];
|
loadedURLs[url] = true;
|
||||||
|
//console.log('onload:', url, performance.now() - perf);
|
||||||
|
callback && callback();
|
||||||
|
});
|
||||||
|
|
||||||
|
if(callback) {
|
||||||
|
loader.addEventListener('error', callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function putPreloader(elem: Element, returnDiv = false) {
|
export function putPreloader(elem: Element, returnDiv = false) {
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
import { isInDOM } from "../lib/utils";
|
import { isInDOM, cancelEvent } from "../lib/utils";
|
||||||
import { CancellablePromise } from "../lib/polyfill";
|
import { CancellablePromise } from "../lib/polyfill";
|
||||||
|
|
||||||
export default class ProgressivePreloader {
|
export default class ProgressivePreloader {
|
||||||
public preloader: HTMLDivElement = null;
|
public preloader: HTMLDivElement;
|
||||||
private circle: SVGCircleElement = null;
|
private circle: SVGCircleElement;
|
||||||
private promise: CancellablePromise<any> = null;
|
|
||||||
private tempID = 0;
|
private tempID = 0;
|
||||||
private detached = true;
|
private detached = true;
|
||||||
|
|
||||||
|
private promise: CancellablePromise<any> = null;
|
||||||
|
|
||||||
constructor(elem?: Element, private cancelable = true) {
|
constructor(elem?: Element, private cancelable = true) {
|
||||||
this.preloader = document.createElement('div');
|
this.preloader = document.createElement('div');
|
||||||
this.preloader.classList.add('preloader-container');
|
this.preloader.classList.add('preloader-container');
|
||||||
@ -36,7 +38,9 @@ export default class ProgressivePreloader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(this.cancelable) {
|
if(this.cancelable) {
|
||||||
this.preloader.addEventListener('click', () => {
|
this.preloader.addEventListener('click', (e) => {
|
||||||
|
cancelEvent(e);
|
||||||
|
|
||||||
if(this.promise && this.promise.cancel) {
|
if(this.promise && this.promise.cancel) {
|
||||||
this.promise.cancel();
|
this.promise.cancel();
|
||||||
this.detach();
|
this.detach();
|
||||||
@ -44,13 +48,14 @@ export default class ProgressivePreloader {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public attach(elem: Element, reset = true, promise?: CancellablePromise<any>, append = true) {
|
public attach(elem: Element, reset = true, promise?: CancellablePromise<any>, append = true) {
|
||||||
if(promise) {
|
if(promise) {
|
||||||
this.promise = promise;
|
this.promise = promise;
|
||||||
|
|
||||||
let tempID = --this.tempID;
|
const tempID = --this.tempID;
|
||||||
let onEnd = () => {
|
|
||||||
|
const onEnd = () => {
|
||||||
promise.notify = null;
|
promise.notify = null;
|
||||||
|
|
||||||
if(tempID == this.tempID) {
|
if(tempID == this.tempID) {
|
||||||
@ -58,37 +63,41 @@ export default class ProgressivePreloader {
|
|||||||
this.promise = promise = null;
|
this.promise = promise = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//promise.catch(onEnd);
|
//promise.catch(onEnd);
|
||||||
promise.finally(onEnd);
|
promise.finally(onEnd);
|
||||||
|
|
||||||
promise.notify = (details: {done: number, total: number}) => {
|
if(promise.addNotifyListener) {
|
||||||
/* if(details.done >= details.total) {
|
promise.addNotifyListener((details: {done: number, total: number}) => {
|
||||||
onEnd();
|
/* if(details.done >= details.total) {
|
||||||
} */
|
onEnd();
|
||||||
|
} */
|
||||||
if(tempID != this.tempID) return;
|
|
||||||
|
if(tempID != this.tempID) return;
|
||||||
//console.log('preloader download', promise, details);
|
|
||||||
let percents = details.done / details.total * 100;
|
//console.log('preloader download', promise, details);
|
||||||
this.setProgress(percents);
|
const percents = details.done / details.total * 100;
|
||||||
};
|
this.setProgress(percents);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.cancelable && reset) {
|
|
||||||
this.setProgress(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.detached = false;
|
this.detached = false;
|
||||||
window.requestAnimationFrame(() => {
|
window.requestAnimationFrame(() => {
|
||||||
if(this.detached) return;
|
if(this.detached) return;
|
||||||
this.detached = false;
|
this.detached = false;
|
||||||
|
|
||||||
elem[append ? 'append' : 'prepend'](this.preloader);
|
elem[append ? 'append' : 'prepend'](this.preloader);
|
||||||
|
|
||||||
|
if(this.cancelable && reset) {
|
||||||
|
this.setProgress(0);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public detach() {
|
public detach() {
|
||||||
this.detached = true;
|
this.detached = true;
|
||||||
|
|
||||||
if(this.preloader.parentElement) {
|
if(this.preloader.parentElement) {
|
||||||
window.requestAnimationFrame(() => {
|
window.requestAnimationFrame(() => {
|
||||||
if(!this.detached) return;
|
if(!this.detached) return;
|
||||||
@ -112,7 +121,7 @@ export default class ProgressivePreloader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let totalLength = this.circle.getTotalLength();
|
const totalLength = this.circle.getTotalLength();
|
||||||
//console.log('setProgress', (percents / 100 * totalLength));
|
//console.log('setProgress', (percents / 100 * totalLength));
|
||||||
this.circle.style.strokeDasharray = '' + Math.max(5, percents / 100 * totalLength) + ', 200';
|
this.circle.style.strokeDasharray = '' + Math.max(5, percents / 100 * totalLength) + ', 200';
|
||||||
} catch(err) {}
|
} catch(err) {}
|
||||||
|
@ -1,123 +0,0 @@
|
|||||||
import { isInDOM, $rootScope, cancelEvent } from "../lib/utils";
|
|
||||||
import appDownloadManager, { Progress } from "../lib/appManagers/appDownloadManager";
|
|
||||||
|
|
||||||
export default class ProgressivePreloader {
|
|
||||||
public preloader: HTMLDivElement;
|
|
||||||
private circle: SVGCircleElement;
|
|
||||||
|
|
||||||
//private tempID = 0;
|
|
||||||
private detached = true;
|
|
||||||
|
|
||||||
private fileName: string;
|
|
||||||
public controller: AbortController;
|
|
||||||
|
|
||||||
constructor(elem?: Element, private cancelable = true) {
|
|
||||||
this.preloader = document.createElement('div');
|
|
||||||
this.preloader.classList.add('preloader-container');
|
|
||||||
|
|
||||||
this.preloader.innerHTML = `
|
|
||||||
<div class="you-spin-me-round">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="preloader-circular" viewBox="25 25 50 50">
|
|
||||||
<circle class="preloader-path-new" cx="50" cy="50" r="23" fill="none" stroke-miterlimit="10"/>
|
|
||||||
</svg>
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
if(cancelable) {
|
|
||||||
this.preloader.innerHTML += `
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="preloader-close" viewBox="0 0 20 20">
|
|
||||||
<line x1="0" y1="20" x2="20" y2="0" stroke-width="2" stroke-linecap="round"></line>
|
|
||||||
<line x1="0" y1="0" x2="20" y2="20" stroke-width="2" stroke-linecap="round"></line>
|
|
||||||
</svg>`;
|
|
||||||
} else {
|
|
||||||
this.preloader.classList.add('preloader-swing');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.circle = this.preloader.firstElementChild.firstElementChild.firstElementChild as SVGCircleElement;
|
|
||||||
|
|
||||||
if(elem) {
|
|
||||||
this.attach(elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.cancelable) {
|
|
||||||
this.preloader.addEventListener('click', (e) => {
|
|
||||||
cancelEvent(e);
|
|
||||||
this.detach();
|
|
||||||
|
|
||||||
if(!this.fileName) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const download = appDownloadManager.getDownload(this.fileName);
|
|
||||||
if(download && download.controller && !download.controller.signal.aborted) {
|
|
||||||
download.controller.abort();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadProgressHandler = (details: Progress) => {
|
|
||||||
if(details.done >= details.total) {
|
|
||||||
this.detach();
|
|
||||||
}
|
|
||||||
|
|
||||||
//console.log('preloader download', promise, details);
|
|
||||||
let percents = details.done / details.total * 100;
|
|
||||||
this.setProgress(percents);
|
|
||||||
};
|
|
||||||
|
|
||||||
public attach(elem: Element, reset = true, fileName?: string, append = true) {
|
|
||||||
this.fileName = fileName;
|
|
||||||
if(this.fileName) {
|
|
||||||
const download = appDownloadManager.getDownload(fileName);
|
|
||||||
download.promise.catch(() => {
|
|
||||||
this.detach();
|
|
||||||
});
|
|
||||||
|
|
||||||
appDownloadManager.addProgressCallback(this.fileName, this.downloadProgressHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.detached = false;
|
|
||||||
window.requestAnimationFrame(() => {
|
|
||||||
if(this.detached) return;
|
|
||||||
this.detached = false;
|
|
||||||
|
|
||||||
elem[append ? 'append' : 'prepend'](this.preloader);
|
|
||||||
|
|
||||||
if(this.cancelable && reset) {
|
|
||||||
this.setProgress(0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public detach() {
|
|
||||||
this.detached = true;
|
|
||||||
|
|
||||||
if(this.preloader.parentElement) {
|
|
||||||
window.requestAnimationFrame(() => {
|
|
||||||
if(!this.detached) return;
|
|
||||||
this.detached = true;
|
|
||||||
|
|
||||||
if(this.preloader.parentElement) {
|
|
||||||
this.preloader.parentElement.removeChild(this.preloader);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public setProgress(percents: number) {
|
|
||||||
if(!isInDOM(this.circle)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(percents == 0) {
|
|
||||||
this.circle.style.strokeDasharray = '';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
let totalLength = this.circle.getTotalLength();
|
|
||||||
//console.log('setProgress', (percents / 100 * totalLength));
|
|
||||||
this.circle.style.strokeDasharray = '' + Math.max(5, percents / 100 * totalLength) + ', 200';
|
|
||||||
} catch(err) {}
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,11 +3,10 @@ import LottieLoader from '../lib/lottieLoader';
|
|||||||
import appDocsManager from "../lib/appManagers/appDocsManager";
|
import appDocsManager from "../lib/appManagers/appDocsManager";
|
||||||
import { formatBytes, getEmojiToneIndex } from "../lib/utils";
|
import { formatBytes, getEmojiToneIndex } from "../lib/utils";
|
||||||
import ProgressivePreloader from './preloader';
|
import ProgressivePreloader from './preloader';
|
||||||
import ProgressivePreloaderNew from './preloader_new';
|
|
||||||
import LazyLoadQueue from './lazyLoadQueue';
|
import LazyLoadQueue from './lazyLoadQueue';
|
||||||
import VideoPlayer from '../lib/mediaPlayer';
|
import VideoPlayer from '../lib/mediaPlayer';
|
||||||
import { RichTextProcessor } from '../lib/richtextprocessor';
|
import { RichTextProcessor } from '../lib/richtextprocessor';
|
||||||
import { renderImageFromUrl } from './misc';
|
import { renderImageFromUrl, loadedURLs } from './misc';
|
||||||
import appMessagesManager from '../lib/appManagers/appMessagesManager';
|
import appMessagesManager from '../lib/appManagers/appMessagesManager';
|
||||||
import { Layouter, RectPart } from './groupedLayout';
|
import { Layouter, RectPart } from './groupedLayout';
|
||||||
import PollElement from './poll';
|
import PollElement from './poll';
|
||||||
@ -15,7 +14,7 @@ import { mediaSizes, isSafari } from '../lib/config';
|
|||||||
import { MTDocument, MTPhotoSize } from '../types';
|
import { MTDocument, MTPhotoSize } from '../types';
|
||||||
import animationIntersector from './animationIntersector';
|
import animationIntersector from './animationIntersector';
|
||||||
import AudioElement from './audio';
|
import AudioElement from './audio';
|
||||||
import { Download } from '../lib/appManagers/appDownloadManager';
|
import appDownloadManager, { Download } from '../lib/appManagers/appDownloadManager';
|
||||||
import { webpWorkerController } from '../lib/webp/webpWorkerController';
|
import { webpWorkerController } from '../lib/webp/webpWorkerController';
|
||||||
|
|
||||||
export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue, noInfo, group}: {
|
export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue, noInfo, group}: {
|
||||||
@ -228,7 +227,7 @@ export function wrapDocument(doc: MTDocument, withTime = false, uploading = fals
|
|||||||
|
|
||||||
if(!uploading) {
|
if(!uploading) {
|
||||||
let downloadDiv = docDiv.querySelector('.document-download') as HTMLDivElement;
|
let downloadDiv = docDiv.querySelector('.document-download') as HTMLDivElement;
|
||||||
let preloader: ProgressivePreloaderNew;
|
let preloader: ProgressivePreloader;
|
||||||
let download: Download;
|
let download: Download;
|
||||||
|
|
||||||
docDiv.addEventListener('click', () => {
|
docDiv.addEventListener('click', () => {
|
||||||
@ -238,13 +237,13 @@ export function wrapDocument(doc: MTDocument, withTime = false, uploading = fals
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!preloader) {
|
if(!preloader) {
|
||||||
preloader = new ProgressivePreloaderNew(null, true);
|
preloader = new ProgressivePreloader(null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
download = appDocsManager.saveDocFile(doc);
|
download = appDocsManager.saveDocFile(doc);
|
||||||
preloader.attach(downloadDiv, true, appDocsManager.getInputFileName(doc));
|
preloader.attach(downloadDiv, true, download);
|
||||||
|
|
||||||
download.promise.then(() => {
|
download.then(() => {
|
||||||
downloadDiv.remove();
|
downloadDiv.remove();
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
if(err.name === 'AbortError') {
|
if(err.name === 'AbortError') {
|
||||||
@ -256,7 +255,7 @@ export function wrapDocument(doc: MTDocument, withTime = false, uploading = fals
|
|||||||
|
|
||||||
downloadDiv.classList.add('downloading');
|
downloadDiv.classList.add('downloading');
|
||||||
} else {
|
} else {
|
||||||
download.controller.abort();
|
download.cancel();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -328,7 +327,7 @@ function wrapMediaWithTail(photo: any, message: {mid: number, message: string},
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function wrapPhoto(photoID: any, message: any, container: HTMLDivElement, boxWidth = mediaSizes.active.regular.width, boxHeight = mediaSizes.active.regular.height, withTail = true, isOut = false, lazyLoadQueue: LazyLoadQueue, middleware: () => boolean, size: MTPhotoSize = null) {
|
export function wrapPhoto(photoID: any, message: any, container: HTMLDivElement, boxWidth = mediaSizes.active.regular.width, boxHeight = mediaSizes.active.regular.height, withTail = true, isOut = false, lazyLoadQueue: LazyLoadQueue, middleware: () => boolean, size: MTPhotoSize = null) {
|
||||||
let photo = appPhotosManager.getPhoto(photoID);
|
const photo = appPhotosManager.getPhoto(photoID);
|
||||||
|
|
||||||
let image: HTMLImageElement;
|
let image: HTMLImageElement;
|
||||||
if(withTail) {
|
if(withTail) {
|
||||||
@ -351,36 +350,39 @@ export function wrapPhoto(photoID: any, message: any, container: HTMLDivElement,
|
|||||||
|
|
||||||
//console.log('wrapPhoto downloaded:', photo, photo.downloaded, container);
|
//console.log('wrapPhoto downloaded:', photo, photo.downloaded, container);
|
||||||
|
|
||||||
// так нельзя делать, потому что может быть загружен неправильный размер картинки
|
const cacheContext = appPhotosManager.getCacheContext(photo);
|
||||||
/* if(photo.downloaded && photo.url) {
|
|
||||||
renderImageFromUrl(image, photo.url);
|
|
||||||
return;
|
|
||||||
} */
|
|
||||||
|
|
||||||
let preloader: ProgressivePreloader;
|
let preloader: ProgressivePreloader;
|
||||||
if(message.media.preloader) { // means upload
|
if(message.media.preloader) { // means upload
|
||||||
message.media.preloader.attach(container);
|
message.media.preloader.attach(container);
|
||||||
} else if(!photo.downloaded) {
|
} else if(!cacheContext.downloaded) {
|
||||||
preloader = new ProgressivePreloader(container, false);
|
preloader = new ProgressivePreloader(container, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
let load = () => {
|
const load = () => {
|
||||||
let promise = appPhotosManager.preloadPhoto(photoID, size);
|
const promise = appPhotosManager.preloadPhoto(photoID, size);
|
||||||
|
|
||||||
if(preloader) {
|
if(preloader) {
|
||||||
preloader.attach(container, true, promise);
|
preloader.attach(container, true, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* const url = appPhotosManager.getPhotoURL(photoID, size);
|
||||||
|
return renderImageFromUrl(image || container, url).then(() => {
|
||||||
|
photo.downloaded = true;
|
||||||
|
}); */
|
||||||
|
|
||||||
|
/* if(preloader) {
|
||||||
|
preloader.attach(container, true, promise);
|
||||||
|
} */
|
||||||
|
|
||||||
return promise.then(() => {
|
return promise.then(() => {
|
||||||
if(middleware && !middleware()) return;
|
if(middleware && !middleware()) return;
|
||||||
|
|
||||||
renderImageFromUrl(image || container, photo._ == 'photo' ? photo.url : appPhotosManager.getDocumentCachedThumb(photo.id).url);
|
renderImageFromUrl(image || container, cacheContext.url);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/////////console.log('wrapPhoto', load, container, image);
|
return cacheContext.downloaded ? load() : lazyLoadQueue.push({div: container, load: load, wasSeen: true});
|
||||||
|
|
||||||
return photo.downloaded ? load() : lazyLoadQueue.push({div: container, load: load, wasSeen: true});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, onlyThumb, emoji, width, height, withThumb, loop}: {
|
export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, onlyThumb, emoji, width, height, withThumb, loop}: {
|
||||||
@ -439,7 +441,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
|
|||||||
img = new Image();
|
img = new Image();
|
||||||
|
|
||||||
if((!isSafari || doc.stickerThumbConverted)/* && false */) {
|
if((!isSafari || doc.stickerThumbConverted)/* && false */) {
|
||||||
renderImageFromUrl(img, appPhotosManager.getPreviewURLFromThumb(thumb, true)).then(afterRender);
|
renderImageFromUrl(img, appPhotosManager.getPreviewURLFromThumb(thumb, true), afterRender);
|
||||||
} else {
|
} else {
|
||||||
webpWorkerController.convert(doc.id, thumb.bytes).then(bytes => {
|
webpWorkerController.convert(doc.id, thumb.bytes).then(bytes => {
|
||||||
if(middleware && !middleware()) return;
|
if(middleware && !middleware()) return;
|
||||||
@ -448,7 +450,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
|
|||||||
doc.stickerThumbConverted = true;
|
doc.stickerThumbConverted = true;
|
||||||
|
|
||||||
if(!div.childElementCount) {
|
if(!div.childElementCount) {
|
||||||
renderImageFromUrl(img, appPhotosManager.getPreviewURLFromThumb(thumb, true)).then(afterRender);
|
renderImageFromUrl(img, appPhotosManager.getPreviewURLFromThumb(thumb, true), afterRender);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -461,11 +463,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
|
|||||||
|
|
||||||
const load = () => {
|
const load = () => {
|
||||||
if(div.childElementCount || (middleware && !middleware())) return;
|
if(div.childElementCount || (middleware && !middleware())) return;
|
||||||
const promise = renderImageFromUrl(img, appDocsManager.getFileURL(doc, false, thumb));
|
renderImageFromUrl(img, appDocsManager.getFileURL(doc, false, thumb), afterRender);
|
||||||
|
|
||||||
//if(!downloaded) {
|
|
||||||
promise.then(afterRender);
|
|
||||||
//}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* let downloaded = appDocsManager.hasDownloadedThumb(doc.id, thumb.type);
|
/* let downloaded = appDocsManager.hasDownloadedThumb(doc.id, thumb.type);
|
||||||
@ -483,10 +481,12 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
|
|||||||
|
|
||||||
let load = () => {
|
let load = () => {
|
||||||
let img = new Image();
|
let img = new Image();
|
||||||
return renderImageFromUrl(img, appDocsManager.getFileURL(doc, false, thumb)).then(() => {
|
renderImageFromUrl(img, appDocsManager.getFileURL(doc, false, thumb), () => {
|
||||||
if(middleware && !middleware()) return;
|
if(middleware && !middleware()) return;
|
||||||
div.append(img);
|
div.append(img);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
};
|
};
|
||||||
|
|
||||||
return lazyLoadQueue ? (lazyLoadQueue.push({div, load}), Promise.resolve()) : load();
|
return lazyLoadQueue ? (lazyLoadQueue.push({div, load}), Promise.resolve()) : load();
|
||||||
@ -504,11 +504,14 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
|
|||||||
//console.time('download sticker' + doc.id);
|
//console.time('download sticker' + doc.id);
|
||||||
|
|
||||||
//appDocsManager.downloadDocNew(doc.id).promise.then(res => res.json()).then(async(json) => {
|
//appDocsManager.downloadDocNew(doc.id).promise.then(res => res.json()).then(async(json) => {
|
||||||
fetch(doc.url).then(res => res.json()).then(async(json) => {
|
//fetch(doc.url).then(res => res.json()).then(async(json) => {
|
||||||
|
appDownloadManager.download(doc.url, appDocsManager.getInputFileName(doc), 'json').then(async(json) => {
|
||||||
//console.timeEnd('download sticker' + doc.id);
|
//console.timeEnd('download sticker' + doc.id);
|
||||||
//console.log('loaded sticker:', doc, div);
|
//console.log('loaded sticker:', doc, div);
|
||||||
if(middleware && !middleware()) return;
|
if(middleware && !middleware()) return;
|
||||||
|
|
||||||
|
//await new Promise((resolve) => setTimeout(resolve, 5e3));
|
||||||
|
|
||||||
let animation = await LottieLoader.loadAnimationWorker/* loadAnimation */({
|
let animation = await LottieLoader.loadAnimationWorker/* loadAnimation */({
|
||||||
container: div,
|
container: div,
|
||||||
loop: loop && !emoji,
|
loop: loop && !emoji,
|
||||||
@ -554,7 +557,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
renderImageFromUrl(img, doc.url).then(() => {
|
renderImageFromUrl(img, doc.url, () => {
|
||||||
if(div.firstElementChild && div.firstElementChild != img) {
|
if(div.firstElementChild && div.firstElementChild != img) {
|
||||||
div.firstElementChild.remove();
|
div.firstElementChild.remove();
|
||||||
}
|
}
|
||||||
|
@ -12,4 +12,4 @@ export const isAndroid = navigator.userAgent.toLowerCase().indexOf('android') !=
|
|||||||
*/
|
*/
|
||||||
const ctx = typeof(window) !== 'undefined' ? window : self;
|
const ctx = typeof(window) !== 'undefined' ? window : self;
|
||||||
|
|
||||||
export const isSafari = !!('safari' in ctx) || !!(userAgent && (/\b(iPad|iPhone|iPod)\b/.test(userAgent) || (!!userAgent.match('Safari') && !userAgent.match('Chrome'))));
|
export const isSafari = !!('safari' in ctx) || !!(userAgent && (/\b(iPad|iPhone|iPod)\b/.test(userAgent) || (!!userAgent.match('Safari') && !userAgent.match('Chrome'))))/* || true */;
|
@ -14,16 +14,21 @@ class AppDocsManager {
|
|||||||
public saveDoc(apiDoc: MTDocument, context?: any) {
|
public saveDoc(apiDoc: MTDocument, context?: any) {
|
||||||
//console.log('saveDoc', apiDoc, this.docs[apiDoc.id]);
|
//console.log('saveDoc', apiDoc, this.docs[apiDoc.id]);
|
||||||
if(this.docs[apiDoc.id]) {
|
if(this.docs[apiDoc.id]) {
|
||||||
let d = this.docs[apiDoc.id];
|
const d = this.docs[apiDoc.id];
|
||||||
|
|
||||||
if(apiDoc.thumbs) {
|
if(apiDoc.thumbs) {
|
||||||
if(!d.thumbs) d.thumbs = apiDoc.thumbs;
|
if(!d.thumbs) d.thumbs = apiDoc.thumbs;
|
||||||
else if(apiDoc.thumbs[0].bytes && !d.thumbs[0].bytes) {
|
/* else if(apiDoc.thumbs[0].bytes && !d.thumbs[0].bytes) {
|
||||||
d.thumbs.unshift(apiDoc.thumbs[0]);
|
d.thumbs.unshift(apiDoc.thumbs[0]);
|
||||||
}
|
} else if(d.thumbs[0].url) { // fix for converted thumb in safari
|
||||||
|
apiDoc.thumbs[0] = d.thumbs[0];
|
||||||
|
} */
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.assign(d, apiDoc, context);
|
d.file_reference = apiDoc.file_reference;
|
||||||
|
return d;
|
||||||
|
|
||||||
|
//return Object.assign(d, apiDoc, context);
|
||||||
//return context ? Object.assign(d, context) : d;
|
//return context ? Object.assign(d, context) : d;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,10 +38,6 @@ class AppDocsManager {
|
|||||||
|
|
||||||
this.docs[apiDoc.id] = apiDoc;
|
this.docs[apiDoc.id] = apiDoc;
|
||||||
|
|
||||||
if(apiDoc.thumb && apiDoc.thumb._ == 'photoSizeEmpty') {
|
|
||||||
delete apiDoc.thumb;
|
|
||||||
}
|
|
||||||
|
|
||||||
apiDoc.attributes.forEach((attribute: any) => {
|
apiDoc.attributes.forEach((attribute: any) => {
|
||||||
switch(attribute._) {
|
switch(attribute._) {
|
||||||
case 'documentAttributeFilename':
|
case 'documentAttributeFilename':
|
||||||
@ -216,7 +217,7 @@ class AppDocsManager {
|
|||||||
|
|
||||||
const thumb = doc.thumbs.find(t => !t.bytes);
|
const thumb = doc.thumbs.find(t => !t.bytes);
|
||||||
if(thumb) {
|
if(thumb) {
|
||||||
const url = appDocsManager.getFileURL(doc, false, thumb);
|
const url = this.getFileURL(doc, false, thumb);
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -304,15 +305,15 @@ class AppDocsManager {
|
|||||||
return download;
|
return download;
|
||||||
}
|
}
|
||||||
|
|
||||||
download = appDownloadManager.download(fileName, doc.url/* , method */);
|
download = appDownloadManager.download(doc.url, fileName, /*method*/);
|
||||||
|
|
||||||
const originalPromise = download.promise;
|
const originalPromise = download;
|
||||||
originalPromise.then(() => {
|
originalPromise.then(() => {
|
||||||
doc.downloaded = true;
|
doc.downloaded = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
if(doc.type == 'voice' && !opusDecodeController.isPlaySupported()) {
|
if(doc.type == 'voice' && !opusDecodeController.isPlaySupported()) {
|
||||||
download.promise = originalPromise.then(async(blob) => {
|
download = originalPromise.then(async(blob) => {
|
||||||
let reader = new FileReader();
|
let reader = new FileReader();
|
||||||
|
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
@ -344,7 +345,7 @@ class AppDocsManager {
|
|||||||
const url = this.getFileURL(doc, true);
|
const url = this.getFileURL(doc, true);
|
||||||
const fileName = this.getInputFileName(doc);
|
const fileName = this.getInputFileName(doc);
|
||||||
|
|
||||||
return appDownloadManager.downloadToDisc(fileName, url);
|
return appDownloadManager.downloadToDisc(fileName, url, doc.file_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
import { $rootScope } from "../utils";
|
import { $rootScope } from "../utils";
|
||||||
import apiManager from "../mtproto/mtprotoworker";
|
import apiManager from "../mtproto/mtprotoworker";
|
||||||
|
import { deferredPromise, CancellablePromise } from "../polyfill";
|
||||||
|
|
||||||
export type ResponseMethodBlob = 'blob';
|
export type ResponseMethodBlob = 'blob';
|
||||||
export type ResponseMethodJson = 'json';
|
export type ResponseMethodJson = 'json';
|
||||||
export type ResponseMethod = ResponseMethodBlob | ResponseMethodJson;
|
export type ResponseMethod = ResponseMethodBlob | ResponseMethodJson;
|
||||||
|
|
||||||
export type DownloadBlob = {promise: Promise<Blob>, controller: AbortController};
|
/* export type DownloadBlob = {promise: Promise<Blob>, controller: AbortController};
|
||||||
export type DownloadJson = {promise: Promise<any>, controller: AbortController};
|
export type DownloadJson = {promise: Promise<any>, controller: AbortController}; */
|
||||||
|
export type DownloadBlob = CancellablePromise<Blob>;
|
||||||
|
export type DownloadJson = CancellablePromise<any>;
|
||||||
|
//export type Download = DownloadBlob/* | DownloadJson */;
|
||||||
export type Download = DownloadBlob/* | DownloadJson */;
|
export type Download = DownloadBlob/* | DownloadJson */;
|
||||||
|
|
||||||
export type Progress = {done: number, fileName: string, total: number, offset: number};
|
export type Progress = {done: number, fileName: string, total: number, offset: number};
|
||||||
@ -26,17 +30,25 @@ export class AppDownloadManager {
|
|||||||
if(callbacks) {
|
if(callbacks) {
|
||||||
callbacks.forEach(callback => callback(details));
|
callbacks.forEach(callback => callback(details));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const download = this.downloads[details.fileName];
|
||||||
|
if(download) {
|
||||||
|
download.notifyAll(details);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public download(fileName: string, url: string, responseMethod?: ResponseMethodBlob): DownloadBlob;
|
public download(url: string, fileName: string, responseMethod?: ResponseMethodBlob): DownloadBlob;
|
||||||
public download(fileName: string, url: string, responseMethod?: ResponseMethodJson): DownloadJson;
|
public download(url: string, fileName: string, responseMethod?: ResponseMethodJson): DownloadJson;
|
||||||
public download(fileName: string, url: string, responseMethod: ResponseMethod = 'blob'): Download {
|
public download(url: string, fileName: string, responseMethod: ResponseMethod = 'blob'): Download {
|
||||||
if(this.downloads.hasOwnProperty(fileName)) return this.downloads[fileName];
|
if(this.downloads.hasOwnProperty(fileName)) return this.downloads[fileName];
|
||||||
|
|
||||||
|
const deferred = deferredPromise<Blob>();
|
||||||
|
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const promise = fetch(url, {signal: controller.signal})
|
const promise = fetch(url, {signal: controller.signal})
|
||||||
.then(res => res[responseMethod]())
|
.then(res => res[responseMethod]())
|
||||||
|
.then(res => deferred.resolve(res))
|
||||||
.catch(err => { // Только потому что event.request.signal не работает в SW, либо я кривой?
|
.catch(err => { // Только потому что event.request.signal не работает в SW, либо я кривой?
|
||||||
if(err.name === 'AbortError') {
|
if(err.name === 'AbortError') {
|
||||||
//console.log('Fetch aborted');
|
//console.log('Fetch aborted');
|
||||||
@ -48,6 +60,7 @@ export class AppDownloadManager {
|
|||||||
//console.error('Uh oh, an error!', err);
|
//console.error('Uh oh, an error!', err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deferred.reject(err);
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -57,7 +70,13 @@ export class AppDownloadManager {
|
|||||||
delete this.progressCallbacks[fileName];
|
delete this.progressCallbacks[fileName];
|
||||||
});
|
});
|
||||||
|
|
||||||
return this.downloads[fileName] = {promise, controller};
|
deferred.cancel = () => {
|
||||||
|
controller.abort();
|
||||||
|
deferred.cancel = () => {};
|
||||||
|
};
|
||||||
|
|
||||||
|
//return this.downloads[fileName] = {promise, controller};
|
||||||
|
return this.downloads[fileName] = deferred;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getDownload(fileName: string) {
|
public getDownload(fileName: string) {
|
||||||
@ -73,10 +92,12 @@ export class AppDownloadManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private createDownloadAnchor(url: string, onRemove?: () => void) {
|
private createDownloadAnchor(url: string, fileName: string, onRemove?: () => void) {
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
a.href = url;
|
a.href = url;
|
||||||
|
a.download = fileName;
|
||||||
|
a.target = '_blank';
|
||||||
|
|
||||||
a.style.position = 'absolute';
|
a.style.position = 'absolute';
|
||||||
a.style.top = '1px';
|
a.style.top = '1px';
|
||||||
a.style.left = '1px';
|
a.style.left = '1px';
|
||||||
@ -108,11 +129,11 @@ export class AppDownloadManager {
|
|||||||
return this.download(fileName, url);
|
return this.download(fileName, url);
|
||||||
} */
|
} */
|
||||||
|
|
||||||
public downloadToDisc(fileName: string, url: string) {
|
public downloadToDisc(fileName: string, url: string, discFileName: string) {
|
||||||
const download = this.download(fileName, url);
|
const download = this.download(url, fileName);
|
||||||
download.promise.then(blob => {
|
download/* .promise */.then(blob => {
|
||||||
const objectURL = URL.createObjectURL(blob);
|
const objectURL = URL.createObjectURL(blob);
|
||||||
this.createDownloadAnchor(objectURL, () => {
|
this.createDownloadAnchor(objectURL, discFileName, () => {
|
||||||
URL.revokeObjectURL(objectURL);
|
URL.revokeObjectURL(objectURL);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -553,7 +553,7 @@ export class AppImManager {
|
|||||||
private closeBtn = this.topbar.querySelector('.sidebar-close-button') as HTMLButtonElement;
|
private closeBtn = this.topbar.querySelector('.sidebar-close-button') as HTMLButtonElement;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.log = logger('IM', /* LogLevels.log | LogLevels.warn | LogLevels.debug | */ LogLevels.error);
|
this.log = logger('IM', LogLevels.log | LogLevels.warn | LogLevels.debug | LogLevels.error);
|
||||||
this.chatInputC = new ChatInput();
|
this.chatInputC = new ChatInput();
|
||||||
this.preloader = new ProgressivePreloader(null, false);
|
this.preloader = new ProgressivePreloader(null, false);
|
||||||
this.selectTab(0);
|
this.selectTab(0);
|
||||||
@ -1624,6 +1624,7 @@ export class AppImManager {
|
|||||||
this.peerChanged = true;
|
this.peerChanged = true;
|
||||||
|
|
||||||
this.avatarEl.setAttribute('peer', '' + this.peerID);
|
this.avatarEl.setAttribute('peer', '' + this.peerID);
|
||||||
|
this.avatarEl.update();
|
||||||
|
|
||||||
const isAnyGroup = appPeersManager.isAnyGroup(peerID);
|
const isAnyGroup = appPeersManager.isAnyGroup(peerID);
|
||||||
const isChannel = appPeersManager.isChannel(peerID);
|
const isChannel = appPeersManager.isChannel(peerID);
|
||||||
@ -1822,8 +1823,7 @@ export class AppImManager {
|
|||||||
resolve();
|
resolve();
|
||||||
|
|
||||||
// lol
|
// lol
|
||||||
el.removeEventListener('canplay', onLoad);
|
el.removeEventListener(el instanceof HTMLVideoElement ? 'canplay' : 'load', onLoad);
|
||||||
el.removeEventListener('load', onLoad);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if(el instanceof HTMLVideoElement) {
|
if(el instanceof HTMLVideoElement) {
|
||||||
@ -2286,20 +2286,26 @@ export class AppImManager {
|
|||||||
bubble.classList.add('hide-name', 'photo');
|
bubble.classList.add('hide-name', 'photo');
|
||||||
const tailSupported = !isAndroid;
|
const tailSupported = !isAndroid;
|
||||||
if(tailSupported) bubble.classList.add('with-media-tail');
|
if(tailSupported) bubble.classList.add('with-media-tail');
|
||||||
|
|
||||||
if(message.grouped_id) {
|
if(message.grouped_id) {
|
||||||
bubble.classList.add('is-album');
|
bubble.classList.add('is-album');
|
||||||
|
|
||||||
wrapAlbum({
|
let storage = appMessagesManager.groupedMessagesStorage[message.grouped_id];
|
||||||
groupID: message.grouped_id,
|
if(Object.keys(storage).length != 1) {
|
||||||
attachmentDiv,
|
wrapAlbum({
|
||||||
middleware: this.getMiddleware(),
|
groupID: message.grouped_id,
|
||||||
isOut: our,
|
attachmentDiv,
|
||||||
lazyLoadQueue: this.lazyLoadQueue
|
middleware: this.getMiddleware(),
|
||||||
});
|
isOut: our,
|
||||||
} else {
|
lazyLoadQueue: this.lazyLoadQueue
|
||||||
wrapPhoto(photo.id, message, attachmentDiv, undefined, undefined, tailSupported, isOut, this.lazyLoadQueue, this.getMiddleware());
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wrapPhoto(photo.id, message, attachmentDiv, undefined, undefined, tailSupported, isOut, this.lazyLoadQueue, this.getMiddleware());
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2645,6 +2651,7 @@ export class AppImManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
avatarElem.setAttribute('peer', '' + ((message.fwd_from && this.peerID == this.myID ? message.fwdFromID : message.fromID) || 0));
|
avatarElem.setAttribute('peer', '' + ((message.fwd_from && this.peerID == this.myID ? message.fwdFromID : message.fromID) || 0));
|
||||||
|
avatarElem.update();
|
||||||
|
|
||||||
//this.log('exec loadDialogPhoto', message);
|
//this.log('exec loadDialogPhoto', message);
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ export class AppMediaViewer {
|
|||||||
const download = (e: MouseEvent) => {
|
const download = (e: MouseEvent) => {
|
||||||
let message = appMessagesManager.getMessage(this.currentMessageID);
|
let message = appMessagesManager.getMessage(this.currentMessageID);
|
||||||
if(message.media.photo) {
|
if(message.media.photo) {
|
||||||
appPhotosManager.downloadPhoto(message.media.photo.id);
|
appPhotosManager.savePhotoFile(message.media.photo.id);
|
||||||
} else {
|
} else {
|
||||||
let document: any = null;
|
let document: any = null;
|
||||||
|
|
||||||
@ -895,7 +895,7 @@ export class AppMediaViewer {
|
|||||||
|
|
||||||
//this.log('will renderImageFromUrl:', image, div, target);
|
//this.log('will renderImageFromUrl:', image, div, target);
|
||||||
|
|
||||||
renderImageFromUrl(image, url).then(() => {
|
renderImageFromUrl(image, url, () => {
|
||||||
div.append(image);
|
div.append(image);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import appUsersManager from "./appUsersManager";
|
|
||||||
import { calcImageInBox, isObject, getFileURL } from "../utils";
|
import { calcImageInBox, isObject, getFileURL } from "../utils";
|
||||||
import { bytesFromHex, getFileNameByLocation } from "../bin_utils";
|
import { bytesFromHex, getFileNameByLocation } from "../bin_utils";
|
||||||
//import apiManager from '../mtproto/apiManager';
|
|
||||||
import apiManager from '../mtproto/mtprotoworker';
|
|
||||||
import { MTPhotoSize, inputPhotoFileLocation, inputDocumentFileLocation, FileLocation } from "../../types";
|
import { MTPhotoSize, inputPhotoFileLocation, inputDocumentFileLocation, FileLocation } from "../../types";
|
||||||
import appDownloadManager from "./appDownloadManager";
|
import appDownloadManager, { Download } from "./appDownloadManager";
|
||||||
|
import { deferredPromise, CancellablePromise } from "../polyfill";
|
||||||
|
import { isSafari } from "../../helpers/userAgent";
|
||||||
|
|
||||||
export type MTPhoto = {
|
export type MTPhoto = {
|
||||||
_: 'photo' | 'photoEmpty' | string,
|
_: 'photo' | 'photoEmpty' | string,
|
||||||
@ -98,7 +97,7 @@ export class AppPhotosManager {
|
|||||||
return bestPhotoSize;
|
return bestPhotoSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getUserPhotos(userID: number, maxID: number, limit: number) {
|
/* public getUserPhotos(userID: number, maxID: number, limit: number) {
|
||||||
var inputUser = appUsersManager.getUserInput(userID);
|
var inputUser = appUsersManager.getUserInput(userID);
|
||||||
return apiManager.invokeApi('photos.getUserPhotos', {
|
return apiManager.invokeApi('photos.getUserPhotos', {
|
||||||
user_id: inputUser,
|
user_id: inputUser,
|
||||||
@ -120,7 +119,7 @@ export class AppPhotosManager {
|
|||||||
photos: photoIDs
|
photos: photoIDs
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
} */
|
||||||
|
|
||||||
public getPreviewURLFromBytes(bytes: Uint8Array | number[], isSticker = false) {
|
public getPreviewURLFromBytes(bytes: Uint8Array | number[], isSticker = false) {
|
||||||
let arr: Uint8Array;
|
let arr: Uint8Array;
|
||||||
@ -131,17 +130,15 @@ export class AppPhotosManager {
|
|||||||
} else {
|
} else {
|
||||||
arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
|
arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log('getPreviewURLFromBytes', bytes, arr, div, isSticker);
|
|
||||||
|
|
||||||
/* let reader = new FileReader();
|
let mimeType: string;
|
||||||
reader.onloadend = () => {
|
if(isSticker) {
|
||||||
let src = reader.result;
|
mimeType = isSafari ? 'image/png' : 'image/webp';
|
||||||
};
|
} else {
|
||||||
reader.readAsDataURL(blob); */
|
mimeType = 'image/jpeg';
|
||||||
|
}
|
||||||
let blob = new Blob([arr], {type: "image/jpeg"});
|
|
||||||
|
|
||||||
|
const blob = new Blob([arr], {type: mimeType});
|
||||||
return URL.createObjectURL(blob);
|
return URL.createObjectURL(blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,32 +212,18 @@ export class AppPhotosManager {
|
|||||||
|
|
||||||
return photoSize;
|
return photoSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public preloadPhoto(photoID: any, photoSize?: MTPhotoSize): Promise<Blob | void> {
|
|
||||||
const photo = this.getPhoto(photoID);
|
|
||||||
|
|
||||||
if(!photoSize) {
|
|
||||||
const fullWidth = this.windowW;
|
|
||||||
const fullHeight = this.windowH;
|
|
||||||
|
|
||||||
photoSize = this.choosePhotoSize(photo, fullWidth, fullHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public getPhotoURL(photo: MTPhoto, photoSize: MTPhotoSize) {
|
||||||
const isDocument = photo._ == 'document';
|
const isDocument = photo._ == 'document';
|
||||||
const cacheContext = isDocument ? (this.documentThumbsCache[photo.id] ?? (this.documentThumbsCache[photo.id] = {downloaded: -1, url: ''})) : photo;
|
|
||||||
|
|
||||||
if(cacheContext.downloaded >= photoSize.size && cacheContext.url) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!photoSize || photoSize._ == 'photoSizeEmpty') {
|
if(!photoSize || photoSize._ == 'photoSizeEmpty') {
|
||||||
//console.error('no photoSize by photo:', photo);
|
//console.error('no photoSize by photo:', photo);
|
||||||
return Promise.reject('no photoSize');
|
throw new Error('photoSizeEmpty!');
|
||||||
}
|
}
|
||||||
|
|
||||||
// maybe it's a thumb
|
// maybe it's a thumb
|
||||||
let isPhoto = photoSize.size && photo.access_hash && photo.file_reference;
|
const isPhoto = photoSize.size && photo.access_hash && photo.file_reference;
|
||||||
let location: inputPhotoFileLocation | inputDocumentFileLocation | FileLocation = isPhoto ? {
|
const location: inputPhotoFileLocation | inputDocumentFileLocation | FileLocation = isPhoto ? {
|
||||||
_: isDocument ? 'inputDocumentFileLocation' : 'inputPhotoFileLocation',
|
_: isDocument ? 'inputDocumentFileLocation' : 'inputPhotoFileLocation',
|
||||||
id: photo.id,
|
id: photo.id,
|
||||||
access_hash: photo.access_hash,
|
access_hash: photo.access_hash,
|
||||||
@ -248,30 +231,54 @@ export class AppPhotosManager {
|
|||||||
thumb_size: photoSize.type
|
thumb_size: photoSize.type
|
||||||
} : photoSize.location;
|
} : photoSize.location;
|
||||||
|
|
||||||
const url = getFileURL('photo', {dcID: photo.dc_id, location, size: isPhoto ? photoSize.size : undefined});
|
return {url: getFileURL('photo', {dcID: photo.dc_id, location, size: isPhoto ? photoSize.size : undefined}), location};
|
||||||
let promise: Promise<Blob>;
|
}
|
||||||
if(isPhoto/* && photoSize.size >= 1e6 */) {
|
|
||||||
promise = fetch(url).then(res => res.blob());
|
public preloadPhoto(photoID: any, photoSize?: MTPhotoSize): CancellablePromise<Blob> {
|
||||||
} else {
|
const photo = this.getPhoto(photoID);
|
||||||
//console.log('Photos downloadSmallFile exec', photo, location);
|
|
||||||
promise = fetch(url).then(res => res.blob());
|
if(!photoSize) {
|
||||||
|
const fullWidth = this.windowW;
|
||||||
|
const fullHeight = this.windowH;
|
||||||
|
|
||||||
|
photoSize = this.choosePhotoSize(photo, fullWidth, fullHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
promise.then(blob => {
|
const cacheContext = this.getCacheContext(photo);
|
||||||
|
if(cacheContext.downloaded >= photoSize.size && cacheContext.url) {
|
||||||
|
return Promise.resolve() as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {url, location} = this.getPhotoURL(photo, photoSize);
|
||||||
|
const fileName = getFileNameByLocation(location);
|
||||||
|
|
||||||
|
let download = appDownloadManager.getDownload(fileName);
|
||||||
|
if(download) {
|
||||||
|
return download;
|
||||||
|
}
|
||||||
|
|
||||||
|
download = appDownloadManager.download(url, fileName);
|
||||||
|
download.then(blob => {
|
||||||
if(!cacheContext.downloaded || cacheContext.downloaded < blob.size) {
|
if(!cacheContext.downloaded || cacheContext.downloaded < blob.size) {
|
||||||
cacheContext.downloaded = blob.size;
|
cacheContext.downloaded = blob.size;
|
||||||
//cacheContext.url = URL.createObjectURL(blob);
|
cacheContext.url = URL.createObjectURL(blob);
|
||||||
cacheContext.url = url;
|
|
||||||
|
|
||||||
//console.log('wrote photo:', photo, photoSize, cacheContext, blob);
|
//console.log('wrote photo:', photo, photoSize, cacheContext, blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return blob;
|
||||||
});
|
});
|
||||||
|
|
||||||
return promise;
|
return download;
|
||||||
|
//return fetch(url).then(res => res.blob());
|
||||||
|
}
|
||||||
|
|
||||||
|
public getCacheContext(photo: any) {
|
||||||
|
return photo._ == 'document' ? this.getDocumentCachedThumb(photo.id) : photo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getDocumentCachedThumb(docID: string) {
|
public getDocumentCachedThumb(docID: string) {
|
||||||
return this.documentThumbsCache[docID];
|
return this.documentThumbsCache[docID] ?? (this.documentThumbsCache[docID] = {downloaded: 0, url: ''});
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPhoto(photoID: any): MTPhoto {
|
public getPhoto(photoID: any): MTPhoto {
|
||||||
@ -293,7 +300,7 @@ export class AppPhotosManager {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public downloadPhoto(photoID: string) {
|
public savePhotoFile(photoID: string) {
|
||||||
const photo = this.photos[photoID];
|
const photo = this.photos[photoID];
|
||||||
const fullWidth = this.windowW;
|
const fullWidth = this.windowW;
|
||||||
const fullHeight = this.windowH;
|
const fullHeight = this.windowH;
|
||||||
@ -309,7 +316,7 @@ export class AppPhotosManager {
|
|||||||
const url = getFileURL('download', {dcID: photo.dc_id, location, size: fullPhotoSize.size, fileName: 'photo' + photo.id + '.jpg'});
|
const url = getFileURL('download', {dcID: photo.dc_id, location, size: fullPhotoSize.size, fileName: 'photo' + photo.id + '.jpg'});
|
||||||
const fileName = getFileNameByLocation(location);
|
const fileName = getFileNameByLocation(location);
|
||||||
|
|
||||||
appDownloadManager.downloadToDisc(fileName, url);
|
appDownloadManager.downloadToDisc(fileName, url, 'photo' + photo.id + '.jpg');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -797,14 +797,12 @@ export class AppSidebarRight extends SidebarSlider {
|
|||||||
const url = (photo && photo.url) || appPhotosManager.getDocumentCachedThumb(media.id).url;
|
const url = (photo && photo.url) || appPhotosManager.getDocumentCachedThumb(media.id).url;
|
||||||
if(url) {
|
if(url) {
|
||||||
//if(needBlur) return;
|
//if(needBlur) return;
|
||||||
const p = renderImageFromUrl(img, url);
|
|
||||||
|
|
||||||
if(needBlur) {
|
const needBlurCallback = needBlur ? () => {
|
||||||
p.then(() => {
|
//void img.offsetLeft; // reflow
|
||||||
//void img.offsetLeft; // reflow
|
img.style.opacity = '';
|
||||||
img.style.opacity = '';
|
} : undefined;
|
||||||
});
|
renderImageFromUrl(img, url, needBlurCallback);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -8,13 +8,39 @@ export interface CancellablePromise<T> extends Promise<T> {
|
|||||||
resolve?: (...args: any[]) => void,
|
resolve?: (...args: any[]) => void,
|
||||||
reject?: (...args: any[]) => void,
|
reject?: (...args: any[]) => void,
|
||||||
cancel?: () => void,
|
cancel?: () => void,
|
||||||
|
|
||||||
notify?: (...args: any[]) => void,
|
notify?: (...args: any[]) => void,
|
||||||
|
notifyAll?: (...args: any[]) => void,
|
||||||
|
lastNotify?: any,
|
||||||
|
listeners?: Array<(...args: any[]) => void>,
|
||||||
|
addNotifyListener?: (callback: (...args: any[]) => void) => void,
|
||||||
|
|
||||||
isFulfilled?: boolean,
|
isFulfilled?: boolean,
|
||||||
isRejected?: boolean
|
isRejected?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deferredPromise<T>() {
|
export function deferredPromise<T>() {
|
||||||
let deferredHelper: any = {notify: () => {}, isFulfilled: false, isRejected: false};
|
let deferredHelper: any = {
|
||||||
|
isFulfilled: false,
|
||||||
|
isRejected: false,
|
||||||
|
|
||||||
|
notify: () => {},
|
||||||
|
notifyAll: (...args: any[]) => {
|
||||||
|
deferredHelper.lastNotify = args;
|
||||||
|
deferredHelper.listeners.forEach((callback: any) => callback(...args));
|
||||||
|
},
|
||||||
|
|
||||||
|
lastNotify: undefined,
|
||||||
|
listeners: [],
|
||||||
|
addNotifyListener: (callback: (...args: any[]) => void) => {
|
||||||
|
if(deferredHelper.lastNotify) {
|
||||||
|
callback(...deferredHelper.lastNotify);
|
||||||
|
}
|
||||||
|
|
||||||
|
deferredHelper.listeners.push(callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let deferred: CancellablePromise<T> = new Promise<T>((resolve, reject) => {
|
let deferred: CancellablePromise<T> = new Promise<T>((resolve, reject) => {
|
||||||
deferredHelper.resolve = (value: T) => {
|
deferredHelper.resolve = (value: T) => {
|
||||||
if(deferred.isFulfilled) return;
|
if(deferred.isFulfilled) return;
|
||||||
@ -30,6 +56,13 @@ export function deferredPromise<T>() {
|
|||||||
reject(...args);
|
reject(...args);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
deferred.finally(() => {
|
||||||
|
deferred.notify = null;
|
||||||
|
deferred.listeners.length = 0;
|
||||||
|
deferred.lastNotify = null;
|
||||||
|
});
|
||||||
|
|
||||||
Object.assign(deferred, deferredHelper);
|
Object.assign(deferred, deferredHelper);
|
||||||
|
|
||||||
return deferred;
|
return deferred;
|
||||||
|
@ -40,6 +40,10 @@ export class WebpWorkerController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
convert(fileName: string, bytes: Uint8Array) {
|
convert(fileName: string, bytes: Uint8Array) {
|
||||||
|
if(this.convertPromises.hasOwnProperty(fileName)) {
|
||||||
|
return this.convertPromises[fileName];
|
||||||
|
}
|
||||||
|
|
||||||
const convertPromise = deferredPromise<Uint8Array>();
|
const convertPromise = deferredPromise<Uint8Array>();
|
||||||
|
|
||||||
fileName = 'main-' + fileName;
|
fileName = 'main-' + fileName;
|
||||||
|
@ -114,9 +114,14 @@ div.scrollable::-webkit-scrollbar-thumb {
|
|||||||
// BROWSER SCROLL
|
// BROWSER SCROLL
|
||||||
div.scrollable-y::-webkit-scrollbar {
|
div.scrollable-y::-webkit-scrollbar {
|
||||||
width: .375rem;
|
width: .375rem;
|
||||||
|
opacity: 0; // for safari
|
||||||
//height: 200px;
|
//height: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.scrollable:hover::-webkit-scrollbar {
|
||||||
|
opacity: 1; // for safari
|
||||||
|
}
|
||||||
|
|
||||||
/* div.scrollable-y::-webkit-scrollbar-thumb {
|
/* div.scrollable-y::-webkit-scrollbar-thumb {
|
||||||
border: 2px solid rgba(0, 0, 0, 0);
|
border: 2px solid rgba(0, 0, 0, 0);
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
|
5
src/types.d.ts
vendored
5
src/types.d.ts
vendored
@ -12,7 +12,6 @@ export type MTDocument = {
|
|||||||
dc_id: number,
|
dc_id: number,
|
||||||
attributes: any[],
|
attributes: any[],
|
||||||
|
|
||||||
thumb?: MTPhotoSize,
|
|
||||||
type?: 'gif' | 'sticker' | 'audio' | 'voice' | 'video' | 'round' | 'photo',
|
type?: 'gif' | 'sticker' | 'audio' | 'voice' | 'video' | 'round' | 'photo',
|
||||||
h?: number,
|
h?: number,
|
||||||
w?: number,
|
w?: number,
|
||||||
@ -42,7 +41,9 @@ export type MTPhotoSize = {
|
|||||||
size?: number,
|
size?: number,
|
||||||
type?: string, // i, m, x, y, w by asc
|
type?: string, // i, m, x, y, w by asc
|
||||||
location?: FileLocation,
|
location?: FileLocation,
|
||||||
bytes?: Uint8Array // if type == 'i'
|
bytes?: Uint8Array, // if type == 'i',
|
||||||
|
|
||||||
|
url?: string
|
||||||
};
|
};
|
||||||
|
|
||||||
export type InvokeApiOptions = Partial<{
|
export type InvokeApiOptions = Partial<{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user