TelegramPWA/src/components/middle/EmojiInteractionAnimation.tsx
2022-01-24 05:35:45 +01:00

126 lines
4.0 KiB
TypeScript

import React, {
FC, memo, useCallback, useEffect, useLayoutEffect, useState,
} from '../../lib/teact/teact';
import { getDispatch, withGlobal } from '../../lib/teact/teactn';
import { ActiveEmojiInteraction } from '../../global/types';
import { ApiMediaFormat } from '../../api/types';
import { IS_ANDROID } from '../../util/environment';
import useFlag from '../../hooks/useFlag';
import useMedia from '../../hooks/useMedia';
import buildClassName from '../../util/buildClassName';
import {
selectAnimatedEmojiEffect,
} from '../../modules/selectors';
import getAnimationData, { ANIMATED_STICKERS_PATHS } from '../common/helpers/animatedAssets';
import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck';
import AnimatedSticker from '../common/AnimatedSticker';
import './EmojiInteractionAnimation.scss';
export type OwnProps = {
emojiInteraction: ActiveEmojiInteraction;
};
type StateProps = {
effectAnimationId?: string;
localEffectAnimation?: string;
isReversed?: boolean;
};
const HIDE_ANIMATION_DURATION = 250;
const PLAYING_DURATION = 3000;
const EFFECT_SIZE = 240;
const EmojiInteractionAnimation: FC<OwnProps & StateProps> = ({
emojiInteraction,
effectAnimationId,
localEffectAnimation,
isReversed,
}) => {
const { stopActiveEmojiInteraction } = getDispatch();
const [isHiding, startHiding] = useFlag(false);
const [isPlaying, startPlaying] = useFlag(false);
const stop = useCallback(() => {
startHiding();
setTimeout(() => {
stopActiveEmojiInteraction();
}, HIDE_ANIMATION_DURATION);
}, [startHiding, stopActiveEmojiInteraction]);
useEffect(() => {
document.addEventListener('touchstart', stop);
document.addEventListener('touchmove', stop);
document.addEventListener('mousedown', stop);
document.addEventListener('wheel', stop);
return () => {
document.removeEventListener('touchstart', stop);
document.removeEventListener('touchmove', stop);
document.removeEventListener('mousedown', stop);
document.removeEventListener('wheel', stop);
};
}, [stop]);
useLayoutEffect(() => {
const dispatchHeavyAnimationStop = dispatchHeavyAnimationEvent();
setTimeout(() => {
stop();
dispatchHeavyAnimationStop();
}, PLAYING_DURATION);
}, [stop]);
const effectAnimationData = useMedia(`sticker${effectAnimationId}`, !effectAnimationId, ApiMediaFormat.Lottie);
const [localEffectAnimationData, setLocalEffectAnimationData] = useState<string | undefined>();
useEffect(() => {
if (localEffectAnimation) {
getAnimationData(localEffectAnimation as keyof typeof ANIMATED_STICKERS_PATHS).then((data) => {
setLocalEffectAnimationData(data);
});
}
}, [localEffectAnimation]);
const scale = (emojiInteraction.startSize || 0) / EFFECT_SIZE;
return (
<div
className={buildClassName(
'EmojiInteractionAnimation', isHiding && 'hiding', isPlaying && 'playing', isReversed && 'reversed',
)}
// @ts-ignore teact feature
style={`--scale: ${scale}; --start-x: ${emojiInteraction.x}px; --start-y: ${emojiInteraction.y}px;`}
>
<AnimatedSticker
id={`effect_${effectAnimationId}`}
size={EFFECT_SIZE}
animationData={localEffectAnimationData || effectAnimationData}
play={isPlaying}
quality={IS_ANDROID ? 0.5 : undefined}
forceOnHeavyAnimation
noLoop
onLoad={startPlaying}
/>
</div>
);
};
export default memo(withGlobal<OwnProps>(
(global, { emojiInteraction }): StateProps => {
const animatedEffect = emojiInteraction.animatedEffect !== undefined
&& selectAnimatedEmojiEffect(global, emojiInteraction.animatedEffect);
return {
effectAnimationId: animatedEffect ? animatedEffect.id : undefined,
localEffectAnimation: !animatedEffect && emojiInteraction.animatedEffect
&& Object.keys(ANIMATED_STICKERS_PATHS).includes(emojiInteraction.animatedEffect)
? emojiInteraction.animatedEffect : undefined,
isReversed: emojiInteraction.isReversed,
};
},
)(EmojiInteractionAnimation));