feat(reactions): port new reations

This commit is contained in:
AtilaU19 2023-09-18 19:02:21 -03:00
parent 7cd78b3414
commit f1dbc8a8eb
6 changed files with 142 additions and 1 deletions

View File

@ -140,7 +140,7 @@ const ReactionsButton = (props) => {
return (
<BBBMenu
trigger={(
<Styled.ReactionsDropdown>
<Styled.ReactionsDropdown id="interactionsButton">
<Styled.RaiseHandButton
data-test="reactionsButton"
icon={icon}

View File

@ -29,6 +29,7 @@ import WebcamContainer from '../webcam/container';
import PresentationAreaContainer from '../presentation/presentation-area/container';
import ScreenshareContainer from '../screenshare/container';
import ExternalVideoContainer from '../external-video-player/container';
import EmojiRainContainer from '../emoji-rain/container';
import Styled from './styles';
import { DEVICE_TYPE, ACTIONS, SMALL_VIEWPORT_BREAKPOINT, PANELS } from '../layout/enums';
import {
@ -647,6 +648,7 @@ class App extends Component {
<PadsSessionsContainer />
<WakeLockContainer />
{this.renderActionsBar()}
<EmojiRainContainer />
{customStyleUrl ? <link rel="stylesheet" type="text/css" href={customStyleUrl} /> : null}
{customStyle ? <link rel="stylesheet" type="text/css" href={`data:text/css;charset=UTF-8,${encodeURIComponent(customStyle)}`} /> : null}
{isRandomUserSelectModalOpen ? <RandomUserSelectContainer

View File

@ -0,0 +1,111 @@
import React, { useRef, useState, useEffect } from 'react';
import Settings from '/imports/ui/services/settings';
import Service from './service';
const EmojiRain = ({ reactions }) => {
const containerRef = useRef(null);
const [isAnimating, setIsAnimating] = useState(false);
const EMOJI_SIZE = Meteor.settings.public.app.emojiRain.emojiSize;
const NUMBER_OF_EMOJIS = Meteor.settings.public.app.emojiRain.numberOfEmojis;
const EMOJI_RAIN_ENABLED = Meteor.settings.public.app.emojiRain.enabled;
const { animations } = Settings.application;
function createEmojiRain(emoji) {
const coord = Service.getInteractionsButtonCoordenates();
const flyingEmojis = [];
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) => {
if (Date() == reaction.creationDate && (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;

View File

@ -0,0 +1,11 @@
import React from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import EmojiRain from './component';
import UserReaction from '/imports/api/user-reaction';
import Auth from '/imports/ui/services/auth';
const EmojiRainContainer = (props) => <EmojiRain {...props} />;
export default withTracker(() => ({
reactions: UserReaction.find({ meetingId: Auth.meetingID }).fetch(),
}))(EmojiRainContainer);

View File

@ -0,0 +1,9 @@
const getInteractionsButtonCoordenates = () => {
const el = document.getElementById('interactionsButton');
const coordenada = el.getBoundingClientRect();
return coordenada;
};
export default {
getInteractionsButtonCoordenates,
};

View File

@ -152,6 +152,14 @@ public:
# Enables the new raiseHand icon inside of the reaction menu (introduced in BBB 2.7)
# If both reactionsButton and raiseHandActionButton are enabled, reactionsButton takes precedence.
enabled: true
emojiRain:
# If true, new reactions will be activated
enabled: false
# Can set the throttle, the number of emojis that will be displayed and their size
intervalEmojis: 2000
numberOfEmojis: 5
# emojiSize: size of the emoji in 'em' units
emojiSize: 2
# If enabled, before joining microphone the client will perform a trickle
# ICE against Kurento and use the information about successfull
# candidate-pairs to filter out local candidates in SIP.js's SDP.