126 lines
4.0 KiB
TypeScript
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));
|