feat(reactions): port new reations
This commit is contained in:
parent
7cd78b3414
commit
f1dbc8a8eb
@ -140,7 +140,7 @@ const ReactionsButton = (props) => {
|
||||
return (
|
||||
<BBBMenu
|
||||
trigger={(
|
||||
<Styled.ReactionsDropdown>
|
||||
<Styled.ReactionsDropdown id="interactionsButton">
|
||||
<Styled.RaiseHandButton
|
||||
data-test="reactionsButton"
|
||||
icon={icon}
|
||||
|
@ -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
|
||||
|
@ -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;
|
@ -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);
|
@ -0,0 +1,9 @@
|
||||
const getInteractionsButtonCoordenates = () => {
|
||||
const el = document.getElementById('interactionsButton');
|
||||
const coordenada = el.getBoundingClientRect();
|
||||
return coordenada;
|
||||
};
|
||||
|
||||
export default {
|
||||
getInteractionsButtonCoordenates,
|
||||
};
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user