From 82043e4c4a624316d34a9919e3ae1c738c7adc3f Mon Sep 17 00:00:00 2001 From: morethanwords Date: Mon, 24 Aug 2020 17:09:31 +0300 Subject: [PATCH] Several little fixes Gif Safari & round videos fix --- src/components/emoticonsDropdown.ts | 121 ++++++-- src/components/horizontalMenu.ts | 159 ++++++++++ src/components/misc.ts | 327 +------------------- src/components/poll.ts | 2 +- src/components/popup.ts | 2 +- src/components/popupCreatePoll.ts | 3 +- src/components/ripple.ts | 145 +++++++++ src/components/sidebarLeft/chatFolders.ts | 2 +- src/components/sidebarLeft/editFolder.ts | 6 +- src/components/slider.ts | 2 +- src/components/toast.ts | 12 + src/components/wrappers.ts | 142 +++++---- src/global.d.ts | 5 +- src/lib/appManagers/AppInlineBotsManager.ts | 2 +- src/lib/appManagers/appDialogsManager.ts | 6 +- src/lib/appManagers/appDocsManager.ts | 111 ++++--- src/lib/appManagers/appImManager.ts | 32 +- src/lib/appManagers/appMediaViewer.ts | 36 +-- src/lib/appManagers/appMessagesManager.ts | 2 +- src/lib/appManagers/appSidebarRight.ts | 4 +- src/lib/bin_utils.ts | 2 +- src/lib/mtproto/apiFileManager.ts | 44 +-- src/lib/mtproto/mtproto.service.ts | 126 +++++--- src/lib/utils.ts | 3 +- src/pages/pagesManager.ts | 2 +- src/scss/partials/_emojiDropdown.scss | 9 +- tsconfig.json | 32 +- webpack.common.js | 7 +- 28 files changed, 740 insertions(+), 606 deletions(-) create mode 100644 src/components/horizontalMenu.ts create mode 100644 src/components/ripple.ts create mode 100644 src/components/toast.ts diff --git a/src/components/emoticonsDropdown.ts b/src/components/emoticonsDropdown.ts index b8332388..f10b2f83 100644 --- a/src/components/emoticonsDropdown.ts +++ b/src/components/emoticonsDropdown.ts @@ -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 { 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 { //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 { 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 { //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).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 = ``; + div.innerHTML = ``; }); - return promise; + return download.promise; } - }); + }); */ } }); @@ -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(); diff --git a/src/components/horizontalMenu.ts b/src/components/horizontalMenu.ts new file mode 100644 index 00000000..7ea875a6 --- /dev/null +++ b/src/components/horizontalMenu.ts @@ -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; +} \ No newline at end of file diff --git a/src/components/misc.ts b/src/components/misc.ts index 0355c5db..bfcb249e 100644 --- a/src/components/misc.ts +++ b/src/components/misc.ts @@ -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 = () => 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 { +export async function renderImageFromUrl(elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, url: string): Promise { 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 | loader.addEventListener('error', reject); }); } + } return !!loadedURLs[url]; @@ -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); diff --git a/src/components/poll.ts b/src/components/poll.ts index b1d567a7..513d2dc6 100644 --- a/src/components/poll.ts +++ b/src/components/poll.ts @@ -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; diff --git a/src/components/popup.ts b/src/components/popup.ts index 0245165d..aca2bde8 100644 --- a/src/components/popup.ts +++ b/src/components/popup.ts @@ -1,5 +1,5 @@ import AvatarElement from "./avatar"; -import { ripple } from "./misc"; +import { ripple } from "./ripple"; export class PopupElement { protected element = document.createElement('div'); diff --git a/src/components/popupCreatePoll.ts b/src/components/popupCreatePoll.ts index 0ae34efe..666dc47c 100644 --- a/src/components/popupCreatePoll.ts +++ b/src/components/popupCreatePoll.ts @@ -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'); diff --git a/src/components/ripple.ts b/src/components/ripple.ts new file mode 100644 index 00000000..cd6c0bd4 --- /dev/null +++ b/src/components/ripple.ts @@ -0,0 +1,145 @@ +import { touchSupport } from "../lib/config"; + +let rippleClickID = 0; +export function ripple(elem: HTMLElement, callback: (id: number) => Promise = () => 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}); + }); + } +} \ No newline at end of file diff --git a/src/components/sidebarLeft/chatFolders.ts b/src/components/sidebarLeft/chatFolders.ts index 195939ec..8142a5af 100644 --- a/src/components/sidebarLeft/chatFolders.ts +++ b/src/components/sidebarLeft/chatFolders.ts @@ -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', diff --git a/src/components/sidebarLeft/editFolder.ts b/src/components/sidebarLeft/editFolder.ts index 7541a961..c3f8652b 100644 --- a/src/components/sidebarLeft/editFolder.ts +++ b/src/components/sidebarLeft/editFolder.ts @@ -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; diff --git a/src/components/slider.ts b/src/components/slider.ts index 169129d9..3399aeff 100644 --- a/src/components/slider.ts +++ b/src/components/slider.ts @@ -1,4 +1,4 @@ -import { horizontalMenu } from "./misc"; +import { horizontalMenu } from "./horizontalMenu"; export interface SliderTab { onOpen?: () => void, diff --git a/src/components/toast.ts b/src/components/toast.ts new file mode 100644 index 00000000..1215d641 --- /dev/null +++ b/src/components/toast.ts @@ -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); +} \ No newline at end of file diff --git a/src/components/wrappers.ts b/src/components/wrappers.ts index 2f8a6729..0266df8f 100644 --- a/src/components/wrappers.ts +++ b/src/components/wrappers.ts @@ -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 /* 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 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 } 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 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(); } diff --git a/src/global.d.ts b/src/global.d.ts index f3dfad64..7b7e364d 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -4,4 +4,7 @@ declare module 'worker-loader!*' { } export default WebpackWorker; -} \ No newline at end of file +} + +declare function setInterval(callback: (...args: any[]) => void, ms: number): number; +declare function setTimeout(callback: (...args: any[]) => void, ms: number): number; \ No newline at end of file diff --git a/src/lib/appManagers/AppInlineBotsManager.ts b/src/lib/appManagers/AppInlineBotsManager.ts index 13d5a3b9..0b7e149b 100644 --- a/src/lib/appManagers/AppInlineBotsManager.ts +++ b/src/lib/appManagers/AppInlineBotsManager.ts @@ -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 = {}; diff --git a/src/lib/appManagers/appDialogsManager.ts b/src/lib/appManagers/appDialogsManager.ts index b5d52989..237acc05 100644 --- a/src/lib/appManagers/appDialogsManager.ts +++ b/src/lib/appManagers/appDialogsManager.ts @@ -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"; 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, diff --git a/src/lib/appManagers/appDocsManager.ts b/src/lib/appManagers/appDocsManager.ts index 20aee9d3..ccfeaaf0 100644 --- a/src/lib/appManagers/appDocsManager.ts +++ b/src/lib/appManagers/appDocsManager.ts @@ -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} = {}; private downloadPromises: {[docID: string]: CancellablePromise} = {}; public saveDoc(apiDoc: MTDocument, context?: any) { @@ -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 { 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 { } if(!apiDoc.url) { - apiDoc.url = this.getFileURLByDoc(apiDoc); + apiDoc.url = this.getFileURL(apiDoc); } return apiDoc; @@ -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 { }; } - 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 { 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 { @@ -229,11 +250,7 @@ class AppDocsManager { const deferred = deferredPromise(); - /* 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 { 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); diff --git a/src/lib/appManagers/appImManager.ts b/src/lib/appManagers/appImManager.ts index 4130672b..302d0cd5 100644 --- a/src/lib/appManagers/appImManager.ts +++ b/src/lib/appManagers/appImManager.ts @@ -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'; 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 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 { //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 { } 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 { //bubble.remove(); }); - animationIntersector.checkAnimations(false, 'chat'); + animationIntersector.checkAnimations(false, ANIMATIONGROUP); this.deleteEmptyDateGroups(); } @@ -1805,15 +1809,12 @@ export class AppImManager { let promises: Promise[] = []; (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 { 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 { withTail: tailSupported, isOut: isOut, lazyLoadQueue: this.lazyLoadQueue, - middleware: null + middleware: null, + group: ANIMATIONGROUP }); preloader.attach(attachmentDiv, false); @@ -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 { 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 { withTail: tailSupported, isOut: isOut, lazyLoadQueue: this.lazyLoadQueue, - middleware: this.getMiddleware() + middleware: this.getMiddleware(), + group: ANIMATIONGROUP }); } diff --git a/src/lib/appManagers/appMediaViewer.ts b/src/lib/appManagers/appMediaViewer.ts index 61b7cce0..979aeb64 100644 --- a/src/lib/appManagers/appMediaViewer.ts +++ b/src/lib/appManagers/appMediaViewer.ts @@ -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 { 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 { 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 { 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 { 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 { 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 { } */ }; - 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 { 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 { } // я хз что это такое, видео появляются просто чёрными и не включаются без этого кода снизу - source.remove(); + /* source.remove(); window.requestAnimationFrame(() => { window.requestAnimationFrame(() => { //parent.append(video); video.append(source); }); - }); + }); */ player = createPlayer(); }); diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index 0b058b06..efe7b780 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -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({ diff --git a/src/lib/appManagers/appSidebarRight.ts b/src/lib/appManagers/appSidebarRight.ts index aaa823f6..88ed7ac4 100644 --- a/src/lib/appManagers/appSidebarRight.ts +++ b/src/lib/appManagers/appSidebarRight.ts @@ -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"; 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; diff --git a/src/lib/bin_utils.ts b/src/lib/bin_utils.ts index 695ea408..de7f4c0e 100644 --- a/src/lib/bin_utils.ts +++ b/src/lib/bin_utils.ts @@ -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._) { diff --git a/src/lib/mtproto/apiFileManager.ts b/src/lib/mtproto/apiFileManager.ts index 2ffd141a..da84192d 100644 --- a/src/lib/mtproto/apiFileManager.ts +++ b/src/lib/mtproto/apiFileManager.ts @@ -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 { } 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 { 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 { 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 { let startOffset = 0; let writeFilePromise: CancellablePromise = Promise.resolve(), writeFileDeferred: CancellablePromise; - 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 { 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) { diff --git a/src/lib/mtproto/mtproto.service.ts b/src/lib/mtproto/mtproto.service.ts index 34f8570d..bfcf7a80 100644 --- a/src/lib/mtproto/mtproto.service.ts +++ b/src/lib/mtproto/mtproto.service.ts @@ -178,6 +178,23 @@ function timeout(delay: number): Promise { })); } +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 => { 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 => { 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 => { } 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((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 = { - '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 = { + '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; } diff --git a/src/lib/utils.ts b/src/lib/utils.ts index f3e688fb..55701b68 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -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, diff --git a/src/pages/pagesManager.ts b/src/pages/pagesManager.ts index 9b1d73a8..77f4f5f4 100644 --- a/src/pages/pagesManager.ts +++ b/src/pages/pagesManager.ts @@ -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; diff --git a/src/scss/partials/_emojiDropdown.scss b/src/scss/partials/_emojiDropdown.scss index e66c4498..31aa4e6b 100644 --- a/src/scss/partials/_emojiDropdown.scss +++ b/src/scss/partials/_emojiDropdown.scss @@ -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; } } } diff --git a/tsconfig.json b/tsconfig.json index 2062d2ac..00fd065d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -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 @@ // "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 diff --git a/webpack.common.js b/webpack.common.js index 48dbedcd..5848e7ef 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -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 = { 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(); } });