ESG GIFs try
Websocket fix instant connect retry Possible fix to stream FILE_REFERENCE_EXPIRED
This commit is contained in:
parent
9948bab8b0
commit
097a55850c
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 => {
|
||||
|
@ -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);
|
||||
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
@ -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<void> {
|
||||
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) {
|
||||
|
@ -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) => {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1150,7 +1150,7 @@ img.emoji {
|
||||
.grid-item {
|
||||
height: 0;
|
||||
padding-bottom: 100%;
|
||||
overflow: hidden;
|
||||
//overflow: hidden;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
|
5
src/types.d.ts
vendored
5
src/types.d.ts
vendored
@ -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<T, R> = Omit<T, keyof R> & R;
|
||||
|
Loading…
x
Reference in New Issue
Block a user