mirror of https://github.com/jitsi/jitsi-meet
parent
e59761baa2
commit
b48c897d9b
@ -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 |
||||
}; |
||||
} |
@ -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)); |
Loading…
Reference in new issue