Several little fixes
Gif Safari & round videos fix
This commit is contained in:
parent
8000ed3285
commit
82043e4c4a
@ -1,22 +1,23 @@
|
|||||||
import appImManager from "../lib/appManagers/appImManager";
|
import appImManager from "../lib/appManagers/appImManager";
|
||||||
import { horizontalMenu, renderImageFromUrl, putPreloader } from "./misc";
|
import { renderImageFromUrl, putPreloader } from "./misc";
|
||||||
import lottieLoader from "../lib/lottieLoader";
|
import lottieLoader from "../lib/lottieLoader";
|
||||||
//import Scrollable from "./scrollable";
|
//import Scrollable from "./scrollable";
|
||||||
import Scrollable from "./scrollable_new";
|
import Scrollable from "./scrollable_new";
|
||||||
import { findUpTag, whichChild, calcImageInBox, emojiUnicode, $rootScope } from "../lib/utils";
|
import { findUpTag, whichChild, calcImageInBox, emojiUnicode, $rootScope, cancelEvent, findUpClassName } from "../lib/utils";
|
||||||
import { RichTextProcessor } from "../lib/richtextprocessor";
|
import { RichTextProcessor } from "../lib/richtextprocessor";
|
||||||
import appStickersManager, { MTStickerSet } from "../lib/appManagers/appStickersManager";
|
import appStickersManager, { MTStickerSet } from "../lib/appManagers/appStickersManager";
|
||||||
//import apiManager from '../lib/mtproto/apiManager';
|
//import apiManager from '../lib/mtproto/apiManager';
|
||||||
import apiManager from '../lib/mtproto/mtprotoworker';
|
import apiManager from '../lib/mtproto/mtprotoworker';
|
||||||
import LazyLoadQueue from "./lazyLoadQueue";
|
import LazyLoadQueue from "./lazyLoadQueue";
|
||||||
import { wrapSticker } from "./wrappers";
|
import { wrapSticker, wrapVideo } from "./wrappers";
|
||||||
import appDocsManager from "../lib/appManagers/appDocsManager";
|
import appDocsManager from "../lib/appManagers/appDocsManager";
|
||||||
import ProgressivePreloader from "./preloader";
|
import ProgressivePreloader from "./preloader_new";
|
||||||
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";
|
||||||
import appSidebarRight from "../lib/appManagers/appSidebarRight";
|
import appSidebarRight from "../lib/appManagers/appSidebarRight";
|
||||||
import appStateManager from "../lib/appManagers/appStateManager";
|
import appStateManager from "../lib/appManagers/appStateManager";
|
||||||
|
import { horizontalMenu } from "./horizontalMenu";
|
||||||
|
|
||||||
export const EMOTICONSSTICKERGROUP = 'emoticons-dropdown';
|
export const EMOTICONSSTICKERGROUP = 'emoticons-dropdown';
|
||||||
|
|
||||||
@ -160,6 +161,10 @@ class EmojiTab implements EmoticonsTab {
|
|||||||
|
|
||||||
spanEmoji.innerHTML = kek;
|
spanEmoji.innerHTML = kek;
|
||||||
|
|
||||||
|
if(spanEmoji.firstElementChild) {
|
||||||
|
(spanEmoji.firstElementChild as HTMLImageElement).setAttribute('loading', 'lazy');
|
||||||
|
}
|
||||||
|
|
||||||
//spanEmoji = spanEmoji.firstElementChild as HTMLSpanElement;
|
//spanEmoji = spanEmoji.firstElementChild as HTMLSpanElement;
|
||||||
//spanEmoji.setAttribute('emoji', emoji);
|
//spanEmoji.setAttribute('emoji', emoji);
|
||||||
if(prepend) container.prepend(spanEmoji);
|
if(prepend) container.prepend(spanEmoji);
|
||||||
@ -561,10 +566,10 @@ class GifsTab implements EmoticonsTab {
|
|||||||
preloader.remove();
|
preloader.remove();
|
||||||
|
|
||||||
for(let i = 0, length = res.gifs.length; i < length;) {
|
for(let i = 0, length = res.gifs.length; i < length;) {
|
||||||
let gif = res.gifs[i];
|
const doc = res.gifs[i];
|
||||||
|
|
||||||
let gifWidth = gif.w;
|
let gifWidth = doc.w;
|
||||||
let gifHeight = gif.h;
|
let gifHeight = doc.h;
|
||||||
if(gifHeight < height) {
|
if(gifHeight < height) {
|
||||||
gifWidth = height / gifHeight * gifWidth;
|
gifWidth = height / gifHeight * gifWidth;
|
||||||
gifHeight = height;
|
gifHeight = height;
|
||||||
@ -588,28 +593,103 @@ class GifsTab implements EmoticonsTab {
|
|||||||
//console.log('gif:', gif, w, h);
|
//console.log('gif:', gif, w, h);
|
||||||
|
|
||||||
let div = document.createElement('div');
|
let div = document.createElement('div');
|
||||||
|
div.classList.add('gif', 'fade-in-transition');
|
||||||
div.style.width = w + 'px';
|
div.style.width = w + 'px';
|
||||||
|
div.style.opacity = '0';
|
||||||
//div.style.height = h + 'px';
|
//div.style.height = h + 'px';
|
||||||
div.dataset.docID = gif.id;
|
div.dataset.docID = doc.id;
|
||||||
|
|
||||||
masonry.append(div);
|
masonry.append(div);
|
||||||
|
|
||||||
let preloader = new ProgressivePreloader(div);
|
//let preloader = new ProgressivePreloader(div);
|
||||||
EmoticonsDropdown.lazyLoadQueue.push({
|
|
||||||
|
const posterURL = appDocsManager.getThumbURL(doc, false);
|
||||||
|
let img: HTMLImageElement;
|
||||||
|
if(posterURL) {
|
||||||
|
img = new Image();
|
||||||
|
img.src = posterURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mouseOut = false;
|
||||||
|
const onMouseOver = (e: MouseEvent) => {
|
||||||
|
//console.log('onMouseOver', doc.id);
|
||||||
|
//cancelEvent(e);
|
||||||
|
mouseOut = false;
|
||||||
|
|
||||||
|
wrapVideo({
|
||||||
|
doc,
|
||||||
|
container: div,
|
||||||
|
//lazyLoadQueue: EmoticonsDropdown.lazyLoadQueue,
|
||||||
|
//group: EMOTICONSSTICKERGROUP,
|
||||||
|
noInfo: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const video = div.querySelector('video');
|
||||||
|
video.addEventListener('loadeddata', () => {
|
||||||
|
div.style.opacity = '';
|
||||||
|
if(!mouseOut) {
|
||||||
|
img && img.remove();
|
||||||
|
} else {
|
||||||
|
div.innerHTML = '';
|
||||||
|
div.append(img);
|
||||||
|
}
|
||||||
|
}, {once: true});
|
||||||
|
};
|
||||||
|
|
||||||
|
((posterURL ? renderImageFromUrl(img, posterURL) : Promise.resolve()) as Promise<any>).then(() => {
|
||||||
|
if(img) {
|
||||||
|
div.append(img);
|
||||||
|
div.style.opacity = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
div.addEventListener('mouseover', onMouseOver, {once: true});
|
||||||
|
div.addEventListener('mouseout', (e) => {
|
||||||
|
const toElement = (e as any).toElement as Element;
|
||||||
|
//console.log('onMouseOut', doc.id, e);
|
||||||
|
if(findUpClassName(toElement, 'gif') == div) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//cancelEvent(e);
|
||||||
|
|
||||||
|
mouseOut = true;
|
||||||
|
|
||||||
|
div.innerHTML = '';
|
||||||
|
div.append(img);
|
||||||
|
div.addEventListener('mouseover', onMouseOver, {once: true});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/* wrapVideo({
|
||||||
|
doc,
|
||||||
|
container: div,
|
||||||
|
lazyLoadQueue: EmoticonsDropdown.lazyLoadQueue,
|
||||||
|
group: EMOTICONSSTICKERGROUP,
|
||||||
|
noInfo: true,
|
||||||
|
}); */
|
||||||
|
|
||||||
|
/* EmoticonsDropdown.lazyLoadQueue.push({
|
||||||
div,
|
div,
|
||||||
load: () => {
|
load: () => {
|
||||||
let promise = appDocsManager.downloadDoc(gif);
|
const download = appDocsManager.downloadDocNew(doc);
|
||||||
preloader.attach(div, true, promise);
|
|
||||||
|
|
||||||
promise.then(blob => {
|
let thumbSize: string, posterURL: string;
|
||||||
|
if(doc.thumbs?.length) {
|
||||||
|
thumbSize = doc.thumbs[0].type;
|
||||||
|
posterURL = appDocsManager.getThumbURL(doc, thumbSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
preloader.attach(div, true, appDocsManager.getInputFileName(doc, thumbSize));
|
||||||
|
|
||||||
|
download.promise.then(blob => {
|
||||||
preloader.detach();
|
preloader.detach();
|
||||||
|
|
||||||
div.innerHTML = `<video autoplay="true" muted="true" loop="true" src="${gif.url}" type="video/mp4"></video>`;
|
div.innerHTML = `<video autoplay="true" muted="true" loop="true" src="${doc.url}" poster="${posterURL}" type="${doc.mime_type}"></video>`;
|
||||||
});
|
});
|
||||||
|
|
||||||
return promise;
|
return download.promise;
|
||||||
}
|
}
|
||||||
});
|
}); */
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -661,6 +741,11 @@ class EmoticonsDropdown {
|
|||||||
//this.displayTimeout = setTimeout(() => {
|
//this.displayTimeout = setTimeout(() => {
|
||||||
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;
|
||||||
|
if(findUpClassName(toElement, 'emoji-dropdown')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
clearTimeout(this.displayTimeout);
|
clearTimeout(this.displayTimeout);
|
||||||
this.displayTimeout = setTimeout(() => {
|
this.displayTimeout = setTimeout(() => {
|
||||||
this.toggle();
|
this.toggle();
|
||||||
|
159
src/components/horizontalMenu.ts
Normal file
159
src/components/horizontalMenu.ts
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
import { findUpTag, whichChild } from "../lib/utils";
|
||||||
|
|
||||||
|
function slideNavigation(tabContent: HTMLElement, prevTabContent: HTMLElement, toRight: boolean) {
|
||||||
|
/* if(toRight) {
|
||||||
|
//prevTabContent.style.filter = `brightness(80%)`;
|
||||||
|
prevTabContent.style.transform = `translateX(-25%)`;
|
||||||
|
tabContent.style.transform = `translateX(20%)`;
|
||||||
|
} else {
|
||||||
|
//tabContent.style.filter = `brightness(80%)`;
|
||||||
|
tabContent.style.transform = `translateX(-25%)`;
|
||||||
|
prevTabContent.style.transform = `translateX(20%)`;
|
||||||
|
} */
|
||||||
|
if(toRight) {
|
||||||
|
prevTabContent.style.filter = `brightness(80%)`;
|
||||||
|
prevTabContent.style.transform = `translateX(-25%)`;
|
||||||
|
tabContent.style.transform = `translateX(100%)`;
|
||||||
|
} else {
|
||||||
|
tabContent.style.filter = `brightness(80%)`;
|
||||||
|
tabContent.style.transform = `translateX(-25%)`;
|
||||||
|
prevTabContent.style.transform = `translateX(100%)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
tabContent.classList.add('active');
|
||||||
|
void tabContent.offsetWidth; // reflow
|
||||||
|
|
||||||
|
tabContent.style.transform = '';
|
||||||
|
tabContent.style.filter = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function slideTabs(tabContent: HTMLElement, prevTabContent: HTMLElement, toRight: boolean) {
|
||||||
|
if(toRight) {
|
||||||
|
tabContent.style.transform = `translateX(100%)`;
|
||||||
|
prevTabContent.style.transform = `translateX(-100%)`;
|
||||||
|
} else {
|
||||||
|
tabContent.style.transform = `translateX(-100%)`;
|
||||||
|
prevTabContent.style.transform = `translateX(100%)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
tabContent.classList.add('active');
|
||||||
|
void tabContent.offsetWidth; // reflow
|
||||||
|
|
||||||
|
tabContent.style.transform = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick?: (id: number, tabContent: HTMLDivElement) => void, onTransitionEnd?: () => void, transitionTime = 300) {
|
||||||
|
const hideTimeouts: {[id: number]: number} = {};
|
||||||
|
let prevTabContent: HTMLElement = null;
|
||||||
|
let prevId = -1;
|
||||||
|
|
||||||
|
const selectTab = (id: number) => {
|
||||||
|
if(id == prevId) return false;
|
||||||
|
|
||||||
|
//console.log('selectTab id:', id);
|
||||||
|
|
||||||
|
const p = prevTabContent;
|
||||||
|
const tabContent = content.children[id] as HTMLElement;
|
||||||
|
|
||||||
|
if(content.dataset.slider == 'none') {
|
||||||
|
if(p) {
|
||||||
|
p.classList.remove('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
tabContent.classList.add('active');
|
||||||
|
|
||||||
|
prevId = id;
|
||||||
|
prevTabContent = tabContent;
|
||||||
|
|
||||||
|
if(onTransitionEnd) onTransitionEnd();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const toRight = prevId < id;
|
||||||
|
if(prevId != -1) {
|
||||||
|
if(tabs || content.dataset.slider == 'tabs') {
|
||||||
|
slideTabs(tabContent, prevTabContent, toRight);
|
||||||
|
} else {
|
||||||
|
slideNavigation(tabContent, prevTabContent, toRight);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tabContent.classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
const _prevId = prevId;
|
||||||
|
if(hideTimeouts.hasOwnProperty(id)) clearTimeout(hideTimeouts[id]);
|
||||||
|
if(p/* && false */) {
|
||||||
|
hideTimeouts[_prevId] = setTimeout(() => {
|
||||||
|
p.style.transform = '';
|
||||||
|
p.style.filter = '';
|
||||||
|
p.classList.remove('active');
|
||||||
|
|
||||||
|
delete hideTimeouts[_prevId];
|
||||||
|
|
||||||
|
if(onTransitionEnd) onTransitionEnd();
|
||||||
|
}, /* 420 */transitionTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
prevId = id;
|
||||||
|
prevTabContent = tabContent;
|
||||||
|
};
|
||||||
|
|
||||||
|
if(tabs) {
|
||||||
|
let activeStripe: HTMLSpanElement;
|
||||||
|
if(!tabs.classList.contains('no-stripe')) {
|
||||||
|
activeStripe = document.createElement('span');
|
||||||
|
activeStripe.classList.add('menu-horizontal__stripe');
|
||||||
|
|
||||||
|
tabs.append(activeStripe);
|
||||||
|
}
|
||||||
|
|
||||||
|
const tagName = 'LI';//tabs.firstElementChild.tagName;
|
||||||
|
tabs.addEventListener('click', function(e) {
|
||||||
|
let target = e.target as HTMLElement;
|
||||||
|
|
||||||
|
if(target.tagName != tagName) {
|
||||||
|
target = findUpTag(target, tagName);
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log('tabs click:', target);
|
||||||
|
|
||||||
|
if(!target) return false;
|
||||||
|
|
||||||
|
let id: number;
|
||||||
|
if(target.dataset.tab) {
|
||||||
|
id = +target.dataset.tab;
|
||||||
|
if(id == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
id = whichChild(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
const tabContent = content.children[id] as HTMLDivElement;
|
||||||
|
|
||||||
|
if(onClick) onClick(id, tabContent);
|
||||||
|
if(target.classList.contains('active') || id == prevId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const prev = tabs.querySelector(tagName.toLowerCase() + '.active') as HTMLElement;
|
||||||
|
prev && prev.classList.remove('active');
|
||||||
|
|
||||||
|
if(activeStripe) {
|
||||||
|
const tabsRect = tabs.getBoundingClientRect();
|
||||||
|
const targetRect = target.getBoundingClientRect();
|
||||||
|
const width = 50;
|
||||||
|
activeStripe.style.cssText = `width: ${width}px; transform: translateX(${targetRect.left - tabsRect.left + ((targetRect.width - width) / 2)}px);`;
|
||||||
|
/* const textRect = target.firstElementChild.getBoundingClientRect();
|
||||||
|
activeStripe.style.cssText = `width: ${textRect.width + (2 * 2)}px; transform: translateX(${textRect.left - tabsRect.left}px);`; */
|
||||||
|
//activeStripe.style.transform = `scaleX(${textRect.width}) translateX(${(textRect.left - tabsRect.left) / textRect.width + 0.5}px)`;
|
||||||
|
//console.log('tabs click:', tabsRect, textRect);
|
||||||
|
}
|
||||||
|
|
||||||
|
target.classList.add('active');
|
||||||
|
selectTab(id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return selectTab;
|
||||||
|
}
|
@ -1,176 +1,18 @@
|
|||||||
import { whichChild, findUpTag, cancelEvent, findUpClassName } from "../lib/utils";
|
|
||||||
import Config, { touchSupport, isApple, mediaSizes } from "../lib/config";
|
import Config, { touchSupport, isApple, mediaSizes } from "../lib/config";
|
||||||
|
|
||||||
let rippleClickID = 0;
|
|
||||||
export function ripple(elem: HTMLElement, callback: (id: number) => Promise<boolean | void> = () => Promise.resolve(), onEnd: (id: number) => void = null) {
|
|
||||||
//return;
|
|
||||||
if(elem.querySelector('.c-ripple')) return;
|
|
||||||
elem.classList.add('rp');
|
|
||||||
|
|
||||||
let r = document.createElement('div');
|
|
||||||
r.classList.add('c-ripple');
|
|
||||||
|
|
||||||
const isSquare = elem.classList.contains('rp-square');
|
|
||||||
if(isSquare) {
|
|
||||||
r.classList.add('is-square');
|
|
||||||
}
|
|
||||||
|
|
||||||
const duration = isSquare ? 200 : 700;
|
|
||||||
|
|
||||||
elem.append(r);
|
|
||||||
|
|
||||||
let handler: () => void;
|
|
||||||
let drawRipple = (clientX: number, clientY: number) => {
|
|
||||||
let startTime = Date.now();
|
|
||||||
let span = document.createElement('span');
|
|
||||||
|
|
||||||
let clickID = rippleClickID++;
|
|
||||||
|
|
||||||
//console.log('ripple drawRipple');
|
|
||||||
|
|
||||||
handler = () => {
|
|
||||||
let elapsedTime = Date.now() - startTime;
|
|
||||||
if(elapsedTime < duration) {
|
|
||||||
let delay = Math.max(duration - elapsedTime, duration / 2);
|
|
||||||
setTimeout(() => span.classList.add('hiding'), Math.max(delay - duration / 2, 0));
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
//console.log('ripple elapsedTime total pre-remove:', Date.now() - startTime);
|
|
||||||
span.remove();
|
|
||||||
if(onEnd) onEnd(clickID);
|
|
||||||
}, delay);
|
|
||||||
} else {
|
|
||||||
span.classList.add('hiding');
|
|
||||||
setTimeout(() => {
|
|
||||||
//console.log('ripple elapsedTime total pre-remove:', Date.now() - startTime);
|
|
||||||
span.remove();
|
|
||||||
if(onEnd) onEnd(clickID);
|
|
||||||
}, duration / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
handler = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
callback && callback(clickID);
|
|
||||||
|
|
||||||
/* callback().then((bad) => {
|
|
||||||
if(bad) {
|
|
||||||
span.remove();
|
|
||||||
return;
|
|
||||||
} */
|
|
||||||
|
|
||||||
//console.log('ripple after promise', Date.now() - startTime);
|
|
||||||
//console.log('ripple tooSlow:', tooSlow);
|
|
||||||
/* if(tooSlow) {
|
|
||||||
span.remove();
|
|
||||||
return;
|
|
||||||
} */
|
|
||||||
|
|
||||||
window.requestAnimationFrame(() => {
|
|
||||||
span.classList.add('c-ripple__circle');
|
|
||||||
let rect = r.getBoundingClientRect();
|
|
||||||
|
|
||||||
let clickX = clientX - rect.left;
|
|
||||||
let clickY = clientY - rect.top;
|
|
||||||
|
|
||||||
let size: number, clickPos: number;
|
|
||||||
if(rect.width > rect.height) {
|
|
||||||
size = rect.width;
|
|
||||||
clickPos = clickX;
|
|
||||||
} else {
|
|
||||||
size = rect.height;
|
|
||||||
clickPos = clickY;
|
|
||||||
}
|
|
||||||
|
|
||||||
let offsetFromCenter = clickPos > (size / 2) ? size - clickPos : clickPos;
|
|
||||||
size = size - offsetFromCenter;
|
|
||||||
size *= 1.1;
|
|
||||||
|
|
||||||
// center of circle
|
|
||||||
let x = clickX - size / 2;
|
|
||||||
let y = clickY - size / 2;
|
|
||||||
|
|
||||||
//console.log('ripple click', offsetFromCenter, size, clickX, clickY);
|
|
||||||
|
|
||||||
span.style.width = span.style.height = size + 'px';
|
|
||||||
span.style.left = x + 'px';
|
|
||||||
span.style.top = y + 'px';
|
|
||||||
|
|
||||||
r.append(span);
|
|
||||||
//r.classList.add('active');
|
|
||||||
//handler();
|
|
||||||
});
|
|
||||||
//});
|
|
||||||
};
|
|
||||||
|
|
||||||
let touchStartFired = false;
|
|
||||||
if(touchSupport) {
|
|
||||||
let touchEnd = () => {
|
|
||||||
handler && handler();
|
|
||||||
};
|
|
||||||
|
|
||||||
elem.addEventListener('touchstart', (e) => {
|
|
||||||
//console.log('ripple touchstart', e);
|
|
||||||
if(e.touches.length > 1 || ((e.target as HTMLElement).tagName == 'BUTTON' && e.target != elem)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//console.log('touchstart', e);
|
|
||||||
touchStartFired = true;
|
|
||||||
|
|
||||||
let {clientX, clientY} = e.touches[0];
|
|
||||||
drawRipple(clientX, clientY);
|
|
||||||
window.addEventListener('touchend', touchEnd, {once: true});
|
|
||||||
|
|
||||||
window.addEventListener('touchmove', (e) => {
|
|
||||||
e.cancelBubble = true;
|
|
||||||
e.stopPropagation();
|
|
||||||
handler && handler();
|
|
||||||
window.removeEventListener('touchend', touchEnd);
|
|
||||||
}, {once: true});
|
|
||||||
}, {passive: true});
|
|
||||||
} else {
|
|
||||||
elem.addEventListener('mousedown', (e) => {
|
|
||||||
if(elem.dataset.ripple == '0') {
|
|
||||||
return false;
|
|
||||||
} else if(touchStartFired) {
|
|
||||||
touchStartFired = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let {clientX, clientY} = e;
|
|
||||||
drawRipple(clientX, clientY);
|
|
||||||
window.addEventListener('mouseup', handler, {once: true});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const toastEl = document.createElement('div');
|
|
||||||
toastEl.classList.add('toast');
|
|
||||||
export function toast(html: string) {
|
|
||||||
toastEl.innerHTML = html;
|
|
||||||
document.body.append(toastEl);
|
|
||||||
|
|
||||||
if(toastEl.dataset.timeout) clearTimeout(+toastEl.dataset.timeout);
|
|
||||||
toastEl.dataset.timeout = '' + setTimeout(() => {
|
|
||||||
toastEl.remove();
|
|
||||||
delete toastEl.dataset.timeout;
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
let loadedURLs: {[url: string]: boolean} = {};
|
let loadedURLs: {[url: string]: boolean} = {};
|
||||||
let set = (elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLSourceElement, url: string) => {
|
let set = (elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, url: string) => {
|
||||||
if(elem instanceof HTMLImageElement || elem instanceof HTMLSourceElement) 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 | HTMLSourceElement, url: string): Promise<boolean> {
|
export async function renderImageFromUrl(elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, url: string): Promise<boolean> {
|
||||||
if(loadedURLs[url]) {
|
if(loadedURLs[url]) {
|
||||||
set(elem, url);
|
set(elem, url);
|
||||||
} else {
|
} else {
|
||||||
if(elem instanceof HTMLSourceElement) {
|
if(elem instanceof HTMLVideoElement) {
|
||||||
elem.src = url;
|
set(elem, url);
|
||||||
} else {
|
} else {
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
let loader = new Image();
|
let loader = new Image();
|
||||||
@ -185,6 +27,7 @@ export async function renderImageFromUrl(elem: HTMLElement | HTMLImageElement |
|
|||||||
loader.addEventListener('error', reject);
|
loader.addEventListener('error', reject);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return !!loadedURLs[url];
|
return !!loadedURLs[url];
|
||||||
@ -211,164 +54,6 @@ export function putPreloader(elem: Element, returnDiv = false) {
|
|||||||
elem.innerHTML += html;
|
elem.innerHTML += html;
|
||||||
}
|
}
|
||||||
|
|
||||||
function slideNavigation(tabContent: HTMLElement, prevTabContent: HTMLElement, toRight: boolean) {
|
|
||||||
/* if(toRight) {
|
|
||||||
//prevTabContent.style.filter = `brightness(80%)`;
|
|
||||||
prevTabContent.style.transform = `translateX(-25%)`;
|
|
||||||
tabContent.style.transform = `translateX(20%)`;
|
|
||||||
} else {
|
|
||||||
//tabContent.style.filter = `brightness(80%)`;
|
|
||||||
tabContent.style.transform = `translateX(-25%)`;
|
|
||||||
prevTabContent.style.transform = `translateX(20%)`;
|
|
||||||
} */
|
|
||||||
if(toRight) {
|
|
||||||
prevTabContent.style.filter = `brightness(80%)`;
|
|
||||||
prevTabContent.style.transform = `translateX(-25%)`;
|
|
||||||
tabContent.style.transform = `translateX(100%)`;
|
|
||||||
} else {
|
|
||||||
tabContent.style.filter = `brightness(80%)`;
|
|
||||||
tabContent.style.transform = `translateX(-25%)`;
|
|
||||||
prevTabContent.style.transform = `translateX(100%)`;
|
|
||||||
}
|
|
||||||
|
|
||||||
tabContent.classList.add('active');
|
|
||||||
void tabContent.offsetWidth; // reflow
|
|
||||||
|
|
||||||
tabContent.style.transform = '';
|
|
||||||
tabContent.style.filter = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
function slideTabs(tabContent: HTMLElement, prevTabContent: HTMLElement, toRight: boolean) {
|
|
||||||
if(toRight) {
|
|
||||||
tabContent.style.transform = `translateX(100%)`;
|
|
||||||
prevTabContent.style.transform = `translateX(-100%)`;
|
|
||||||
} else {
|
|
||||||
tabContent.style.transform = `translateX(-100%)`;
|
|
||||||
prevTabContent.style.transform = `translateX(100%)`;
|
|
||||||
}
|
|
||||||
|
|
||||||
tabContent.classList.add('active');
|
|
||||||
void tabContent.offsetWidth; // reflow
|
|
||||||
|
|
||||||
tabContent.style.transform = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick?: (id: number, tabContent: HTMLDivElement) => void, onTransitionEnd?: () => void, transitionTime = 300) {
|
|
||||||
const hideTimeouts: {[id: number]: number} = {};
|
|
||||||
let prevTabContent: HTMLElement = null;
|
|
||||||
let prevId = -1;
|
|
||||||
|
|
||||||
const selectTab = (id: number) => {
|
|
||||||
if(id == prevId) return false;
|
|
||||||
|
|
||||||
//console.log('selectTab id:', id);
|
|
||||||
|
|
||||||
const p = prevTabContent;
|
|
||||||
const tabContent = content.children[id] as HTMLElement;
|
|
||||||
|
|
||||||
if(content.dataset.slider == 'none') {
|
|
||||||
if(p) {
|
|
||||||
p.classList.remove('active');
|
|
||||||
}
|
|
||||||
|
|
||||||
tabContent.classList.add('active');
|
|
||||||
|
|
||||||
prevId = id;
|
|
||||||
prevTabContent = tabContent;
|
|
||||||
|
|
||||||
if(onTransitionEnd) onTransitionEnd();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const toRight = prevId < id;
|
|
||||||
if(prevId != -1) {
|
|
||||||
if(tabs || content.dataset.slider == 'tabs') {
|
|
||||||
slideTabs(tabContent, prevTabContent, toRight);
|
|
||||||
} else {
|
|
||||||
slideNavigation(tabContent, prevTabContent, toRight);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tabContent.classList.add('active');
|
|
||||||
}
|
|
||||||
|
|
||||||
const _prevId = prevId;
|
|
||||||
if(hideTimeouts.hasOwnProperty(id)) clearTimeout(hideTimeouts[id]);
|
|
||||||
if(p/* && false */) {
|
|
||||||
hideTimeouts[_prevId] = setTimeout(() => {
|
|
||||||
p.style.transform = '';
|
|
||||||
p.style.filter = '';
|
|
||||||
p.classList.remove('active');
|
|
||||||
|
|
||||||
delete hideTimeouts[_prevId];
|
|
||||||
|
|
||||||
if(onTransitionEnd) onTransitionEnd();
|
|
||||||
}, /* 420 */transitionTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
prevId = id;
|
|
||||||
prevTabContent = tabContent;
|
|
||||||
};
|
|
||||||
|
|
||||||
if(tabs) {
|
|
||||||
let activeStripe: HTMLSpanElement;
|
|
||||||
if(!tabs.classList.contains('no-stripe')) {
|
|
||||||
activeStripe = document.createElement('span');
|
|
||||||
activeStripe.classList.add('menu-horizontal__stripe');
|
|
||||||
|
|
||||||
tabs.append(activeStripe);
|
|
||||||
}
|
|
||||||
|
|
||||||
const tagName = 'LI';//tabs.firstElementChild.tagName;
|
|
||||||
tabs.addEventListener('click', function(e) {
|
|
||||||
let target = e.target as HTMLElement;
|
|
||||||
|
|
||||||
if(target.tagName != tagName) {
|
|
||||||
target = findUpTag(target, tagName);
|
|
||||||
}
|
|
||||||
|
|
||||||
//console.log('tabs click:', target);
|
|
||||||
|
|
||||||
if(!target) return false;
|
|
||||||
|
|
||||||
let id: number;
|
|
||||||
if(target.dataset.tab) {
|
|
||||||
id = +target.dataset.tab;
|
|
||||||
if(id == -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
id = whichChild(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
const tabContent = content.children[id] as HTMLDivElement;
|
|
||||||
|
|
||||||
if(onClick) onClick(id, tabContent);
|
|
||||||
if(target.classList.contains('active') || id == prevId) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const prev = tabs.querySelector(tagName.toLowerCase() + '.active') as HTMLElement;
|
|
||||||
prev && prev.classList.remove('active');
|
|
||||||
|
|
||||||
if(activeStripe) {
|
|
||||||
const tabsRect = tabs.getBoundingClientRect();
|
|
||||||
const targetRect = target.getBoundingClientRect();
|
|
||||||
const width = 50;
|
|
||||||
activeStripe.style.cssText = `width: ${width}px; transform: translateX(${targetRect.left - tabsRect.left + ((targetRect.width - width) / 2)}px);`;
|
|
||||||
/* const textRect = target.firstElementChild.getBoundingClientRect();
|
|
||||||
activeStripe.style.cssText = `width: ${textRect.width + (2 * 2)}px; transform: translateX(${textRect.left - tabsRect.left}px);`; */
|
|
||||||
//activeStripe.style.transform = `scaleX(${textRect.width}) translateX(${(textRect.left - tabsRect.left) / textRect.width + 0.5}px)`;
|
|
||||||
//console.log('tabs click:', tabsRect, textRect);
|
|
||||||
}
|
|
||||||
|
|
||||||
target.classList.add('active');
|
|
||||||
selectTab(id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return selectTab;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function formatPhoneNumber(str: string) {
|
export function formatPhoneNumber(str: string) {
|
||||||
str = str.replace(/\D/g, '');
|
str = str.replace(/\D/g, '');
|
||||||
let phoneCode = str.slice(0, 6);
|
let phoneCode = str.slice(0, 6);
|
||||||
|
@ -2,10 +2,10 @@ import appPollsManager, { PollResults, Poll } from "../lib/appManagers/appPollsM
|
|||||||
import { RichTextProcessor } from "../lib/richtextprocessor";
|
import { RichTextProcessor } from "../lib/richtextprocessor";
|
||||||
import { findUpClassName, $rootScope, cancelEvent } from "../lib/utils";
|
import { findUpClassName, $rootScope, cancelEvent } from "../lib/utils";
|
||||||
import { mediaSizes, touchSupport } from "../lib/config";
|
import { mediaSizes, touchSupport } from "../lib/config";
|
||||||
import { ripple } from "./misc";
|
|
||||||
import appSidebarRight from "../lib/appManagers/appSidebarRight";
|
import appSidebarRight from "../lib/appManagers/appSidebarRight";
|
||||||
import appImManager from "../lib/appManagers/appImManager";
|
import appImManager from "../lib/appManagers/appImManager";
|
||||||
import serverTimeManager from "../lib/mtproto/serverTimeManager";
|
import serverTimeManager from "../lib/mtproto/serverTimeManager";
|
||||||
|
import { ripple } from "./ripple";
|
||||||
|
|
||||||
let lineTotalLength = 0;
|
let lineTotalLength = 0;
|
||||||
const tailLength = 9;
|
const tailLength = 9;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import AvatarElement from "./avatar";
|
import AvatarElement from "./avatar";
|
||||||
import { ripple } from "./misc";
|
import { ripple } from "./ripple";
|
||||||
|
|
||||||
export class PopupElement {
|
export class PopupElement {
|
||||||
protected element = document.createElement('div');
|
protected element = document.createElement('div');
|
||||||
|
@ -3,8 +3,7 @@ import Scrollable from "./scrollable_new";
|
|||||||
import appMessagesManager from "../lib/appManagers/appMessagesManager";
|
import appMessagesManager from "../lib/appManagers/appMessagesManager";
|
||||||
import { $rootScope } from "../lib/utils";
|
import { $rootScope } from "../lib/utils";
|
||||||
import { Poll } from "../lib/appManagers/appPollsManager";
|
import { Poll } from "../lib/appManagers/appPollsManager";
|
||||||
import { nextRandomInt, bigint } from "../lib/bin_utils";
|
import { toast } from "./toast";
|
||||||
import { toast } from "./misc";
|
|
||||||
|
|
||||||
const InputField = (placeholder: string, label: string, name: string) => {
|
const InputField = (placeholder: string, label: string, name: string) => {
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
|
145
src/components/ripple.ts
Normal file
145
src/components/ripple.ts
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
import { touchSupport } from "../lib/config";
|
||||||
|
|
||||||
|
let rippleClickID = 0;
|
||||||
|
export function ripple(elem: HTMLElement, callback: (id: number) => Promise<boolean | void> = () => Promise.resolve(), onEnd: (id: number) => void = null) {
|
||||||
|
//return;
|
||||||
|
if(elem.querySelector('.c-ripple')) return;
|
||||||
|
elem.classList.add('rp');
|
||||||
|
|
||||||
|
let r = document.createElement('div');
|
||||||
|
r.classList.add('c-ripple');
|
||||||
|
|
||||||
|
const isSquare = elem.classList.contains('rp-square');
|
||||||
|
if(isSquare) {
|
||||||
|
r.classList.add('is-square');
|
||||||
|
}
|
||||||
|
|
||||||
|
const duration = isSquare ? 200 : 700;
|
||||||
|
|
||||||
|
elem.append(r);
|
||||||
|
|
||||||
|
let handler: () => void;
|
||||||
|
let drawRipple = (clientX: number, clientY: number) => {
|
||||||
|
let startTime = Date.now();
|
||||||
|
let span = document.createElement('span');
|
||||||
|
|
||||||
|
let clickID = rippleClickID++;
|
||||||
|
|
||||||
|
//console.log('ripple drawRipple');
|
||||||
|
|
||||||
|
handler = () => {
|
||||||
|
let elapsedTime = Date.now() - startTime;
|
||||||
|
if(elapsedTime < duration) {
|
||||||
|
let delay = Math.max(duration - elapsedTime, duration / 2);
|
||||||
|
setTimeout(() => span.classList.add('hiding'), Math.max(delay - duration / 2, 0));
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
//console.log('ripple elapsedTime total pre-remove:', Date.now() - startTime);
|
||||||
|
span.remove();
|
||||||
|
if(onEnd) onEnd(clickID);
|
||||||
|
}, delay);
|
||||||
|
} else {
|
||||||
|
span.classList.add('hiding');
|
||||||
|
setTimeout(() => {
|
||||||
|
//console.log('ripple elapsedTime total pre-remove:', Date.now() - startTime);
|
||||||
|
span.remove();
|
||||||
|
if(onEnd) onEnd(clickID);
|
||||||
|
}, duration / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
handler = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
callback && callback(clickID);
|
||||||
|
|
||||||
|
/* callback().then((bad) => {
|
||||||
|
if(bad) {
|
||||||
|
span.remove();
|
||||||
|
return;
|
||||||
|
} */
|
||||||
|
|
||||||
|
//console.log('ripple after promise', Date.now() - startTime);
|
||||||
|
//console.log('ripple tooSlow:', tooSlow);
|
||||||
|
/* if(tooSlow) {
|
||||||
|
span.remove();
|
||||||
|
return;
|
||||||
|
} */
|
||||||
|
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
span.classList.add('c-ripple__circle');
|
||||||
|
let rect = r.getBoundingClientRect();
|
||||||
|
|
||||||
|
let clickX = clientX - rect.left;
|
||||||
|
let clickY = clientY - rect.top;
|
||||||
|
|
||||||
|
let size: number, clickPos: number;
|
||||||
|
if(rect.width > rect.height) {
|
||||||
|
size = rect.width;
|
||||||
|
clickPos = clickX;
|
||||||
|
} else {
|
||||||
|
size = rect.height;
|
||||||
|
clickPos = clickY;
|
||||||
|
}
|
||||||
|
|
||||||
|
let offsetFromCenter = clickPos > (size / 2) ? size - clickPos : clickPos;
|
||||||
|
size = size - offsetFromCenter;
|
||||||
|
size *= 1.1;
|
||||||
|
|
||||||
|
// center of circle
|
||||||
|
let x = clickX - size / 2;
|
||||||
|
let y = clickY - size / 2;
|
||||||
|
|
||||||
|
//console.log('ripple click', offsetFromCenter, size, clickX, clickY);
|
||||||
|
|
||||||
|
span.style.width = span.style.height = size + 'px';
|
||||||
|
span.style.left = x + 'px';
|
||||||
|
span.style.top = y + 'px';
|
||||||
|
|
||||||
|
r.append(span);
|
||||||
|
//r.classList.add('active');
|
||||||
|
//handler();
|
||||||
|
});
|
||||||
|
//});
|
||||||
|
};
|
||||||
|
|
||||||
|
let touchStartFired = false;
|
||||||
|
if(touchSupport) {
|
||||||
|
let touchEnd = () => {
|
||||||
|
handler && handler();
|
||||||
|
};
|
||||||
|
|
||||||
|
elem.addEventListener('touchstart', (e) => {
|
||||||
|
//console.log('ripple touchstart', e);
|
||||||
|
if(e.touches.length > 1 || ((e.target as HTMLElement).tagName == 'BUTTON' && e.target != elem)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log('touchstart', e);
|
||||||
|
touchStartFired = true;
|
||||||
|
|
||||||
|
let {clientX, clientY} = e.touches[0];
|
||||||
|
drawRipple(clientX, clientY);
|
||||||
|
window.addEventListener('touchend', touchEnd, {once: true});
|
||||||
|
|
||||||
|
window.addEventListener('touchmove', (e) => {
|
||||||
|
e.cancelBubble = true;
|
||||||
|
e.stopPropagation();
|
||||||
|
handler && handler();
|
||||||
|
window.removeEventListener('touchend', touchEnd);
|
||||||
|
}, {once: true});
|
||||||
|
}, {passive: true});
|
||||||
|
} else {
|
||||||
|
elem.addEventListener('mousedown', (e) => {
|
||||||
|
if(elem.dataset.ripple == '0') {
|
||||||
|
return false;
|
||||||
|
} else if(touchStartFired) {
|
||||||
|
touchStartFired = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let {clientX, clientY} = e;
|
||||||
|
drawRipple(clientX, clientY);
|
||||||
|
window.addEventListener('mouseup', handler, {once: true});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -4,9 +4,9 @@ import apiManager from "../../lib/mtproto/mtprotoworker";
|
|||||||
import appMessagesManager, { DialogFilter } from "../../lib/appManagers/appMessagesManager";
|
import appMessagesManager, { DialogFilter } from "../../lib/appManagers/appMessagesManager";
|
||||||
import { RichTextProcessor } from "../../lib/richtextprocessor";
|
import { RichTextProcessor } from "../../lib/richtextprocessor";
|
||||||
import appPeersManager from "../../lib/appManagers/appPeersManager";
|
import appPeersManager from "../../lib/appManagers/appPeersManager";
|
||||||
import { ripple } from "../misc";
|
|
||||||
import { $rootScope, cancelEvent } from "../../lib/utils";
|
import { $rootScope, cancelEvent } from "../../lib/utils";
|
||||||
import appSidebarLeft from "../../lib/appManagers/appSidebarLeft";
|
import appSidebarLeft from "../../lib/appManagers/appSidebarLeft";
|
||||||
|
import { ripple } from "../ripple";
|
||||||
|
|
||||||
type DialogFilterSuggested = {
|
type DialogFilterSuggested = {
|
||||||
_: 'dialogFilterSuggested',
|
_: 'dialogFilterSuggested',
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import { SliderTab } from "../slider";
|
import { SliderTab } from "../slider";
|
||||||
import appSidebarLeft, { AppSidebarLeft } from "../../lib/appManagers/appSidebarLeft";
|
import appSidebarLeft, { AppSidebarLeft } from "../../lib/appManagers/appSidebarLeft";
|
||||||
import lottieLoader, { RLottiePlayer } from "../../lib/lottieLoader";
|
import lottieLoader, { RLottiePlayer } from "../../lib/lottieLoader";
|
||||||
import appMessagesManager, { DialogFilter, Dialog } from "../../lib/appManagers/appMessagesManager";
|
import appMessagesManager, { DialogFilter } from "../../lib/appManagers/appMessagesManager";
|
||||||
import { parseMenuButtonsTo, ripple, toast } from "../misc";
|
import { parseMenuButtonsTo } from "../misc";
|
||||||
import appDialogsManager from "../../lib/appManagers/appDialogsManager";
|
import appDialogsManager from "../../lib/appManagers/appDialogsManager";
|
||||||
import { copy, deepEqual } from "../../lib/utils";
|
import { copy, deepEqual } from "../../lib/utils";
|
||||||
|
import { toast } from "../toast";
|
||||||
|
import { ripple } from "../ripple";
|
||||||
|
|
||||||
export default class AppEditFolderTab implements SliderTab {
|
export default class AppEditFolderTab implements SliderTab {
|
||||||
public container: HTMLElement;
|
public container: HTMLElement;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { horizontalMenu } from "./misc";
|
import { horizontalMenu } from "./horizontalMenu";
|
||||||
|
|
||||||
export interface SliderTab {
|
export interface SliderTab {
|
||||||
onOpen?: () => void,
|
onOpen?: () => void,
|
||||||
|
12
src/components/toast.ts
Normal file
12
src/components/toast.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
const toastEl = document.createElement('div');
|
||||||
|
toastEl.classList.add('toast');
|
||||||
|
export function toast(html: string) {
|
||||||
|
toastEl.innerHTML = html;
|
||||||
|
document.body.append(toastEl);
|
||||||
|
|
||||||
|
if(toastEl.dataset.timeout) clearTimeout(+toastEl.dataset.timeout);
|
||||||
|
toastEl.dataset.timeout = '' + setTimeout(() => {
|
||||||
|
toastEl.remove();
|
||||||
|
delete toastEl.dataset.timeout;
|
||||||
|
}, 3000);
|
||||||
|
}
|
@ -18,19 +18,23 @@ import AudioElement from './audio';
|
|||||||
import { Download } from '../lib/appManagers/appDownloadManager';
|
import { 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}: {
|
export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue, noInfo, group}: {
|
||||||
doc: MTDocument,
|
doc: MTDocument,
|
||||||
container: HTMLDivElement,
|
container?: HTMLDivElement,
|
||||||
message: any,
|
message?: any,
|
||||||
boxWidth: number,
|
boxWidth?: number,
|
||||||
boxHeight: number,
|
boxHeight?: number,
|
||||||
withTail?: boolean,
|
withTail?: boolean,
|
||||||
isOut?: boolean,
|
isOut?: boolean,
|
||||||
middleware: () => boolean,
|
middleware?: () => boolean,
|
||||||
lazyLoadQueue: LazyLoadQueue
|
lazyLoadQueue?: LazyLoadQueue,
|
||||||
|
noInfo?: true,
|
||||||
|
group?: string,
|
||||||
}) {
|
}) {
|
||||||
let span: HTMLSpanElement, spanPlay: HTMLSpanElement;
|
if(!noInfo) {
|
||||||
if(doc.type != 'round') {
|
if(doc.type != 'round') {
|
||||||
|
let span: HTMLSpanElement, spanPlay: HTMLSpanElement;
|
||||||
|
|
||||||
span = document.createElement('span');
|
span = document.createElement('span');
|
||||||
span.classList.add('video-time');
|
span.classList.add('video-time');
|
||||||
container.append(span);
|
container.append(span);
|
||||||
@ -45,12 +49,16 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
|||||||
span.innerText = 'GIF';
|
span.innerText = 'GIF';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const video = document.createElement('video');
|
||||||
|
|
||||||
|
let img: HTMLImageElement;
|
||||||
|
if(message) {
|
||||||
if(doc.type == 'video') {
|
if(doc.type == 'video') {
|
||||||
return wrapPhoto(doc, message, container, boxWidth, boxHeight, withTail, isOut, lazyLoadQueue, middleware);
|
return wrapPhoto(doc, message, container, boxWidth, boxHeight, withTail, isOut, lazyLoadQueue, middleware);
|
||||||
}
|
}
|
||||||
|
|
||||||
let img: HTMLImageElement;
|
|
||||||
if(withTail) {
|
if(withTail) {
|
||||||
img = wrapMediaWithTail(doc, message, container, boxWidth, boxHeight, isOut);
|
img = wrapMediaWithTail(doc, message, container, boxWidth, boxHeight, isOut);
|
||||||
} else {
|
} else {
|
||||||
@ -75,21 +83,27 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
|||||||
img.classList.add('thumbnail');
|
img.classList.add('thumbnail');
|
||||||
}
|
}
|
||||||
|
|
||||||
const video = document.createElement('video');
|
|
||||||
const source = document.createElement('source');
|
|
||||||
video.append(source);
|
|
||||||
|
|
||||||
if(withTail) {
|
if(withTail) {
|
||||||
const foreignObject = img.parentElement;
|
const foreignObject = img.parentElement;
|
||||||
video.width = +foreignObject.getAttributeNS(null, 'width');
|
video.width = +foreignObject.getAttributeNS(null, 'width');
|
||||||
video.height = +foreignObject.getAttributeNS(null, 'height');
|
video.height = +foreignObject.getAttributeNS(null, 'height');
|
||||||
foreignObject.append(video);
|
foreignObject.append(video);
|
||||||
} else {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!img?.parentElement) {
|
||||||
|
const posterURL = appDocsManager.getThumbURL(doc, false);
|
||||||
|
if(posterURL) {
|
||||||
|
video.poster = posterURL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!video.parentElement && container) {
|
||||||
container.append(video);
|
container.append(video);
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadVideo = async() => {
|
const loadVideo = async() => {
|
||||||
if(message.media.preloader) { // means upload
|
if(message?.media?.preloader) { // means upload
|
||||||
(message.media.preloader as ProgressivePreloader).attach(container, undefined, undefined, false);
|
(message.media.preloader as ProgressivePreloader).attach(container, undefined, undefined, false);
|
||||||
} else if(!doc.downloaded) {
|
} else if(!doc.downloaded) {
|
||||||
/* const promise = appDocsManager.downloadDoc(doc.id);
|
/* const promise = appDocsManager.downloadDoc(doc.id);
|
||||||
@ -117,13 +131,13 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
|||||||
/* if(!video.paused) {
|
/* if(!video.paused) {
|
||||||
video.pause();
|
video.pause();
|
||||||
} */
|
} */
|
||||||
animationIntersector.addAnimation(video, 'chat');
|
if(group) {
|
||||||
|
animationIntersector.addAnimation(video, group);
|
||||||
|
}
|
||||||
}, {once: true});
|
}, {once: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
renderImageFromUrl(source, doc.url);
|
renderImageFromUrl(video, doc.url);
|
||||||
source.type = doc.mime_type;
|
|
||||||
video.append(source);
|
|
||||||
video.setAttribute('playsinline', '');
|
video.setAttribute('playsinline', '');
|
||||||
|
|
||||||
/* if(!container.parentElement) {
|
/* if(!container.parentElement) {
|
||||||
@ -161,8 +175,8 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
|||||||
return;
|
return;
|
||||||
} */
|
} */
|
||||||
|
|
||||||
//return;
|
doc.downloaded || !lazyLoadQueue/* && false */ ? loadVideo() : lazyLoadQueue.push({div: container, load: loadVideo/* , wasSeen: true */});
|
||||||
return doc.downloaded/* && false */ ? loadVideo() : lazyLoadQueue.push({div: container, load: loadVideo/* , wasSeen: true */});
|
return video;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const formatDate = (timestamp: number, monthShort = false, withYear = true) => {
|
export const formatDate = (timestamp: number, monthShort = false, withYear = true) => {
|
||||||
@ -445,14 +459,14 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
|
|||||||
} else if(!onlyThumb && stickerType == 2 && withThumb && toneIndex <= 0) {
|
} else if(!onlyThumb && stickerType == 2 && withThumb && toneIndex <= 0) {
|
||||||
img = new Image();
|
img = new Image();
|
||||||
|
|
||||||
const load = () => appDocsManager.downloadDocThumb(doc, thumb.type).then(url => {
|
const load = () => {
|
||||||
if(div.childElementCount || (middleware && !middleware())) return;
|
if(div.childElementCount || (middleware && !middleware())) return;
|
||||||
const promise = renderImageFromUrl(img, url);
|
const promise = renderImageFromUrl(img, appDocsManager.getFileURL(doc, false, thumb));
|
||||||
|
|
||||||
//if(!downloaded) {
|
//if(!downloaded) {
|
||||||
promise.then(afterRender);
|
promise.then(afterRender);
|
||||||
//}
|
//}
|
||||||
});
|
};
|
||||||
|
|
||||||
/* let downloaded = appDocsManager.hasDownloadedThumb(doc.id, thumb.type);
|
/* let downloaded = appDocsManager.hasDownloadedThumb(doc.id, thumb.type);
|
||||||
if(downloaded) {
|
if(downloaded) {
|
||||||
@ -467,13 +481,13 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
|
|||||||
if(onlyThumb && doc.thumbs) { // for sticker panel
|
if(onlyThumb && doc.thumbs) { // for sticker panel
|
||||||
let thumb = doc.thumbs[0];
|
let thumb = doc.thumbs[0];
|
||||||
|
|
||||||
let load = () => appDocsManager.downloadDocThumb(doc, thumb.type).then(url => {
|
let load = () => {
|
||||||
let img = new Image();
|
let img = new Image();
|
||||||
renderImageFromUrl(img, url).then(() => {
|
return renderImageFromUrl(img, appDocsManager.getFileURL(doc, false, thumb)).then(() => {
|
||||||
if(middleware && !middleware()) return;
|
if(middleware && !middleware()) return;
|
||||||
div.append(img);
|
div.append(img);
|
||||||
});
|
});
|
||||||
});
|
};
|
||||||
|
|
||||||
return lazyLoadQueue ? (lazyLoadQueue.push({div, load}), Promise.resolve()) : load();
|
return lazyLoadQueue ? (lazyLoadQueue.push({div, load}), Promise.resolve()) : load();
|
||||||
}
|
}
|
||||||
|
3
src/global.d.ts
vendored
3
src/global.d.ts
vendored
@ -5,3 +5,6 @@ declare module 'worker-loader!*' {
|
|||||||
|
|
||||||
export default WebpackWorker;
|
export default WebpackWorker;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare function setInterval(callback: (...args: any[]) => void, ms: number): number;
|
||||||
|
declare function setTimeout(callback: (...args: any[]) => void, ms: number): number;
|
@ -2,8 +2,8 @@ import appMessagesManager from "./appMessagesManager";
|
|||||||
import apiManagerProxy from "../mtproto/mtprotoworker";
|
import apiManagerProxy from "../mtproto/mtprotoworker";
|
||||||
import appPeersManager from "../appManagers/appPeersManager";
|
import appPeersManager from "../appManagers/appPeersManager";
|
||||||
import appMessagesIDsManager from "./appMessagesIDsManager";
|
import appMessagesIDsManager from "./appMessagesIDsManager";
|
||||||
import { toast } from "../../components/misc";
|
|
||||||
import { RichTextProcessor } from "../richtextprocessor";
|
import { RichTextProcessor } from "../richtextprocessor";
|
||||||
|
import { toast } from "../../components/toast";
|
||||||
|
|
||||||
export class AppInlineBotsManager {
|
export class AppInlineBotsManager {
|
||||||
/* private inlineResults: any = {};
|
/* private inlineResults: any = {};
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { findUpClassName, $rootScope, escapeRegExp, whichChild, findUpTag, cancelEvent, formatNumber } from "../utils";
|
import { findUpClassName, $rootScope, escapeRegExp, whichChild, findUpTag, cancelEvent } from "../utils";
|
||||||
import appImManager, { AppImManager } from "./appImManager";
|
import appImManager, { AppImManager } from "./appImManager";
|
||||||
import appPeersManager from './appPeersManager';
|
import appPeersManager from './appPeersManager';
|
||||||
import appMessagesManager, { AppMessagesManager, Dialog, DialogFilter } from "./appMessagesManager";
|
import appMessagesManager, { AppMessagesManager, Dialog, DialogFilter } from "./appMessagesManager";
|
||||||
import appUsersManager, { User } from "./appUsersManager";
|
import appUsersManager, { User } from "./appUsersManager";
|
||||||
import { RichTextProcessor } from "../richtextprocessor";
|
import { RichTextProcessor } from "../richtextprocessor";
|
||||||
import { ripple, putPreloader, positionMenu, openBtnMenu, parseMenuButtonsTo, horizontalMenu, attachContextMenuListener } from "../../components/misc";
|
import { putPreloader, positionMenu, openBtnMenu, parseMenuButtonsTo, attachContextMenuListener } from "../../components/misc";
|
||||||
//import Scrollable from "../../components/scrollable";
|
//import Scrollable from "../../components/scrollable";
|
||||||
import Scrollable from "../../components/scrollable_new";
|
import Scrollable from "../../components/scrollable_new";
|
||||||
import { logger, LogLevels } from "../logger";
|
import { logger, LogLevels } from "../logger";
|
||||||
@ -14,6 +14,8 @@ import { PopupButton, PopupPeer } from "../../components/popup";
|
|||||||
import { SliderTab } from "../../components/slider";
|
import { SliderTab } from "../../components/slider";
|
||||||
import appStateManager from "./appStateManager";
|
import appStateManager from "./appStateManager";
|
||||||
import { touchSupport, isSafari } from "../config";
|
import { touchSupport, isSafari } from "../config";
|
||||||
|
import { horizontalMenu } from "../../components/horizontalMenu";
|
||||||
|
import { ripple } from "../../components/ripple";
|
||||||
|
|
||||||
type DialogDom = {
|
type DialogDom = {
|
||||||
avatarEl: AvatarElement,
|
avatarEl: AvatarElement,
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import {RichTextProcessor} from '../richtextprocessor';
|
import {RichTextProcessor} from '../richtextprocessor';
|
||||||
import { CancellablePromise, deferredPromise } from '../polyfill';
|
import { CancellablePromise, deferredPromise } from '../polyfill';
|
||||||
import { isObject, getFileURL } from '../utils';
|
import { isObject, getFileURL, FileURLType } from '../utils';
|
||||||
import opusDecodeController from '../opusDecodeController';
|
import opusDecodeController from '../opusDecodeController';
|
||||||
import { MTDocument, inputDocumentFileLocation } from '../../types';
|
import { MTDocument, inputDocumentFileLocation, MTPhotoSize } from '../../types';
|
||||||
import { getFileNameByLocation } from '../bin_utils';
|
import { getFileNameByLocation } from '../bin_utils';
|
||||||
import appDownloadManager, { Download, ResponseMethod } from './appDownloadManager';
|
import appDownloadManager, { Download, ResponseMethod } from './appDownloadManager';
|
||||||
|
import appPhotosManager from './appPhotosManager';
|
||||||
|
|
||||||
class AppDocsManager {
|
class AppDocsManager {
|
||||||
private docs: {[docID: string]: MTDocument} = {};
|
private docs: {[docID: string]: MTDocument} = {};
|
||||||
private thumbs: {[docIDAndSize: string]: Promise<string>} = {};
|
|
||||||
private downloadPromises: {[docID: string]: CancellablePromise<Blob>} = {};
|
private downloadPromises: {[docID: string]: CancellablePromise<Blob>} = {};
|
||||||
|
|
||||||
public saveDoc(apiDoc: MTDocument, context?: any) {
|
public saveDoc(apiDoc: MTDocument, context?: any) {
|
||||||
@ -49,16 +49,16 @@ class AppDocsManager {
|
|||||||
apiDoc.audioPerformer = attribute.performer;
|
apiDoc.audioPerformer = attribute.performer;
|
||||||
apiDoc.type = attribute.pFlags.voice && apiDoc.mime_type == "audio/ogg" ? 'voice' : 'audio';
|
apiDoc.type = attribute.pFlags.voice && apiDoc.mime_type == "audio/ogg" ? 'voice' : 'audio';
|
||||||
|
|
||||||
if(apiDoc.type == 'audio') {
|
/* if(apiDoc.type == 'audio') {
|
||||||
apiDoc.supportsStreaming = true;
|
apiDoc.supportsStreaming = true;
|
||||||
}
|
} */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'documentAttributeVideo':
|
case 'documentAttributeVideo':
|
||||||
apiDoc.duration = attribute.duration;
|
apiDoc.duration = attribute.duration;
|
||||||
apiDoc.w = attribute.w;
|
apiDoc.w = attribute.w;
|
||||||
apiDoc.h = attribute.h;
|
apiDoc.h = attribute.h;
|
||||||
apiDoc.supportsStreaming = attribute.pFlags?.supports_streaming/* && apiDoc.size > 524288 */;
|
//apiDoc.supportsStreaming = attribute.pFlags?.supports_streaming/* && apiDoc.size > 524288 */;
|
||||||
if(apiDoc.thumbs && attribute.pFlags.round_message) {
|
if(apiDoc.thumbs && attribute.pFlags.round_message) {
|
||||||
apiDoc.type = 'round';
|
apiDoc.type = 'round';
|
||||||
} else /* if(apiDoc.thumbs) */ {
|
} else /* if(apiDoc.thumbs) */ {
|
||||||
@ -125,6 +125,10 @@ class AppDocsManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if((apiDoc.type == 'gif' && apiDoc.size > 8e6) || apiDoc.type == 'audio' || apiDoc.type == 'video') {
|
||||||
|
apiDoc.supportsStreaming = true;
|
||||||
|
}
|
||||||
|
|
||||||
if(!apiDoc.file_name) {
|
if(!apiDoc.file_name) {
|
||||||
apiDoc.file_name = '';
|
apiDoc.file_name = '';
|
||||||
}
|
}
|
||||||
@ -140,7 +144,7 @@ class AppDocsManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!apiDoc.url) {
|
if(!apiDoc.url) {
|
||||||
apiDoc.url = this.getFileURLByDoc(apiDoc);
|
apiDoc.url = this.getFileURL(apiDoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
return apiDoc;
|
return apiDoc;
|
||||||
@ -150,8 +154,7 @@ class AppDocsManager {
|
|||||||
return isObject(docID) && typeof(docID) !== 'string' ? docID : this.docs[docID as string];
|
return isObject(docID) && typeof(docID) !== 'string' ? docID : this.docs[docID as string];
|
||||||
}
|
}
|
||||||
|
|
||||||
public getMediaInputByID(docID: any) {
|
public getMediaInput(doc: MTDocument) {
|
||||||
let doc = this.getDoc(docID);
|
|
||||||
return {
|
return {
|
||||||
_: 'inputMediaDocument',
|
_: 'inputMediaDocument',
|
||||||
flags: 0,
|
flags: 0,
|
||||||
@ -165,9 +168,7 @@ class AppDocsManager {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public getInputByID(docID: any, thumbSize?: string): inputDocumentFileLocation {
|
public getInput(doc: MTDocument, thumbSize?: string): inputDocumentFileLocation {
|
||||||
let doc = this.getDoc(docID);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
_: 'inputDocumentFileLocation',
|
_: 'inputDocumentFileLocation',
|
||||||
id: doc.id,
|
id: doc.id,
|
||||||
@ -177,34 +178,54 @@ class AppDocsManager {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public getFileName(doc: MTDocument) {
|
public getFileURL(doc: MTDocument, download = false, thumb?: MTPhotoSize) {
|
||||||
if(doc.file_name) {
|
const inputFileLocation = this.getInput(doc, thumb?.type);
|
||||||
return doc.file_name;
|
|
||||||
|
let type: FileURLType;
|
||||||
|
if(download) {
|
||||||
|
type = 'download';
|
||||||
|
} else if(thumb) {
|
||||||
|
type = 'thumb';
|
||||||
|
} else if(doc.supportsStreaming) {
|
||||||
|
type = 'stream';
|
||||||
|
} else {
|
||||||
|
type = 'document';
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileExt = '.' + doc.mime_type.split('/')[1];
|
let mimeType: string;
|
||||||
if(fileExt == '.octet-stream') {
|
if(thumb) {
|
||||||
fileExt = '';
|
mimeType = doc.sticker ? 'image/webp' : 'image/jpeg'/* doc.mime_type */;
|
||||||
|
} else {
|
||||||
|
mimeType = doc.mime_type || 'application/octet-stream';
|
||||||
}
|
}
|
||||||
|
|
||||||
return 't_' + (doc.type || 'file') + doc.id + fileExt;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getFileURLByDoc(doc: MTDocument, download = false) {
|
|
||||||
const inputFileLocation = this.getInputByID(doc);
|
|
||||||
const type = download ? 'download' : (doc.supportsStreaming ? 'stream' : 'document');
|
|
||||||
|
|
||||||
return getFileURL(type, {
|
return getFileURL(type, {
|
||||||
dcID: doc.dc_id,
|
dcID: doc.dc_id,
|
||||||
location: inputFileLocation,
|
location: inputFileLocation,
|
||||||
size: doc.size,
|
size: thumb ? thumb.size : doc.size,
|
||||||
mimeType: doc.mime_type || 'application/octet-stream',
|
mimeType: mimeType,
|
||||||
fileName: doc.file_name
|
fileName: doc.file_name
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public getInputFileName(doc: MTDocument) {
|
public getThumbURL(doc: MTDocument, useBytes = true) {
|
||||||
return getFileNameByLocation(this.getInputByID(doc));
|
if(doc.thumbs?.length) {
|
||||||
|
if(doc.thumbs[0].bytes && useBytes) {
|
||||||
|
return appPhotosManager.getPreviewURLFromBytes(doc.thumbs[0].bytes, !!doc.sticker);
|
||||||
|
}
|
||||||
|
|
||||||
|
const thumb = doc.thumbs.find(t => !t.bytes);
|
||||||
|
if(thumb) {
|
||||||
|
const url = appDocsManager.getFileURL(doc, false, thumb);
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public getInputFileName(doc: MTDocument, thumbSize?: string) {
|
||||||
|
return getFileNameByLocation(this.getInput(doc, thumbSize), {fileName: doc.file_name});
|
||||||
}
|
}
|
||||||
|
|
||||||
public downloadDoc(docID: string | MTDocument, toFileEntry?: any): CancellablePromise<Blob> {
|
public downloadDoc(docID: string | MTDocument, toFileEntry?: any): CancellablePromise<Blob> {
|
||||||
@ -229,11 +250,7 @@ class AppDocsManager {
|
|||||||
|
|
||||||
const deferred = deferredPromise<Blob>();
|
const deferred = deferredPromise<Blob>();
|
||||||
|
|
||||||
/* if(doc.supportsStreaming) {
|
const url = this.getFileURL(doc);
|
||||||
doc.url = '/stream/' + '';
|
|
||||||
} */
|
|
||||||
|
|
||||||
const url = this.getFileURLByDoc(doc);
|
|
||||||
fetch(url).then(res => res.blob())
|
fetch(url).then(res => res.blob())
|
||||||
/* downloadPromise */.then((blob) => {
|
/* downloadPromise */.then((blob) => {
|
||||||
if(blob) {
|
if(blob) {
|
||||||
@ -323,30 +340,8 @@ class AppDocsManager {
|
|||||||
return download;
|
return download;
|
||||||
}
|
}
|
||||||
|
|
||||||
public downloadDocThumb(docID: any, thumbSize: string) {
|
|
||||||
let doc = this.getDoc(docID);
|
|
||||||
|
|
||||||
let key = doc.id + '-' + thumbSize;
|
|
||||||
if(this.thumbs[key]) {
|
|
||||||
return this.thumbs[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
let input = this.getInputByID(doc, thumbSize);
|
|
||||||
|
|
||||||
if(doc._ == 'documentEmpty') {
|
|
||||||
return Promise.reject();
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = getFileURL('thumb', {dcID: doc.dc_id, location: input, mimeType: doc.sticker ? 'image/webp' : doc.mime_type});
|
|
||||||
return this.thumbs[key] = Promise.resolve(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
public hasDownloadedThumb(docID: string, thumbSize: string) {
|
|
||||||
return !!this.thumbs[docID + '-' + thumbSize];
|
|
||||||
}
|
|
||||||
|
|
||||||
public saveDocFile(doc: MTDocument) {
|
public saveDocFile(doc: MTDocument) {
|
||||||
const url = this.getFileURLByDoc(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);
|
||||||
|
@ -16,7 +16,7 @@ import appSidebarLeft from "./appSidebarLeft";
|
|||||||
import appChatsManager, { Channel, Chat } from "./appChatsManager";
|
import appChatsManager, { Channel, Chat } from "./appChatsManager";
|
||||||
import { wrapDocument, wrapPhoto, wrapVideo, wrapSticker, wrapReply, wrapAlbum, wrapPoll, formatDate } from '../../components/wrappers';
|
import { wrapDocument, wrapPhoto, wrapVideo, wrapSticker, wrapReply, wrapAlbum, wrapPoll, formatDate } from '../../components/wrappers';
|
||||||
import ProgressivePreloader from '../../components/preloader';
|
import ProgressivePreloader from '../../components/preloader';
|
||||||
import { openBtnMenu, formatPhoneNumber, positionMenu, ripple, parseMenuButtonsTo, horizontalMenu, attachContextMenuListener } from '../../components/misc';
|
import { openBtnMenu, formatPhoneNumber, positionMenu, parseMenuButtonsTo, attachContextMenuListener } from '../../components/misc';
|
||||||
import { ChatInput } from '../../components/chatInput';
|
import { ChatInput } from '../../components/chatInput';
|
||||||
//import Scrollable from '../../components/scrollable';
|
//import Scrollable from '../../components/scrollable';
|
||||||
import Scrollable from '../../components/scrollable_new';
|
import Scrollable from '../../components/scrollable_new';
|
||||||
@ -37,6 +37,8 @@ import AppSearch, { SearchGroup } from '../../components/appSearch';
|
|||||||
import PopupDatePicker from '../../components/popupDatepicker';
|
import PopupDatePicker from '../../components/popupDatepicker';
|
||||||
import appAudio from '../../components/appAudio';
|
import appAudio from '../../components/appAudio';
|
||||||
import appPollsManager from './appPollsManager';
|
import appPollsManager from './appPollsManager';
|
||||||
|
import { ripple } from '../../components/ripple';
|
||||||
|
import { horizontalMenu } from '../../components/horizontalMenu';
|
||||||
|
|
||||||
//console.log('appImManager included33!');
|
//console.log('appImManager included33!');
|
||||||
|
|
||||||
@ -44,6 +46,8 @@ appSidebarLeft; // just to include
|
|||||||
|
|
||||||
const testScroll = false;
|
const testScroll = false;
|
||||||
|
|
||||||
|
const ANIMATIONGROUP = 'chat';
|
||||||
|
|
||||||
class ChatContextMenu {
|
class ChatContextMenu {
|
||||||
private element = document.getElementById('bubble-contextmenu') as HTMLDivElement;
|
private element = document.getElementById('bubble-contextmenu') as HTMLDivElement;
|
||||||
private buttons: {
|
private buttons: {
|
||||||
@ -1511,7 +1515,7 @@ export class AppImManager {
|
|||||||
|
|
||||||
//console.timeEnd('appImManager setPeer pre promise');
|
//console.timeEnd('appImManager setPeer pre promise');
|
||||||
|
|
||||||
animationIntersector.lockGroup('chat');
|
animationIntersector.lockGroup(ANIMATIONGROUP);
|
||||||
this.setPeerPromise = Promise.all([
|
this.setPeerPromise = Promise.all([
|
||||||
promise.then(() => {
|
promise.then(() => {
|
||||||
////this.log('setPeer removing preloader');
|
////this.log('setPeer removing preloader');
|
||||||
@ -1532,8 +1536,8 @@ export class AppImManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.scrollable.container.append(this.chatInner);
|
this.scrollable.container.append(this.chatInner);
|
||||||
animationIntersector.unlockGroup('chat');
|
animationIntersector.unlockGroup(ANIMATIONGROUP);
|
||||||
animationIntersector.checkAnimations(false, 'chat'/* , true */);
|
animationIntersector.checkAnimations(false, ANIMATIONGROUP/* , true */);
|
||||||
//this.scrollable.attachSentinels();
|
//this.scrollable.attachSentinels();
|
||||||
//this.scrollable.container.insertBefore(this.chatInner, this.scrollable.container.lastElementChild);
|
//this.scrollable.container.insertBefore(this.chatInner, this.scrollable.container.lastElementChild);
|
||||||
|
|
||||||
@ -1699,7 +1703,7 @@ export class AppImManager {
|
|||||||
//bubble.remove();
|
//bubble.remove();
|
||||||
});
|
});
|
||||||
|
|
||||||
animationIntersector.checkAnimations(false, 'chat');
|
animationIntersector.checkAnimations(false, ANIMATIONGROUP);
|
||||||
this.deleteEmptyDateGroups();
|
this.deleteEmptyDateGroups();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1805,15 +1809,12 @@ export class AppImManager {
|
|||||||
let promises: Promise<any>[] = [];
|
let promises: Promise<any>[] = [];
|
||||||
(Array.from(bubble.querySelectorAll('img, video')) as HTMLImageElement[]).forEach(el => {
|
(Array.from(bubble.querySelectorAll('img, video')) as HTMLImageElement[]).forEach(el => {
|
||||||
if(el instanceof HTMLVideoElement) {
|
if(el instanceof HTMLVideoElement) {
|
||||||
let source = el.firstElementChild as HTMLSourceElement;
|
if(!el.src) {
|
||||||
if(!source || !source.src) {
|
|
||||||
//this.log.warn('no source', el, source, 'src', source.src);
|
//this.log.warn('no source', el, source, 'src', source.src);
|
||||||
return;
|
return;
|
||||||
} else if(el.readyState >= 4) return;
|
} else if(el.readyState >= 4) return;
|
||||||
} else if(el.complete || !el.src) return;
|
} else if(el.complete || !el.src) return;
|
||||||
|
|
||||||
let src = el.src;
|
|
||||||
|
|
||||||
let promise = new Promise((resolve, reject) => {
|
let promise = new Promise((resolve, reject) => {
|
||||||
let r: () => boolean;
|
let r: () => boolean;
|
||||||
let onLoad = () => {
|
let onLoad = () => {
|
||||||
@ -2046,7 +2047,7 @@ export class AppImManager {
|
|||||||
|
|
||||||
let messageMedia = message.media;
|
let messageMedia = message.media;
|
||||||
|
|
||||||
if(totalEntities) {
|
if(totalEntities && !messageMedia) {
|
||||||
let emojiEntities = totalEntities.filter((e: any) => e._ == 'messageEntityEmoji');
|
let emojiEntities = totalEntities.filter((e: any) => e._ == 'messageEntityEmoji');
|
||||||
let strLength = messageMessage.length;
|
let strLength = messageMessage.length;
|
||||||
let emojiStrLength = emojiEntities.reduce((acc: number, curr: any) => acc + curr.length, 0);
|
let emojiStrLength = emojiEntities.reduce((acc: number, curr: any) => acc + curr.length, 0);
|
||||||
@ -2237,7 +2238,8 @@ export class AppImManager {
|
|||||||
withTail: tailSupported,
|
withTail: tailSupported,
|
||||||
isOut: isOut,
|
isOut: isOut,
|
||||||
lazyLoadQueue: this.lazyLoadQueue,
|
lazyLoadQueue: this.lazyLoadQueue,
|
||||||
middleware: null
|
middleware: null,
|
||||||
|
group: ANIMATIONGROUP
|
||||||
});
|
});
|
||||||
|
|
||||||
preloader.attach(attachmentDiv, false);
|
preloader.attach(attachmentDiv, false);
|
||||||
@ -2342,7 +2344,8 @@ export class AppImManager {
|
|||||||
boxHeight: mediaSizes.active.webpage.height,
|
boxHeight: mediaSizes.active.webpage.height,
|
||||||
lazyLoadQueue: this.lazyLoadQueue,
|
lazyLoadQueue: this.lazyLoadQueue,
|
||||||
middleware: this.getMiddleware(),
|
middleware: this.getMiddleware(),
|
||||||
isOut
|
isOut,
|
||||||
|
group: ANIMATIONGROUP
|
||||||
});
|
});
|
||||||
//}
|
//}
|
||||||
} else {
|
} else {
|
||||||
@ -2428,7 +2431,7 @@ export class AppImManager {
|
|||||||
div: attachmentDiv,
|
div: attachmentDiv,
|
||||||
middleware: this.getMiddleware(),
|
middleware: this.getMiddleware(),
|
||||||
lazyLoadQueue: this.lazyLoadQueue,
|
lazyLoadQueue: this.lazyLoadQueue,
|
||||||
group: 'chat',
|
group: ANIMATIONGROUP,
|
||||||
//play: !!message.pending || !multipleRender,
|
//play: !!message.pending || !multipleRender,
|
||||||
play: true,
|
play: true,
|
||||||
loop: true,
|
loop: true,
|
||||||
@ -2463,7 +2466,8 @@ export class AppImManager {
|
|||||||
withTail: tailSupported,
|
withTail: tailSupported,
|
||||||
isOut: isOut,
|
isOut: isOut,
|
||||||
lazyLoadQueue: this.lazyLoadQueue,
|
lazyLoadQueue: this.lazyLoadQueue,
|
||||||
middleware: this.getMiddleware()
|
middleware: this.getMiddleware(),
|
||||||
|
group: ANIMATIONGROUP
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,9 +311,7 @@ export class AppMediaViewer {
|
|||||||
src = target.src;
|
src = target.src;
|
||||||
} else if(target instanceof HTMLVideoElement) {
|
} else if(target instanceof HTMLVideoElement) {
|
||||||
let video = mediaElement = document.createElement('video');
|
let video = mediaElement = document.createElement('video');
|
||||||
let source = document.createElement('source');
|
video.src = target?.src;
|
||||||
src = target.querySelector('source')?.src;
|
|
||||||
video.append(source);
|
|
||||||
} else if(target instanceof SVGSVGElement) {
|
} else if(target instanceof SVGSVGElement) {
|
||||||
let clipID = target.dataset.clipID;
|
let clipID = target.dataset.clipID;
|
||||||
let newClipID = clipID + '-mv';
|
let newClipID = clipID + '-mv';
|
||||||
@ -327,12 +325,6 @@ export class AppMediaViewer {
|
|||||||
newSvg.insertAdjacentHTML('beforeend', target.firstElementChild.outerHTML.replace(clipID, newClipID));
|
newSvg.insertAdjacentHTML('beforeend', target.firstElementChild.outerHTML.replace(clipID, newClipID));
|
||||||
newSvg.insertAdjacentHTML('beforeend', target.lastElementChild.outerHTML.replace(clipID, newClipID));
|
newSvg.insertAdjacentHTML('beforeend', target.lastElementChild.outerHTML.replace(clipID, newClipID));
|
||||||
|
|
||||||
// FIX STREAM
|
|
||||||
let source = newSvg.querySelector('source');
|
|
||||||
if(source) {
|
|
||||||
source.removeAttribute('src');
|
|
||||||
}
|
|
||||||
|
|
||||||
// теперь надо выставить новую позицию для хвостика
|
// теперь надо выставить новую позицию для хвостика
|
||||||
let defs = newSvg.firstElementChild;
|
let defs = newSvg.firstElementChild;
|
||||||
let use = defs.firstElementChild.firstElementChild as SVGUseElement;
|
let use = defs.firstElementChild.firstElementChild as SVGUseElement;
|
||||||
@ -655,7 +647,7 @@ export class AppMediaViewer {
|
|||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateMediaSource(target: HTMLElement, url: string, tagName: 'source' | 'img') {
|
public updateMediaSource(target: HTMLElement, url: string, tagName: 'video' | 'img') {
|
||||||
//if(target instanceof SVGSVGElement) {
|
//if(target instanceof SVGSVGElement) {
|
||||||
let el = target.querySelector(tagName) as HTMLElement;
|
let el = target.querySelector(tagName) as HTMLElement;
|
||||||
renderImageFromUrl(el, url);
|
renderImageFromUrl(el, url);
|
||||||
@ -778,11 +770,6 @@ export class AppMediaViewer {
|
|||||||
if(isVideo) {
|
if(isVideo) {
|
||||||
////////this.log('will wrap video', media, size);
|
////////this.log('will wrap video', media, size);
|
||||||
|
|
||||||
/* let source = target.querySelector('source');
|
|
||||||
if(source && source.src) {
|
|
||||||
source.src = '';
|
|
||||||
} */
|
|
||||||
|
|
||||||
setMoverPromise = this.setMoverToTarget(target, false, fromRight).then(() => {
|
setMoverPromise = this.setMoverToTarget(target, false, fromRight).then(() => {
|
||||||
//return; // set and don't move
|
//return; // set and don't move
|
||||||
//if(wasActive) return;
|
//if(wasActive) return;
|
||||||
@ -790,9 +777,7 @@ export class AppMediaViewer {
|
|||||||
|
|
||||||
const div = mover.firstElementChild && mover.firstElementChild.classList.contains('media-viewer-aspecter') ? mover.firstElementChild : mover;
|
const div = mover.firstElementChild && mover.firstElementChild.classList.contains('media-viewer-aspecter') ? mover.firstElementChild : mover;
|
||||||
const video = mover.querySelector('video') || document.createElement('video');
|
const video = mover.querySelector('video') || document.createElement('video');
|
||||||
const source = video.firstElementChild as HTMLSourceElement || document.createElement('source');
|
video.src = '';
|
||||||
|
|
||||||
source.removeAttribute('src');
|
|
||||||
|
|
||||||
video.setAttribute('playsinline', '');
|
video.setAttribute('playsinline', '');
|
||||||
if(media.type == 'gif') {
|
if(media.type == 'gif') {
|
||||||
@ -801,10 +786,6 @@ export class AppMediaViewer {
|
|||||||
video.loop = true;
|
video.loop = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!source.parentElement) {
|
|
||||||
video.append(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
const createPlayer = () => {
|
const createPlayer = () => {
|
||||||
if(media.type != 'gif') {
|
if(media.type != 'gif') {
|
||||||
video.dataset.ckin = 'default';
|
video.dataset.ckin = 'default';
|
||||||
@ -823,7 +804,7 @@ export class AppMediaViewer {
|
|||||||
} */
|
} */
|
||||||
};
|
};
|
||||||
|
|
||||||
if(!source.src || (media.url && media.url != source.src)) {
|
if(!video.src || (media.url && media.url != video.src)) {
|
||||||
const load = () => {
|
const load = () => {
|
||||||
const promise = appDocsManager.downloadDoc(media.id);
|
const promise = appDocsManager.downloadDoc(media.id);
|
||||||
|
|
||||||
@ -845,12 +826,11 @@ export class AppMediaViewer {
|
|||||||
div.firstElementChild.lastElementChild.append(video);
|
div.firstElementChild.lastElementChild.append(video);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateMediaSource(mover, url, 'source');
|
this.updateMediaSource(mover, url, 'video');
|
||||||
//this.updateMediaSource(target, url, 'source');
|
//this.updateMediaSource(target, url, 'source');
|
||||||
} else {
|
} else {
|
||||||
//const promise = new Promise((resolve) => video.addEventListener('loadeddata', resolve, {once: true}));
|
//const promise = new Promise((resolve) => video.addEventListener('loadeddata', resolve, {once: true}));
|
||||||
renderImageFromUrl(source, url);
|
renderImageFromUrl(video, url);
|
||||||
source.type = media.mime_type;
|
|
||||||
|
|
||||||
//await promise;
|
//await promise;
|
||||||
const first = div.firstElementChild as HTMLImageElement;
|
const first = div.firstElementChild as HTMLImageElement;
|
||||||
@ -864,13 +844,13 @@ export class AppMediaViewer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// я хз что это такое, видео появляются просто чёрными и не включаются без этого кода снизу
|
// я хз что это такое, видео появляются просто чёрными и не включаются без этого кода снизу
|
||||||
source.remove();
|
/* source.remove();
|
||||||
window.requestAnimationFrame(() => {
|
window.requestAnimationFrame(() => {
|
||||||
window.requestAnimationFrame(() => {
|
window.requestAnimationFrame(() => {
|
||||||
//parent.append(video);
|
//parent.append(video);
|
||||||
video.append(source);
|
video.append(source);
|
||||||
});
|
});
|
||||||
});
|
}); */
|
||||||
|
|
||||||
player = createPlayer();
|
player = createPlayer();
|
||||||
});
|
});
|
||||||
|
@ -1576,7 +1576,7 @@ export class AppMessagesManager {
|
|||||||
} else {
|
} else {
|
||||||
let doc = messageMedia.document;
|
let doc = messageMedia.document;
|
||||||
appDocsManager.saveDoc(doc);
|
appDocsManager.saveDoc(doc);
|
||||||
inputMedia = appDocsManager.getMediaInputByID(doc.id);
|
inputMedia = appDocsManager.getMediaInput(doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
inputs.push({
|
inputs.push({
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { horizontalMenu, putPreloader, renderImageFromUrl, ripple } from "../../components/misc";
|
import { putPreloader, renderImageFromUrl } from "../../components/misc";
|
||||||
//import Scrollable from '../../components/scrollable';
|
//import Scrollable from '../../components/scrollable';
|
||||||
import Scrollable from '../../components/scrollable_new';
|
import Scrollable from '../../components/scrollable_new';
|
||||||
import { $rootScope, findUpClassName } from "../utils";
|
import { $rootScope, findUpClassName } from "../utils";
|
||||||
@ -25,6 +25,8 @@ import SearchInput from "../../components/searchInput";
|
|||||||
import appPollsManager from "./appPollsManager";
|
import appPollsManager from "./appPollsManager";
|
||||||
import { roundPercents } from "../../components/poll";
|
import { roundPercents } from "../../components/poll";
|
||||||
import appDialogsManager from "./appDialogsManager";
|
import appDialogsManager from "./appDialogsManager";
|
||||||
|
import { ripple } from "../../components/ripple";
|
||||||
|
import { horizontalMenu } from "../../components/horizontalMenu";
|
||||||
|
|
||||||
const testScroll = false;
|
const testScroll = false;
|
||||||
|
|
||||||
|
@ -386,7 +386,7 @@ export function nextRandomInt(maxValue: number) {
|
|||||||
export function getFileNameByLocation(location: InputFileLocation | FileLocation, options?: Partial<{
|
export function getFileNameByLocation(location: InputFileLocation | FileLocation, options?: Partial<{
|
||||||
fileName: string
|
fileName: string
|
||||||
}>) {
|
}>) {
|
||||||
const fileName = (options?.fileName || '').split('.');
|
const fileName = '';//(options?.fileName || '').split('.');
|
||||||
const ext = fileName[fileName.length - 1] || '';
|
const ext = fileName[fileName.length - 1] || '';
|
||||||
|
|
||||||
switch(location._) {
|
switch(location._) {
|
||||||
|
@ -71,7 +71,7 @@ export class ApiFileManager {
|
|||||||
const downloadPull = this.downloadPulls[dcID];
|
const downloadPull = this.downloadPulls[dcID];
|
||||||
//const downloadLimit = dcID == 'upload' ? 11 : 5;
|
//const downloadLimit = dcID == 'upload' ? 11 : 5;
|
||||||
//const downloadLimit = 24;
|
//const downloadLimit = 24;
|
||||||
const downloadLimit = dcID == 'upload' ? 11 : 50;
|
const downloadLimit = dcID == 'upload' ? 11 : 48;
|
||||||
|
|
||||||
if(this.downloadActives[dcID] >= downloadLimit || !downloadPull || !downloadPull.length) {
|
if(this.downloadActives[dcID] >= downloadLimit || !downloadPull || !downloadPull.length) {
|
||||||
return false;
|
return false;
|
||||||
@ -115,7 +115,8 @@ export class ApiFileManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public requestFilePart(dcID: number, location: InputFileLocation | FileLocation, offset: number, limit: number, checkCancel?: () => void) {
|
public requestFilePart(dcID: number, location: InputFileLocation | FileLocation, offset: number, limit: number, checkCancel?: () => void) {
|
||||||
const delta = limit / 1024 / 256;
|
//const delta = limit / 1024 / 256;
|
||||||
|
const delta = limit / 1024 / 128;
|
||||||
return this.downloadRequest(dcID, async() => {
|
return this.downloadRequest(dcID, async() => {
|
||||||
checkCancel && checkCancel();
|
checkCancel && checkCancel();
|
||||||
|
|
||||||
@ -138,9 +139,10 @@ export class ApiFileManager {
|
|||||||
private getLimitPart(size: number): number {
|
private getLimitPart(size: number): number {
|
||||||
let bytes: number;
|
let bytes: number;
|
||||||
|
|
||||||
if(size < 1e6 || !size) bytes = 512;
|
bytes = 512;
|
||||||
|
/* if(size < 1e6 || !size) bytes = 512;
|
||||||
else if(size < 3e6) bytes = 256;
|
else if(size < 3e6) bytes = 256;
|
||||||
else bytes = 128;
|
else bytes = 128; */
|
||||||
|
|
||||||
return bytes * 1024;
|
return bytes * 1024;
|
||||||
}
|
}
|
||||||
@ -186,11 +188,11 @@ export class ApiFileManager {
|
|||||||
options.mimeType = 'application/json';
|
options.mimeType = 'application/json';
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileName = getFileNameByLocation(location);
|
const fileName = getFileNameByLocation(location, {fileName: options.fileName});
|
||||||
const cachedPromise = this.cachedDownloadPromises[fileName];
|
const cachedPromise = this.cachedDownloadPromises[fileName];
|
||||||
const fileStorage = this.getFileStorage();
|
const fileStorage = this.getFileStorage();
|
||||||
|
|
||||||
//this.log('downloadFile', fileName, size, location, options.mimeType, process);
|
this.log('downloadFile', fileName, size, location, options.mimeType, process);
|
||||||
|
|
||||||
if(cachedPromise) {
|
if(cachedPromise) {
|
||||||
if(options.processPart) {
|
if(options.processPart) {
|
||||||
@ -267,7 +269,7 @@ export class ApiFileManager {
|
|||||||
let startOffset = 0;
|
let startOffset = 0;
|
||||||
let writeFilePromise: CancellablePromise<unknown> = Promise.resolve(),
|
let writeFilePromise: CancellablePromise<unknown> = Promise.resolve(),
|
||||||
writeFileDeferred: CancellablePromise<unknown>;
|
writeFileDeferred: CancellablePromise<unknown>;
|
||||||
const maxRequests = options.processPart ? 5 : 10;
|
const maxRequests = options.processPart ? 5 : 5;
|
||||||
|
|
||||||
/* if(fileWriter.length) {
|
/* if(fileWriter.length) {
|
||||||
startOffset = fileWriter.length;
|
startOffset = fileWriter.length;
|
||||||
@ -334,13 +336,15 @@ export class ApiFileManager {
|
|||||||
superpuper();
|
superpuper();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.log('downloadFile requestFilePart result:', fileName, result);
|
||||||
|
const isFinal = offset + limit >= size || !result.bytes.byteLength;
|
||||||
|
if(result.bytes.byteLength) {
|
||||||
//done += limit;
|
//done += limit;
|
||||||
done += result.bytes.byteLength;
|
done += result.bytes.byteLength;
|
||||||
|
|
||||||
const processedResult = await processDownloaded(result.bytes, offset);
|
const processedResult = await processDownloaded(result.bytes, offset);
|
||||||
checkCancel();
|
checkCancel();
|
||||||
|
|
||||||
const isFinal = offset + limit >= size;
|
|
||||||
//if(!isFinal) {
|
//if(!isFinal) {
|
||||||
////this.log('deferred notify 2:', {done: offset + limit, total: size}, deferred);
|
////this.log('deferred notify 2:', {done: offset + limit, total: size}, deferred);
|
||||||
deferred.notify({done, offset, total: size});
|
deferred.notify({done, offset, total: size});
|
||||||
@ -350,6 +354,8 @@ export class ApiFileManager {
|
|||||||
checkCancel();
|
checkCancel();
|
||||||
|
|
||||||
await FileManager.write(fileWriter, processedResult);
|
await FileManager.write(fileWriter, processedResult);
|
||||||
|
}
|
||||||
|
|
||||||
writeFileDeferred.resolve();
|
writeFileDeferred.resolve();
|
||||||
|
|
||||||
if(isFinal) {
|
if(isFinal) {
|
||||||
|
@ -178,6 +178,23 @@ function timeout(delay: number): Promise<Response> {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function responseForSafariFirstRange(range: [number, number], mimeType: string, size: number): Response {
|
||||||
|
if(range[0] === 0 && range[1] === 1) {
|
||||||
|
return new Response(new Uint8Array(2).buffer, {
|
||||||
|
status: 206,
|
||||||
|
statusText: 'Partial Content',
|
||||||
|
headers: {
|
||||||
|
'Accept-Ranges': 'bytes',
|
||||||
|
'Content-Range': `bytes 0-1/${size || '*'}`,
|
||||||
|
'Content-Length': '2',
|
||||||
|
'Content-Type': mimeType || 'video/mp4',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
ctx.addEventListener('error', (error) => {
|
ctx.addEventListener('error', (error) => {
|
||||||
log.error('error:', error);
|
log.error('error:', error);
|
||||||
});
|
});
|
||||||
@ -196,7 +213,17 @@ ctx.addEventListener('fetch', (event: FetchEvent): void => {
|
|||||||
case 'document':
|
case 'document':
|
||||||
case 'photo': {
|
case 'photo': {
|
||||||
const info: DownloadOptions = JSON.parse(decodeURIComponent(params));
|
const info: DownloadOptions = JSON.parse(decodeURIComponent(params));
|
||||||
const fileName = getFileNameByLocation(info.location);
|
|
||||||
|
const rangeHeader = event.request.headers.get('Range');
|
||||||
|
if(rangeHeader && info.mimeType && info.size) { // maybe safari
|
||||||
|
const range = parseRange(event.request.headers.get('Range'));
|
||||||
|
const possibleResponse = responseForSafariFirstRange(range, info.mimeType, info.size);
|
||||||
|
if(possibleResponse) {
|
||||||
|
return event.respondWith(possibleResponse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileName = getFileNameByLocation(info.location, {fileName: info.fileName});
|
||||||
|
|
||||||
/* event.request.signal.addEventListener('abort', (e) => {
|
/* event.request.signal.addEventListener('abort', (e) => {
|
||||||
console.log('[SW] user aborted request:', fileName);
|
console.log('[SW] user aborted request:', fileName);
|
||||||
@ -221,7 +248,20 @@ ctx.addEventListener('fetch', (event: FetchEvent): void => {
|
|||||||
|
|
||||||
log.debug('[fetch] file:', /* info, */fileName);
|
log.debug('[fetch] file:', /* info, */fileName);
|
||||||
|
|
||||||
const promise = cancellablePromise.then(b => new Response(b));
|
const promise = cancellablePromise.then(b => {
|
||||||
|
const responseInit: ResponseInit = {};
|
||||||
|
|
||||||
|
if(rangeHeader) {
|
||||||
|
responseInit.headers = {
|
||||||
|
'Accept-Ranges': 'bytes',
|
||||||
|
'Content-Range': `bytes 0-${info.size - 1}/${info.size || '*'}`,
|
||||||
|
'Content-Length': `${info.size}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(b, responseInit);
|
||||||
|
});
|
||||||
|
|
||||||
event.respondWith(Promise.race([
|
event.respondWith(Promise.race([
|
||||||
timeout(45 * 1000),
|
timeout(45 * 1000),
|
||||||
promise
|
promise
|
||||||
@ -231,28 +271,21 @@ ctx.addEventListener('fetch', (event: FetchEvent): void => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'stream': {
|
case 'stream': {
|
||||||
const [offset, end] = parseRange(event.request.headers.get('Range'));
|
const range = parseRange(event.request.headers.get('Range'));
|
||||||
|
const [offset, end] = range;
|
||||||
|
|
||||||
const info: DownloadOptions = JSON.parse(decodeURIComponent(params));
|
const info: DownloadOptions = JSON.parse(decodeURIComponent(params));
|
||||||
//const fileName = getFileNameByLocation(info.location);
|
//const fileName = getFileNameByLocation(info.location);
|
||||||
|
|
||||||
log.debug('[stream]', url, offset, end);
|
log.debug('[stream]', url, offset, end);
|
||||||
|
|
||||||
event.respondWith(new Promise((resolve, reject) => {
|
event.respondWith(Promise.race([
|
||||||
|
timeout(45 * 1000),
|
||||||
|
new Promise<Response>((resolve, reject) => {
|
||||||
// safari workaround
|
// safari workaround
|
||||||
if(offset === 0 && end === 1) {
|
const possibleResponse = responseForSafariFirstRange(range, info.mimeType, info.size);
|
||||||
resolve(new Response(new Uint8Array(2).buffer, {
|
if(possibleResponse) {
|
||||||
status: 206,
|
return resolve(possibleResponse);
|
||||||
statusText: 'Partial Content',
|
|
||||||
headers: {
|
|
||||||
'Accept-Ranges': 'bytes',
|
|
||||||
'Content-Range': `bytes 0-1/${info.size || '*'}`,
|
|
||||||
'Content-Length': '2',
|
|
||||||
'Content-Type': info.mimeType || 'video/mp4',
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const limit = end && end < STREAM_CHUNK_UPPER_LIMIT ? alignLimit(end - offset + 1) : STREAM_CHUNK_UPPER_LIMIT;
|
const limit = end && end < STREAM_CHUNK_UPPER_LIMIT ? alignLimit(end - offset + 1) : STREAM_CHUNK_UPPER_LIMIT;
|
||||||
@ -285,7 +318,8 @@ ctx.addEventListener('fetch', (event: FetchEvent): void => {
|
|||||||
headers,
|
headers,
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
}));
|
})
|
||||||
|
]));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -538,7 +538,8 @@ export function getEmojiToneIndex(input: string) {
|
|||||||
return match ? 5 - (57343 - match[0].charCodeAt(0)) : 0;
|
return match ? 5 - (57343 - match[0].charCodeAt(0)) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFileURL(type: 'photo' | 'thumb' | 'document' | 'stream' | 'download', options: {
|
export type FileURLType = 'photo' | 'thumb' | 'document' | 'stream' | 'download';
|
||||||
|
export function getFileURL(type: FileURLType, options: {
|
||||||
dcID: number,
|
dcID: number,
|
||||||
location: InputFileLocation | FileLocation,
|
location: InputFileLocation | FileLocation,
|
||||||
size?: number,
|
size?: number,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import Page from "./page";
|
import Page from "./page";
|
||||||
import { whichChild } from "../lib/utils";
|
import { whichChild } from "../lib/utils";
|
||||||
import { horizontalMenu } from "../components/misc";
|
|
||||||
import lottieLoader from "../lib/lottieLoader";
|
import lottieLoader from "../lib/lottieLoader";
|
||||||
|
import { horizontalMenu } from "../components/horizontalMenu";
|
||||||
|
|
||||||
class PagesManager {
|
class PagesManager {
|
||||||
private pageID = -1;
|
private pageID = -1;
|
||||||
|
@ -328,19 +328,22 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
||||||
> div {
|
> .gif {
|
||||||
flex: 1 0 auto;
|
flex: 1 0 auto;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
margin: 2.5px;
|
margin: 2.5px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background: #000;
|
//background: #000;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
video {
|
video, img {
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||||
"types": ["chrome"/* , "node" */, "jest"],/* Type declaration files to be included in compilation. */
|
"types": ["chrome", "jest"], /* Type declaration files to be included in compilation. */
|
||||||
"allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
"allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||||
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||||
|
@ -6,7 +6,7 @@ const postcssPresetEnv = require('postcss-preset-env');
|
|||||||
const ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin');
|
const ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
const allowedIPs = ['195.66.140.39', '192.168.31.144', '127.0.0.1', '192.168.31.1', '192.168.31.192', '176.100.18.181'];
|
const allowedIPs = ['195.66.140.39', '192.168.31.144', '127.0.0.1', '192.168.31.1', '192.168.31.192', '176.100.18.181', '46.219.250.22'];
|
||||||
const devMode = process.env.NODE_ENV !== 'production';
|
const devMode = process.env.NODE_ENV !== 'production';
|
||||||
const useLocal = false;
|
const useLocal = false;
|
||||||
|
|
||||||
@ -115,7 +115,10 @@ module.exports = {
|
|||||||
console.log('Bad IP connecting: ' + IP, req.url);
|
console.log('Bad IP connecting: ' + IP, req.url);
|
||||||
res.status(404).send('Nothing interesting here.');
|
res.status(404).send('Nothing interesting here.');
|
||||||
} else {
|
} else {
|
||||||
|
if(req.url.indexOf('/assets/') !== 0) {
|
||||||
console.log(req.url, IP);
|
console.log(req.url, IP);
|
||||||
|
}
|
||||||
|
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user