Browse Source

Several little fixes

Gif Safari & round videos fix
master
morethanwords 4 years ago
parent
commit
82043e4c4a
  1. 121
      src/components/emoticonsDropdown.ts
  2. 159
      src/components/horizontalMenu.ts
  3. 327
      src/components/misc.ts
  4. 2
      src/components/poll.ts
  5. 2
      src/components/popup.ts
  6. 3
      src/components/popupCreatePoll.ts
  7. 145
      src/components/ripple.ts
  8. 2
      src/components/sidebarLeft/chatFolders.ts
  9. 6
      src/components/sidebarLeft/editFolder.ts
  10. 2
      src/components/slider.ts
  11. 12
      src/components/toast.ts
  12. 142
      src/components/wrappers.ts
  13. 5
      src/global.d.ts
  14. 2
      src/lib/appManagers/AppInlineBotsManager.ts
  15. 6
      src/lib/appManagers/appDialogsManager.ts
  16. 111
      src/lib/appManagers/appDocsManager.ts
  17. 32
      src/lib/appManagers/appImManager.ts
  18. 36
      src/lib/appManagers/appMediaViewer.ts
  19. 2
      src/lib/appManagers/appMessagesManager.ts
  20. 4
      src/lib/appManagers/appSidebarRight.ts
  21. 2
      src/lib/bin_utils.ts
  22. 44
      src/lib/mtproto/apiFileManager.ts
  23. 126
      src/lib/mtproto/mtproto.service.ts
  24. 3
      src/lib/utils.ts
  25. 2
      src/pages/pagesManager.ts
  26. 9
      src/scss/partials/_emojiDropdown.scss
  27. 32
      tsconfig.json
  28. 7
      webpack.common.js

121
src/components/emoticonsDropdown.ts

