From e5b563ba46f168b622bf4ccdae1695b438bc7487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Wed, 29 Apr 2020 09:35:18 +0200 Subject: [PATCH] rn,flags: add more feature flags to toggle specific behavior - Invite funcionality (altogether) - Recording - Live streaming - Meeting name - Meeting password --- react/features/base/flags/constants.js | 32 ++++++++++++++++++ .../chat/components/native/ChatButton.js | 13 +++++--- .../native/LonelyMeetingExperience.js | 6 ++-- .../components/native/NavigationBar.js | 25 +++++++++----- react/features/invite/actions.native.js | 4 +-- .../add-people-dialog/native/InviteButton.js | 4 ++- .../LiveStream/native/LiveStreamButton.js | 24 ++++++++++++-- .../Recording/native/RecordButton.js | 27 +++++++++++++-- .../room-lock/components/RoomLockButton.js | 17 +++++----- .../toolbox/components/native/OverflowMenu.js | 11 ++----- .../toolbox/components/native/Toolbox.js | 33 +++---------------- 11 files changed, 129 insertions(+), 67 deletions(-) diff --git a/react/features/base/flags/constants.js b/react/features/base/flags/constants.js index e5d122e77..7cb793cfc 100644 --- a/react/features/base/flags/constants.js +++ b/react/features/base/flags/constants.js @@ -1,5 +1,11 @@ // @flow +/** + * Flag indicating if add-people functionality should be enabled. + * Default: enabled (true). + */ +export const ADD_PEOPLE_ENABLED = 'add-people.enabled'; + /** * Flag indicating if calendar integration should be enabled. * Default: enabled (true) on Android, auto-detected on iOS. @@ -37,12 +43,38 @@ export const INVITE_ENABLED = 'invite.enabled'; */ export const IOS_RECORDING_ENABLED = 'ios.recording.enabled'; +/** + * Flag indicating if live-streaming should be enabled. + * Default: auto-detected. + */ +export const LIVE_STREAMING_ENABLED = 'live-streaming.enabled'; + +/** + * Flag indicating if displaying the meeting name should be enabled. + * Default: enabled (true). + */ +export const MEETING_NAME_ENABLED = 'meeting-name.enabled'; + +/** + * Flag indicating if the meeting password button should be enabled. + * Note that this flag just decides on the buttton, if a meeting has a password + * set, the password ddialog will still show up. + * Default: enabled (true). + */ +export const MEETING_PASSWORD_ENABLED = 'meeting-password.enabled'; + /** * Flag indicating if Picture-in-Picture should be enabled. * Default: auto-detected. */ export const PIP_ENABLED = 'pip.enabled'; +/** + * Flag indicating if recording should be enabled. + * Default: auto-detected. + */ +export const RECORDING_ENABLED = 'recording.enabled'; + /** * Flag indicating if the welcome page should be enabled. * Default: disabled (false). diff --git a/react/features/chat/components/native/ChatButton.js b/react/features/chat/components/native/ChatButton.js index 0e2f675e2..681562ed1 100644 --- a/react/features/chat/components/native/ChatButton.js +++ b/react/features/chat/components/native/ChatButton.js @@ -1,5 +1,6 @@ // @flow +import { CHAT_ENABLED, getFeatureFlag } from '../../../base/flags'; import { IconChat, IconChatUnread } from '../../../base/icons'; import { setActiveModalId } from '../../../base/modal'; import { getLocalParticipant } from '../../../base/participants'; @@ -114,16 +115,18 @@ function _mapDispatchToProps(dispatch: Function) { * Maps part of the redux state to the component's props. * * @param {Object} state - The Redux state. - * @returns {{ - * _unreadMessageCount - * }} + * @param {Object} ownProps - The properties explicitly passed to the component instance. + * @returns {Props} */ -function _mapStateToProps(state) { +function _mapStateToProps(state, ownProps) { const localParticipant = getLocalParticipant(state); + const enabled = getFeatureFlag(state, CHAT_ENABLED, true); + const { visible = enabled } = ownProps; return { _showNamePrompt: !localParticipant.name, - _unreadMessageCount: getUnreadCount(state) + _unreadMessageCount: getUnreadCount(state), + visible }; } diff --git a/react/features/conference/components/native/LonelyMeetingExperience.js b/react/features/conference/components/native/LonelyMeetingExperience.js index 340cc2581..6731af176 100644 --- a/react/features/conference/components/native/LonelyMeetingExperience.js +++ b/react/features/conference/components/native/LonelyMeetingExperience.js @@ -4,9 +4,10 @@ import React, { PureComponent } from 'react'; import { Text, TouchableOpacity, View } from 'react-native'; import { ColorSchemeRegistry } from '../../../base/color-scheme'; +import { getFeatureFlag, INVITE_ENABLED } from '../../../base/flags'; +import { translate } from '../../../base/i18n'; import { connect } from '../../../base/redux'; import { StyleType } from '../../../base/styles'; -import { translate } from '../../../base/i18n'; import { getParticipantCount } from '../../../base/participants'; import { doInvitePeople } from '../../../invite/actions.native'; @@ -125,9 +126,10 @@ class LonelyMeetingExperience extends PureComponent { */ function _mapStateToProps(state): $Shape { const { disableInviteFunctions } = state['features/base/config']; + const flag = getFeatureFlag(state, INVITE_ENABLED, true); return { - _isInviteFunctionsDiabled: disableInviteFunctions, + _isInviteFunctionsDiabled: !flag || disableInviteFunctions, _isLonelyMeeting: getParticipantCount(state) === 1, _styles: ColorSchemeRegistry.get(state, 'Conference') }; diff --git a/react/features/conference/components/native/NavigationBar.js b/react/features/conference/components/native/NavigationBar.js index 02fecfbd7..1ca789154 100644 --- a/react/features/conference/components/native/NavigationBar.js +++ b/react/features/conference/components/native/NavigationBar.js @@ -5,6 +5,7 @@ import { SafeAreaView, Text, View } from 'react-native'; import LinearGradient from 'react-native-linear-gradient'; import { getConferenceName } from '../../../base/conference'; +import { getFeatureFlag, MEETING_NAME_ENABLED } from '../../../base/flags'; import { connect } from '../../../base/redux'; import { PictureInPictureButton } from '../../../mobile/picture-in-picture'; import { isToolboxVisible } from '../../../toolbox'; @@ -19,6 +20,11 @@ type Props = { */ _meetingName: string, + /** + * Whether displaying the current meeting name is enabled or not. + */ + _meetingNameEnabled: boolean, + /** * True if the navigation bar should be visible. */ @@ -59,11 +65,14 @@ class NavigationBar extends Component { - - { this.props._meetingName } - + { + this.props._meetingNameEnabled + && + { this.props._meetingName } + + } @@ -76,14 +85,12 @@ class NavigationBar extends Component { * Maps part of the Redux store to the props of this component. * * @param {Object} state - The Redux state. - * @returns {{ - * _meetingName: string, - * _visible: boolean - * }} + * @returns {Props} */ function _mapStateToProps(state) { return { _meetingName: getConferenceName(state), + _meetingNameEnabled: getFeatureFlag(state, MEETING_NAME_ENABLED, true), _visible: isToolboxVisible(state) }; } diff --git a/react/features/invite/actions.native.js b/react/features/invite/actions.native.js index 7ebcf6fa2..7d37e8548 100644 --- a/react/features/invite/actions.native.js +++ b/react/features/invite/actions.native.js @@ -2,7 +2,7 @@ import type { Dispatch } from 'redux'; -import { getFeatureFlag, INVITE_ENABLED } from '../base/flags'; +import { getFeatureFlag, ADD_PEOPLE_ENABLED } from '../base/flags'; import { setActiveModalId } from '../base/modal'; import { beginShareRoom } from '../share-room'; @@ -20,7 +20,7 @@ export * from './actions.any'; export function doInvitePeople() { return (dispatch: Dispatch, getState: Function) => { const state = getState(); - const addPeopleEnabled = getFeatureFlag(state, INVITE_ENABLED, true) + const addPeopleEnabled = getFeatureFlag(state, ADD_PEOPLE_ENABLED, true) && (isAddPeopleEnabled(state) || isDialOutEnabled(state)); if (addPeopleEnabled) { diff --git a/react/features/invite/components/add-people-dialog/native/InviteButton.js b/react/features/invite/components/add-people-dialog/native/InviteButton.js index a7feb8b07..181b46cde 100644 --- a/react/features/invite/components/add-people-dialog/native/InviteButton.js +++ b/react/features/invite/components/add-people-dialog/native/InviteButton.js @@ -2,6 +2,7 @@ import type { Dispatch } from 'redux'; +import { getFeatureFlag, INVITE_ENABLED } from '../../../../base/flags'; import { translate } from '../../../../base/i18n'; import { IconAddPeople } from '../../../../base/icons'; import { connect } from '../../../../base/redux'; @@ -47,9 +48,10 @@ class InviteButton extends AbstractButton { */ function _mapStateToProps(state, ownProps: Props) { const { disableInviteFunctions } = state['features/base/config']; + const flag = getFeatureFlag(state, INVITE_ENABLED, true); return { - visible: !disableInviteFunctions && ownProps.visible + visible: flag && !disableInviteFunctions && ownProps.visible }; } diff --git a/react/features/recording/components/LiveStream/native/LiveStreamButton.js b/react/features/recording/components/LiveStream/native/LiveStreamButton.js index e3035eadd..08bad8ff4 100644 --- a/react/features/recording/components/LiveStream/native/LiveStreamButton.js +++ b/react/features/recording/components/LiveStream/native/LiveStreamButton.js @@ -2,10 +2,11 @@ import { translate } from '../../../../base/i18n'; import { IconLiveStreaming } from '../../../../base/icons'; +import { LIVE_STREAMING_ENABLED, getFeatureFlag } from '../../../../base/flags'; import { connect } from '../../../../base/redux'; import AbstractLiveStreamButton, { - _mapStateToProps, + _mapStateToProps as _abstractMapStateToProps, type Props } from '../AbstractLiveStreamButton'; @@ -16,4 +17,23 @@ class LiveStreamButton extends AbstractLiveStreamButton { icon = IconLiveStreaming; } -export default translate(connect(_mapStateToProps)(LiveStreamButton)); +/** + * Maps (parts of) the redux state to the associated props for this component. + * + * @param {Object} state - The redux state. + * @param {Object} ownProps - The properties explicitly passed to the component + * instance. + * @private + * @returns {Props} + */ +export function mapStateToProps(state: Object, ownProps: Object) { + const enabled = getFeatureFlag(state, LIVE_STREAMING_ENABLED, true); + const abstractProps = _abstractMapStateToProps(state, ownProps); + + return { + ...abstractProps, + visible: enabled && abstractProps.visible + }; +} + +export default translate(connect(mapStateToProps)(LiveStreamButton)); diff --git a/react/features/recording/components/Recording/native/RecordButton.js b/react/features/recording/components/Recording/native/RecordButton.js index ffeb6a200..fd6b88055 100644 --- a/react/features/recording/components/Recording/native/RecordButton.js +++ b/react/features/recording/components/Recording/native/RecordButton.js @@ -1,11 +1,14 @@ // @flow +import { Platform } from 'react-native'; + +import { IOS_RECORDING_ENABLED, RECORDING_ENABLED, getFeatureFlag } from '../../../../base/flags'; import { translate } from '../../../../base/i18n'; import { IconToggleRecording } from '../../../../base/icons'; import { connect } from '../../../../base/redux'; import AbstractRecordButton, { - _mapStateToProps, + _mapStateToProps as _abstractMapStateToProps, type Props } from '../AbstractRecordButton'; @@ -16,4 +19,24 @@ class RecordButton extends AbstractRecordButton { icon = IconToggleRecording; } -export default translate(connect(_mapStateToProps)(RecordButton)); +/** + * Maps (parts of) the redux state to the associated props for this component. + * + * @param {Object} state - The redux state. + * @param {Object} ownProps - The properties explicitly passed to the component + * instance. + * @private + * @returns {Props} + */ +export function mapStateToProps(state: Object, ownProps: Object) { + const enabled = getFeatureFlag(state, RECORDING_ENABLED, true); + const iosEnabled = Platform.OS !== 'ios' || getFeatureFlag(state, IOS_RECORDING_ENABLED, false); + const abstractProps = _abstractMapStateToProps(state, ownProps); + + return { + ...abstractProps, + visible: enabled && iosEnabled && abstractProps.visible + }; +} + +export default translate(connect(mapStateToProps)(RecordButton)); diff --git a/react/features/room-lock/components/RoomLockButton.js b/react/features/room-lock/components/RoomLockButton.js index 015c648ce..6afd5a007 100644 --- a/react/features/room-lock/components/RoomLockButton.js +++ b/react/features/room-lock/components/RoomLockButton.js @@ -1,5 +1,6 @@ // @flow +import { MEETING_PASSWORD_ENABLED, getFeatureFlag } from '../../base/flags'; import { translate } from '../../base/i18n'; import { IconRoomLock, IconRoomUnlock } from '../../base/icons'; import { isLocalParticipantModerator } from '../../base/participants'; @@ -83,19 +84,19 @@ class RoomLockButton extends AbstractButton { * {@code RoomLockButton} component. * * @param {Object} state - The Redux state. + * @param {Object} ownProps - The properties explicitly passed to the component instance. * @private - * @returns {{ - * _localParticipantModerator: boolean, - * _locked: boolean - * }} + * @returns {Props} */ -function _mapStateToProps(state): Object { +function _mapStateToProps(state, ownProps): Object { const { conference, locked } = state['features/base/conference']; + const enabled = getFeatureFlag(state, MEETING_PASSWORD_ENABLED, true); + const { visible = enabled } = ownProps; return { - _localParticipantModerator: - Boolean(conference && isLocalParticipantModerator(state)), - _locked: Boolean(conference && locked) + _localParticipantModerator: Boolean(conference && isLocalParticipantModerator(state)), + _locked: Boolean(conference && locked), + visible }; } diff --git a/react/features/toolbox/components/native/OverflowMenu.js b/react/features/toolbox/components/native/OverflowMenu.js index 091ddc7c2..a2442d171 100644 --- a/react/features/toolbox/components/native/OverflowMenu.js +++ b/react/features/toolbox/components/native/OverflowMenu.js @@ -1,12 +1,11 @@ // @flow import React, { PureComponent } from 'react'; -import { Platform, TouchableOpacity, View } from 'react-native'; +import { TouchableOpacity, View } from 'react-native'; import Collapsible from 'react-native-collapsible'; import { ColorSchemeRegistry } from '../../../base/color-scheme'; import { BottomSheet, hideDialog, isDialogOpen } from '../../../base/dialog'; -import { IOS_RECORDING_ENABLED, getFeatureFlag } from '../../../base/flags'; import { IconDragHandle } from '../../../base/icons'; import { connect } from '../../../base/redux'; import { StyleType } from '../../../base/styles'; @@ -134,10 +133,7 @@ class OverflowMenu extends PureComponent { - { - this.props._recordingEnabled - && - } + @@ -240,8 +236,7 @@ class OverflowMenu extends PureComponent { function _mapStateToProps(state) { return { _bottomSheetStyles: ColorSchemeRegistry.get(state, 'BottomSheet'), - _isOpen: isDialogOpen(state, OverflowMenu_), - _recordingEnabled: Platform.OS !== 'ios' || getFeatureFlag(state, IOS_RECORDING_ENABLED) + _isOpen: isDialogOpen(state, OverflowMenu_) }; } diff --git a/react/features/toolbox/components/native/Toolbox.js b/react/features/toolbox/components/native/Toolbox.js index 13e5bebca..c688df37f 100644 --- a/react/features/toolbox/components/native/Toolbox.js +++ b/react/features/toolbox/components/native/Toolbox.js @@ -4,12 +4,10 @@ import React, { PureComponent } from 'react'; import { View } from 'react-native'; import { ColorSchemeRegistry } from '../../../base/color-scheme'; -import { CHAT_ENABLED, getFeatureFlag } from '../../../base/flags'; import { Container } from '../../../base/react'; import { connect } from '../../../base/redux'; import { StyleType } from '../../../base/styles'; import { ChatButton } from '../../../chat'; -import { InviteButton } from '../../../invite'; import { isToolboxVisible } from '../../functions'; @@ -25,11 +23,6 @@ import VideoMuteButton from '../VideoMuteButton'; */ type Props = { - /** - * Whether the chat feature has been enabled. The meeting info button will be displayed in its place when disabled. - */ - _chatEnabled: boolean, - /** * The color-schemed stylesheet of the feature. */ @@ -105,7 +98,7 @@ class Toolbox extends PureComponent { * @returns {React$Node} */ _renderToolbar() { - const { _chatEnabled, _styles } = this.props; + const { _styles } = this.props; const { buttonStyles, buttonStylesBorderless, hangupButtonStyles, toggledButtonStyles } = _styles; return ( @@ -113,20 +106,9 @@ class Toolbox extends PureComponent { accessibilityRole = 'toolbar' pointerEvents = 'box-none' style = { styles.toolbar }> - { - _chatEnabled - && - } - { - !_chatEnabled - && - } + @@ -150,15 +132,10 @@ class Toolbox extends PureComponent { * @param {Object} state - The redux state of which parts are to be mapped to * {@code Toolbox} props. * @private - * @returns {{ - * _chatEnabled: boolean, - * _styles: StyleType, - * _visible: boolean - * }} + * @returns {Props} */ function _mapStateToProps(state: Object): Object { return { - _chatEnabled: getFeatureFlag(state, CHAT_ENABLED, true), _styles: ColorSchemeRegistry.get(state, 'Toolbox'), _visible: isToolboxVisible(state) };