[WEB] Move RecordButton to the new ToolBox abstraction layer
This commit is contained in:
parent
e59761baa2
commit
b48c897d9b
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
|
@ -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));
|
||||
|
|
|
@ -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));
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue