fix(reactions) Batch events before sending
This commit is contained in:
parent
4276f82c03
commit
251eec19cd
|
@ -823,8 +823,8 @@
|
||||||
"hangup": "Leave the meeting",
|
"hangup": "Leave the meeting",
|
||||||
"help": "Help",
|
"help": "Help",
|
||||||
"invite": "Invite people",
|
"invite": "Invite people",
|
||||||
"joy": "Laugh",
|
|
||||||
"kick": "Kick participant",
|
"kick": "Kick participant",
|
||||||
|
"laugh": "Laugh",
|
||||||
"like": "Thumbs Up",
|
"like": "Thumbs Up",
|
||||||
"lobbyButton": "Enable/disable lobby mode",
|
"lobbyButton": "Enable/disable lobby mode",
|
||||||
"localRecording": "Toggle local recording controls",
|
"localRecording": "Toggle local recording controls",
|
||||||
|
@ -892,7 +892,7 @@
|
||||||
"hangup": "Leave the meeting",
|
"hangup": "Leave the meeting",
|
||||||
"help": "Help",
|
"help": "Help",
|
||||||
"invite": "Invite people",
|
"invite": "Invite people",
|
||||||
"joy": "Laugh",
|
"laugh": "Laugh",
|
||||||
"like": "Thumbs Up",
|
"like": "Thumbs Up",
|
||||||
"lobbyButtonDisable": "Disable lobby mode",
|
"lobbyButtonDisable": "Disable lobby mode",
|
||||||
"lobbyButtonEnable": "Enable lobby mode",
|
"lobbyButtonEnable": "Enable lobby mode",
|
||||||
|
@ -922,7 +922,7 @@
|
||||||
"raiseYourHand": "Raise your hand",
|
"raiseYourHand": "Raise your hand",
|
||||||
"reactionBoo": "Send boo reaction",
|
"reactionBoo": "Send boo reaction",
|
||||||
"reactionClap": "Send clap reaction",
|
"reactionClap": "Send clap reaction",
|
||||||
"reactionJoy": "Send laugh reaction",
|
"reactionLaugh": "Send laugh reaction",
|
||||||
"reactionLike": "Send thumbs up reaction",
|
"reactionLike": "Send thumbs up reaction",
|
||||||
"reactionParty": "Send party popper reaction",
|
"reactionParty": "Send party popper reaction",
|
||||||
"reactionSurprised": "Send surprised reaction",
|
"reactionSurprised": "Send surprised reaction",
|
||||||
|
|
|
@ -795,6 +795,23 @@ export function createToolbarEvent(buttonName, attributes = {}) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an event associated with a reaction button being clicked/pressed.
|
||||||
|
*
|
||||||
|
* @param {string} buttonName - The identifier of the reaction button which was
|
||||||
|
* clicked/pressed.
|
||||||
|
* @returns {Object} The event in a format suitable for sending via
|
||||||
|
* sendAnalytics.
|
||||||
|
*/
|
||||||
|
export function createReactionMenuEvent(buttonName) {
|
||||||
|
return {
|
||||||
|
action: 'clicked',
|
||||||
|
actionSubject: buttonName,
|
||||||
|
source: 'reaction.button',
|
||||||
|
type: TYPE_UI
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an event which indicates that a local track was muted.
|
* Creates an event which indicates that a local track was muted.
|
||||||
*
|
*
|
||||||
|
|
|
@ -22,11 +22,9 @@ import {
|
||||||
import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
|
import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
|
||||||
import { playSound, registerSound, unregisterSound } from '../base/sounds';
|
import { playSound, registerSound, unregisterSound } from '../base/sounds';
|
||||||
import { openDisplayNamePrompt } from '../display-name';
|
import { openDisplayNamePrompt } from '../display-name';
|
||||||
import { ADD_REACTIONS_MESSAGE } from '../reactions/actionTypes';
|
import { ADD_REACTION_MESSAGE } from '../reactions/actionTypes';
|
||||||
import {
|
import { pushReactions } from '../reactions/actions.any';
|
||||||
pushReaction
|
import { getReactionMessageFromBuffer } from '../reactions/functions.any';
|
||||||
} from '../reactions/actions.any';
|
|
||||||
import { REACTIONS } from '../reactions/constants';
|
|
||||||
import { endpointMessageReceived } from '../subtitles';
|
import { endpointMessageReceived } from '../subtitles';
|
||||||
import { showToolbox } from '../toolbox/actions';
|
import { showToolbox } from '../toolbox/actions';
|
||||||
import {
|
import {
|
||||||
|
@ -158,7 +156,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case ADD_REACTIONS_MESSAGE: {
|
case ADD_REACTION_MESSAGE: {
|
||||||
_handleReceivedMessage(store, {
|
_handleReceivedMessage(store, {
|
||||||
id: localParticipant.id,
|
id: localParticipant.id,
|
||||||
message: action.message,
|
message: action.message,
|
||||||
|
@ -212,8 +210,6 @@ StateListenerRegistry.register(
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
function _addChatMsgListener(conference, store) {
|
function _addChatMsgListener(conference, store) {
|
||||||
const reactions = {};
|
|
||||||
|
|
||||||
if (store.getState()['features/base/config'].iAmRecorder) {
|
if (store.getState()['features/base/config'].iAmRecorder) {
|
||||||
// We don't register anything on web if we are in iAmRecorder mode
|
// We don't register anything on web if we are in iAmRecorder mode
|
||||||
return;
|
return;
|
||||||
|
@ -252,30 +248,21 @@ function _addChatMsgListener(conference, store) {
|
||||||
const [ { _id }, eventData ] = args;
|
const [ { _id }, eventData ] = args;
|
||||||
|
|
||||||
if (eventData.name === ENDPOINT_REACTION_NAME) {
|
if (eventData.name === ENDPOINT_REACTION_NAME) {
|
||||||
reactions[_id] = reactions[_id] ?? {
|
|
||||||
timeout: null,
|
|
||||||
message: ''
|
|
||||||
};
|
|
||||||
batch(() => {
|
batch(() => {
|
||||||
store.dispatch(pushReaction(eventData.reaction));
|
|
||||||
store.dispatch(setToolboxVisible(true));
|
store.dispatch(setToolboxVisible(true));
|
||||||
store.dispatch(setToolboxTimeout(
|
store.dispatch(setToolboxTimeout(
|
||||||
() => store.dispatch(hideToolbox()),
|
() => store.dispatch(hideToolbox()),
|
||||||
5000)
|
5000)
|
||||||
);
|
);
|
||||||
|
store.dispatch(pushReactions(eventData.reactions));
|
||||||
});
|
});
|
||||||
|
|
||||||
clearTimeout(reactions[_id].timeout);
|
_handleReceivedMessage(store, {
|
||||||
reactions[_id].message = `${reactions[_id].message}${REACTIONS[eventData.reaction].message}`;
|
id: _id,
|
||||||
reactions[_id].timeout = setTimeout(() => {
|
message: getReactionMessageFromBuffer(eventData.reactions),
|
||||||
_handleReceivedMessage(store, {
|
privateMessage: false,
|
||||||
id: _id,
|
timestamp: eventData.timestamp
|
||||||
message: reactions[_id].message,
|
}, false);
|
||||||
privateMessage: false,
|
|
||||||
timestamp: eventData.timestamp
|
|
||||||
}, false);
|
|
||||||
delete reactions[_id];
|
|
||||||
}, 500);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -13,21 +13,31 @@ export const TOGGLE_REACTIONS_VISIBLE = 'TOGGLE_REACTIONS_VISIBLE';
|
||||||
* a new timeout.
|
* a new timeout.
|
||||||
*
|
*
|
||||||
* {
|
* {
|
||||||
* type: SET_REACTION_MESSAGE,
|
* type: ADD_REACTION_BUFFER,
|
||||||
* message: string,
|
* message: string,
|
||||||
* timeoutID: number
|
* timeoutID: number
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
export const SET_REACTIONS_MESSAGE = 'SET_REACTIONS_MESSAGE';
|
export const ADD_REACTION_BUFFER = 'ADD_REACTION_BUFFER';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of the action which resets the reactions message and timeout.
|
* The type of the action which sends the reaction buffer and resets it.
|
||||||
*
|
*
|
||||||
* {
|
* {
|
||||||
* type: CLEAR_REACTION_MESSAGE
|
* type: FLUSH_REACTION_BUFFER
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
export const CLEAR_REACTIONS_MESSAGE = 'CLEAR_REACTIONS_MESSAGE';
|
export const FLUSH_REACTION_BUFFER = 'FLUSH_REACTION_BUFFER';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of the action which adds a new reaction message to the chat.
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* type: ADD_REACTION_MESSAGE,
|
||||||
|
* message: string,
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
export const ADD_REACTION_MESSAGE = 'ADD_REACTION_MESSAGE';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of the action which sets the reactions queue.
|
* The type of the action which sets the reactions queue.
|
||||||
|
@ -42,14 +52,9 @@ export const SET_REACTION_QUEUE = 'SET_REACTION_QUEUE';
|
||||||
/**
|
/**
|
||||||
* The type of the action which signals a send reaction to everyone in the conference.
|
* The type of the action which signals a send reaction to everyone in the conference.
|
||||||
*/
|
*/
|
||||||
export const SEND_REACTION = 'SEND_REACTION';
|
export const SEND_REACTIONS = 'SEND_REACTIONS';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of the action to add a reaction message to the chat.
|
* The type of action to adds reactions to the queue.
|
||||||
*/
|
*/
|
||||||
export const ADD_REACTIONS_MESSAGE = 'ADD_REACTIONS_MESSAGE';
|
export const PUSH_REACTIONS = 'PUSH_REACTIONS';
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of action to add a reaction to the queue.
|
|
||||||
*/
|
|
||||||
export const PUSH_REACTION = 'PUSH_REACTION';
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ADD_REACTIONS_MESSAGE,
|
ADD_REACTION_BUFFER,
|
||||||
CLEAR_REACTIONS_MESSAGE,
|
ADD_REACTION_MESSAGE,
|
||||||
PUSH_REACTION,
|
FLUSH_REACTION_BUFFER,
|
||||||
SEND_REACTION,
|
PUSH_REACTIONS,
|
||||||
SET_REACTIONS_MESSAGE,
|
SEND_REACTIONS,
|
||||||
SET_REACTION_QUEUE
|
SET_REACTION_QUEUE
|
||||||
} from './actionTypes';
|
} from './actionTypes';
|
||||||
import { type ReactionEmojiProps } from './constants';
|
import { type ReactionEmojiProps } from './constants';
|
||||||
|
@ -23,42 +23,6 @@ export function setReactionQueue(value: Array<ReactionEmojiProps>) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends the reactions message to the chat and resets the state.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
export function flushReactionsToChat() {
|
|
||||||
return {
|
|
||||||
type: CLEAR_REACTIONS_MESSAGE
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a new reaction to the reactions message.
|
|
||||||
*
|
|
||||||
* @param {boolean} value - The new reaction.
|
|
||||||
* @returns {Object}
|
|
||||||
*/
|
|
||||||
export function addReactionsMessage(value: string) {
|
|
||||||
return {
|
|
||||||
type: SET_REACTIONS_MESSAGE,
|
|
||||||
reaction: value
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a new reaction to the reactions message.
|
|
||||||
*
|
|
||||||
* @param {boolean} value - Reaction to be added to queue.
|
|
||||||
* @returns {Object}
|
|
||||||
*/
|
|
||||||
export function pushReaction(value: string) {
|
|
||||||
return {
|
|
||||||
type: PUSH_REACTION,
|
|
||||||
reaction: value
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a reaction from the queue.
|
* Removes a reaction from the queue.
|
||||||
|
@ -76,33 +40,75 @@ export function removeReaction(uid: number) {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a reaction message to everyone in the conference.
|
* Sends the reactions buffer to everyone in the conference.
|
||||||
*
|
*
|
||||||
* @param {string} reaction - The reaction to send out.
|
|
||||||
* @returns {{
|
* @returns {{
|
||||||
* type: SEND_REACTION,
|
* type: SEND_REACTION
|
||||||
|
* }}
|
||||||
|
*/
|
||||||
|
export function sendReactions() {
|
||||||
|
return {
|
||||||
|
type: SEND_REACTIONS
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a reaction to the local buffer.
|
||||||
|
*
|
||||||
|
* @param {string} reaction - The reaction to be added.
|
||||||
|
* @returns {{
|
||||||
|
* type: ADD_REACTION_BUFFER,
|
||||||
* reaction: string
|
* reaction: string
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
export function sendReaction(reaction: string) {
|
export function addReactionToBuffer(reaction: string) {
|
||||||
return {
|
return {
|
||||||
type: SEND_REACTION,
|
type: ADD_REACTION_BUFFER,
|
||||||
reaction
|
reaction
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a reactions message to the chat.
|
* Clears the reaction buffer.
|
||||||
*
|
*
|
||||||
* @param {string} message - The reactions message to add to chat.
|
|
||||||
* @returns {{
|
* @returns {{
|
||||||
* type: ADD_REACTIONS_MESSAGE,
|
* type: FLUSH_REACTION_BUFFER
|
||||||
|
* }}
|
||||||
|
*/
|
||||||
|
export function flushReactionBuffer() {
|
||||||
|
return {
|
||||||
|
type: FLUSH_REACTION_BUFFER
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a reaction message to the chat.
|
||||||
|
*
|
||||||
|
* @param {string} message - The reaction message.
|
||||||
|
* @returns {{
|
||||||
|
* type: ADD_REACTION_MESSAGE,
|
||||||
* message: string
|
* message: string
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
export function addReactionsMessageToChat(message: string) {
|
export function addReactionsToChat(message: string) {
|
||||||
return {
|
return {
|
||||||
type: ADD_REACTIONS_MESSAGE,
|
type: ADD_REACTION_MESSAGE,
|
||||||
message
|
message
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds reactions to the animation queue.
|
||||||
|
*
|
||||||
|
* @param {Array} reactions - The reactions to be animated.
|
||||||
|
* @returns {{
|
||||||
|
* type: PUSH_REACTIONS,
|
||||||
|
* reactions: Array
|
||||||
|
* }}
|
||||||
|
*/
|
||||||
|
export function pushReactions(reactions: Array<string>) {
|
||||||
|
return {
|
||||||
|
type: PUSH_REACTIONS,
|
||||||
|
reactions
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -4,9 +4,10 @@ import React from 'react';
|
||||||
import { Text, TouchableHighlight } from 'react-native';
|
import { Text, TouchableHighlight } from 'react-native';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
|
|
||||||
|
import { createReactionMenuEvent, sendAnalytics } from '../../../analytics';
|
||||||
import { translate } from '../../../base/i18n';
|
import { translate } from '../../../base/i18n';
|
||||||
import type { StyleType } from '../../../base/styles';
|
import type { StyleType } from '../../../base/styles';
|
||||||
import { sendReaction } from '../../actions.any';
|
import { addReactionToBuffer } from '../../actions.any';
|
||||||
import { REACTIONS } from '../../constants';
|
import { REACTIONS } from '../../constants';
|
||||||
|
|
||||||
|
|
||||||
|
@ -78,7 +79,8 @@ function ReactionButton({
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
function _onClick() {
|
function _onClick() {
|
||||||
dispatch(sendReaction(reaction));
|
dispatch(addReactionToBuffer(reaction));
|
||||||
|
sendAnalytics(createReactionMenuEvent(reaction));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -4,6 +4,7 @@ import React, { Component } from 'react';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
createReactionMenuEvent,
|
||||||
createToolbarEvent,
|
createToolbarEvent,
|
||||||
sendAnalytics
|
sendAnalytics
|
||||||
} from '../../../analytics';
|
} from '../../../analytics';
|
||||||
|
@ -11,7 +12,7 @@ import { translate } from '../../../base/i18n';
|
||||||
import { getLocalParticipant, getParticipantCount, participantUpdated } from '../../../base/participants';
|
import { getLocalParticipant, getParticipantCount, participantUpdated } from '../../../base/participants';
|
||||||
import { connect } from '../../../base/redux';
|
import { connect } from '../../../base/redux';
|
||||||
import { dockToolbox } from '../../../toolbox/actions.web';
|
import { dockToolbox } from '../../../toolbox/actions.web';
|
||||||
import { sendReaction } from '../../actions.any';
|
import { addReactionToBuffer } from '../../actions.any';
|
||||||
import { toggleReactionsMenuVisibility } from '../../actions.web';
|
import { toggleReactionsMenuVisibility } from '../../actions.web';
|
||||||
import { REACTIONS } from '../../constants';
|
import { REACTIONS } from '../../constants';
|
||||||
|
|
||||||
|
@ -144,6 +145,13 @@ class ReactionsMenu extends Component<Props> {
|
||||||
*/
|
*/
|
||||||
_getReactionButtons() {
|
_getReactionButtons() {
|
||||||
const { t, dispatch } = this.props;
|
const { t, dispatch } = this.props;
|
||||||
|
let modifierKey = 'Alt';
|
||||||
|
|
||||||
|
if (window.navigator?.platform) {
|
||||||
|
if (window.navigator.platform.indexOf('Mac') !== -1) {
|
||||||
|
modifierKey = '⌥';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Object.keys(REACTIONS).map(key => {
|
return Object.keys(REACTIONS).map(key => {
|
||||||
/**
|
/**
|
||||||
|
@ -151,17 +159,18 @@ class ReactionsMenu extends Component<Props> {
|
||||||
*
|
*
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
function sendMessage() {
|
function doSendReaction() {
|
||||||
dispatch(sendReaction(key));
|
dispatch(addReactionToBuffer(key));
|
||||||
|
sendAnalytics(createReactionMenuEvent(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (<ReactionButton
|
return (<ReactionButton
|
||||||
accessibilityLabel = { t(`toolbar.accessibilityLabel.${key}`) }
|
accessibilityLabel = { t(`toolbar.accessibilityLabel.${key}`) }
|
||||||
icon = { REACTIONS[key].emoji }
|
icon = { REACTIONS[key].emoji }
|
||||||
key = { key }
|
key = { key }
|
||||||
onClick = { sendMessage }
|
onClick = { doSendReaction }
|
||||||
toggled = { false }
|
toggled = { false }
|
||||||
tooltip = { t(`toolbar.${key}`) } />);
|
tooltip = { `${t(`toolbar.${key}`)} (${modifierKey} + ${REACTIONS[key].shortcutChar})` } />);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import React, { useEffect } from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { translate } from '../../../base/i18n';
|
import { translate } from '../../../base/i18n';
|
||||||
import { IconRaisedHand } from '../../../base/icons';
|
import { IconRaisedHand } from '../../../base/icons';
|
||||||
import { getLocalParticipant } from '../../../base/participants';
|
import { getLocalParticipant } from '../../../base/participants';
|
||||||
import { connect } from '../../../base/redux';
|
import { connect } from '../../../base/redux';
|
||||||
import ToolbarButton from '../../../toolbox/components/web/ToolbarButton';
|
import ToolbarButton from '../../../toolbox/components/web/ToolbarButton';
|
||||||
import { sendReaction } from '../../actions.any';
|
|
||||||
import { toggleReactionsMenuVisibility } from '../../actions.web';
|
import { toggleReactionsMenuVisibility } from '../../actions.web';
|
||||||
import { REACTIONS, type ReactionEmojiProps } from '../../constants';
|
import { type ReactionEmojiProps } from '../../constants';
|
||||||
import { getReactionsQueue } from '../../functions.any';
|
import { getReactionsQueue } from '../../functions.any';
|
||||||
import { getReactionsMenuVisibility } from '../../functions.web';
|
import { getReactionsMenuVisibility } from '../../functions.web';
|
||||||
|
|
||||||
|
@ -65,32 +64,6 @@ function ReactionsMenuButton({
|
||||||
dispatch
|
dispatch
|
||||||
}: Props) {
|
}: Props) {
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const KEYBOARD_SHORTCUTS = Object.keys(REACTIONS).map(key => {
|
|
||||||
return {
|
|
||||||
character: REACTIONS[key].shortcutChar,
|
|
||||||
exec: () => dispatch(sendReaction(key)),
|
|
||||||
helpDescription: t(`toolbar.reaction${key.charAt(0).toUpperCase()}${key.slice(1)}`),
|
|
||||||
altKey: true
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
KEYBOARD_SHORTCUTS.forEach(shortcut => {
|
|
||||||
APP.keyboardshortcut.registerShortcut(
|
|
||||||
shortcut.character,
|
|
||||||
null,
|
|
||||||
shortcut.exec,
|
|
||||||
shortcut.helpDescription,
|
|
||||||
shortcut.altKey);
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
Object.keys(REACTIONS).map(key => REACTIONS[key].shortcutChar)
|
|
||||||
.forEach(letter =>
|
|
||||||
APP.keyboardshortcut.unregisterShortcut(letter, true));
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggles the reactions menu visibility.
|
* Toggles the reactions menu visibility.
|
||||||
*
|
*
|
||||||
|
|
|
@ -11,7 +11,7 @@ export const REACTIONS = {
|
||||||
emoji: '👏',
|
emoji: '👏',
|
||||||
shortcutChar: 'C'
|
shortcutChar: 'C'
|
||||||
},
|
},
|
||||||
joy: {
|
laugh: {
|
||||||
message: ':grinning_face:',
|
message: ':grinning_face:',
|
||||||
emoji: '😀',
|
emoji: '😀',
|
||||||
shortcutChar: 'L'
|
shortcutChar: 'L'
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
|
import uuid from 'uuid';
|
||||||
|
|
||||||
import { getLocalParticipant } from '../base/participants';
|
import { getLocalParticipant } from '../base/participants';
|
||||||
import { extractFqnFromPath } from '../dynamic-branding/functions';
|
import { extractFqnFromPath } from '../dynamic-branding/functions';
|
||||||
|
|
||||||
|
@ -17,28 +19,28 @@ export function getReactionsQueue(state: Object) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns reaction key from the reaction message.
|
* Returns chat message from reactions buffer.
|
||||||
*
|
*
|
||||||
* @param {string} message - The reaction message.
|
* @param {Array} buffer - The reactions buffer.
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
export function getReactionKeyByMessage(message: string): ?string {
|
export function getReactionMessageFromBuffer(buffer: Array<string>) {
|
||||||
return Object.keys(REACTIONS).find(key => REACTIONS[key].message === `:${message}:`);
|
return buffer.map(reaction => REACTIONS[reaction].message).reduce((acc, val) => `${acc}${val}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets reactions key array from concatenated message.
|
* Returns reactions array with uid.
|
||||||
*
|
*
|
||||||
* @param {string} message - The reaction message.
|
* @param {Array} buffer - The reactions buffer.
|
||||||
* @returns {Array}
|
* @returns {Array}
|
||||||
*/
|
*/
|
||||||
export function messageToKeyArray(message: string) {
|
export function getReactionsWithId(buffer: Array<string>) {
|
||||||
let formattedMessage = message.replace(/::/g, '-');
|
return buffer.map<Object>(reaction => {
|
||||||
|
return {
|
||||||
formattedMessage = formattedMessage.replace(/:/g, '');
|
reaction,
|
||||||
const messageArray = formattedMessage.split('-');
|
uid: uuid.v4()
|
||||||
|
};
|
||||||
return messageArray.map<?string>(getReactionKeyByMessage);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,24 +1,25 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
|
import { batch } from 'react-redux';
|
||||||
|
|
||||||
import { ENDPOINT_REACTION_NAME } from '../../../modules/API/constants';
|
import { ENDPOINT_REACTION_NAME } from '../../../modules/API/constants';
|
||||||
import { MiddlewareRegistry } from '../base/redux';
|
import { MiddlewareRegistry } from '../base/redux';
|
||||||
import { isVpaasMeeting } from '../jaas/functions';
|
import { isVpaasMeeting } from '../jaas/functions';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SET_REACTIONS_MESSAGE,
|
ADD_REACTION_BUFFER,
|
||||||
CLEAR_REACTIONS_MESSAGE,
|
FLUSH_REACTION_BUFFER,
|
||||||
SEND_REACTION,
|
SEND_REACTIONS,
|
||||||
PUSH_REACTION
|
PUSH_REACTIONS
|
||||||
} from './actionTypes';
|
} from './actionTypes';
|
||||||
import {
|
import {
|
||||||
addReactionsMessage,
|
addReactionsToChat,
|
||||||
addReactionsMessageToChat,
|
flushReactionBuffer,
|
||||||
flushReactionsToChat,
|
pushReactions,
|
||||||
pushReaction,
|
sendReactions,
|
||||||
setReactionQueue
|
setReactionQueue
|
||||||
} from './actions.any';
|
} from './actions.any';
|
||||||
import { REACTIONS } from './constants';
|
import { getReactionMessageFromBuffer, getReactionsWithId, sendReactionsWebhook } from './functions.any';
|
||||||
import { messageToKeyArray, sendReactionsWebhook } from './functions.any';
|
|
||||||
|
|
||||||
|
|
||||||
declare var APP: Object;
|
declare var APP: Object;
|
||||||
|
@ -34,56 +35,57 @@ MiddlewareRegistry.register(store => next => action => {
|
||||||
const { dispatch, getState } = store;
|
const { dispatch, getState } = store;
|
||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case SET_REACTIONS_MESSAGE: {
|
case ADD_REACTION_BUFFER: {
|
||||||
const { timeoutID, message } = getState()['features/reactions'];
|
const { timeoutID, buffer } = getState()['features/reactions'];
|
||||||
const { reaction } = action;
|
const { reaction } = action;
|
||||||
|
|
||||||
clearTimeout(timeoutID);
|
clearTimeout(timeoutID);
|
||||||
action.message = `${message}${reaction}`;
|
buffer.push(reaction);
|
||||||
|
action.buffer = buffer;
|
||||||
action.timeoutID = setTimeout(() => {
|
action.timeoutID = setTimeout(() => {
|
||||||
dispatch(flushReactionsToChat());
|
dispatch(flushReactionBuffer());
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case CLEAR_REACTIONS_MESSAGE: {
|
case FLUSH_REACTION_BUFFER: {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const { message } = state['features/reactions'];
|
const { buffer } = state['features/reactions'];
|
||||||
|
|
||||||
|
batch(() => {
|
||||||
|
dispatch(sendReactions());
|
||||||
|
dispatch(addReactionsToChat(getReactionMessageFromBuffer(buffer)));
|
||||||
|
dispatch(pushReactions(buffer));
|
||||||
|
});
|
||||||
|
|
||||||
if (isVpaasMeeting(state)) {
|
if (isVpaasMeeting(state)) {
|
||||||
sendReactionsWebhook(state, messageToKeyArray(message));
|
sendReactionsWebhook(state, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(addReactionsMessageToChat(message));
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SEND_REACTION: {
|
case SEND_REACTIONS: {
|
||||||
const state = store.getState();
|
const state = getState();
|
||||||
|
const { buffer } = state['features/reactions'];
|
||||||
const { conference } = state['features/base/conference'];
|
const { conference } = state['features/base/conference'];
|
||||||
|
|
||||||
if (conference) {
|
if (conference) {
|
||||||
conference.sendEndpointMessage('', {
|
conference.sendEndpointMessage('', {
|
||||||
name: ENDPOINT_REACTION_NAME,
|
name: ENDPOINT_REACTION_NAME,
|
||||||
reaction: action.reaction,
|
reactions: buffer,
|
||||||
timestamp: Date.now()
|
timestamp: Date.now()
|
||||||
});
|
});
|
||||||
dispatch(addReactionsMessage(REACTIONS[action.reaction].message));
|
|
||||||
dispatch(pushReaction(action.reaction));
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case PUSH_REACTION: {
|
case PUSH_REACTIONS: {
|
||||||
const queue = store.getState()['features/reactions'].queue;
|
const queue = store.getState()['features/reactions'].queue;
|
||||||
const reaction = action.reaction;
|
const reactions = action.reactions;
|
||||||
|
|
||||||
dispatch(setReactionQueue([ ...queue, {
|
dispatch(setReactionQueue([ ...queue, ...getReactionsWithId(reactions) ]));
|
||||||
reaction,
|
|
||||||
uid: window.Date.now()
|
|
||||||
} ]));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,9 @@ import { ReducerRegistry } from '../base/redux';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
TOGGLE_REACTIONS_VISIBLE,
|
TOGGLE_REACTIONS_VISIBLE,
|
||||||
SET_REACTIONS_MESSAGE,
|
SET_REACTION_QUEUE,
|
||||||
CLEAR_REACTIONS_MESSAGE,
|
ADD_REACTION_BUFFER,
|
||||||
SET_REACTION_QUEUE
|
FLUSH_REACTION_BUFFER
|
||||||
} from './actionTypes';
|
} from './actionTypes';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -30,11 +30,11 @@ function _getInitialState() {
|
||||||
visible: false,
|
visible: false,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A string that contains the message to be added to the chat.
|
* An array that contains the reactions buffer to be sent.
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {Array}
|
||||||
*/
|
*/
|
||||||
message: '',
|
buffer: [],
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A number, non-zero value which identifies the timer created by a call
|
* A number, non-zero value which identifies the timer created by a call
|
||||||
|
@ -64,17 +64,17 @@ ReducerRegistry.register(
|
||||||
visible: !state.visible
|
visible: !state.visible
|
||||||
};
|
};
|
||||||
|
|
||||||
case SET_REACTIONS_MESSAGE:
|
case ADD_REACTION_BUFFER:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
message: action.message,
|
buffer: action.buffer,
|
||||||
timeoutID: action.timeoutID
|
timeoutID: action.timeoutID
|
||||||
};
|
};
|
||||||
|
|
||||||
case CLEAR_REACTIONS_MESSAGE:
|
case FLUSH_REACTION_BUFFER:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
message: '',
|
buffer: [],
|
||||||
timeoutID: null
|
timeoutID: null
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,9 @@ import {
|
||||||
} from '../../../participants-pane/actions';
|
} from '../../../participants-pane/actions';
|
||||||
import ParticipantsPaneButton from '../../../participants-pane/components/ParticipantsPaneButton';
|
import ParticipantsPaneButton from '../../../participants-pane/components/ParticipantsPaneButton';
|
||||||
import { getParticipantsPaneOpen } from '../../../participants-pane/functions';
|
import { getParticipantsPaneOpen } from '../../../participants-pane/functions';
|
||||||
|
import { addReactionToBuffer } from '../../../reactions/actions.any';
|
||||||
import { ReactionsMenuButton } from '../../../reactions/components';
|
import { ReactionsMenuButton } from '../../../reactions/components';
|
||||||
|
import { REACTIONS } from '../../../reactions/constants';
|
||||||
import {
|
import {
|
||||||
LiveStreamButton,
|
LiveStreamButton,
|
||||||
RecordButton
|
RecordButton
|
||||||
|
@ -268,7 +270,7 @@ class Toolbox extends Component<Props> {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { _toolbarButtons } = this.props;
|
const { _toolbarButtons, t, dispatch } = this.props;
|
||||||
const KEYBOARD_SHORTCUTS = [
|
const KEYBOARD_SHORTCUTS = [
|
||||||
isToolbarButtonEnabled('videoquality', _toolbarButtons) && {
|
isToolbarButtonEnabled('videoquality', _toolbarButtons) && {
|
||||||
character: 'A',
|
character: 'A',
|
||||||
|
@ -316,6 +318,31 @@ class Toolbox extends Component<Props> {
|
||||||
shortcut.helpDescription);
|
shortcut.helpDescription);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const REACTION_SHORTCUTS = Object.keys(REACTIONS).map(key => {
|
||||||
|
const onShortcutSendReaction = () => {
|
||||||
|
dispatch(addReactionToBuffer(key));
|
||||||
|
sendAnalytics(createShortcutEvent(
|
||||||
|
`reaction.${key}`
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
character: REACTIONS[key].shortcutChar,
|
||||||
|
exec: onShortcutSendReaction,
|
||||||
|
helpDescription: t(`toolbar.reaction${key.charAt(0).toUpperCase()}${key.slice(1)}`),
|
||||||
|
altKey: true
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
REACTION_SHORTCUTS.forEach(shortcut => {
|
||||||
|
APP.keyboardshortcut.registerShortcut(
|
||||||
|
shortcut.character,
|
||||||
|
null,
|
||||||
|
shortcut.exec,
|
||||||
|
shortcut.helpDescription,
|
||||||
|
shortcut.altKey);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -346,6 +373,10 @@ class Toolbox extends Component<Props> {
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
[ 'A', 'C', 'D', 'R', 'S' ].forEach(letter =>
|
[ 'A', 'C', 'D', 'R', 'S' ].forEach(letter =>
|
||||||
APP.keyboardshortcut.unregisterShortcut(letter));
|
APP.keyboardshortcut.unregisterShortcut(letter));
|
||||||
|
|
||||||
|
Object.keys(REACTIONS).map(key => REACTIONS[key].shortcutChar)
|
||||||
|
.forEach(letter =>
|
||||||
|
APP.keyboardshortcut.unregisterShortcut(letter, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue