fix(reactions) Moved reactions behind feature flag

pull/9602/head jitsi-meet_6108
robertpin 3 years ago committed by GitHub
parent 2209394d09
commit 2d04f3852c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      config.js
  2. 10
      css/_reactions-menu.scss
  3. 6
      react/features/base/flags/constants.js
  4. 19
      react/features/toolbox/components/native/OverflowMenu.js
  5. 105
      react/features/toolbox/components/native/RaiseHandButton.js
  6. 21
      react/features/toolbox/components/native/Toolbox.js
  7. 83
      react/features/toolbox/components/web/RaiseHandButton.js
  8. 103
      react/features/toolbox/components/web/Toolbox.js

@ -70,6 +70,9 @@ var config = {
// callStatsThreshold: 5 // enable callstats for 5% of the users.
},
// Enables reactions feature.
enableReactions: false,
// Disables ICE/UDP by filtering out local and remote UDP candidates in
// signalling.
// webrtcIceUdpDisable: false,

@ -90,7 +90,7 @@
width: 20%;
bottom: 0;
left: 40%;
height: 48px;
height: 0;
}
.reactions-menu-popup-container,
@ -111,8 +111,8 @@ $reactionCount: 20;
line-height: 32px;
width: 32px;
height: 32px;
top: 32px;
left: 10px;
top: 0;
left: 20px;
opacity: 0;
z-index: 1;
@ -123,8 +123,8 @@ $reactionCount: 20;
@for $i from 1 through $reactionCount {
&.reaction-#{$i} {
animation: animation-#{$i} 5s forwards ease-in-out;
top: #{random(50, 0)}px;
left: #{random(-10, 10)}px;
top: #{random(-40, 10)}px;
left: #{random(0, 30)}px;
}
}
}

@ -214,3 +214,9 @@ export const VIDEO_SHARE_BUTTON_ENABLED = 'video-share.enabled';
* Default: disabled (false).
*/
export const WELCOME_PAGE_ENABLED = 'welcomepage.enabled';
/**
* Flag indicating if the reactions feature should be enabled.
* Default: disabled (false).
*/
export const REACTIONS_ENABLED = 'reactions.enabled';

@ -4,6 +4,7 @@ import React, { PureComponent } from 'react';
import { ColorSchemeRegistry } from '../../../base/color-scheme';
import { BottomSheet, hideDialog, isDialogOpen } from '../../../base/dialog';
import { getFeatureFlag, REACTIONS_ENABLED } from '../../../base/flags';
import { connect } from '../../../base/redux';
import { StyleType } from '../../../base/styles';
import { SharedDocumentButton } from '../../../etherpad';
@ -22,6 +23,7 @@ import MuteEveryoneButton from '../MuteEveryoneButton';
import MuteEveryonesVideoButton from '../MuteEveryonesVideoButton';
import AudioOnlyButton from './AudioOnlyButton';
import RaiseHandButton from './RaiseHandButton';
import ScreenSharingButton from './ScreenSharingButton.js';
import ToggleCameraButton from './ToggleCameraButton';
@ -50,6 +52,11 @@ type Props = {
*/
_width: number,
/**
* Whether or not the reactions feature is enabled.
*/
_reactionsEnabled: boolean,
/**
* Used for hiding the dialog when the selection was completed.
*/
@ -102,7 +109,7 @@ class OverflowMenu extends PureComponent<Props, State> {
* @returns {ReactElement}
*/
render() {
const { _bottomSheetStyles, _width } = this.props;
const { _bottomSheetStyles, _width, _reactionsEnabled } = this.props;
const toolbarButtons = getMovableButtons(_width);
const buttonProps = {
@ -128,13 +135,14 @@ class OverflowMenu extends PureComponent<Props, State> {
return (
<BottomSheet
onCancel = { this._onCancel }
renderFooter = { toolbarButtons.has('raisehand')
? null
: this._renderReactionMenu }>
renderFooter = { _reactionsEnabled && !toolbarButtons.has('raisehand')
? this._renderReactionMenu
: null }>
<AudioRouteButton { ...topButtonProps } />
<ParticipantsPaneButton { ...buttonProps } />
{!toolbarButtons.has('invite') && <InviteButton { ...buttonProps } />}
<AudioOnlyButton { ...buttonProps } />
{!_reactionsEnabled && !toolbarButtons.has('raisehand') && <RaiseHandButton { ...buttonProps } />}
<SecurityDialogButton { ...buttonProps } />
<ScreenSharingButton { ...buttonProps } />
{!toolbarButtons.has('togglecamera') && <ToggleCameraButton { ...buttonProps } />}
@ -194,7 +202,8 @@ function _mapStateToProps(state) {
return {
_bottomSheetStyles: ColorSchemeRegistry.get(state, 'BottomSheet'),
_isOpen: isDialogOpen(state, OverflowMenu_),
_width: state['features/base/responsive-ui'].clientWidth
_width: state['features/base/responsive-ui'].clientWidth,
_reactionsEnabled: getFeatureFlag(state, REACTIONS_ENABLED, false)
};
}

@ -0,0 +1,105 @@
// @flow
import { type Dispatch } from 'redux';
import {
createToolbarEvent,
sendAnalytics
} from '../../../analytics';
import { RAISE_HAND_ENABLED, getFeatureFlag } from '../../../base/flags';
import { translate } from '../../../base/i18n';
import { IconRaisedHand } from '../../../base/icons';
import {
getLocalParticipant,
raiseHand
} from '../../../base/participants';
import { connect } from '../../../base/redux';
import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
/**
* The type of the React {@code Component} props of {@link RaiseHandButton}.
*/
type Props = AbstractButtonProps & {
/**
* The local participant.
*/
_localParticipant: Object,
/**
* Whether the participant raused their hand or not.
*/
_raisedHand: boolean,
/**
* The redux {@code dispatch} function.
*/
dispatch: Dispatch<any>
};
/**
* An implementation of a button to raise or lower hand.
*/
class RaiseHandButton extends AbstractButton<Props, *> {
accessibilityLabel = 'toolbar.accessibilityLabel.raiseHand';
icon = IconRaisedHand;
label = 'toolbar.raiseYourHand';
toggledLabel = 'toolbar.lowerYourHand';
/**
* Handles clicking / pressing the button.
*
* @override
* @protected
* @returns {void}
*/
_handleClick() {
this._toggleRaisedHand();
}
/**
* Indicates whether this button is in toggled state or not.
*
* @override
* @protected
* @returns {boolean}
*/
_isToggled() {
return this.props._raisedHand;
}
/**
* Toggles the rased hand status of the local participant.
*
* @returns {void}
*/
_toggleRaisedHand() {
const enable = !this.props._raisedHand;
sendAnalytics(createToolbarEvent('raise.hand', { enable }));
this.props.dispatch(raiseHand(enable));
}
}
/**
* Maps part of the Redux state to the props of this component.
*
* @param {Object} state - The Redux state.
* @param {Object} ownProps - The properties explicitly passed to the component instance.
* @private
* @returns {Props}
*/
function _mapStateToProps(state, ownProps): Object {
const _localParticipant = getLocalParticipant(state);
const enabled = getFeatureFlag(state, RAISE_HAND_ENABLED, true);
const { visible = enabled } = ownProps;
return {
_localParticipant,
_raisedHand: _localParticipant.raisedHand,
visible
};
}
export default translate(connect(_mapStateToProps)(RaiseHandButton));

@ -4,6 +4,7 @@ import React from 'react';
import { SafeAreaView, View } from 'react-native';
import { ColorSchemeRegistry } from '../../../base/color-scheme';
import { getFeatureFlag, REACTIONS_ENABLED } from '../../../base/flags';
import { connect } from '../../../base/redux';
import { StyleType } from '../../../base/styles';
import { ChatButton } from '../../../chat';
@ -16,6 +17,7 @@ import HangupButton from '../HangupButton';
import VideoMuteButton from '../VideoMuteButton';
import OverflowMenuButton from './OverflowMenuButton';
import RaiseHandButton from './RaiseHandButton';
import ToggleCameraButton from './ToggleCameraButton';
import styles from './styles';
@ -39,6 +41,11 @@ type Props = {
*/
_width: number,
/**
* Whether or not the reactions feature is enabled.
*/
_reactionsEnabled: boolean,
/**
* The redux {@code dispatch} function.
*/
@ -56,7 +63,7 @@ function Toolbox(props: Props) {
return null;
}
const { _styles, _width } = props;
const { _styles, _width, _reactionsEnabled } = props;
const { buttonStylesBorderless, hangupButtonStyles, toggledButtonStyles } = _styles;
const additionalButtons = getMovableButtons(_width);
const backgroundToggledStyle = {
@ -86,10 +93,13 @@ function Toolbox(props: Props) {
styles = { buttonStylesBorderless }
toggledStyles = { backgroundToggledStyle } />}
{ additionalButtons.has('raisehand')
&& <ReactionsMenuButton
{ additionalButtons.has('raisehand') && (_reactionsEnabled
? <ReactionsMenuButton
styles = { buttonStylesBorderless }
toggledStyles = { backgroundToggledStyle } />
: <RaiseHandButton
styles = { buttonStylesBorderless }
toggledStyles = { backgroundToggledStyle } />}
toggledStyles = { backgroundToggledStyle } />)}
{additionalButtons.has('tileview') && <TileViewButton styles = { buttonStylesBorderless } />}
{additionalButtons.has('invite') && <InviteButton styles = { buttonStylesBorderless } />}
{additionalButtons.has('togglecamera')
@ -119,7 +129,8 @@ function _mapStateToProps(state: Object): Object {
return {
_styles: ColorSchemeRegistry.get(state, 'Toolbox'),
_visible: isToolboxVisible(state),
_width: state['features/base/responsive-ui'].clientWidth
_width: state['features/base/responsive-ui'].clientWidth,
_reactionsEnabled: getFeatureFlag(state, REACTIONS_ENABLED, false)
};
}

@ -0,0 +1,83 @@
// @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,
/**
* External handler for click action.
*/
handleClick: Function
};
/**
* 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() {
this.props.handleClick();
}
/**
* 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));

@ -85,6 +85,7 @@ 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 VideoSettingsButton from './VideoSettingsButton';
@ -213,7 +214,12 @@ type Props = {
/**
* Returns the selected virtual source object.
*/
_virtualSource: Object,
_virtualSource: Object,
/**
* Whether or not reactions feature is enabled.
*/
_reactionsEnabled: boolean,
/**
* Invoked to active other features of the app.
@ -259,6 +265,7 @@ class Toolbox extends Component<Props> {
this._onToolbarOpenVideoQuality = this._onToolbarOpenVideoQuality.bind(this);
this._onToolbarToggleChat = this._onToolbarToggleChat.bind(this);
this._onToolbarToggleFullScreen = this._onToolbarToggleFullScreen.bind(this);
this._onToolbarToggleRaiseHand = this._onToolbarToggleRaiseHand.bind(this);
this._onToolbarToggleScreenshare = this._onToolbarToggleScreenshare.bind(this);
this._onShortcutToggleTileView = this._onShortcutToggleTileView.bind(this);
this._onEscKey = this._onEscKey.bind(this);
@ -271,7 +278,7 @@ class Toolbox extends Component<Props> {
* @returns {void}
*/
componentDidMount() {
const { _toolbarButtons, t, dispatch } = this.props;
const { _toolbarButtons, t, dispatch, _reactionsEnabled } = this.props;
const KEYBOARD_SHORTCUTS = [
isToolbarButtonEnabled('videoquality', _toolbarButtons) && {
character: 'A',
@ -320,30 +327,32 @@ class Toolbox extends Component<Props> {
}
});
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);
});
if (_reactionsEnabled) {
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);
});
}
}
/**
@ -375,9 +384,11 @@ class Toolbox extends Component<Props> {
[ 'A', 'C', 'D', 'R', 'S' ].forEach(letter =>
APP.keyboardshortcut.unregisterShortcut(letter));
Object.keys(REACTIONS).map(key => REACTIONS[key].shortcutChar)
.forEach(letter =>
APP.keyboardshortcut.unregisterShortcut(letter, true));
if (this.props._reactionsEnabled) {
Object.keys(REACTIONS).map(key => REACTIONS[key].shortcutChar)
.forEach(letter =>
APP.keyboardshortcut.unregisterShortcut(letter, true));
}
}
/**
@ -541,7 +552,8 @@ class Toolbox extends Component<Props> {
const {
_feedbackConfigured,
_isMobile,
_screenSharing
_screenSharing,
_reactionsEnabled
} = this.props;
const microphone = {
@ -578,7 +590,8 @@ class Toolbox extends Component<Props> {
const raisehand = {
key: 'raisehand',
Content: ReactionsMenuButton,
Content: _reactionsEnabled ? ReactionsMenuButton : RaiseHandButton,
handleClick: _reactionsEnabled ? null : this._onToolbarToggleRaiseHand,
group: 2
};
@ -1054,6 +1067,23 @@ class Toolbox extends Component<Props> {
this._doToggleFullScreen();
}
_onToolbarToggleRaiseHand: () => void;
/**
* Creates an analytics toolbar event and dispatches an action for toggling
* raise hand.
*
* @private
* @returns {void}
*/
_onToolbarToggleRaiseHand() {
sendAnalytics(createToolbarEvent(
'raise.hand',
{ enable: !this.props._raisedHand }));
this._doToggleRaiseHand();
}
_onToolbarToggleScreenshare: () => void;
/**
@ -1131,7 +1161,8 @@ class Toolbox extends Component<Props> {
_isMobile,
_overflowMenuVisible,
_toolbarButtons,
t
t,
_reactionsEnabled
} = this.props;
const toolbarAccLabel = 'toolbar.accessibilityLabel.moreActionsMenu';
@ -1160,7 +1191,7 @@ class Toolbox extends Component<Props> {
key = 'overflow-menu'
onVisibilityChange = { this._onSetOverflowVisible }
showMobileReactions = {
overflowMenuButtons.find(({ key }) => key === 'raisehand')
_reactionsEnabled && overflowMenuButtons.find(({ key }) => key === 'raisehand')
}>
<ul
aria-label = { t(toolbarAccLabel) }
@ -1171,7 +1202,7 @@ class Toolbox extends Component<Props> {
{overflowMenuButtons.map(({ group, key, Content, ...rest }, index, arr) => {
const showSeparator = index > 0 && arr[index - 1].group !== group;
return key !== 'raisehand'
return (key !== 'raisehand' || !_reactionsEnabled)
&& <>
{showSeparator && <Separator key = { `hr${group}` } />}
<Content
@ -1218,6 +1249,7 @@ function _mapStateToProps(state) {
const localParticipant = getLocalParticipant(state);
const localVideo = getLocalVideoTrack(state['features/base/tracks']);
const { clientWidth } = state['features/base/responsive-ui'];
const { enableReactions } = state['features/base/config'];
let desktopSharingDisabledTooltipKey;
@ -1253,7 +1285,8 @@ function _mapStateToProps(state) {
_screenSharing: isScreenVideoShared(state),
_toolbarButtons: getToolbarButtons(state),
_visible: isToolboxVisible(state),
_visibleButtons: getToolbarButtons(state)
_visibleButtons: getToolbarButtons(state),
_reactionsEnabled: enableReactions
};
}

Loading…
Cancel
Save