[WEB] Move RecordButton to the new ToolBox abstraction layer

This commit is contained in:
Bettenbuk Zoltan 2018-06-28 15:26:52 +02:00 committed by Saúl Ibarra Corretgé
parent e59761baa2
commit b48c897d9b
5 changed files with 275 additions and 215 deletions

View File

@ -70,9 +70,7 @@ export default class ToolboxItem extends AbstractToolboxItem<Props> {
const elementType = showLabel ? 'span' : 'div';
const className
= showLabel ? 'overflow-menu-item-icon' : 'toolbox-icon';
const iconWrapper
= React.createElement(elementType, { className }, icon);
return iconWrapper;
return React.createElement(elementType, { className }, icon);
}
}

View File

@ -0,0 +1,138 @@
// @flow
import {
createToolbarEvent,
sendAnalytics
} from '../../../analytics';
import { openDialog } from '../../../base/dialog';
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
import {
getLocalParticipant,
isLocalParticipantModerator
} from '../../../base/participants';
import {
AbstractButton,
type AbstractButtonProps
} from '../../../base/toolbox';
import { getActiveSession } from '../../functions';
import StartRecordingDialog from './StartRecordingDialog';
import StopRecordingDialog from './StopRecordingDialog';
/**
* The type of the React {@code Component} props of
* {@link AbstractRecordButton}.
*/
export type Props = AbstractButtonProps & {
/**
* True if there is a running active recording, false otherwise.
*/
_isRecordingRunning: boolean,
/**
* The redux {@code dispatch} function.
*/
dispatch: Function,
/**
* The i18n translate function.
*/
t: Function
};
/**
* An abstract implementation of a button for starting and stopping recording.
*/
export default class AbstractRecordButton<P: Props>
extends AbstractButton<P, *> {
accessibilityLabel = 'toolbar.accessibilityLabel.recording';
label = 'dialog.startRecording';
toggledLabel = 'dialog.stopRecording';
/**
* Handles clicking / pressing the button.
*
* @override
* @protected
* @returns {void}
*/
_handleClick() {
const { _isRecordingRunning, dispatch } = this.props;
sendAnalytics(createToolbarEvent(
'recording.button',
{
'is_recording': _isRecordingRunning,
type: JitsiRecordingConstants.mode.FILE
}));
dispatch(openDialog(
_isRecordingRunning ? StopRecordingDialog : StartRecordingDialog
));
}
/**
* Helper function to be implemented by subclasses, which must return a
* boolean value indicating if this button is disabled or not.
*
* @override
* @protected
* @returns {boolean}
*/
_isDisabled() {
return false;
}
/**
* Indicates whether this button is in toggled state or not.
*
* @override
* @protected
* @returns {boolean}
*/
_isToggled() {
return this.props._isRecordingRunning;
}
}
/**
* Maps (parts of) the redux state to the associated props for the
* {@code RecordButton} component.
*
* @param {Object} state - The Redux state.
* @param {Props} ownProps - The own props of the Component.
* @private
* @returns {{
* _isRecordingRunning: boolean,
* visible: boolean
* }}
*/
export function _mapStateToProps(state: Object, ownProps: Props): Object {
let { visible } = ownProps;
if (typeof visible === 'undefined') {
// If the containing component provides the visible prop, that is one
// above all, but if not, the button should be autonomus and decide on
// its own to be visible or not.
const isModerator = isLocalParticipantModerator(state);
const {
enableFeaturesBasedOnToken,
fileRecordingsEnabled
} = state['features/base/config'];
const { features = {} } = getLocalParticipant(state);
visible = isModerator
&& fileRecordingsEnabled
&& (!enableFeaturesBasedOnToken
|| String(features.recording) === 'true');
}
return {
_isRecordingRunning:
Boolean(getActiveSession(state, JitsiRecordingConstants.mode.FILE)),
visible
};
}

View File

