diff --git a/react/features/reactions/actionTypes.js b/react/features/reactions/actionTypes.js index e69de29bb..ced53c618 100644 --- a/react/features/reactions/actionTypes.js +++ b/react/features/reactions/actionTypes.js @@ -0,0 +1,11 @@ +/** + * The type of (redux) action which signals that a specific reaction has been + * received by the local participant from a specific remote participant. + * + * { + * type: ADD_RECEIVED_REACTION, + * participant: Object, + * reaction: string + * } + */ +export const ADD_RECEIVED_REACTION = Symbol('ADD_RECEIVED_REACTION'); diff --git a/react/features/reactions/actions.js b/react/features/reactions/actions.js index ac61c9d97..03fb427dc 100644 --- a/react/features/reactions/actions.js +++ b/react/features/reactions/actions.js @@ -1,5 +1,27 @@ // @flow +import { ADD_RECEIVED_REACTION } from './actionTypes'; + +/** + * Creates a redux action which signals that a specific reaction has been + * received by the local participant from a specific remote participant. + * + * @param {string} reaction - The reaction which has been received. + * @param {Object} participant - The remote participant who sent the reaction. + * @returns {{ + * type: ADD_RECEIVED_REACTION, + * participant: Object, + * reaction: string + * }} + */ +export function addReceivedReaction(reaction: string, participant: Object) { + return { + type: ADD_RECEIVED_REACTION, + participant, + reaction + }; +} + /** * Sends a specific reaction of the local participant to the remote * participants. diff --git a/react/features/reactions/components/ReactionsCanvas.web.js b/react/features/reactions/components/ReactionsCanvas.web.js index 29bd476de..1e28f1f62 100644 --- a/react/features/reactions/components/ReactionsCanvas.web.js +++ b/react/features/reactions/components/ReactionsCanvas.web.js @@ -1,9 +1,13 @@ -/* @flow */ +// @flow import React, { Component } from 'react'; +import { connect } from 'react-redux'; import EmojiIcon from './EmojiIcon'; +type Props = { + _reaction: string +}; /** * Implements a React {@link Component} which represents the large video (a.k.a. @@ -11,7 +15,7 @@ import EmojiIcon from './EmojiIcon'; * * @extends Component */ -export default class ReactionsCanvas extends Component<*> { +class ReactionsCanvas extends Component { /** * Implements React's {@link Component#render()}. @@ -20,12 +24,51 @@ export default class ReactionsCanvas extends Component<*> { * @returns {ReactElement} */ render() { + const reaction = this.props._reaction; + + if (!reaction) { + return null; + } + + // FIXME The use of key bellow is somewhat of a hack/workaround for + // EmojiIcon: EmojiIcon doesn't seem to restart its animation upon + // changing its emojiName value. So the key will force a new EmojiIcon + // instance upon different reactions. + return (
+ emojiName = { reaction } + key = { reaction } />
); } } +/** + * Maps (parts of) the redux state to the associated ReactionsCanvas' props. + * + * @param {Object} state - The redux state. + * @private + * @returns {{ + * _reaction: string + * }} + */ +function _mapStateToProps(state) { // eslint-disable-line no-unused-vars + const stateFeatureReactions = state['features/reactions']; + const receivedReactions + = stateFeatureReactions && stateFeatureReactions.receivedReactions; + + // TODO Eventually and wherever appropriate (which is definitely not here), + // pop reactions which have been rendered. + const reaction + = receivedReactions && receivedReactions.length + ? receivedReactions[0] + : undefined; + + return { + _reaction: reaction + }; +} + +export default connect(_mapStateToProps)(ReactionsCanvas); diff --git a/react/features/reactions/index.js b/react/features/reactions/index.js index f4ab4e579..a29aa08e0 100644 --- a/react/features/reactions/index.js +++ b/react/features/reactions/index.js @@ -3,3 +3,4 @@ export * from './actionTypes'; export * from './components'; import './middleware'; +import './reducer'; diff --git a/react/features/reactions/middleware.js b/react/features/reactions/middleware.js index a33ae2c72..751695c7c 100644 --- a/react/features/reactions/middleware.js +++ b/react/features/reactions/middleware.js @@ -3,6 +3,8 @@ import { CONFERENCE_JOINED } from '../base/conference'; import { MiddlewareRegistry } from '../base/redux'; +import { addReceivedReaction } from './actions'; + MiddlewareRegistry.register(store => next => action => { switch (action.type) { case CONFERENCE_JOINED: @@ -26,17 +28,18 @@ MiddlewareRegistry.register(store => next => action => { * @returns {Object} The new state that is the result of the reduction of the * specified {@code action}. */ -function _conferenceJoined({ getState }, next, action) { +function _conferenceJoined({ dispatch, getState }, next, action) { const result = next(action); getState()['features/base/conference'].conference.on( 'conference.endpoint_message_received', - (participant, payload) => { - if (payload - && payload.payload - && payload.payload.type === 'reaction') { - // TODO Utilize payload (and maybe participant). - console.log('Received:', payload); + (participant, message) => { + let payload; + + if (message + && (payload = message.payload) + && payload.type === 'reaction') { + dispatch(addReceivedReaction(payload.reaction, participant)); } }); diff --git a/react/features/reactions/reducer.js b/react/features/reactions/reducer.js new file mode 100644 index 000000000..9083d3863 --- /dev/null +++ b/react/features/reactions/reducer.js @@ -0,0 +1,28 @@ +// @flow + +import { ReducerRegistry } from '../base/redux'; + +import { ADD_RECEIVED_REACTION } from './actionTypes'; + +const _INITIAL_STATE = { + receivedReactions: [] +}; + +ReducerRegistry.register( + 'features/reactions', (state = _INITIAL_STATE, action) => { + switch (action.type) { + case ADD_RECEIVED_REACTION: + return { + ...state, + receivedReactions: [ + + // FIXME Push the latest reaction at the top because I'm + // currently rendering only the first reaction. + action.reaction, + ...state.receivedReactions + ] + }; + } + + return state; + }); diff --git a/webpack.config.js b/webpack.config.js index 8b26affe9..fffbf32f0 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -183,8 +183,9 @@ module.exports = [ * target, undefined; otherwise, the path to the local file to be served. */ function devServerProxyBypass({ path }) { - if (path.startsWith('/css/') || path.startsWith('/doc/') - || path.startsWith('/images/')) { + if (path.startsWith('/css/') + || path.startsWith('/doc/') + || path.startsWith('/images/')) { return path; }