Fix freeze on fast chat switch
This commit is contained in:
parent
f8ae31b6ac
commit
080fb56d82
@ -45,7 +45,7 @@ import AudioElement from "../audio";
|
|||||||
import { Message, MessageEntity, MessageReplyHeader } from "../../layer";
|
import { Message, MessageEntity, MessageReplyHeader } from "../../layer";
|
||||||
import { REPLIES_PEER_ID } from "../../lib/mtproto/mtproto_config";
|
import { REPLIES_PEER_ID } from "../../lib/mtproto/mtproto_config";
|
||||||
import { FocusDirection } from "../../helpers/fastSmoothScroll";
|
import { FocusDirection } from "../../helpers/fastSmoothScroll";
|
||||||
import useHeavyAnimationCheck, { getHeavyAnimationPromise, dispatchHeavyAnimationEvent } from "../../hooks/useHeavyAnimationCheck";
|
import useHeavyAnimationCheck, { getHeavyAnimationPromise, dispatchHeavyAnimationEvent, interruptHeavyAnimation } from "../../hooks/useHeavyAnimationCheck";
|
||||||
import { fastRaf } from "../../helpers/schedulers";
|
import { fastRaf } from "../../helpers/schedulers";
|
||||||
import { deferredPromise } from "../../helpers/cancellablePromise";
|
import { deferredPromise } from "../../helpers/cancellablePromise";
|
||||||
import RepliesElement from "./replies";
|
import RepliesElement from "./replies";
|
||||||
@ -67,6 +67,7 @@ import reflowScrollableElement from "../../helpers/dom/reflowScrollableElement";
|
|||||||
import replaceContent from "../../helpers/dom/replaceContent";
|
import replaceContent from "../../helpers/dom/replaceContent";
|
||||||
import setInnerHTML from "../../helpers/dom/setInnerHTML";
|
import setInnerHTML from "../../helpers/dom/setInnerHTML";
|
||||||
import whichChild from "../../helpers/dom/whichChild";
|
import whichChild from "../../helpers/dom/whichChild";
|
||||||
|
import { cancelAnimationByKey } from "../../helpers/animation";
|
||||||
|
|
||||||
const USE_MEDIA_TAILS = false;
|
const USE_MEDIA_TAILS = false;
|
||||||
const IGNORE_ACTIONS: Message.messageService['action']['_'][] = [/* 'messageActionHistoryClear' */];
|
const IGNORE_ACTIONS: Message.messageService['action']['_'][] = [/* 'messageActionHistoryClear' */];
|
||||||
@ -1403,6 +1404,12 @@ export default class ChatBubbles {
|
|||||||
this.scrollable.loadedAll.top = false;
|
this.scrollable.loadedAll.top = false;
|
||||||
this.scrollable.loadedAll.bottom = false;
|
this.scrollable.loadedAll.bottom = false;
|
||||||
|
|
||||||
|
// cancel scroll
|
||||||
|
cancelAnimationByKey(this.scrollable.container);
|
||||||
|
|
||||||
|
// do not wait ending of previous scale animation
|
||||||
|
interruptHeavyAnimation();
|
||||||
|
|
||||||
if(TEST_SCROLL !== undefined) {
|
if(TEST_SCROLL !== undefined) {
|
||||||
TEST_SCROLL = TEST_SCROLL_TIMES;
|
TEST_SCROLL = TEST_SCROLL_TIMES;
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
import _DEBUG from '../config/debug';
|
import _DEBUG from '../config/debug';
|
||||||
import fastBlur from '../vendor/fastBlur';
|
import fastBlur from '../vendor/fastBlur';
|
||||||
import pushHeavyTask from './heavyQueue';
|
import addHeavyTask from './heavyQueue';
|
||||||
|
|
||||||
const RADIUS = 2;
|
const RADIUS = 2;
|
||||||
const ITERATIONS = 2;
|
const ITERATIONS = 2;
|
||||||
@ -16,53 +16,67 @@ const DEBUG = _DEBUG && true;
|
|||||||
function processBlur(dataUri: string, radius: number, iterations: number) {
|
function processBlur(dataUri: string, radius: number, iterations: number) {
|
||||||
return new Promise<string>((resolve) => {
|
return new Promise<string>((resolve) => {
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
|
|
||||||
const perf = performance.now();
|
const perf = performance.now();
|
||||||
if(DEBUG) {
|
if(DEBUG) {
|
||||||
console.log('[blur] start');
|
console.log('[blur] start');
|
||||||
}
|
}
|
||||||
|
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
const canvas = document.createElement('canvas');
|
const canvas = document.createElement('canvas');
|
||||||
canvas.width = img.width;
|
canvas.width = img.width;
|
||||||
canvas.height = img.height;
|
canvas.height = img.height;
|
||||||
|
|
||||||
const ctx = canvas.getContext('2d')!;
|
const ctx = canvas.getContext('2d')!;
|
||||||
|
|
||||||
|
//ctx.filter = 'blur(2px)';
|
||||||
ctx.drawImage(img, 0, 0);
|
ctx.drawImage(img, 0, 0);
|
||||||
fastBlur(ctx, 0, 0, canvas.width, canvas.height, radius, iterations);
|
fastBlur(ctx, 0, 0, canvas.width, canvas.height, radius, iterations);
|
||||||
|
|
||||||
//resolve(canvas.toDataURL());
|
resolve(canvas.toDataURL());
|
||||||
canvas.toBlob(blob => {
|
if(DEBUG) {
|
||||||
|
console.log(`[blur] end, radius: ${radius}, iterations: ${iterations}, time: ${performance.now() - perf}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* canvas.toBlob(blob => {
|
||||||
resolve(URL.createObjectURL(blob));
|
resolve(URL.createObjectURL(blob));
|
||||||
|
|
||||||
if(DEBUG) {
|
if(DEBUG) {
|
||||||
console.log(`[blur] end, radius: ${radius}, iterations: ${iterations}, time: ${performance.now() - perf}`);
|
console.log(`[blur] end, radius: ${radius}, iterations: ${iterations}, time: ${performance.now() - perf}`);
|
||||||
}
|
}
|
||||||
});
|
}); */
|
||||||
};
|
};
|
||||||
|
|
||||||
img.src = dataUri;
|
img.src = dataUri;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const blurPromises: {[dataUri: string]: Promise<string>} = {};
|
const blurPromises: Map<string, Promise<string>> = new Map();
|
||||||
|
const CACHE_SIZE = 1000;
|
||||||
|
|
||||||
export default function blur(dataUri: string, radius: number = RADIUS, iterations: number = ITERATIONS) {
|
export default function blur(dataUri: string, radius: number = RADIUS, iterations: number = ITERATIONS) {
|
||||||
if(!dataUri) {
|
if(!dataUri) {
|
||||||
console.error('no dataUri for blur', dataUri);
|
console.error('no dataUri for blur', dataUri);
|
||||||
return Promise.resolve(dataUri);
|
return Promise.resolve(dataUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(blurPromises.size > CACHE_SIZE) {
|
||||||
|
blurPromises.clear();
|
||||||
|
}
|
||||||
|
|
||||||
if(blurPromises[dataUri]) return blurPromises[dataUri];
|
if(blurPromises.has(dataUri)) return blurPromises.get(dataUri);
|
||||||
return blurPromises[dataUri] = new Promise<string>((resolve) => {
|
const promise = new Promise<string>((resolve) => {
|
||||||
//return resolve(dataUri);
|
//return resolve(dataUri);
|
||||||
pushHeavyTask({
|
addHeavyTask({
|
||||||
items: [[dataUri, radius, iterations]],
|
items: [[dataUri, radius, iterations]],
|
||||||
context: null,
|
context: null,
|
||||||
process: processBlur
|
process: processBlur
|
||||||
}).then(results => {
|
}, 'unshift').then(results => {
|
||||||
resolve(results[0]);
|
resolve(results[0]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
blurPromises.set(dataUri, promise);
|
||||||
|
|
||||||
|
return promise;
|
||||||
}
|
}
|
||||||
|
@ -17,13 +17,13 @@ type HeavyQueue<T> = {
|
|||||||
const heavyQueue: HeavyQueue<any>[] = [];
|
const heavyQueue: HeavyQueue<any>[] = [];
|
||||||
let processingQueue = false;
|
let processingQueue = false;
|
||||||
|
|
||||||
export default function pushHeavyTask<T>(queue: HeavyQueue<T>) {
|
export default function addHeavyTask<T>(queue: HeavyQueue<T>, method: 'push' | 'unshift' = 'push') {
|
||||||
if(!queue.items.length) {
|
if(!queue.items.length) {
|
||||||
return Promise.resolve([]);
|
return Promise.resolve([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
queue.promise = deferredPromise<T[]>();
|
queue.promise = deferredPromise<T[]>();
|
||||||
heavyQueue.push(queue);
|
heavyQueue[method](queue);
|
||||||
processHeavyQueue();
|
processHeavyQueue();
|
||||||
|
|
||||||
return queue.promise;
|
return queue.promise;
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
// * Jolly Cobra's useHeavyAnimationCheck.ts, patched
|
// * Jolly Cobra's useHeavyAnimationCheck.ts, patched
|
||||||
|
|
||||||
//import { useEffect } from '../lib/teact/teact';
|
|
||||||
import { AnyToVoidFunction } from '../types';
|
import { AnyToVoidFunction } from '../types';
|
||||||
import ListenerSetter from '../helpers/listenerSetter';
|
import ListenerSetter from '../helpers/listenerSetter';
|
||||||
import { CancellablePromise, deferredPromise } from '../helpers/cancellablePromise';
|
import { CancellablePromise, deferredPromise } from '../helpers/cancellablePromise';
|
||||||
@ -18,12 +17,14 @@ const ANIMATION_START_EVENT = 'event-heavy-animation-start';
|
|||||||
const ANIMATION_END_EVENT = 'event-heavy-animation-end';
|
const ANIMATION_END_EVENT = 'event-heavy-animation-end';
|
||||||
|
|
||||||
let isAnimating = false;
|
let isAnimating = false;
|
||||||
let heavyAnimationPromise: CancellablePromise<void> = Promise.resolve();
|
let heavyAnimationPromise: CancellablePromise<void> = deferredPromise<void>();
|
||||||
let promisesInQueue = 0;
|
let promisesInQueue = 0;
|
||||||
|
|
||||||
|
heavyAnimationPromise.resolve();
|
||||||
|
|
||||||
const log = console.log.bind(console.log, '[HEAVY-ANIMATION]:');
|
const log = console.log.bind(console.log, '[HEAVY-ANIMATION]:');
|
||||||
|
|
||||||
export const dispatchHeavyAnimationEvent = (promise: Promise<any>, timeout?: number) => {
|
export function dispatchHeavyAnimationEvent(promise: Promise<any>, timeout?: number) {
|
||||||
if(!isAnimating) {
|
if(!isAnimating) {
|
||||||
heavyAnimationPromise = deferredPromise<void>();
|
heavyAnimationPromise = deferredPromise<void>();
|
||||||
rootScope.broadcast(ANIMATION_START_EVENT);
|
rootScope.broadcast(ANIMATION_START_EVENT);
|
||||||
@ -40,29 +41,48 @@ export const dispatchHeavyAnimationEvent = (promise: Promise<any>, timeout?: num
|
|||||||
].filter(Boolean);
|
].filter(Boolean);
|
||||||
|
|
||||||
const perf = performance.now();
|
const perf = performance.now();
|
||||||
|
const _heavyAnimationPromise = heavyAnimationPromise;
|
||||||
Promise.race(promises).then(() => {
|
Promise.race(promises).then(() => {
|
||||||
|
if(heavyAnimationPromise !== _heavyAnimationPromise || heavyAnimationPromise.isFulfilled) { // interrupted
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
--promisesInQueue;
|
--promisesInQueue;
|
||||||
DEBUG && log('promise end, length:', promisesInQueue, performance.now() - perf);
|
DEBUG && log('promise end, length:', promisesInQueue, performance.now() - perf);
|
||||||
if(!promisesInQueue) {
|
if(promisesInQueue <= 0) {
|
||||||
isAnimating = false;
|
onHeavyAnimationEnd();
|
||||||
promisesInQueue = 0;
|
|
||||||
rootScope.broadcast(ANIMATION_END_EVENT);
|
|
||||||
heavyAnimationPromise.resolve();
|
|
||||||
|
|
||||||
DEBUG && log('end');
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return heavyAnimationPromise;
|
return heavyAnimationPromise;
|
||||||
};
|
}
|
||||||
|
|
||||||
export const getHeavyAnimationPromise = () => heavyAnimationPromise;
|
function onHeavyAnimationEnd() {
|
||||||
|
if(heavyAnimationPromise.isFulfilled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
export default (
|
isAnimating = false;
|
||||||
|
promisesInQueue = 0;
|
||||||
|
rootScope.broadcast(ANIMATION_END_EVENT);
|
||||||
|
heavyAnimationPromise.resolve();
|
||||||
|
|
||||||
|
DEBUG && log('end');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function interruptHeavyAnimation() {
|
||||||
|
onHeavyAnimationEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getHeavyAnimationPromise() {
|
||||||
|
return heavyAnimationPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function(
|
||||||
handleAnimationStart: AnyToVoidFunction,
|
handleAnimationStart: AnyToVoidFunction,
|
||||||
handleAnimationEnd: AnyToVoidFunction,
|
handleAnimationEnd: AnyToVoidFunction,
|
||||||
listenerSetter?: ListenerSetter
|
listenerSetter?: ListenerSetter
|
||||||
) => {
|
) {
|
||||||
//useEffect(() => {
|
//useEffect(() => {
|
||||||
if(isAnimating) {
|
if(isAnimating) {
|
||||||
handleAnimationStart();
|
handleAnimationStart();
|
||||||
@ -78,4 +98,4 @@ export default (
|
|||||||
remove(ANIMATION_START_EVENT, handleAnimationStart);
|
remove(ANIMATION_START_EVENT, handleAnimationStart);
|
||||||
};
|
};
|
||||||
//}, [handleAnimationEnd, handleAnimationStart]);
|
//}, [handleAnimationEnd, handleAnimationStart]);
|
||||||
};
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user