@ -2,107 +2,19 @@
import { connect } from 'react-redux';
import { openDialog } from '../../../base/dialog';
import { translate } from '../../../base/i18n';
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
import {
isLocalParticipantModerator
} from '../../../base/participants';
import {
AbstractButton,
type AbstractButtonProps
} from '../../../base/toolbox';
import { getActiveSession } from '../../functions';
import StartRecordingDialog from './StartRecordingDialog';
import StopRecordingDialog from './StopRecordingDialog';
/**
* The type of the React {@code Component} props of {@link RecordButton}.
*/
type Props = AbstractButtonProps & {
/**
* The current conference object.
*/
_conference: Object,
/**
* True if there is a running active recording, false otherwise.
*/
_isRecordingRunning: boolean,
/**
* The redux {@code dispatch} function.
*/
dispatch: Function,
/**
* The i18n translate function.
*/
t: Function
};
import AbstractRecordButton, {
_mapStateToProps,
type Props
} from './AbstractRecordButton';
/**
* An implementation of a button for starting and stopping recording.
*/
class RecordButton extends AbstractButton<Props, *> {
accessibilityLabel = 'Recording';
iconName = 'recEnable';
label = 'dialog.startRecording';
toggledIconName = 'recDisable';
toggledLabel = 'dialog.stopRecording';
/**
* Handles clicking / pressing the button.
*
* @override
* @protected
* @returns {void}
*/
_handleClick() {
const { _isRecordingRunning, dispatch } = this.props;
dispatch(openDialog(
_isRecordingRunning ? StopRecordingDialog : StartRecordingDialog
));
}
/**
* Indicates whether this button is in toggled state or not.
*
* @override
* @protected
* @returns {boolean}
*/
_isToggled() {
return this.props._isRecordingRunning;
}
}
/**
* Maps (parts of) the redux state to the associated props for the
* {@code RecordButton} component.
*
* @param {Object} state - The Redux state.
* @private
* @returns {{
* _conference: Object,
* _isRecordingRunning: boolean,
* visible: boolean
* }}
*/
function _mapStateToProps(state): Object {
const isModerator = isLocalParticipantModerator(state);
const { fileRecordingsEnabled } = state['features/base/config'];
return {
_conference: state['features/base/conference'].conference,
_isRecordingRunning:
Boolean(getActiveSession(state, JitsiRecordingConstants.mode.FILE)),
visible: isModerator && fileRecordingsEnabled
};
class RecordButton extends AbstractRecordButton<Props> {
iconName = 'camera-take-picture';
toggledIconName = 'camera-take-picture';
}
export default translate(connect(_mapStateToProps)(RecordButton));

View File

@ -0,0 +1,124 @@
// @flow
import { connect } from 'react-redux';
import { translate } from '../../../base/i18n';
import { getLocalParticipant } from '../../../base/participants';
import AbstractRecordButton, {
_mapStateToProps as _abstractMapStateToProps,
type Props as AbstractProps
} from './AbstractRecordButton';
declare var interfaceConfig: Object;
type Props = AbstractProps & {
/**
* True if the button should be disabled, false otherwise.
*
* NOTE: On web, if the feature is not disabled on purpose, then we still
* show the button but disabled and with a tooltip rendered on it,
* explaining why it's not available.
*/
_disabled: boolean,
/**
* Tooltip for the button when it's disabled in a certain way.
*/
_fileRecordingsDisabledTooltipKey: ?string
}
/**
* An implementation of a button for starting and stopping recording.
*/
class RecordButton extends AbstractRecordButton<Props> {
iconName = 'icon-camera-take-picture';
toggledIconName = 'icon-camera-take-picture';
/**
* Constructor of the component.
*
* @inheritdoc
*/
constructor(props: Props) {
super(props);
this.tooltip = props._fileRecordingsDisabledTooltipKey;
}
/**
* Implements {@code Component}'s componentWillReceiveProps.
*
* @inheritdoc
*/
componentWillReceiveProps(newProps: Props) {
this.tooltip = newProps._fileRecordingsDisabledTooltipKey;
}
/**
* Helper function to be implemented by subclasses, which must return a
* boolean value indicating if this button is disabled or not.
*
* @override
* @protected
* @returns {boolean}
*/
_isDisabled() {
return this.props._disabled;
}
}
/**
* Maps (parts of) the redux state to the associated props for the
* {@code RecordButton} component.
*
* @param {Object} state - The Redux state.
* @param {Props} ownProps - The own props of the Component.
* @private
* @returns {{
* _fileRecordingsDisabledTooltipKey: ?string,
* _isRecordingRunning: boolean,
* _disabled: boolean,
* visible: boolean
* }}
*/
export function _mapStateToProps(state: Object, ownProps: Props): Object {
const abstractProps = _abstractMapStateToProps(state, ownProps);
const localParticipant = getLocalParticipant(state);
const { features = {} } = localParticipant;
let { visible } = ownProps;
let _disabled = false;
let _fileRecordingsDisabledTooltipKey;
if (!abstractProps.visible
&& String(features.recording) !== 'disabled') {
_disabled = true;
// button and tooltip
if (state['features/base/jwt'].isGuest) {
_fileRecordingsDisabledTooltipKey
= 'dialog.recordingDisabledForGuestTooltip';
} else {
_fileRecordingsDisabledTooltipKey
= 'dialog.recordingDisabledTooltip';
}
}
if (typeof visible === 'undefined') {
const visibleButtons = new Set(interfaceConfig.TOOLBAR_BUTTONS);
visible = visibleButtons.has('recording')
&& (abstractProps.visible || _fileRecordingsDisabledTooltipKey);
}
return {
...abstractProps,
visible,
_disabled,
_fileRecordingsDisabledTooltipKey
};
}
export default translate(connect(_mapStateToProps)(RecordButton));

View File

@ -30,10 +30,9 @@ import {
} from '../../../invite';
import { openKeyboardShortcutsDialog } from '../../../keyboard-shortcuts';
import {
RecordButton,
StartLiveStreamDialog,
StartRecordingDialog,
StopLiveStreamDialog,
StopRecordingDialog,
getActiveSession
} from '../../../recording';
import {
@ -108,22 +107,6 @@ type Props = {
*/
_feedbackConfigured: boolean,
/**
* The tooltip key to use when file recording is disabled. Or undefined
* if non to be shown and the button to be hidden.
*/
_fileRecordingsDisabledTooltipKey: boolean,
/**
* Whether or not the file recording feature is enabled for use.
*/
_fileRecordingsEnabled: boolean,
/**
* The current file recording session, if any.
*/
_fileRecordingSession: Object,
/**
* Whether or not the app is currently in full screen.
*/
@ -253,8 +236,6 @@ class Toolbox extends Component<Props> {
= this._onToolbarToggleProfile.bind(this);
this._onToolbarToggleRaiseHand
= this._onToolbarToggleRaiseHand.bind(this);
this._onToolbarToggleRecording
= this._onToolbarToggleRecording.bind(this);
this._onToolbarToggleScreenshare
= this._onToolbarToggleScreenshare.bind(this);
this._onToolbarToggleSharedVideo
@ -543,21 +524,6 @@ class Toolbox extends Component<Props> {
}));
}
/**
* Dispatches an action to toggle recording.
*
* @private
* @returns {void}
*/
_doToggleRecording() {
const { _fileRecordingSession } = this.props;
const dialog = _fileRecordingSession
? StopRecordingDialog : StartRecordingDialog;
this.props.dispatch(
openDialog(dialog, { session: _fileRecordingSession }));
}
/**
* Dispatches an action to toggle screensharing.
*
@ -875,25 +841,6 @@ class Toolbox extends Component<Props> {
this._doToggleRaiseHand();
}
_onToolbarToggleRecording: () => void;
/**
* Dispatches an action to toggle recording.
*
* @private
* @returns {void}
*/
_onToolbarToggleRecording() {
sendAnalytics(createToolbarEvent(
'recording.button',
{
'is_recording': Boolean(this.props._fileRecordingSession),
type: JitsiRecordingConstants.mode.FILE
}));
this._doToggleRecording();
}
_onToolbarToggleScreenshare: () => void;
/**
@ -1020,8 +967,6 @@ class Toolbox extends Component<Props> {
_editingDocument,
_etherpadInitialized,
_feedbackConfigured,
_fileRecordingsDisabledTooltipKey,
_fileRecordingsEnabled,
_fullScreen,
_isGuest,
_liveStreamingDisabledTooltipKey,
@ -1055,9 +1000,9 @@ class Toolbox extends Component<Props> {
(_liveStreamingEnabled || _liveStreamingDisabledTooltipKey)
&& this._shouldShowButton('livestreaming')
&& this._renderLiveStreamingButton(),
(_fileRecordingsEnabled || _fileRecordingsDisabledTooltipKey)
&& this._shouldShowButton('recording')
&& this._renderRecordingButton(),
<RecordButton
key = 'record'
showLabel = { true } />,
this._shouldShowButton('sharedvideo')
&& <OverflowMenuItem
accessibilityLabel =
@ -1111,37 +1056,6 @@ class Toolbox extends Component<Props> {
];
}
/**
* Renders an {@code OverflowMenuItem} to start or stop recording of the
* current conference.
*
* @private
* @returns {ReactElement}
*/
_renderRecordingButton() {
const {
_fileRecordingSession,
_fileRecordingsDisabledTooltipKey,
_fileRecordingsEnabled,
t } = this.props;
const translationKey = _fileRecordingSession
? 'dialog.stopRecording'
: 'dialog.startRecording';
return (
<OverflowMenuItem
accessibilityLabel =
{ t('toolbar.accessibilityLabel.recording') }
disabled = { !_fileRecordingsEnabled }
icon = 'icon-camera-take-picture'
key = 'recording'
onClick = { this._onToolbarToggleRecording }
text = { t(translationKey) }
tooltip = { t(_fileRecordingsDisabledTooltipKey) } />
);
}
_shouldShowButton: (string) => boolean;
/**
@ -1172,10 +1086,7 @@ function _mapStateToProps(state) {
callStatsID,
iAmRecorder
} = state['features/base/config'];
let {
fileRecordingsEnabled,
liveStreamingEnabled
} = state['features/base/config'];
let { liveStreamingEnabled } = state['features/base/config'];
const sharedVideoStatus = state['features/shared-video'].status;
const { current } = state['features/side-panel'];
const {
@ -1191,11 +1102,8 @@ function _mapStateToProps(state) {
const dialOutEnabled = isDialOutEnabled(state);
let desktopSharingDisabledTooltipKey;
let fileRecordingsDisabledTooltipKey;
let liveStreamingDisabledTooltipKey;
fileRecordingsEnabled
= isLocalParticipantModerator(state) && fileRecordingsEnabled;
liveStreamingEnabled
= isLocalParticipantModerator(state) && liveStreamingEnabled;
@ -1220,22 +1128,6 @@ function _mapStateToProps(state) {
const { features = {} } = localParticipant;
const { isGuest } = state['features/base/jwt'];
fileRecordingsEnabled
= fileRecordingsEnabled && String(features.recording) === 'true';
// if the feature is disabled on purpose, do no show it, no tooltip
if (!fileRecordingsEnabled
&& String(features.recording) !== 'disabled') {
// button and tooltip
if (isGuest) {
fileRecordingsDisabledTooltipKey
= 'dialog.recordingDisabledForGuestTooltip';
} else {
fileRecordingsDisabledTooltipKey
= 'dialog.recordingDisabledTooltip';
}
}
liveStreamingEnabled
= liveStreamingEnabled && String(features.livestreaming) === 'true';
@ -1265,10 +1157,6 @@ function _mapStateToProps(state) {
_hideInviteButton:
iAmRecorder || (!addPeopleEnabled && !dialOutEnabled),
_isGuest: state['features/base/jwt'].isGuest,
_fileRecordingsDisabledTooltipKey: fileRecordingsDisabledTooltipKey,
_fileRecordingsEnabled: fileRecordingsEnabled,
_fileRecordingSession:
getActiveSession(state, JitsiRecordingConstants.mode.FILE),
_fullScreen: fullScreen,
_liveStreamingDisabledTooltipKey: liveStreamingDisabledTooltipKey,
_liveStreamingEnabled: liveStreamingEnabled,