From 097a55850cb5ca332bc7b981690dfff480c4724e Mon Sep 17 00:00:00 2001 From: morethanwords Date: Sun, 4 Oct 2020 02:10:57 +0300 Subject: [PATCH] ESG GIFs try Websocket fix instant connect retry Possible fix to stream FILE_REFERENCE_EXPIRED --- src/components/emoticonsDropdown/index.ts | 22 +++++++++- src/components/emoticonsDropdown/tabs/gifs.ts | 8 ++-- .../emoticonsDropdown/tabs/stickers.ts | 18 +-------- src/components/gifsMasonry.ts | 10 +++-- src/components/sidebarRight/forward.ts | 2 + src/components/sidebarRight/sharedMedia.ts | 9 +---- src/components/wrappers.ts | 10 ++++- src/lib/appManagers/appDownloadManager.ts | 21 ++-------- src/lib/appManagers/appStickersManager.ts | 2 + src/lib/crypto/crypto_utils.ts | 2 +- src/lib/mtproto/mtproto.service.ts | 9 +++-- src/lib/mtproto/mtproto.worker.ts | 6 +-- src/lib/mtproto/mtprotoworker.ts | 30 +++++++++++++- src/lib/mtproto/referenceDatabase.ts | 29 ++++++++++++++ src/lib/mtproto/transports/websocket.ts | 40 ++++++++++++------- src/scss/partials/_rightSidebar.scss | 18 ++++++--- src/scss/style.scss | 2 +- src/types.d.ts | 5 ++- 18 files changed, 160 insertions(+), 83 deletions(-) diff --git a/src/components/emoticonsDropdown/index.ts b/src/components/emoticonsDropdown/index.ts index 27ea8498..91c32193 100644 --- a/src/components/emoticonsDropdown/index.ts +++ b/src/components/emoticonsDropdown/index.ts @@ -1,4 +1,4 @@ -import LazyLoadQueue from "../lazyLoadQueue"; +import LazyLoadQueue, { LazyLoadQueueIntersector } from "../lazyLoadQueue"; import GifsTab from "./tabs/gifs"; import { findUpClassName, findUpTag, whichChild } from "../../lib/utils"; import { horizontalMenu } from "../horizontalMenu"; @@ -324,6 +324,26 @@ export class EmoticonsDropdown { console.warn('got no doc by id:', fileID); } }; + + public addLazyLoadQueueRepeat(lazyLoadQueue: LazyLoadQueueIntersector, processInvisibleDiv: (div: HTMLElement) => void) { + this.events.onClose.push(() => { + lazyLoadQueue.lock(); + }); + + this.events.onCloseAfter.push(() => { + const divs = lazyLoadQueue.intersector.getVisible(); + + for(const div of divs) { + processInvisibleDiv(div); + } + + lazyLoadQueue.intersector.clearVisible(); + }); + + this.events.onOpenAfter.push(() => { + lazyLoadQueue.unlockAndRefresh(); + }); + } } const emoticonsDropdown = new EmoticonsDropdown(); diff --git a/src/components/emoticonsDropdown/tabs/gifs.ts b/src/components/emoticonsDropdown/tabs/gifs.ts index d8a9d60c..c82ecd9d 100644 --- a/src/components/emoticonsDropdown/tabs/gifs.ts +++ b/src/components/emoticonsDropdown/tabs/gifs.ts @@ -1,4 +1,4 @@ -import { EmoticonsDropdown, EmoticonsTab, EMOTICONSSTICKERGROUP } from ".."; +import emoticonsDropdown, { EmoticonsDropdown, EmoticonsTab, EMOTICONSSTICKERGROUP } from ".."; import GifsMasonry from "../../gifsMasonry"; import Scrollable from "../../scrollable_new"; import { putPreloader } from "../../misc"; @@ -24,15 +24,15 @@ export default class GifsTab implements EmoticonsTab { res.gifs.forEach((doc, idx) => { res.gifs[idx] = doc = appDocsManager.saveDoc(doc); //if(doc._ == 'documentEmpty') return; - //masonry.add(doc as MyDocument); + masonry.add(doc as MyDocument); }); } - //let line: MTDocument[] = []; - preloader.remove(); }); + emoticonsDropdown.addLazyLoadQueueRepeat(masonry.lazyLoadQueue, masonry.processInvisibleDiv); + this.init = null; } diff --git a/src/components/emoticonsDropdown/tabs/stickers.ts b/src/components/emoticonsDropdown/tabs/stickers.ts index ea402c06..78a59531 100644 --- a/src/components/emoticonsDropdown/tabs/stickers.ts +++ b/src/components/emoticonsDropdown/tabs/stickers.ts @@ -321,23 +321,7 @@ export default class StickersTab implements EmoticonsTab { } }); - emoticonsDropdown.events.onClose.push(() => { - this.lazyLoadQueue.lock(); - }); - - emoticonsDropdown.events.onCloseAfter.push(() => { - const divs = this.lazyLoadQueue.intersector.getVisible(); - - for(const div of divs) { - this.processInvisibleDiv(div); - } - - this.lazyLoadQueue.intersector.clearVisible(); - }); - - emoticonsDropdown.events.onOpenAfter.push(() => { - this.lazyLoadQueue.unlockAndRefresh(); - }); + emoticonsDropdown.addLazyLoadQueueRepeat(this.lazyLoadQueue, this.processInvisibleDiv); /* setInterval(() => { // @ts-ignore diff --git a/src/components/gifsMasonry.ts b/src/components/gifsMasonry.ts index 427551a6..230e14a7 100644 --- a/src/components/gifsMasonry.ts +++ b/src/components/gifsMasonry.ts @@ -24,14 +24,14 @@ export default class GifsMasonry { } }); - setInterval(() => { + /* setInterval(() => { // @ts-ignore const players = animationIntersector.byGroups[group]; if(players) { console.log(`GIFS RENDERED IN ${group}:`, players.length, players.filter(p => !p.animation.paused).length, this.lazyLoadQueue.intersector.getVisible().length); } - }, .25e3); + }, .25e3); */ let timeout = 0; // memory leak @@ -51,12 +51,14 @@ export default class GifsMasonry { }); } - private processVisibleDiv = (div: HTMLElement) => { + processVisibleDiv = (div: HTMLElement) => { const video = div.querySelector('video'); if(video) { return; } + //console.log('processVisibleDiv'); + const load = () => { const docID = div.dataset.docID; const doc = appDocsManager.getDoc(docID); @@ -110,7 +112,7 @@ export default class GifsMasonry { this.lazyLoadQueue.push({div, load}); }; - private processInvisibleDiv = async(div: HTMLElement) => { + processInvisibleDiv = async(div: HTMLElement) => { return this.scrollPromise.then(async() => { //return; diff --git a/src/components/sidebarRight/forward.ts b/src/components/sidebarRight/forward.ts index 7c9a3919..0dd96f1f 100644 --- a/src/components/sidebarRight/forward.ts +++ b/src/components/sidebarRight/forward.ts @@ -13,6 +13,7 @@ export default class AppForwardTab implements SliderTab { private msgIDs: number[] = []; onCloseAfterTimeout() { + document.body.classList.remove('is-forward-active'); this.cleanup(); } @@ -79,6 +80,7 @@ export default class AppForwardTab implements SliderTab { //console.log('forward rendered:', this.container.querySelector('.selector ul').childElementCount); appSidebarRight.selectTab(AppSidebarRight.SLIDERITEMSIDS.forward); appSidebarRight.toggleSidebar(true); + document.body.classList.add('is-forward-active'); }); } } diff --git a/src/components/sidebarRight/sharedMedia.ts b/src/components/sidebarRight/sharedMedia.ts index b6d38227..460b8948 100644 --- a/src/components/sidebarRight/sharedMedia.ts +++ b/src/components/sidebarRight/sharedMedia.ts @@ -263,17 +263,10 @@ export default class AppSharedMediaTab implements SliderTab { case 'inputMessagesFilterDocument': { for(let message of messages) { - if(!message.media.document || ['voice', 'audio', 'gif'].includes(message.media.document.type)) { + if(!message.media.document || ['voice', 'audio', 'gif', 'sticker'].includes(message.media.document.type)) { continue; } - let doc = message.media.document; - if(doc.attributes) { - if(doc.attributes.find((a: any) => a._ == "documentAttributeSticker")) { - continue; - } - } - filtered.push(message); } break; diff --git a/src/components/wrappers.ts b/src/components/wrappers.ts index 0964a393..08ee48b1 100644 --- a/src/components/wrappers.ts +++ b/src/components/wrappers.ts @@ -234,7 +234,15 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai }, {once: true}); //} - video.addEventListener('error', deferred.reject); + video.addEventListener('error', (e) => { + deferred.resolve(); + /* console.error('video error', e, video.src); + if(video.src) { // if wasn't cleaned + deferred.reject(e); + } else { + deferred.resolve(); + } */ + }); //if(doc.type != 'round') { renderImageFromUrl(video, doc.url); diff --git a/src/lib/appManagers/appDownloadManager.ts b/src/lib/appManagers/appDownloadManager.ts index dc95b43e..59e675a9 100644 --- a/src/lib/appManagers/appDownloadManager.ts +++ b/src/lib/appManagers/appDownloadManager.ts @@ -5,7 +5,7 @@ import type { DownloadOptions } from "../mtproto/apiFileManager"; import { getFileNameByLocation } from "../bin_utils"; import { InputFile } from "../../layer"; import referenceDatabase, {ReferenceBytes} from "../mtproto/referenceDatabase"; -import appMessagesManager from "./appMessagesManager"; +import type { ApiError } from "../mtproto/apiManager"; export type ResponseMethodBlob = 'blob'; export type ResponseMethodJson = 'json'; @@ -77,30 +77,17 @@ export class AppDownloadManager { const deferred = this.getNewDeferred(fileName); - const onError = (err: any) => { + const onError = (err: ApiError) => { switch(err.type) { case 'FILE_REFERENCE_EXPIRED': { // @ts-ignore const bytes: ReferenceBytes = options?.location?.file_reference; if(bytes) { - const context = referenceDatabase.getContext(bytes); - switch(context?.type) { - case 'message': { - return appMessagesManager.wrapSingleMessage(context.messageID, true).then(() => { - //console.log('FILE_REFERENCE_EXPIRED: got message', context, options, appMessagesManager.getMessage(context.messageID).media); - return tryDownload(); - }); - } - - default: { - console.warn('FILE_REFERENCE_EXPIRED: not implemented context', context); - } - } + referenceDatabase.refreshReference(bytes).then(tryDownload); + break; } else { console.warn('FILE_REFERENCE_EXPIRED: no context for bytes:', bytes); } - - break; } default: diff --git a/src/lib/appManagers/appStickersManager.ts b/src/lib/appManagers/appStickersManager.ts index a2acd37d..89edf1d6 100644 --- a/src/lib/appManagers/appStickersManager.ts +++ b/src/lib/appManagers/appStickersManager.ts @@ -6,6 +6,8 @@ import { Modify } from '../../types'; import appStateManager from './appStateManager'; import { MOUNT_CLASS_TO } from '../mtproto/mtproto_config'; +// TODO: если пак будет сохранён и потом обновлён, то недостающие стикеры не подгрузит + export class AppStickersManager { private stickerSets: { [stickerSetID: string]: MessagesStickerSet diff --git a/src/lib/crypto/crypto_utils.ts b/src/lib/crypto/crypto_utils.ts index 61d8d41a..a513478a 100644 --- a/src/lib/crypto/crypto_utils.ts +++ b/src/lib/crypto/crypto_utils.ts @@ -29,7 +29,7 @@ export function longToBytes(sLong: string) { } console.log('longToBytes LEEMON', sLong, performance.now() - perf); */ - const bytes = bigInt2bytes(str2bigInt(sLong, 10), false); + const bytes = addPadding(bigInt2bytes(str2bigInt(sLong, 10), false), 8, true, false, false); //console.log('longToBytes', bytes, b); return bytes; diff --git a/src/lib/mtproto/mtproto.service.ts b/src/lib/mtproto/mtproto.service.ts index 33f3f03d..b7109c48 100644 --- a/src/lib/mtproto/mtproto.service.ts +++ b/src/lib/mtproto/mtproto.service.ts @@ -15,10 +15,10 @@ ctx.addEventListener('message', (e) => { const task = e.data as ServiceWorkerTaskResponse; const promise = deferredPromises[task.id]; - if(task.payload) { - promise.resolve(task.payload); + if(task.error) { + promise.reject(task.error); } else { - promise.reject(); + promise.resolve(task.payload); } delete deferredPromises[task.id]; @@ -33,7 +33,8 @@ export interface ServiceWorkerTask extends WorkerTaskTemplate { export interface ServiceWorkerTaskResponse extends WorkerTaskTemplate { type: 'requestFilePart', - payload: UploadFile.uploadFile + payload?: UploadFile.uploadFile, + originalPayload?: ServiceWorkerTask['payload'] }; const onFetch = (event: FetchEvent): void => { diff --git a/src/lib/mtproto/mtproto.worker.ts b/src/lib/mtproto/mtproto.worker.ts index e33490ea..62d8b877 100644 --- a/src/lib/mtproto/mtproto.worker.ts +++ b/src/lib/mtproto/mtproto.worker.ts @@ -82,15 +82,15 @@ ctx.addEventListener('message', async(e) => { const task = e.data as ServiceWorkerTask; const responseTask: ServiceWorkerTaskResponse = { type: task.type, - id: task.id, - payload: null + id: task.id }; try { const res = await apiFileManager.requestFilePart(...task.payload); responseTask.payload = res; } catch(err) { - + responseTask.originalPayload = task.payload; + responseTask.error = err; } respond(responseTask); diff --git a/src/lib/mtproto/mtprotoworker.ts b/src/lib/mtproto/mtprotoworker.ts index e242ae8e..43e33dd4 100644 --- a/src/lib/mtproto/mtprotoworker.ts +++ b/src/lib/mtproto/mtprotoworker.ts @@ -9,6 +9,8 @@ import type { ServiceWorkerTask, ServiceWorkerTaskResponse } from './mtproto.ser import { MethodDeclMap } from '../../layer'; import { MOUNT_CLASS_TO } from './mtproto_config'; import $rootScope from '../rootScope'; +import referenceDatabase from './referenceDatabase'; +import { ApiError } from './apiManager'; type Task = { taskID: number, @@ -126,7 +128,31 @@ class ApiManagerProxy extends CryptoWorkerMethods { } else if(task.type == 'convertWebp') { webpWorkerController.postMessage(task); } else if((task as ServiceWorkerTaskResponse).type == 'requestFilePart') { - navigator.serviceWorker.controller.postMessage(task); + const _task = task as ServiceWorkerTaskResponse; + + if(_task.error) { + const onError = (error: ApiError) => { + if(error?.type == 'FILE_REFERENCE_EXPIRED') { + // @ts-ignore + const bytes = _task.originalPayload[1].file_reference; + referenceDatabase.refreshReference(bytes).then(() => { + const newTask: ServiceWorkerTask = { + type: _task.type, + id: _task.id, + payload: _task.originalPayload + }; + + this.postMessage(newTask); + }).catch(onError); + } else { + navigator.serviceWorker.controller.postMessage(task); + } + }; + + onError(_task.error); + } else { + navigator.serviceWorker.controller.postMessage(task); + } } else { this.finalizeTask(task.taskID, task.result, task.error); } @@ -137,7 +163,7 @@ class ApiManagerProxy extends CryptoWorkerMethods { const deferred = this.awaiting[taskID]; if(deferred !== undefined) { this.log.debug('done', deferred.taskName, result, error); - result === undefined ? deferred.reject(error) : deferred.resolve(result); + error ? deferred.reject(error) : deferred.resolve(result); delete this.awaiting[taskID]; } } diff --git a/src/lib/mtproto/referenceDatabase.ts b/src/lib/mtproto/referenceDatabase.ts index e3b11826..49c6de1c 100644 --- a/src/lib/mtproto/referenceDatabase.ts +++ b/src/lib/mtproto/referenceDatabase.ts @@ -1,3 +1,4 @@ +import appMessagesManager from "../appManagers/appMessagesManager"; import { Photo } from "../../layer"; import { deepEqual } from "../utils"; import { MOUNT_CLASS_TO } from "./mtproto_config"; @@ -57,6 +58,34 @@ class ReferenceDatabase { return false; } + public refreshReference(reference: ReferenceBytes): Promise { + const context = this.getContext(reference); + switch(context?.type) { + case 'message': { + return appMessagesManager.wrapSingleMessage(context.messageID, true); + // .then(() => { + // console.log('FILE_REFERENCE_EXPIRED: got message', context, options, appMessagesManager.getMessage(context.messageID).media); + // }); + } + + default: { + console.warn('FILE_REFERENCE_EXPIRED: not implemented context', context); + return Promise.reject(); + } + } + } + + /* handleReferenceError = (reference: ReferenceBytes, error: ApiError) => { + switch(error.type) { + case 'FILE_REFERENCE_EXPIRED': { + return this.refreshReference(reference); + } + + default: + return Promise.reject(error); + } + }; */ + /* public replaceReference(oldReference: ReferenceBytes, newReference: ReferenceBytes) { const contexts = this.contexts.get(oldReference); if(contexts) { diff --git a/src/lib/mtproto/transports/websocket.ts b/src/lib/mtproto/transports/websocket.ts index b828de6f..00509085 100644 --- a/src/lib/mtproto/transports/websocket.ts +++ b/src/lib/mtproto/transports/websocket.ts @@ -107,6 +107,8 @@ export class Obfuscation { } } +const CONNECTION_RETRY_TIMEOUT = 30000; + export default class Socket extends MTTransport { ws: WebSocket; @@ -129,6 +131,8 @@ export default class Socket extends MTTransport { codec = intermediatePacketCodec; + lastCloseTime: number; + constructor(dcID: number, url: string) { super(dcID, url); @@ -169,27 +173,35 @@ export default class Socket extends MTTransport { this.log('closed', event, this.pending); this.connected = false; + const time = Date.now(); + const diff = time - this.lastCloseTime; + let needTimeout = !isNaN(diff) && diff < CONNECTION_RETRY_TIMEOUT ? CONNECTION_RETRY_TIMEOUT - diff : 0; + //this.pending.length = 0; /* if(this.networker) { this.networker.resend(); this.networker.cleanupSent(); } */ - this.log('trying to reconnect...'); - this.connect(); - - for(let pending of this.pending) { - if(pending.bodySent) { - pending.bodySent = false; + this.log('will try to reconnect after timeout:', needTimeout / 1000); + setTimeout(() => { + this.log('trying to reconnect...'); + this.lastCloseTime = Date.now(); + this.connect(); + + for(let pending of this.pending) { + if(pending.bodySent) { + pending.bodySent = false; + } } - } - - if(this.networker) { - this.ws.addEventListener('open', () => { - this.networker.resend(); - this.networker.cleanupSent(); - }, {once: true}); - } + + if(this.networker) { + this.ws.addEventListener('open', () => { + this.networker.resend(); + this.networker.cleanupSent(); + }, {once: true}); + } + }, needTimeout); }; handleMessage = (event: MessageEvent) => { diff --git a/src/scss/partials/_rightSidebar.scss b/src/scss/partials/_rightSidebar.scss index b1048076..4f45455b 100644 --- a/src/scss/partials/_rightSidebar.scss +++ b/src/scss/partials/_rightSidebar.scss @@ -34,6 +34,10 @@ border-left: 1px solid #DADCE0; } + body.is-forward-active & { + z-index: 4; + } + .sidebar-header { flex: 0 0 auto; @@ -315,12 +319,16 @@ color: white; } - .grid-item-media { - opacity: 1; - transition: opacity .2s ease; + .grid-item { + overflow: hidden; - html:not(.is-mac) &.thumbnail { - filter: blur(7px); + &-media { + opacity: 1; + transition: opacity .2s ease; + + html:not(.is-mac) &.thumbnail { + filter: blur(7px); + } } } diff --git a/src/scss/style.scss b/src/scss/style.scss index 116184b1..ea986b3f 100644 --- a/src/scss/style.scss +++ b/src/scss/style.scss @@ -1150,7 +1150,7 @@ img.emoji { .grid-item { height: 0; padding-bottom: 100%; - overflow: hidden; + //overflow: hidden; position: relative; cursor: pointer; user-select: none; diff --git a/src/types.d.ts b/src/types.d.ts index 5574ff0a..120f33a5 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -1,3 +1,5 @@ +import type { ApiError } from "./lib/mtproto/apiManager"; + export type InvokeApiOptions = Partial<{ dcID: number, timeout: number, @@ -20,7 +22,8 @@ export type InvokeApiOptions = Partial<{ export type WorkerTaskTemplate = { type: string, id: number, - payload: any + payload?: any, + error?: ApiError }; export type Modify = Omit & R;