09d0030de4
Fix replies layout with custom emoji Fix wrapping some custom emojis Fix opening restricted chat Fix wrapping encoded spoiler Fix playing custom emoji interactive animation Fix loading archived chatlist Fix reaction effect at top left corner
160 lines
4.6 KiB
TypeScript
160 lines
4.6 KiB
TypeScript
/*
|
|
* https://github.com/morethanwords/tweb
|
|
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
|
*/
|
|
|
|
import IS_VIBRATE_SUPPORTED from '../../environment/vibrateSupport';
|
|
import assumeType from '../../helpers/assumeType';
|
|
import isInDOM from '../../helpers/dom/isInDOM';
|
|
import makeError from '../../helpers/makeError';
|
|
import {getMiddleware, Middleware} from '../../helpers/middleware';
|
|
import throttleWithRaf from '../../helpers/schedulers/throttleWithRaf';
|
|
import windowSize from '../../helpers/windowSize';
|
|
import {PhotoSize, VideoSize} from '../../layer';
|
|
import {MyDocument} from '../../lib/appManagers/appDocsManager';
|
|
import appImManager from '../../lib/appManagers/appImManager';
|
|
import {AppManagers} from '../../lib/appManagers/managers';
|
|
import RLottiePlayer from '../../lib/rlottie/rlottiePlayer';
|
|
import wrapSticker from './sticker';
|
|
|
|
export default function wrapStickerAnimation({
|
|
size,
|
|
doc,
|
|
middleware,
|
|
target,
|
|
side,
|
|
skipRatio,
|
|
play,
|
|
managers,
|
|
fullThumb,
|
|
withRandomOffset,
|
|
relativeEffect,
|
|
loopEffect
|
|
}: {
|
|
size: number,
|
|
doc: MyDocument,
|
|
middleware?: Middleware,
|
|
target: HTMLElement,
|
|
side: 'left' | 'center' | 'right',
|
|
skipRatio?: number,
|
|
play: boolean,
|
|
managers?: AppManagers,
|
|
fullThumb?: PhotoSize | VideoSize,
|
|
withRandomOffset?: boolean,
|
|
relativeEffect?: boolean,
|
|
loopEffect?: boolean
|
|
}) {
|
|
const animationDiv = document.createElement('div');
|
|
animationDiv.classList.add('emoji-animation');
|
|
|
|
// const size = 280;
|
|
animationDiv.style.width = size + 'px';
|
|
animationDiv.style.height = size + 'px';
|
|
|
|
let animation: RLottiePlayer;
|
|
const unmountAnimation = () => {
|
|
middlewareHelper.clean();
|
|
animation?.remove();
|
|
animationDiv.remove();
|
|
appImManager.chat.bubbles.scrollable.container.removeEventListener('scroll', onScroll);
|
|
};
|
|
|
|
const middlewareHelper = middleware?.create() ?? getMiddleware();
|
|
middleware = middlewareHelper.get();
|
|
|
|
const stickerPromise = wrapSticker({
|
|
div: animationDiv,
|
|
doc,
|
|
middleware,
|
|
withThumb: false,
|
|
needFadeIn: false,
|
|
loop: !!loopEffect,
|
|
width: size,
|
|
height: size,
|
|
play,
|
|
group: 'none',
|
|
skipRatio,
|
|
managers,
|
|
fullThumb
|
|
}).then(({render}) => render).then((_animation) => {
|
|
assumeType<RLottiePlayer>(_animation);
|
|
if(!middleware()) {
|
|
_animation.remove();
|
|
throw makeError('MIDDLEWARE');
|
|
}
|
|
|
|
animation = _animation;
|
|
animation.addEventListener('enterFrame', (frameNo) => {
|
|
if((!loopEffect && frameNo === animation.maxFrame) || !isInDOM(target)) {
|
|
unmountAnimation();
|
|
}
|
|
});
|
|
|
|
if(IS_VIBRATE_SUPPORTED) {
|
|
animation.addEventListener('firstFrame', () => {
|
|
navigator.vibrate(100);
|
|
}, {once: true});
|
|
}
|
|
|
|
return animation;
|
|
});
|
|
|
|
const generateRandomSigned = (max: number) => {
|
|
const r = Math.random() * max * 2;
|
|
return r > max ? -r % max : r;
|
|
};
|
|
|
|
const randomOffsetX = withRandomOffset ? generateRandomSigned(16) : 0;
|
|
const randomOffsetY = withRandomOffset ? generateRandomSigned(4) : 0;
|
|
const setPosition = () => {
|
|
if(!isInDOM(target)) {
|
|
unmountAnimation();
|
|
return;
|
|
}
|
|
|
|
const rect = target.getBoundingClientRect();
|
|
|
|
const factor = rect.width / 200;
|
|
const stableOffsetX = side === 'center' ? 0 : 16 * (side === 'right' ? 1 : -1) * factor;
|
|
// const stableOffsetY = side === 'center' ? 0 : -50 * factor;
|
|
const stableOffsetY = side === 'center' ? 0 : 0 * factor;
|
|
|
|
const rectX = side === 'right' ? rect.right : rect.left;
|
|
const rectY = rect.top;
|
|
|
|
const addOffsetX = (side === 'center' ? (rect.width - size) / 2 : (side === 'right' ? -size : 0)) + stableOffsetX + randomOffsetX;
|
|
const addOffsetY = (side === 'center' || true ? (rect.height - size) / 2 : 0) + stableOffsetY + randomOffsetY;
|
|
const x = rectX + addOffsetX;
|
|
const y = rectY + addOffsetY;
|
|
|
|
if(y <= -size || y >= windowSize.height) {
|
|
unmountAnimation();
|
|
return;
|
|
}
|
|
|
|
if(relativeEffect) {
|
|
if(side !== 'center') animationDiv.style[side] = Math.abs(stableOffsetX) * -1 + 'px';
|
|
else animationDiv.style.left = addOffsetX + 'px';
|
|
animationDiv.style.top = addOffsetY + 'px';
|
|
} else {
|
|
animationDiv.style.top = y + 'px';
|
|
animationDiv.style.left = x + 'px';
|
|
}
|
|
};
|
|
|
|
const onScroll = throttleWithRaf(setPosition);
|
|
appImManager.chat.bubbles.scrollable.container.addEventListener('scroll', onScroll);
|
|
|
|
setPosition();
|
|
|
|
if(relativeEffect) {
|
|
animationDiv.classList.add('is-relative');
|
|
target.parentElement.append(animationDiv);
|
|
} else {
|
|
appImManager.emojiAnimationContainer.append(animationDiv);
|
|
}
|
|
|
|
return {animationDiv, stickerPromise};
|
|
}
|