@ -1,22 +1,23 @@ @@ -1,22 +1,23 @@
import appImManager from "../lib/appManagers/appImManager";
import { horizontalMenu, renderImageFromUrl, putPreloader } from "./misc";
import { renderImageFromUrl, putPreloader } from "./misc";
import lottieLoader from "../lib/lottieLoader";
//import Scrollable from "./scrollable";
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 appStickersManager, { MTStickerSet } from "../lib/appManagers/appStickersManager";
//import apiManager from '../lib/mtproto/apiManager';
import apiManager from '../lib/mtproto/mtprotoworker';
import LazyLoadQueue from "./lazyLoadQueue";
import { wrapSticker } from "./wrappers";
import { wrapSticker, wrapVideo } from "./wrappers";
import appDocsManager from "../lib/appManagers/appDocsManager";
import ProgressivePreloader from "./preloader";
import ProgressivePreloader from "./preloader_new";
import Config, { touchSupport } from "../lib/config";
import { MTDocument } from "../types";
import animationIntersector from "./animationIntersector";
import appSidebarRight from "../lib/appManagers/appSidebarRight";
import appStateManager from "../lib/appManagers/appStateManager";
import { horizontalMenu } from "./horizontalMenu";
export const EMOTICONSSTICKERGROUP = 'emoticons-dropdown';
@ -150,7 +151,7 @@ class EmojiTab implements EmoticonsTab { @@ -150,7 +151,7 @@ class EmojiTab implements EmoticonsTab {
const spanEmoji = document.createElement('span');
const kek = RichTextProcessor.wrapEmojiText(emoji);
/* if(!kek.includes('emoji')) {
console.log(emoji, kek, spanEmoji, emoji.length, new TextEncoder().encode(emoji), emojiUnicode(emoji));
return;
@ -159,6 +160,10 @@ class EmojiTab implements EmoticonsTab { @@ -159,6 +160,10 @@ class EmojiTab implements EmoticonsTab {
//console.log(kek);
spanEmoji.innerHTML = kek;
if(spanEmoji.firstElementChild) {
(spanEmoji.firstElementChild as HTMLImageElement).setAttribute('loading', 'lazy');
}
//spanEmoji = spanEmoji.firstElementChild as HTMLSpanElement;
//spanEmoji.setAttribute('emoji', emoji);
@ -561,10 +566,10 @@ class GifsTab implements EmoticonsTab { @@ -561,10 +566,10 @@ class GifsTab implements EmoticonsTab {
preloader.remove();
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 gifHeight = gif.h;
let gifWidth = doc.w;
let gifHeight = doc.h;
if(gifHeight < height) {
gifWidth = height / gifHeight * gifWidth;
gifHeight = height;
@ -588,28 +593,103 @@ class GifsTab implements EmoticonsTab { @@ -588,28 +593,103 @@ class GifsTab implements EmoticonsTab {
//console.log('gif:', gif, w, h);
let div = document.createElement('div');
div.classList.add('gif', 'fade-in-transition');
div.style.width = w + 'px';
div.style.opacity = '0';
//div.style.height = h + 'px';
div.dataset.docID = gif.id;
div.dataset.docID = doc.id;
masonry.append(div);
let preloader = new ProgressivePreloader(div);
EmoticonsDropdown.lazyLoadQueue.push({
//let preloader = new ProgressivePreloader(div);
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,
load: () => {
let promise = appDocsManager.downloadDoc(gif);
preloader.attach(div, true, promise);
promise.then(blob => {
const download = appDocsManager.downloadDocNew(doc);
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();
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 { @@ -661,6 +741,11 @@ class EmoticonsDropdown {
//this.displayTimeout = setTimeout(() => {
if(firstTime) {
this.toggleEl.onmouseout = this.element.onmouseout = (e) => {
const toElement = (e as any).toElement as Element;
if(findUpClassName(toElement, 'emoji-dropdown')) {
return;
}
clearTimeout(this.displayTimeout);
this.displayTimeout = setTimeout(() => {
this.toggle();

159
src/components/horizontalMenu.ts

@ -0,0 +1,159 @@ @@ -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;
}

327
src/components/misc.ts

@ -1,176 +1,18 @@ @@ -1,176 +1,18 @@
import { whichChild, findUpTag, cancelEvent, findUpClassName } from "../lib/utils";
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 set = (elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLSourceElement, url: string) => {
if(elem instanceof HTMLImageElement || elem instanceof HTMLSourceElement) elem.src = url;
let set = (elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, url: string) => {
if(elem instanceof HTMLImageElement || elem instanceof HTMLVideoElement) elem.src = url;
else if(elem instanceof SVGImageElement) elem.setAttributeNS(null, 'href', 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]) {
set(elem, url);
} else {
if(elem instanceof HTMLSourceElement) {
elem.src = url;
if(elem instanceof HTMLVideoElement) {
set(elem, url);
} else {
await new Promise((resolve, reject) => {
let loader = new Image();
@ -185,6 +27,7 @@ export async function renderImageFromUrl(elem: HTMLElement | HTMLImageElement | @@ -185,6 +27,7 @@ export async function renderImageFromUrl(elem: HTMLElement | HTMLImageElement |
loader.addEventListener('error', reject);
});
}
}
return !!loadedURLs[url];
@ -211,164 +54,6 @@ export function putPreloader(elem: Element, returnDiv = false) { @@ -211,164 +54,6 @@ export function putPreloader(elem: Element, returnDiv = false) {
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) {
str = str.replace(/\D/g, '');
let phoneCode = str.slice(0, 6);

2
src/components/poll.ts

@ -2,10 +2,10 @@ import appPollsManager, { PollResults, Poll } from "../lib/appManagers/appPollsM @@ -2,10 +2,10 @@ import appPollsManager, { PollResults, Poll } from "../lib/appManagers/appPollsM
import { RichTextProcessor } from "../lib/richtextprocessor";
import { findUpClassName, $rootScope, cancelEvent } from "../lib/utils";
import { mediaSizes, touchSupport } from "../lib/config";
import { ripple } from "./misc";
import appSidebarRight from "../lib/appManagers/appSidebarRight";
import appImManager from "../lib/appManagers/appImManager";
import serverTimeManager from "../lib/mtproto/serverTimeManager";
import { ripple } from "./ripple";
let lineTotalLength = 0;
const tailLength = 9;

2
src/components/popup.ts

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
import AvatarElement from "./avatar";
import { ripple } from "./misc";
import { ripple } from "./ripple";
export class PopupElement {
protected element = document.createElement('div');

3
src/components/popupCreatePoll.ts

@ -3,8 +3,7 @@ import Scrollable from "./scrollable_new"; @@ -3,8 +3,7 @@ import Scrollable from "./scrollable_new";
import appMessagesManager from "../lib/appManagers/appMessagesManager";
import { $rootScope } from "../lib/utils";
import { Poll } from "../lib/appManagers/appPollsManager";
import { nextRandomInt, bigint } from "../lib/bin_utils";
import { toast } from "./misc";
import { toast } from "./toast";
const InputField = (placeholder: string, label: string, name: string) => {
const div = document.createElement('div');

145
src/components/ripple.ts

@ -0,0 +1,145 @@ @@ -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});
});
}
}

2
src/components/sidebarLeft/chatFolders.ts

@ -4,9 +4,9 @@ import apiManager from "../../lib/mtproto/mtprotoworker"; @@ -4,9 +4,9 @@ import apiManager from "../../lib/mtproto/mtprotoworker";
import appMessagesManager, { DialogFilter } from "../../lib/appManagers/appMessagesManager";
import { RichTextProcessor } from "../../lib/richtextprocessor";
import appPeersManager from "../../lib/appManagers/appPeersManager";
import { ripple } from "../misc";
import { $rootScope, cancelEvent } from "../../lib/utils";
import appSidebarLeft from "../../lib/appManagers/appSidebarLeft";
import { ripple } from "../ripple";
type DialogFilterSuggested = {
_: 'dialogFilterSuggested',

6
src/components/sidebarLeft/editFolder.ts

@ -1,10 +1,12 @@ @@ -1,10 +1,12 @@
import { SliderTab } from "../slider";
import appSidebarLeft, { AppSidebarLeft } from "../../lib/appManagers/appSidebarLeft";
import lottieLoader, { RLottiePlayer } from "../../lib/lottieLoader";
import appMessagesManager, { DialogFilter, Dialog } from "../../lib/appManagers/appMessagesManager";
import { parseMenuButtonsTo, ripple, toast } from "../misc";
import appMessagesManager, { DialogFilter } from "../../lib/appManagers/appMessagesManager";
import { parseMenuButtonsTo } from "../misc";
import appDialogsManager from "../../lib/appManagers/appDialogsManager";
import { copy, deepEqual } from "../../lib/utils";
import { toast } from "../toast";
import { ripple } from "../ripple";
export default class AppEditFolderTab implements SliderTab {
public container: HTMLElement;

2
src/components/slider.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { horizontalMenu } from "./misc";
import { horizontalMenu } from "./horizontalMenu";
export interface SliderTab {
onOpen?: () => void,

12
src/components/toast.ts

@ -0,0 +1,12 @@ @@ -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);
}

142
src/components/wrappers.ts

@ -18,78 +18,92 @@ import AudioElement from './audio'; @@ -18,78 +18,92 @@ import AudioElement from './audio';
import { Download } from '../lib/appManagers/appDownloadManager';
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,
container: HTMLDivElement,
message: any,
boxWidth: number,
boxHeight: number,
container?: HTMLDivElement,
message?: any,
boxWidth?: number,
boxHeight?: number,
withTail?: boolean,
isOut?: boolean,
middleware: () => boolean,
lazyLoadQueue: LazyLoadQueue
middleware?: () => boolean,
lazyLoadQueue?: LazyLoadQueue,
noInfo?: true,
group?: string,
}) {
let span: HTMLSpanElement, spanPlay: HTMLSpanElement;
if(doc.type != 'round') {
span = document.createElement('span');
span.classList.add('video-time');
container.append(span);
if(doc.type != 'gif') {
span.innerText = (doc.duration + '').toHHMMSS(false);
spanPlay = document.createElement('span');
spanPlay.classList.add('video-play', 'tgico-largeplay', 'btn-circle', 'position-center');
container.append(spanPlay);
} else {
span.innerText = 'GIF';
if(!noInfo) {
if(doc.type != 'round') {
let span: HTMLSpanElement, spanPlay: HTMLSpanElement;
span = document.createElement('span');
span.classList.add('video-time');
container.append(span);
if(doc.type != 'gif') {
span.innerText = (doc.duration + '').toHHMMSS(false);
spanPlay = document.createElement('span');
spanPlay.classList.add('video-play', 'tgico-largeplay', 'btn-circle', 'position-center');
container.append(spanPlay);
} else {
span.innerText = 'GIF';
}
}
}
if(doc.type == 'video') {
return wrapPhoto(doc, message, container, boxWidth, boxHeight, withTail, isOut, lazyLoadQueue, middleware);
}
const video = document.createElement('video');
let img: HTMLImageElement;
if(withTail) {
img = wrapMediaWithTail(doc, message, container, boxWidth, boxHeight, isOut);
} else {
if(!boxWidth && !boxHeight) { // album
let sizes = doc.thumbs;
if(!doc.downloaded && sizes && sizes[0].bytes) {
appPhotosManager.setAttachmentPreview(sizes[0].bytes, container, false);
}
if(message) {
if(doc.type == 'video') {
return wrapPhoto(doc, message, container, boxWidth, boxHeight, withTail, isOut, lazyLoadQueue, middleware);
}
if(withTail) {
img = wrapMediaWithTail(doc, message, container, boxWidth, boxHeight, isOut);
} else {
if(!container.firstElementChild || (container.firstElementChild.tagName != 'IMG' && container.firstElementChild.tagName != 'VIDEO')) {
appPhotosManager.setAttachmentSize(doc, container, boxWidth, boxHeight);
if(!boxWidth && !boxHeight) { // album
let sizes = doc.thumbs;
if(!doc.downloaded && sizes && sizes[0].bytes) {
appPhotosManager.setAttachmentPreview(sizes[0].bytes, container, false);
}
} else {
if(!container.firstElementChild || (container.firstElementChild.tagName != 'IMG' && container.firstElementChild.tagName != 'VIDEO')) {
appPhotosManager.setAttachmentSize(doc, container, boxWidth, boxHeight);
}
}
img = container.lastElementChild as HTMLImageElement;
if(!img || img.tagName != 'IMG') {
container.append(img = new Image());
}
}
if(img) {
img.classList.add('thumbnail');
}
img = container.lastElementChild as HTMLImageElement;
if(!img || img.tagName != 'IMG') {
container.append(img = new Image());
if(withTail) {
const foreignObject = img.parentElement;
video.width = +foreignObject.getAttributeNS(null, 'width');
video.height = +foreignObject.getAttributeNS(null, 'height');
foreignObject.append(video);
}
}
if(img) {
img.classList.add('thumbnail');
if(!img?.parentElement) {
const posterURL = appDocsManager.getThumbURL(doc, false);
if(posterURL) {
video.poster = posterURL;
}
}
const video = document.createElement('video');
const source = document.createElement('source');
video.append(source);
if(withTail) {
const foreignObject = img.parentElement;
video.width = +foreignObject.getAttributeNS(null, 'width');
video.height = +foreignObject.getAttributeNS(null, 'height');
foreignObject.append(video);
} else {
if(!video.parentElement && container) {
container.append(video);
}
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);
} else if(!doc.downloaded) {
/* const promise = appDocsManager.downloadDoc(doc.id);
@ -117,13 +131,13 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai @@ -117,13 +131,13 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
/* if(!video.paused) {
video.pause();
} */
animationIntersector.addAnimation(video, 'chat');
if(group) {
animationIntersector.addAnimation(video, group);
}
}, {once: true});
}
renderImageFromUrl(source, doc.url);
source.type = doc.mime_type;
video.append(source);
renderImageFromUrl(video, doc.url);
video.setAttribute('playsinline', '');
/* if(!container.parentElement) {
@ -160,9 +174,9 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai @@ -160,9 +174,9 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
return;
} */
//return;
return doc.downloaded/* && false */ ? loadVideo() : lazyLoadQueue.push({div: container, load: loadVideo/* , wasSeen: true */});
doc.downloaded || !lazyLoadQueue/* && false */ ? loadVideo() : lazyLoadQueue.push({div: container, load: loadVideo/* , wasSeen: true */});
return video;
}
export const formatDate = (timestamp: number, monthShort = false, withYear = true) => {
@ -445,14 +459,14 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o @@ -445,14 +459,14 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
} else if(!onlyThumb && stickerType == 2 && withThumb && toneIndex <= 0) {
img = new Image();
const load = () => appDocsManager.downloadDocThumb(doc, thumb.type).then(url => {
const load = () => {
if(div.childElementCount || (middleware && !middleware())) return;
const promise = renderImageFromUrl(img, url);
const promise = renderImageFromUrl(img, appDocsManager.getFileURL(doc, false, thumb));
//if(!downloaded) {
promise.then(afterRender);
//}
});
};
/* let downloaded = appDocsManager.hasDownloadedThumb(doc.id, thumb.type);
if(downloaded) {
@ -467,13 +481,13 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o @@ -467,13 +481,13 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
if(onlyThumb && doc.thumbs) { // for sticker panel
let thumb = doc.thumbs[0];
let load = () => appDocsManager.downloadDocThumb(doc, thumb.type).then(url => {
let load = () => {
let img = new Image();
renderImageFromUrl(img, url).then(() => {
return renderImageFromUrl(img, appDocsManager.getFileURL(doc, false, thumb)).then(() => {
if(middleware && !middleware()) return;
div.append(img);
});
});
};
return lazyLoadQueue ? (lazyLoadQueue.push({div, load}), Promise.resolve()) : load();
}

5
src/global.d.ts vendored

@ -4,4 +4,7 @@ declare module 'worker-loader!*' { @@ -4,4 +4,7 @@ declare module 'worker-loader!*' {
}
export default WebpackWorker;
}
}
declare function setInterval(callback: (...args: any[]) => void, ms: number): number;
declare function setTimeout(callback: (...args: any[]) => void, ms: number): number;

2
src/lib/appManagers/AppInlineBotsManager.ts

@ -2,8 +2,8 @@ import appMessagesManager from "./appMessagesManager"; @@ -2,8 +2,8 @@ import appMessagesManager from "./appMessagesManager";
import apiManagerProxy from "../mtproto/mtprotoworker";
import appPeersManager from "../appManagers/appPeersManager";
import appMessagesIDsManager from "./appMessagesIDsManager";
import { toast } from "../../components/misc";
import { RichTextProcessor } from "../richtextprocessor";
import { toast } from "../../components/toast";
export class AppInlineBotsManager {
/* private inlineResults: any = {};

6
src/lib/appManagers/appDialogsManager.ts

@ -1,10 +1,10 @@ @@ -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 appPeersManager from './appPeersManager';
import appMessagesManager, { AppMessagesManager, Dialog, DialogFilter } from "./appMessagesManager";
import appUsersManager, { User } from "./appUsersManager";
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_new";
import { logger, LogLevels } from "../logger";
@ -14,6 +14,8 @@ import { PopupButton, PopupPeer } from "../../components/popup"; @@ -14,6 +14,8 @@ import { PopupButton, PopupPeer } from "../../components/popup";
import { SliderTab } from "../../components/slider";
import appStateManager from "./appStateManager";
import { touchSupport, isSafari } from "../config";
import { horizontalMenu } from "../../components/horizontalMenu";
import { ripple } from "../../components/ripple";
type DialogDom = {
avatarEl: AvatarElement,

111
src/lib/appManagers/appDocsManager.ts

@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
import {RichTextProcessor} from '../richtextprocessor';
import { CancellablePromise, deferredPromise } from '../polyfill';
import { isObject, getFileURL } from '../utils';
import { isObject, getFileURL, FileURLType } from '../utils';
import opusDecodeController from '../opusDecodeController';
import { MTDocument, inputDocumentFileLocation } from '../../types';
import { MTDocument, inputDocumentFileLocation, MTPhotoSize } from '../../types';
import { getFileNameByLocation } from '../bin_utils';
import appDownloadManager, { Download, ResponseMethod } from './appDownloadManager';
import appPhotosManager from './appPhotosManager';
class AppDocsManager {
private docs: {[docID: string]: MTDocument} = {};
private thumbs: {[docIDAndSize: string]: Promise<string>} = {};
private downloadPromises: {[docID: string]: CancellablePromise<Blob>} = {};
public saveDoc(apiDoc: MTDocument, context?: any) {
@ -49,16 +49,16 @@ class AppDocsManager { @@ -49,16 +49,16 @@ class AppDocsManager {
apiDoc.audioPerformer = attribute.performer;
apiDoc.type = attribute.pFlags.voice && apiDoc.mime_type == "audio/ogg" ? 'voice' : 'audio';
if(apiDoc.type == 'audio') {
/* if(apiDoc.type == 'audio') {
apiDoc.supportsStreaming = true;
}
} */
break;
case 'documentAttributeVideo':
apiDoc.duration = attribute.duration;
apiDoc.w = attribute.w;
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) {
apiDoc.type = 'round';
} else /* if(apiDoc.thumbs) */ {
@ -124,6 +124,10 @@ class AppDocsManager { @@ -124,6 +124,10 @@ class AppDocsManager {
break;
}
}
if((apiDoc.type == 'gif' && apiDoc.size > 8e6) || apiDoc.type == 'audio' || apiDoc.type == 'video') {
apiDoc.supportsStreaming = true;
}
if(!apiDoc.file_name) {
apiDoc.file_name = '';
@ -140,7 +144,7 @@ class AppDocsManager { @@ -140,7 +144,7 @@ class AppDocsManager {
}
if(!apiDoc.url) {
apiDoc.url = this.getFileURLByDoc(apiDoc);
apiDoc.url = this.getFileURL(apiDoc);
}
return apiDoc;
@ -150,8 +154,7 @@ class AppDocsManager { @@ -150,8 +154,7 @@ class AppDocsManager {
return isObject(docID) && typeof(docID) !== 'string' ? docID : this.docs[docID as string];
}
public getMediaInputByID(docID: any) {
let doc = this.getDoc(docID);
public getMediaInput(doc: MTDocument) {
return {
_: 'inputMediaDocument',
flags: 0,
@ -165,9 +168,7 @@ class AppDocsManager { @@ -165,9 +168,7 @@ class AppDocsManager {
};
}
public getInputByID(docID: any, thumbSize?: string): inputDocumentFileLocation {
let doc = this.getDoc(docID);
public getInput(doc: MTDocument, thumbSize?: string): inputDocumentFileLocation {
return {
_: 'inputDocumentFileLocation',
id: doc.id,
@ -176,35 +177,55 @@ class AppDocsManager { @@ -176,35 +177,55 @@ class AppDocsManager {
thumb_size: thumbSize
};
}
public getFileName(doc: MTDocument) {
if(doc.file_name) {
return doc.file_name;
}
var fileExt = '.' + doc.mime_type.split('/')[1];
if(fileExt == '.octet-stream') {
fileExt = '';
}
public getFileURL(doc: MTDocument, download = false, thumb?: MTPhotoSize) {
const inputFileLocation = this.getInput(doc, thumb?.type);
return 't_' + (doc.type || 'file') + doc.id + fileExt;
}
let type: FileURLType;
if(download) {
type = 'download';
} else if(thumb) {
type = 'thumb';
} else if(doc.supportsStreaming) {
type = 'stream';
} else {
type = 'document';
}
public getFileURLByDoc(doc: MTDocument, download = false) {
const inputFileLocation = this.getInputByID(doc);
const type = download ? 'download' : (doc.supportsStreaming ? 'stream' : 'document');
let mimeType: string;
if(thumb) {
mimeType = doc.sticker ? 'image/webp' : 'image/jpeg'/* doc.mime_type */;
} else {
mimeType = doc.mime_type || 'application/octet-stream';
}
return getFileURL(type, {
dcID: doc.dc_id,
location: inputFileLocation,
size: doc.size,
mimeType: doc.mime_type || 'application/octet-stream',
size: thumb ? thumb.size : doc.size,
mimeType: mimeType,
fileName: doc.file_name
});
}
public getInputFileName(doc: MTDocument) {
return getFileNameByLocation(this.getInputByID(doc));
public getThumbURL(doc: MTDocument, useBytes = true) {
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> {
@ -229,11 +250,7 @@ class AppDocsManager { @@ -229,11 +250,7 @@ class AppDocsManager {
const deferred = deferredPromise<Blob>();
/* if(doc.supportsStreaming) {
doc.url = '/stream/' + '';
} */
const url = this.getFileURLByDoc(doc);
const url = this.getFileURL(doc);
fetch(url).then(res => res.blob())
/* downloadPromise */.then((blob) => {
if(blob) {
@ -323,30 +340,8 @@ class AppDocsManager { @@ -323,30 +340,8 @@ class AppDocsManager {
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) {
const url = this.getFileURLByDoc(doc, true);
const url = this.getFileURL(doc, true);
const fileName = this.getInputFileName(doc);
return appDownloadManager.downloadToDisc(fileName, url);

32
src/lib/appManagers/appImManager.ts

@ -16,7 +16,7 @@ import appSidebarLeft from "./appSidebarLeft"; @@ -16,7 +16,7 @@ import appSidebarLeft from "./appSidebarLeft";
import appChatsManager, { Channel, Chat } from "./appChatsManager";
import { wrapDocument, wrapPhoto, wrapVideo, wrapSticker, wrapReply, wrapAlbum, wrapPoll, formatDate } from '../../components/wrappers';
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 Scrollable from '../../components/scrollable';
import Scrollable from '../../components/scrollable_new';
@ -37,6 +37,8 @@ import AppSearch, { SearchGroup } from '../../components/appSearch'; @@ -37,6 +37,8 @@ import AppSearch, { SearchGroup } from '../../components/appSearch';
import PopupDatePicker from '../../components/popupDatepicker';
import appAudio from '../../components/appAudio';
import appPollsManager from './appPollsManager';
import { ripple } from '../../components/ripple';
import { horizontalMenu } from '../../components/horizontalMenu';
//console.log('appImManager included33!');
@ -44,6 +46,8 @@ appSidebarLeft; // just to include @@ -44,6 +46,8 @@ appSidebarLeft; // just to include
const testScroll = false;
const ANIMATIONGROUP = 'chat';
class ChatContextMenu {
private element = document.getElementById('bubble-contextmenu') as HTMLDivElement;
private buttons: {
@ -1511,7 +1515,7 @@ export class AppImManager { @@ -1511,7 +1515,7 @@ export class AppImManager {
//console.timeEnd('appImManager setPeer pre promise');
animationIntersector.lockGroup('chat');
animationIntersector.lockGroup(ANIMATIONGROUP);
this.setPeerPromise = Promise.all([
promise.then(() => {
////this.log('setPeer removing preloader');
@ -1532,8 +1536,8 @@ export class AppImManager { @@ -1532,8 +1536,8 @@ export class AppImManager {
}
this.scrollable.container.append(this.chatInner);
animationIntersector.unlockGroup('chat');
animationIntersector.checkAnimations(false, 'chat'/* , true */);
animationIntersector.unlockGroup(ANIMATIONGROUP);
animationIntersector.checkAnimations(false, ANIMATIONGROUP/* , true */);
//this.scrollable.attachSentinels();
//this.scrollable.container.insertBefore(this.chatInner, this.scrollable.container.lastElementChild);
@ -1699,7 +1703,7 @@ export class AppImManager { @@ -1699,7 +1703,7 @@ export class AppImManager {
//bubble.remove();
});
animationIntersector.checkAnimations(false, 'chat');
animationIntersector.checkAnimations(false, ANIMATIONGROUP);
this.deleteEmptyDateGroups();
}
@ -1805,15 +1809,12 @@ export class AppImManager { @@ -1805,15 +1809,12 @@ export class AppImManager {
let promises: Promise<any>[] = [];
(Array.from(bubble.querySelectorAll('img, video')) as HTMLImageElement[]).forEach(el => {
if(el instanceof HTMLVideoElement) {
let source = el.firstElementChild as HTMLSourceElement;
if(!source || !source.src) {
if(!el.src) {
//this.log.warn('no source', el, source, 'src', source.src);
return;
} else if(el.readyState >= 4) return;
} else if(el.complete || !el.src) return;
let src = el.src;
let promise = new Promise((resolve, reject) => {
let r: () => boolean;
let onLoad = () => {
@ -2046,7 +2047,7 @@ export class AppImManager { @@ -2046,7 +2047,7 @@ export class AppImManager {
let messageMedia = message.media;
if(totalEntities) {
if(totalEntities && !messageMedia) {
let emojiEntities = totalEntities.filter((e: any) => e._ == 'messageEntityEmoji');
let strLength = messageMessage.length;
let emojiStrLength = emojiEntities.reduce((acc: number, curr: any) => acc + curr.length, 0);
@ -2237,7 +2238,8 @@ export class AppImManager { @@ -2237,7 +2238,8 @@ export class AppImManager {
withTail: tailSupported,
isOut: isOut,
lazyLoadQueue: this.lazyLoadQueue,
middleware: null
middleware: null,
group: ANIMATIONGROUP
});
preloader.attach(attachmentDiv, false);
@ -2342,7 +2344,8 @@ export class AppImManager { @@ -2342,7 +2344,8 @@ export class AppImManager {
boxHeight: mediaSizes.active.webpage.height,
lazyLoadQueue: this.lazyLoadQueue,
middleware: this.getMiddleware(),
isOut
isOut,
group: ANIMATIONGROUP
});
//}
} else {
@ -2428,7 +2431,7 @@ export class AppImManager { @@ -2428,7 +2431,7 @@ export class AppImManager {
div: attachmentDiv,
middleware: this.getMiddleware(),
lazyLoadQueue: this.lazyLoadQueue,
group: 'chat',
group: ANIMATIONGROUP,
//play: !!message.pending || !multipleRender,
play: true,
loop: true,
@ -2463,7 +2466,8 @@ export class AppImManager { @@ -2463,7 +2466,8 @@ export class AppImManager {
withTail: tailSupported,
isOut: isOut,
lazyLoadQueue: this.lazyLoadQueue,
middleware: this.getMiddleware()
middleware: this.getMiddleware(),
group: ANIMATIONGROUP
});
}

36
src/lib/appManagers/appMediaViewer.ts

@ -311,9 +311,7 @@ export class AppMediaViewer { @@ -311,9 +311,7 @@ export class AppMediaViewer {
src = target.src;
} else if(target instanceof HTMLVideoElement) {
let video = mediaElement = document.createElement('video');
let source = document.createElement('source');
src = target.querySelector('source')?.src;
video.append(source);
video.src = target?.src;
} else if(target instanceof SVGSVGElement) {
let clipID = target.dataset.clipID;
let newClipID = clipID + '-mv';
@ -327,12 +325,6 @@ export class AppMediaViewer { @@ -327,12 +325,6 @@ export class AppMediaViewer {
newSvg.insertAdjacentHTML('beforeend', target.firstElementChild.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 use = defs.firstElementChild.firstElementChild as SVGUseElement;
@ -655,7 +647,7 @@ export class AppMediaViewer { @@ -655,7 +647,7 @@ export class AppMediaViewer {
return promise;
}
public updateMediaSource(target: HTMLElement, url: string, tagName: 'source' | 'img') {
public updateMediaSource(target: HTMLElement, url: string, tagName: 'video' | 'img') {
//if(target instanceof SVGSVGElement) {
let el = target.querySelector(tagName) as HTMLElement;
renderImageFromUrl(el, url);
@ -778,11 +770,6 @@ export class AppMediaViewer { @@ -778,11 +770,6 @@ export class AppMediaViewer {
if(isVideo) {
////////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(() => {
//return; // set and don't move
//if(wasActive) return;
@ -790,9 +777,7 @@ export class AppMediaViewer { @@ -790,9 +777,7 @@ export class AppMediaViewer {
const div = mover.firstElementChild && mover.firstElementChild.classList.contains('media-viewer-aspecter') ? mover.firstElementChild : mover;
const video = mover.querySelector('video') || document.createElement('video');
const source = video.firstElementChild as HTMLSourceElement || document.createElement('source');
source.removeAttribute('src');
video.src = '';
video.setAttribute('playsinline', '');
if(media.type == 'gif') {
@ -801,10 +786,6 @@ export class AppMediaViewer { @@ -801,10 +786,6 @@ export class AppMediaViewer {
video.loop = true;
}
if(!source.parentElement) {
video.append(source);
}
const createPlayer = () => {
if(media.type != 'gif') {
video.dataset.ckin = 'default';
@ -823,7 +804,7 @@ export class AppMediaViewer { @@ -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 promise = appDocsManager.downloadDoc(media.id);
@ -845,12 +826,11 @@ export class AppMediaViewer { @@ -845,12 +826,11 @@ export class AppMediaViewer {
div.firstElementChild.lastElementChild.append(video);
}
this.updateMediaSource(mover, url, 'source');
this.updateMediaSource(mover, url, 'video');
//this.updateMediaSource(target, url, 'source');
} else {
//const promise = new Promise((resolve) => video.addEventListener('loadeddata', resolve, {once: true}));
renderImageFromUrl(source, url);
source.type = media.mime_type;
renderImageFromUrl(video, url);
//await promise;
const first = div.firstElementChild as HTMLImageElement;
@ -864,13 +844,13 @@ export class AppMediaViewer { @@ -864,13 +844,13 @@ export class AppMediaViewer {
}
// я хз что это такое, видео появляются просто чёрными и не включаются без этого кода снизу
source.remove();
/* source.remove();
window.requestAnimationFrame(() => {
window.requestAnimationFrame(() => {
//parent.append(video);
video.append(source);
});
});
}); */
player = createPlayer();
});

2
src/lib/appManagers/appMessagesManager.ts

@ -1576,7 +1576,7 @@ export class AppMessagesManager { @@ -1576,7 +1576,7 @@ export class AppMessagesManager {
} else {
let doc = messageMedia.document;
appDocsManager.saveDoc(doc);
inputMedia = appDocsManager.getMediaInputByID(doc.id);
inputMedia = appDocsManager.getMediaInput(doc);
}
inputs.push({

4
src/lib/appManagers/appSidebarRight.ts

@ -1,4 +1,4 @@ @@ -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_new';
import { $rootScope, findUpClassName } from "../utils";
@ -25,6 +25,8 @@ import SearchInput from "../../components/searchInput"; @@ -25,6 +25,8 @@ import SearchInput from "../../components/searchInput";
import appPollsManager from "./appPollsManager";
import { roundPercents } from "../../components/poll";
import appDialogsManager from "./appDialogsManager";
import { ripple } from "../../components/ripple";
import { horizontalMenu } from "../../components/horizontalMenu";
const testScroll = false;

2
src/lib/bin_utils.ts

@ -386,7 +386,7 @@ export function nextRandomInt(maxValue: number) { @@ -386,7 +386,7 @@ export function nextRandomInt(maxValue: number) {
export function getFileNameByLocation(location: InputFileLocation | FileLocation, options?: Partial<{
fileName: string
}>) {
const fileName = (options?.fileName || '').split('.');
const fileName = '';//(options?.fileName || '').split('.');
const ext = fileName[fileName.length - 1] || '';
switch(location._) {

44
src/lib/mtproto/apiFileManager.ts

@ -71,7 +71,7 @@ export class ApiFileManager { @@ -71,7 +71,7 @@ export class ApiFileManager {
const downloadPull = this.downloadPulls[dcID];
//const downloadLimit = dcID == 'upload' ? 11 : 5;
//const downloadLimit = 24;
const downloadLimit = dcID == 'upload' ? 11 : 50;
const downloadLimit = dcID == 'upload' ? 11 : 48;
if(this.downloadActives[dcID] >= downloadLimit || !downloadPull || !downloadPull.length) {
return false;
@ -115,7 +115,8 @@ export class ApiFileManager { @@ -115,7 +115,8 @@ export class ApiFileManager {
}
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() => {
checkCancel && checkCancel();
@ -138,9 +139,10 @@ export class ApiFileManager { @@ -138,9 +139,10 @@ export class ApiFileManager {
private getLimitPart(size: number): 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 bytes = 128;
else bytes = 128; */
return bytes * 1024;
}
@ -186,11 +188,11 @@ export class ApiFileManager { @@ -186,11 +188,11 @@ export class ApiFileManager {
options.mimeType = 'application/json';
}
const fileName = getFileNameByLocation(location);
const fileName = getFileNameByLocation(location, {fileName: options.fileName});
const cachedPromise = this.cachedDownloadPromises[fileName];
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(options.processPart) {
@ -267,7 +269,7 @@ export class ApiFileManager { @@ -267,7 +269,7 @@ export class ApiFileManager {
let startOffset = 0;
let writeFilePromise: CancellablePromise<unknown> = Promise.resolve(),
writeFileDeferred: CancellablePromise<unknown>;
const maxRequests = options.processPart ? 5 : 10;
const maxRequests = options.processPart ? 5 : 5;
/* if(fileWriter.length) {
startOffset = fileWriter.length;
@ -334,22 +336,26 @@ export class ApiFileManager { @@ -334,22 +336,26 @@ export class ApiFileManager {
superpuper();
}
//done += limit;
done += result.bytes.byteLength;
this.log('downloadFile requestFilePart result:', fileName, result);
const isFinal = offset + limit >= size || !result.bytes.byteLength;
if(result.bytes.byteLength) {
//done += limit;
done += result.bytes.byteLength;
const processedResult = await processDownloaded(result.bytes, offset);
checkCancel();
const processedResult = await processDownloaded(result.bytes, offset);
checkCancel();
const isFinal = offset + limit >= size;
//if(!isFinal) {
////this.log('deferred notify 2:', {done: offset + limit, total: size}, deferred);
deferred.notify({done, offset, total: size});
//}
//if(!isFinal) {
////this.log('deferred notify 2:', {done: offset + limit, total: size}, deferred);
deferred.notify({done, offset, total: size});
//}
await writeFilePromise;
checkCancel();
await writeFilePromise;
checkCancel();
await FileManager.write(fileWriter, processedResult);
}
await FileManager.write(fileWriter, processedResult);
writeFileDeferred.resolve();
if(isFinal) {

126
src/lib/mtproto/mtproto.service.ts

@ -178,6 +178,23 @@ function timeout(delay: number): Promise<Response> { @@ -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) => {
log.error('error:', error);
});
@ -196,7 +213,17 @@ ctx.addEventListener('fetch', (event: FetchEvent): void => { @@ -196,7 +213,17 @@ ctx.addEventListener('fetch', (event: FetchEvent): void => {
case 'document':
case 'photo': {
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) => {
console.log('[SW] user aborted request:', fileName);
@ -221,7 +248,20 @@ ctx.addEventListener('fetch', (event: FetchEvent): void => { @@ -221,7 +248,20 @@ ctx.addEventListener('fetch', (event: FetchEvent): void => {
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([
timeout(45 * 1000),
promise
@ -231,61 +271,55 @@ ctx.addEventListener('fetch', (event: FetchEvent): void => { @@ -231,61 +271,55 @@ ctx.addEventListener('fetch', (event: FetchEvent): void => {
}
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 fileName = getFileNameByLocation(info.location);
log.debug('[stream]', url, offset, end);
event.respondWith(new Promise((resolve, reject) => {
// safari workaround
if(offset === 0 && end === 1) {
resolve(new Response(new Uint8Array(2).buffer, {
status: 206,
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 alignedOffset = alignOffset(offset, limit);
//log.debug('[stream] requestFilePart:', info.dcID, info.location, alignedOffset, limit);
apiFileManager.requestFilePart(info.dcID, info.location, alignedOffset, limit).then(result => {
let ab = result.bytes;
event.respondWith(Promise.race([
timeout(45 * 1000),
new Promise<Response>((resolve, reject) => {
// safari workaround
const possibleResponse = responseForSafariFirstRange(range, info.mimeType, info.size);
if(possibleResponse) {
return resolve(possibleResponse);
}
//log.debug('[stream] requestFilePart result:', result);
const limit = end && end < STREAM_CHUNK_UPPER_LIMIT ? alignLimit(end - offset + 1) : STREAM_CHUNK_UPPER_LIMIT;
const alignedOffset = alignOffset(offset, limit);
const headers: Record<string, string> = {
'Accept-Ranges': 'bytes',
'Content-Range': `bytes ${alignedOffset}-${alignedOffset + ab.byteLength - 1}/${info.size || '*'}`,
'Content-Length': `${ab.byteLength}`,
};
//log.debug('[stream] requestFilePart:', info.dcID, info.location, alignedOffset, limit);
if(info.mimeType) headers['Content-Type'] = info.mimeType;
apiFileManager.requestFilePart(info.dcID, info.location, alignedOffset, limit).then(result => {
let ab = result.bytes;
if(isSafari) {
ab = ab.slice(offset - alignedOffset, end - alignedOffset + 1);
headers['Content-Range'] = `bytes ${offset}-${offset + ab.byteLength - 1}/${info.size || '*'}`;
headers['Content-Length'] = `${ab.byteLength}`;
}
//log.debug('[stream] requestFilePart result:', result);
resolve(new Response(ab, {
status: 206,
statusText: 'Partial Content',
headers,
}));
});
}));
const headers: Record<string, string> = {
'Accept-Ranges': 'bytes',
'Content-Range': `bytes ${alignedOffset}-${alignedOffset + ab.byteLength - 1}/${info.size || '*'}`,
'Content-Length': `${ab.byteLength}`,
};
if(info.mimeType) headers['Content-Type'] = info.mimeType;
if(isSafari) {
ab = ab.slice(offset - alignedOffset, end - alignedOffset + 1);
headers['Content-Range'] = `bytes ${offset}-${offset + ab.byteLength - 1}/${info.size || '*'}`;
headers['Content-Length'] = `${ab.byteLength}`;
}
resolve(new Response(ab, {
status: 206,
statusText: 'Partial Content',
headers,
}));
});
})
]));
break;
}

3
src/lib/utils.ts

@ -538,7 +538,8 @@ export function getEmojiToneIndex(input: string) { @@ -538,7 +538,8 @@ export function getEmojiToneIndex(input: string) {
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,
location: InputFileLocation | FileLocation,
size?: number,

2
src/pages/pagesManager.ts

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
import Page from "./page";
import { whichChild } from "../lib/utils";
import { horizontalMenu } from "../components/misc";
import lottieLoader from "../lib/lottieLoader";
import { horizontalMenu } from "../components/horizontalMenu";
class PagesManager {
private pageID = -1;

9
src/scss/partials/_emojiDropdown.scss

@ -328,19 +328,22 @@ @@ -328,19 +328,22 @@
display: flex;
flex-wrap: wrap;
> div {
> .gif {
flex: 1 0 auto;
max-width: 100%;
height: 100px;
margin: 2.5px;
cursor: pointer;
background: #000;
//background: #000;
position: relative;
video {
video, img {
object-fit: cover;
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
}
}
}

32
tsconfig.json

@ -2,39 +2,39 @@ @@ -2,39 +2,39 @@
"compilerOptions": {
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
//"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"target": "es2016", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
//"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"target": "es2016", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"lib": ["es2016", "dom", "ES2018.Promise", "webworker"], /* Specify library files to be included in the compilation. */
"allowJs": true, /* Allow javascript files to be compiled. */
"lib": ["es2016", "dom", "ES2018.Promise", "webworker"], /* Specify library files to be included in the compilation. */
"allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
"sourceMap": true, /* Generates corresponding '.map' file. */
"sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./dist/", /* Redirect output structure to the directory. */
//"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "outDir": "./dist/", /* Redirect output structure to the directory. */
//"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
//"importHelpers": true, /* Import emit helpers from 'tslib'. */
"downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
//"importHelpers": true, /* Import emit helpers from 'tslib'. */
"downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
"strictNullChecks": false, /* Enable strict null checks. */
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
"strictNullChecks": false, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
"strictPropertyInitialization": false, /* Enable strict checking of property initialization in classes. */
"strictPropertyInitialization": false, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
//"noUnusedLocals": true, /* Report errors on unused locals. */
//"noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
@ -45,9 +45,9 @@ @@ -45,9 +45,9 @@
// "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. */
// "typeRoots": [], /* List of folders to include type definitions from. */
"types": ["chrome"/* , "node" */, "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. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
"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. */
"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. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
"resolveJsonModule": true

7
webpack.common.js

@ -6,7 +6,7 @@ const postcssPresetEnv = require('postcss-preset-env'); @@ -6,7 +6,7 @@ const postcssPresetEnv = require('postcss-preset-env');
const ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin');
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 useLocal = false;
@ -115,7 +115,10 @@ module.exports = { @@ -115,7 +115,10 @@ module.exports = {
console.log('Bad IP connecting: ' + IP, req.url);
res.status(404).send('Nothing interesting here.');
} else {
console.log(req.url, IP);
if(req.url.indexOf('/assets/') !== 0) {
console.log(req.url, IP);
}
next();
}
});

Loading…
Cancel
Save