2023-11-14 02:36:49 +08:00
|
|
|
import React, { useRef, useState, useEffect } from 'react';
|
2024-05-29 21:26:11 +08:00
|
|
|
import { getSettingsSingletonInstance } from '/imports/ui/services/settings';
|
2023-11-14 02:36:49 +08:00
|
|
|
import Service from './service';
|
2024-04-11 21:39:21 +08:00
|
|
|
import logger from '/imports/startup/client/logger';
|
2023-11-14 02:36:49 +08:00
|
|
|
|
|
|
|
const EmojiRain = ({ reactions }) => {
|
2024-05-29 21:26:11 +08:00
|
|
|
const Settings = getSettingsSingletonInstance();
|
2023-11-14 02:36:49 +08:00
|
|
|
const containerRef = useRef(null);
|
|
|
|
const [isAnimating, setIsAnimating] = useState(false);
|
2024-03-07 01:28:18 +08:00
|
|
|
const EMOJI_SIZE = window.meetingClientSettings.public.app.emojiRain.emojiSize;
|
|
|
|
const NUMBER_OF_EMOJIS = window.meetingClientSettings.public.app.emojiRain.numberOfEmojis;
|
|
|
|
const EMOJI_RAIN_ENABLED = window.meetingClientSettings.public.app.emojiRain.enabled;
|
2023-11-14 02:36:49 +08:00
|
|
|
|
|
|
|
const { animations } = Settings.application;
|
|
|
|
|
|
|
|
function createEmojiRain(emoji) {
|
2024-04-11 21:39:21 +08:00
|
|
|
const coord = Service.getInteractionsButtonCoordinates();
|
2023-11-14 02:36:49 +08:00
|
|
|
const flyingEmojis = [];
|
|
|
|
|
2024-04-11 21:39:21 +08:00
|
|
|
if (coord == null) {
|
|
|
|
logger.warn({
|
|
|
|
logCode: 'interactions_emoji_rain_no_coord',
|
|
|
|
}, 'No coordinates for interactions button, skipping emoji rain');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-11-14 02:36:49 +08:00
|
|
|
for (i = 0; i < NUMBER_OF_EMOJIS; i++) {
|
|
|
|
const initialPosition = {
|
|
|
|
x: coord.x + coord.width / 8,
|
|
|
|
y: coord.y + coord.height / 5,
|
|
|
|
};
|
|
|
|
const endPosition = {
|
|
|
|
x: Math.floor(Math.random() * 100) + coord.x - 100 / 2,
|
|
|
|
y: Math.floor(Math.random() * 300) + coord.y / 2,
|
|
|
|
};
|
|
|
|
const scale = Math.floor(Math.random() * (8 - 4 + 1)) - 40;
|
|
|
|
const sec = Math.floor(Math.random() * 1700) + 2000;
|
|
|
|
|
|
|
|
const shapeElement = document.createElement('svg');
|
|
|
|
const emojiElement = document.createElement('text');
|
|
|
|
emojiElement.setAttribute('x', '50%');
|
|
|
|
emojiElement.setAttribute('y', '50%');
|
|
|
|
emojiElement.innerHTML = emoji;
|
|
|
|
|
|
|
|
shapeElement.style.position = 'absolute';
|
|
|
|
shapeElement.style.left = `${initialPosition.x}px`;
|
|
|
|
shapeElement.style.top = `${initialPosition.y}px`;
|
|
|
|
shapeElement.style.transform = `scaleX(0.${scale}) scaleY(0.${scale})`;
|
|
|
|
shapeElement.style.transition = `${sec}ms`;
|
|
|
|
shapeElement.style.fontSize = `${EMOJI_SIZE}em`;
|
|
|
|
shapeElement.style.pointerEvents = 'none';
|
|
|
|
|
|
|
|
shapeElement.appendChild(emojiElement);
|
|
|
|
containerRef.current.appendChild(shapeElement);
|
|
|
|
|
|
|
|
flyingEmojis.push({ shapeElement, endPosition });
|
|
|
|
}
|
|
|
|
|
|
|
|
requestAnimationFrame(() => setTimeout(() => flyingEmojis.forEach((emoji) => {
|
|
|
|
const { shapeElement, endPosition } = emoji;
|
|
|
|
shapeElement.style.left = `${endPosition.x}px`;
|
|
|
|
shapeElement.style.top = `${endPosition.y}px`;
|
|
|
|
shapeElement.style.transform = 'scaleX(0) scaleY(0)';
|
|
|
|
}), 0));
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
flyingEmojis.forEach((emoji) => emoji.shapeElement.remove());
|
|
|
|
flyingEmojis.length = 0;
|
|
|
|
}, 2000);
|
|
|
|
}
|
|
|
|
|
|
|
|
const handleVisibilityChange = () => {
|
|
|
|
if (document.hidden) {
|
|
|
|
setIsAnimating(false);
|
|
|
|
} else if (EMOJI_RAIN_ENABLED && animations) {
|
|
|
|
setIsAnimating(true);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
|
|
|
};
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (EMOJI_RAIN_ENABLED && animations && !isAnimating && !document.hidden) {
|
|
|
|
setIsAnimating(true);
|
|
|
|
} else if (!animations) {
|
|
|
|
setIsAnimating(false);
|
|
|
|
}
|
|
|
|
}, [EMOJI_RAIN_ENABLED, animations, isAnimating]);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (isAnimating) {
|
|
|
|
reactions.forEach((reaction) => {
|
|
|
|
const currentTime = new Date().getTime();
|
|
|
|
const secondsSinceCreated = (currentTime - reaction.creationDate.getTime()) / 1000;
|
|
|
|
if (secondsSinceCreated <= 1 && (reaction.reaction !== 'none')) {
|
|
|
|
createEmojiRain(reaction.reaction);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}, [isAnimating, reactions]);
|
|
|
|
|
|
|
|
const containerStyle = {
|
|
|
|
width: '100vw',
|
|
|
|
height: '100vh',
|
|
|
|
position: 'fixed',
|
|
|
|
top: 0,
|
|
|
|
left: 0,
|
|
|
|
overflow: 'hidden',
|
|
|
|
pointerEvents: 'none',
|
|
|
|
zIndex: 2,
|
|
|
|
};
|
|
|
|
|
|
|
|
return <div ref={containerRef} style={containerStyle} />;
|
|
|
|
};
|
|
|
|
|
|
|
|
export default EmojiRain;
|