fix(reactions) Reactions improvements (#9964)
* Register shortcuts on mount * Changed icon for reactions menu * Enable reactions by default * Fix unreadCount bug When having unread messages and sending a reaction the unread count now shows the correct count * Fix overflow menu bottom color when reactions are enabled * Revert raise hand icon * Update raise hand functionality On desktop show raise button with arrow for reactions. Only show raise hand in the reactions menu on mobile * Fix lint error Add required prop to ToolboxButtonWithIcon * Legacy support for enable reactions If disableReactions is undefined treat it as true * Remove unnecessary code * Fix unread counter showing negative count * Fix unreadCount with reactions UnreadCount ignores all reactions messages * Fixed typo * Fix background color
This commit is contained in:
parent
5f5cac0e01
commit
584ec7c82e
|
@ -77,8 +77,8 @@ var config = {
|
|||
// Disables moderator indicators.
|
||||
// disableModeratorIndicator: false,
|
||||
|
||||
// Enables reactions feature.
|
||||
// enableReactions: false,
|
||||
// Disables the reactions feature.
|
||||
// disableReactions: true,
|
||||
|
||||
// Disables polls feature.
|
||||
// disablePolls: false,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
.reactions-menu {
|
||||
width: 280px;
|
||||
background: #292929;
|
||||
background: $menuBG;
|
||||
box-shadow: 0px 3px 16px rgba(0, 0, 0, 0.6), 0px 0px 4px 1px rgba(0, 0, 0, 0.25);
|
||||
border-radius: 3px;
|
||||
padding: 16px;
|
||||
|
|
|
@ -100,6 +100,7 @@ export default [
|
|||
'disableNS',
|
||||
'disablePolls',
|
||||
'disableProfile',
|
||||
'disableReactions',
|
||||
'disableRecordAudioNotification',
|
||||
'disableRemoteControl',
|
||||
'disableRemoteMute',
|
||||
|
@ -127,7 +128,6 @@ export default [
|
|||
'enableLayerSuspension',
|
||||
'enableLipSync',
|
||||
'enableOpusRed',
|
||||
'enableReactions',
|
||||
'enableRemb',
|
||||
'enableSaveLogs',
|
||||
'enableScreenshotCapture',
|
||||
|
|
|
@ -145,6 +145,7 @@ class BottomSheet extends PureComponent<Props> {
|
|||
renderHeader
|
||||
? _styles.sheetHeader
|
||||
: _styles.sheet,
|
||||
renderFooter && _styles.sheetFooter,
|
||||
style,
|
||||
{
|
||||
maxHeight: _height - 100
|
||||
|
@ -154,7 +155,10 @@ class BottomSheet extends PureComponent<Props> {
|
|||
<ScrollView
|
||||
bounces = { false }
|
||||
showsVerticalScrollIndicator = { false }
|
||||
style = { addScrollViewPadding && styles.scrollView } >
|
||||
style = { [
|
||||
renderFooter && _styles.sheet,
|
||||
addScrollViewPadding && styles.scrollView
|
||||
] } >
|
||||
{ this.props.children }
|
||||
</ScrollView>
|
||||
{ renderFooter && renderFooter() }
|
||||
|
|
|
@ -213,6 +213,13 @@ ColorSchemeRegistry.register('BottomSheet', {
|
|||
*/
|
||||
sheetHeader: {
|
||||
backgroundColor: BaseTheme.palette.ui02
|
||||
},
|
||||
|
||||
/**
|
||||
* Bottom sheet's background color with footer.
|
||||
*/
|
||||
sheetFooter: {
|
||||
backgroundColor: BaseTheme.palette.bottomSheet
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@ import {
|
|||
* "error" or "local" or "remote".
|
||||
* @param {string} messageDetails.timestamp - A timestamp to display for when
|
||||
* the message was received.
|
||||
* @param {string} messageDetails.isReaction - Whether or not the
|
||||
* message is a reaction message.
|
||||
* @returns {{
|
||||
* type: ADD_MESSAGE,
|
||||
* displayName: string,
|
||||
|
@ -29,6 +31,7 @@ import {
|
|||
* message: string,
|
||||
* messageType: string,
|
||||
* timestamp: string,
|
||||
* isReaction: boolean
|
||||
* }}
|
||||
*/
|
||||
export function addMessage(messageDetails: Object) {
|
||||
|
|
|
@ -69,14 +69,30 @@ export function getUnreadCount(state: Object) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
let reactionMessages = 0;
|
||||
|
||||
if (navigator.product === 'ReactNative') {
|
||||
// React native stores the messages in a reversed order.
|
||||
return messages.indexOf(lastReadMessage);
|
||||
const lastReadIndex = messages.indexOf(lastReadMessage);
|
||||
|
||||
for (let i = 0; i < lastReadIndex; i++) {
|
||||
if (messages[i].isReaction) {
|
||||
reactionMessages++;
|
||||
}
|
||||
}
|
||||
|
||||
return lastReadIndex - reactionMessages;
|
||||
}
|
||||
|
||||
const lastReadIndex = messages.lastIndexOf(lastReadMessage);
|
||||
|
||||
return messagesCount - (lastReadIndex + 1);
|
||||
for (let i = lastReadIndex + 1; i < messagesCount; i++) {
|
||||
if (messages[i].isReaction) {
|
||||
reactionMessages++;
|
||||
}
|
||||
}
|
||||
|
||||
return messagesCount - (lastReadIndex + 1) - reactionMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -68,7 +68,12 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
|
||||
switch (action.type) {
|
||||
case ADD_MESSAGE:
|
||||
unreadCount = action.hasRead ? 0 : getUnreadCount(getState()) + 1;
|
||||
unreadCount = getUnreadCount(getState());
|
||||
if (action.isReaction) {
|
||||
action.hasRead = false;
|
||||
} else {
|
||||
unreadCount = action.hasRead ? 0 : unreadCount + 1;
|
||||
}
|
||||
isOpen = getState()['features/chat'].isOpen;
|
||||
|
||||
if (typeof APP !== 'undefined') {
|
||||
|
@ -171,7 +176,7 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
message: action.message,
|
||||
privateMessage: false,
|
||||
timestamp: Date.now()
|
||||
}, false);
|
||||
}, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -270,7 +275,7 @@ function _addChatMsgListener(conference, store) {
|
|||
message: getReactionMessageFromBuffer(eventData.reactions),
|
||||
privateMessage: false,
|
||||
timestamp: eventData.timestamp
|
||||
}, false);
|
||||
}, false, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -304,11 +309,13 @@ function _handleChatError({ dispatch }, error) {
|
|||
* @param {Store} store - The Redux store.
|
||||
* @param {Object} message - The message object.
|
||||
* @param {boolean} shouldPlaySound - Whether or not to play the incoming message sound.
|
||||
* @param {boolean} isReaction - Whether or not the message is a reaction message.
|
||||
* @returns {void}
|
||||
*/
|
||||
function _handleReceivedMessage({ dispatch, getState },
|
||||
{ id, message, privateMessage, timestamp },
|
||||
shouldPlaySound = true
|
||||
shouldPlaySound = true,
|
||||
isReaction = false
|
||||
) {
|
||||
// Logic for all platforms:
|
||||
const state = getState();
|
||||
|
@ -337,7 +344,8 @@ function _handleReceivedMessage({ dispatch, getState },
|
|||
message,
|
||||
privateMessage,
|
||||
recipient: getParticipantDisplayName(state, localParticipant.id),
|
||||
timestamp: millisecondsTimestamp
|
||||
timestamp: millisecondsTimestamp,
|
||||
isReaction
|
||||
}));
|
||||
|
||||
if (typeof APP !== 'undefined') {
|
||||
|
|
|
@ -28,6 +28,7 @@ ReducerRegistry.register('features/chat', (state = DEFAULT_STATE, action) => {
|
|||
displayName: action.displayName,
|
||||
error: action.error,
|
||||
id: action.id,
|
||||
isReaction: action.isReaction,
|
||||
messageType: action.messageType,
|
||||
message: action.message,
|
||||
privateMessage: action.privateMessage,
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
createToolbarEvent,
|
||||
sendAnalytics
|
||||
} from '../../../analytics';
|
||||
import { isMobileBrowser } from '../../../base/environment/utils';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { getLocalParticipant, participantUpdated } from '../../../base/participants';
|
||||
import { connect } from '../../../base/redux';
|
||||
|
@ -21,34 +22,39 @@ import ReactionButton from './ReactionButton';
|
|||
type Props = {
|
||||
|
||||
/**
|
||||
* Used for translation.
|
||||
* Docks the toolbox
|
||||
*/
|
||||
t: Function,
|
||||
_dockToolbox: Function,
|
||||
|
||||
/**
|
||||
* Whether or not the local participant's hand is raised.
|
||||
* Whether or not it's a mobile browser.
|
||||
*/
|
||||
_raisedHand: boolean,
|
||||
_isMobile: boolean,
|
||||
|
||||
/**
|
||||
* The ID of the local participant.
|
||||
*/
|
||||
_localParticipantID: String,
|
||||
|
||||
/**
|
||||
* Whether or not the local participant's hand is raised.
|
||||
*/
|
||||
_raisedHand: boolean,
|
||||
|
||||
/**
|
||||
* The Redux Dispatch function.
|
||||
*/
|
||||
dispatch: Function,
|
||||
|
||||
/**
|
||||
* Docks the toolbox
|
||||
*/
|
||||
_dockToolbox: Function,
|
||||
|
||||
/**
|
||||
* Whether or not it's displayed in the overflow menu.
|
||||
*/
|
||||
overflowMenu: boolean
|
||||
overflowMenu: boolean,
|
||||
|
||||
/**
|
||||
* Used for translation.
|
||||
*/
|
||||
t: Function
|
||||
};
|
||||
|
||||
declare var APP: Object;
|
||||
|
@ -177,13 +183,14 @@ class ReactionsMenu extends Component<Props> {
|
|||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const { _raisedHand, t, overflowMenu } = this.props;
|
||||
const { _raisedHand, t, overflowMenu, _isMobile } = this.props;
|
||||
|
||||
return (
|
||||
<div className = { `reactions-menu ${overflowMenu ? 'overflow' : ''}` }>
|
||||
<div className = 'reactions-row'>
|
||||
{ this._getReactionButtons() }
|
||||
</div>
|
||||
{_isMobile && (
|
||||
<div className = 'raise-hand-row'>
|
||||
<ReactionButton
|
||||
accessibilityLabel = { t('toolbar.accessibilityLabel.raiseHand') }
|
||||
|
@ -196,6 +203,7 @@ class ReactionsMenu extends Component<Props> {
|
|||
onClick = { this._onToolbarToggleRaiseHand }
|
||||
toggled = { true } />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -212,6 +220,7 @@ function mapStateToProps(state) {
|
|||
|
||||
return {
|
||||
_localParticipantID: localParticipant.id,
|
||||
_isMobile: isMobileBrowser(),
|
||||
_raisedHand: localParticipant.raisedHand
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2,14 +2,16 @@
|
|||
|
||||
import React from 'react';
|
||||
|
||||
import { isMobileBrowser } from '../../../base/environment/utils';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { IconRaisedHand } from '../../../base/icons';
|
||||
import { IconArrowUp, IconRaisedHand } from '../../../base/icons';
|
||||
import { getLocalParticipant } from '../../../base/participants';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { ToolboxButtonWithIcon } from '../../../base/toolbox/components';
|
||||
import ToolbarButton from '../../../toolbox/components/web/ToolbarButton';
|
||||
import { toggleReactionsMenuVisibility } from '../../actions.web';
|
||||
import { type ReactionEmojiProps } from '../../constants';
|
||||
import { getReactionsQueue } from '../../functions.any';
|
||||
import { getReactionsQueue, isReactionsEnabled } from '../../functions.any';
|
||||
import { getReactionsMenuVisibility } from '../../functions.web';
|
||||
|
||||
import ReactionEmoji from './ReactionEmoji';
|
||||
|
@ -18,34 +20,44 @@ import ReactionsMenuPopup from './ReactionsMenuPopup';
|
|||
type Props = {
|
||||
|
||||
/**
|
||||
* Used for translation.
|
||||
* Whether or not reactions are enabled.
|
||||
*/
|
||||
t: Function,
|
||||
_reactionsEnabled: Boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the local participant's hand is raised.
|
||||
* Redux dispatch function.
|
||||
*/
|
||||
raisedHand: boolean,
|
||||
dispatch: Function,
|
||||
|
||||
/**
|
||||
* Click handler for the reaction button. Toggles the reactions menu.
|
||||
* Click handler for raise hand functionality.
|
||||
*/
|
||||
onReactionsClick: Function,
|
||||
handleClick: Function,
|
||||
|
||||
/**
|
||||
* Whether or not the reactions menu is open.
|
||||
*/
|
||||
isOpen: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not it's a mobile browser.
|
||||
*/
|
||||
isMobile: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the local participant's hand is raised.
|
||||
*/
|
||||
raisedHand: boolean,
|
||||
|
||||
/**
|
||||
* The array of reactions to be displayed.
|
||||
*/
|
||||
reactionsQueue: Array<ReactionEmojiProps>,
|
||||
|
||||
/**
|
||||
* Redux dispatch function.
|
||||
* Used for translation.
|
||||
*/
|
||||
dispatch: Function
|
||||
t: Function
|
||||
};
|
||||
|
||||
|
||||
|
@ -57,11 +69,14 @@ declare var APP: Object;
|
|||
* @returns {ReactElement}
|
||||
*/
|
||||
function ReactionsMenuButton({
|
||||
t,
|
||||
raisedHand,
|
||||
_reactionsEnabled,
|
||||
dispatch,
|
||||
handleClick,
|
||||
isOpen,
|
||||
isMobile,
|
||||
raisedHand,
|
||||
reactionsQueue,
|
||||
dispatch
|
||||
t
|
||||
}: Props) {
|
||||
|
||||
/**
|
||||
|
@ -73,16 +88,32 @@ function ReactionsMenuButton({
|
|||
dispatch(toggleReactionsMenuVisibility());
|
||||
}
|
||||
|
||||
const raiseHandButton = (<ToolbarButton
|
||||
accessibilityLabel = { t('toolbar.accessibilityLabel.raiseHand') }
|
||||
icon = { IconRaisedHand }
|
||||
key = 'raise-hand'
|
||||
onClick = { handleClick }
|
||||
toggled = { raisedHand }
|
||||
tooltip = { t('toolbar.raiseHand') } />);
|
||||
|
||||
return (
|
||||
<div className = 'reactions-menu-popup-container'>
|
||||
<ReactionsMenuPopup>
|
||||
<ToolbarButton
|
||||
accessibilityLabel = { t('toolbar.accessibilityLabel.reactionsMenu') }
|
||||
icon = { IconRaisedHand }
|
||||
key = 'reactions'
|
||||
onClick = { toggleReactionsMenu }
|
||||
toggled = { raisedHand }
|
||||
tooltip = { t(`toolbar.${isOpen ? 'closeReactionsMenu' : 'openReactionsMenu'}`) } />
|
||||
{!_reactionsEnabled || isMobile ? raiseHandButton
|
||||
: (
|
||||
<ToolboxButtonWithIcon
|
||||
ariaControls = 'reactions-menu-dialog'
|
||||
ariaExpanded = { isOpen }
|
||||
ariaHasPopup = { true }
|
||||
ariaLabel = { t('toolbar.accessibilityLabel.reactionsMenu') }
|
||||
icon = { IconArrowUp }
|
||||
iconDisabled = { false }
|
||||
iconId = 'reactions-menu-button'
|
||||
iconTooltip = { t(`toolbar.${isOpen ? 'closeReactionsMenu' : 'openReactionsMenu'}`) }
|
||||
onIconClick = { toggleReactionsMenu }>
|
||||
{raiseHandButton}
|
||||
</ToolboxButtonWithIcon>
|
||||
)}
|
||||
</ReactionsMenuPopup>
|
||||
{reactionsQueue.map(({ reaction, uid }, index) => (<ReactionEmoji
|
||||
index = { index }
|
||||
|
@ -103,7 +134,9 @@ function mapStateToProps(state) {
|
|||
const localParticipant = getLocalParticipant(state);
|
||||
|
||||
return {
|
||||
_reactionsEnabled: isReactionsEnabled(state),
|
||||
isOpen: getReactionsMenuVisibility(state),
|
||||
isMobile: isMobileBrowser(),
|
||||
reactionsQueue: getReactionsQueue(state),
|
||||
raisedHand: localParticipant?.raisedHand
|
||||
};
|
||||
|
|
|
@ -151,11 +151,11 @@ export function getReactionsSoundsThresholds(reactions: Array<string>) {
|
|||
* @returns {boolean}
|
||||
*/
|
||||
export function isReactionsEnabled(state: Object) {
|
||||
const { enableReactions } = state['features/base/config'];
|
||||
const { disableReactions } = state['features/base/config'];
|
||||
|
||||
if (navigator.product === 'ReactNative') {
|
||||
return enableReactions && getFeatureFlag(state, REACTIONS_ENABLED, true);
|
||||
return !disableReactions && getFeatureFlag(state, REACTIONS_ENABLED, true);
|
||||
}
|
||||
|
||||
return enableReactions;
|
||||
return !disableReactions;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,11 @@ declare var APP: Object;
|
|||
export type Props = {
|
||||
...$Exact<AbstractDialogTabProps>,
|
||||
|
||||
/**
|
||||
* Whether or not the reactions feature is enabled.
|
||||
*/
|
||||
enableReactions: Boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the sound for the incoming message should play.
|
||||
*/
|
||||
|
@ -40,11 +45,6 @@ export type Props = {
|
|||
*/
|
||||
soundsReactions: Boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the reactions feature is enabled.
|
||||
*/
|
||||
enableReactions: Boolean,
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
import { toState } from '../base/redux';
|
||||
import { parseStandardURIString } from '../base/util';
|
||||
import { isFollowMeActive } from '../follow-me';
|
||||
import { isReactionsEnabled } from '../reactions/functions.any';
|
||||
|
||||
import { SS_DEFAULT_FRAME_RATE, SS_SUPPORTED_FRAMERATES } from './constants';
|
||||
|
||||
|
@ -174,7 +175,7 @@ export function getSoundsTabProps(stateful: Object | Function) {
|
|||
soundsTalkWhileMuted,
|
||||
soundsReactions
|
||||
} = state['features/base/settings'];
|
||||
const { enableReactions } = state['features/base/config'];
|
||||
const enableReactions = isReactionsEnabled(state);
|
||||
|
||||
return {
|
||||
soundsIncomingMessage,
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { IconRaisedHand } from '../../../base/icons';
|
||||
import { getLocalParticipant } from '../../../base/participants';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
|
||||
|
||||
type Props = AbstractButtonProps & {
|
||||
|
||||
/**
|
||||
* Whether or not the local participant's hand is raised.
|
||||
*/
|
||||
_raisedHand: boolean,
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of a button for toggling raise hand functionality.
|
||||
*/
|
||||
class RaiseHandButton extends AbstractButton<Props, *> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.raiseHand';
|
||||
icon = IconRaisedHand
|
||||
label = 'toolbar.raiseYourHand';
|
||||
toggledLabel = 'toolbar.lowerYourHand'
|
||||
|
||||
/**
|
||||
* Retrieves tooltip dynamically.
|
||||
*/
|
||||
get tooltip() {
|
||||
return this.props._raisedHand ? 'toolbar.lowerYourHand' : 'toolbar.raiseYourHand';
|
||||
}
|
||||
|
||||
/**
|
||||
* Required by linter due to AbstractButton overwritten prop being writable.
|
||||
*
|
||||
* @param {string} value - The value.
|
||||
*/
|
||||
set tooltip(value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button, and opens the appropriate dialog.
|
||||
*
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this button is in toggled state or not.
|
||||
*
|
||||
* @override
|
||||
* @protected
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_isToggled() {
|
||||
return this.props._raisedHand;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that maps parts of Redux state tree into component props.
|
||||
*
|
||||
* @param {Object} state - Redux state.
|
||||
* @returns {Object}
|
||||
*/
|
||||
const mapStateToProps = state => {
|
||||
const localParticipant = getLocalParticipant(state);
|
||||
|
||||
return {
|
||||
_raisedHand: localParticipant.raisedHand
|
||||
};
|
||||
};
|
||||
|
||||
export default translate(connect(mapStateToProps)(RaiseHandButton));
|
|
@ -17,7 +17,6 @@ import { translate } from '../../../base/i18n';
|
|||
import JitsiMeetJS from '../../../base/lib-jitsi-meet';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
getParticipantCount,
|
||||
haveParticipantWithScreenSharingFeature,
|
||||
raiseHand
|
||||
} from '../../../base/participants';
|
||||
|
@ -87,7 +86,6 @@ import AudioSettingsButton from './AudioSettingsButton';
|
|||
import FullscreenButton from './FullscreenButton';
|
||||
import OverflowMenuButton from './OverflowMenuButton';
|
||||
import ProfileButton from './ProfileButton';
|
||||
import RaiseHandButton from './RaiseHandButton';
|
||||
import Separator from './Separator';
|
||||
import ShareDesktopButton from './ShareDesktopButton';
|
||||
import ToggleCameraButton from './ToggleCameraButton';
|
||||
|
@ -180,11 +178,6 @@ type Props = {
|
|||
*/
|
||||
_overflowMenuVisible: boolean,
|
||||
|
||||
/**
|
||||
* Number of participants in the conference.
|
||||
*/
|
||||
_participantCount: number,
|
||||
|
||||
/**
|
||||
* Whether or not the participants pane is open.
|
||||
*/
|
||||
|
@ -254,16 +247,12 @@ type Props = {
|
|||
|
||||
declare var APP: Object;
|
||||
|
||||
type State = {
|
||||
reactionsShortcutsRegistered: boolean
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements the conference toolbox on React/Web.
|
||||
*
|
||||
* @extends Component
|
||||
*/
|
||||
class Toolbox extends Component<Props, State> {
|
||||
class Toolbox extends Component<Props> {
|
||||
/**
|
||||
* Initializes a new {@code Toolbox} instance.
|
||||
*
|
||||
|
@ -273,10 +262,6 @@ class Toolbox extends Component<Props, State> {
|
|||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
reactionsShortcutsRegistered: false
|
||||
};
|
||||
|
||||
// Bind event handlers so they are only bound once per instance.
|
||||
this._onMouseOut = this._onMouseOut.bind(this);
|
||||
this._onMouseOver = this._onMouseOver.bind(this);
|
||||
|
@ -306,7 +291,7 @@ class Toolbox extends Component<Props, State> {
|
|||
* @returns {void}
|
||||
*/
|
||||
componentDidMount() {
|
||||
const { _toolbarButtons, t, dispatch, _reactionsEnabled, _participantCount } = this.props;
|
||||
const { _toolbarButtons, t, dispatch, _reactionsEnabled } = this.props;
|
||||
const KEYBOARD_SHORTCUTS = [
|
||||
isToolbarButtonEnabled('videoquality', _toolbarButtons) && {
|
||||
character: 'A',
|
||||
|
@ -355,7 +340,7 @@ class Toolbox extends Component<Props, State> {
|
|||
}
|
||||
});
|
||||
|
||||
if (_reactionsEnabled && _participantCount > 1) {
|
||||
if (_reactionsEnabled) {
|
||||
const REACTION_SHORTCUTS = Object.keys(REACTIONS).map(key => {
|
||||
const onShortcutSendReaction = () => {
|
||||
dispatch(addReactionToBuffer(key));
|
||||
|
@ -389,7 +374,7 @@ class Toolbox extends Component<Props, State> {
|
|||
* @inheritdoc
|
||||
*/
|
||||
componentDidUpdate(prevProps) {
|
||||
const { _dialog, _reactionsEnabled, _participantCount, dispatch, t } = this.props;
|
||||
const { _dialog, dispatch } = this.props;
|
||||
|
||||
|
||||
if (prevProps._overflowMenuVisible
|
||||
|
@ -398,41 +383,6 @@ class Toolbox extends Component<Props, State> {
|
|||
this._onSetOverflowVisible(false);
|
||||
dispatch(setToolbarHovered(false));
|
||||
}
|
||||
|
||||
if (!this.state.reactionsShortcutsRegistered
|
||||
&& (prevProps._reactionsEnabled !== _reactionsEnabled
|
||||
|| prevProps._participantCount !== _participantCount)) {
|
||||
if (_reactionsEnabled && _participantCount > 1) {
|
||||
// eslint-disable-next-line react/no-did-update-set-state
|
||||
this.setState({
|
||||
reactionsShortcutsRegistered: true
|
||||
});
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -445,7 +395,7 @@ class Toolbox extends Component<Props, State> {
|
|||
[ 'A', 'C', 'D', 'R', 'S' ].forEach(letter =>
|
||||
APP.keyboardshortcut.unregisterShortcut(letter));
|
||||
|
||||
if (this.props._reactionsEnabled && this.state.reactionsShortcutsRegistered) {
|
||||
if (this.props._reactionsEnabled) {
|
||||
Object.keys(REACTIONS).map(key => REACTIONS[key].shortcutChar)
|
||||
.forEach(letter =>
|
||||
APP.keyboardshortcut.unregisterShortcut(letter, true));
|
||||
|
@ -613,8 +563,7 @@ class Toolbox extends Component<Props, State> {
|
|||
const {
|
||||
_feedbackConfigured,
|
||||
_isMobile,
|
||||
_screenSharing,
|
||||
_reactionsEnabled
|
||||
_screenSharing
|
||||
} = this.props;
|
||||
|
||||
const microphone = {
|
||||
|
@ -651,8 +600,8 @@ class Toolbox extends Component<Props, State> {
|
|||
|
||||
const raisehand = {
|
||||
key: 'raisehand',
|
||||
Content: _reactionsEnabled ? ReactionsMenuButton : RaiseHandButton,
|
||||
handleClick: _reactionsEnabled ? null : this._onToolbarToggleRaiseHand,
|
||||
Content: ReactionsMenuButton,
|
||||
handleClick: this._onToolbarToggleRaiseHand,
|
||||
group: 2
|
||||
};
|
||||
|
||||
|
@ -1388,7 +1337,6 @@ function _mapStateToProps(state, ownProps) {
|
|||
_localParticipantID: localParticipant?.id,
|
||||
_localVideo: localVideo,
|
||||
_overflowMenuVisible: overflowMenuVisible,
|
||||
_participantCount: getParticipantCount(state),
|
||||
_participantsPaneOpen: getParticipantsPaneOpen(state),
|
||||
_raisedHand: localParticipant?.raisedHand,
|
||||
_reactionsEnabled: isReactionsEnabled(state),
|
||||
|
|
Loading…
Reference in New Issue