diff --git a/lang/main.json b/lang/main.json index 81df029b15..d56344c9a6 100644 --- a/lang/main.json +++ b/lang/main.json @@ -483,7 +483,9 @@ "failedToStart": "Transcribing failed to start", "tr": "TR", "labelToolTip": "The meeting is being transcribed", - "ccButtonTooltip": "Start / Stop showing subtitles" + "ccButtonTooltip": "Start / Stop showing subtitles", + "start": "Start showing subtitles", + "stop": "Stop showing subtitles" }, "liveStreaming": { diff --git a/react/features/subtitles/components/AbstractClosedCaptionButton.js b/react/features/subtitles/components/AbstractClosedCaptionButton.js new file mode 100644 index 0000000000..78a174cddb --- /dev/null +++ b/react/features/subtitles/components/AbstractClosedCaptionButton.js @@ -0,0 +1,88 @@ +// @flow + +import { createToolbarEvent, sendAnalytics } from '../../analytics'; +import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox'; + +import { toggleRequestingSubtitles } from '../actions'; + +export type AbstractProps = AbstractButtonProps & { + + /** + * Invoked to obtain translated strings. + */ + t: Function, + + /** + * Invoked to Dispatch an Action to the redux store. + */ + dispatch: Function, + + /** + * Whether the local participant is currently requesting subtitles. + */ + _requestingSubtitles: Boolean +}; + +/** + * The button component which starts/stops the transcription. + */ +export class AbstractClosedCaptionButton + extends AbstractButton { + /** + * Handles clicking / pressing the button. + * + * @override + * @protected + * @returns {void} + */ + _handleClick() { + const { _requestingSubtitles, dispatch } = this.props; + + sendAnalytics(createToolbarEvent('transcribing.ccButton', + { + 'requesting_subtitles': Boolean(_requestingSubtitles) + })); + + dispatch(toggleRequestingSubtitles()); + } + + /** + * Indicates whether 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._requestingSubtitles; + } +} + +/** + * Maps (parts of) the redux state to the associated props for the + * {@code AbstractClosedCaptionButton} component. + * + * @param {Object} state - The redux state. + * @private + * @returns {{ + * __requestingSubtitles: boolean + * }} + */ +export function _abstractMapStateToProps(state: Object) { + const { _requestingSubtitles } = state['features/subtitles']; + + return { + _requestingSubtitles + }; +} diff --git a/react/features/subtitles/components/ClosedCaptionButton.native.js b/react/features/subtitles/components/ClosedCaptionButton.native.js index e69de29bb2..6bb01f1922 100644 --- a/react/features/subtitles/components/ClosedCaptionButton.native.js +++ b/react/features/subtitles/components/ClosedCaptionButton.native.js @@ -0,0 +1,26 @@ +// @flow + +import { connect } from 'react-redux'; + +import { translate } from '../../base/i18n'; + +import { + AbstractClosedCaptionButton, + _abstractMapStateToProps +} from './AbstractClosedCaptionButton'; + +/** + * A button which starts/stops the transcriptions. + */ +class ClosedCaptionButton + extends AbstractClosedCaptionButton { + + accessibilityLabel = 'toolbar.accessibilityLabel.cc'; + iconName = 'closed_caption'; + label = 'transcribing.start'; + toggledIconName = 'closed_caption'; + toggledLabel = 'transcribing.stop'; +} + +export default translate(connect(_abstractMapStateToProps)( + ClosedCaptionButton)); diff --git a/react/features/subtitles/components/ClosedCaptionButton.web.js b/react/features/subtitles/components/ClosedCaptionButton.web.js index 3b85ccd7b0..0454888951 100644 --- a/react/features/subtitles/components/ClosedCaptionButton.web.js +++ b/react/features/subtitles/components/ClosedCaptionButton.web.js @@ -1,115 +1,25 @@ // @flow -import React, { Component } from 'react'; import { connect } from 'react-redux'; -import { translate } from '../../base/i18n/index'; - -import { ToolbarButton } from '../../toolbox/'; - -import { toggleRequestingSubtitles } from '../actions'; -import { createToolbarEvent, sendAnalytics } from '../../analytics'; - - -/** - * The type of the React {@code Component} props of {@link TranscribingLabel}. - */ -type Props = { - - /** - * Invoked to obtain translated strings. - */ - t: Function, - - /** - * Invoked to Dispatch an Action to the redux store. - */ - dispatch: Function, - - /** - * Whether the local participant is currently requesting subtitles. - */ - _requestingSubtitles: Boolean -}; - -/** - * React Component for displaying a label when a transcriber is in the - * conference. - * - * @extends Component - */ -class ClosedCaptionButton extends Component { - - /** - * Initializes a new {@code ClosedCaptionButton} instance. - * - * @param {Props} props - The read-only properties with which the new - * instance is to be initialized. - */ - constructor(props: Props) { - super(props); - - // Bind event handler so it is only bound once for every instance. - this._onToggleButton = this._onToggleButton.bind(this); - } - - /** - * Implements React's {@link Component#render()}. - * - * @inheritdoc - * @returns {ReactElement} - */ - render() { - const { _requestingSubtitles, t } = this.props; - const iconClass = `icon-closed_caption ${_requestingSubtitles - ? 'toggled' : ''}`; - - return ( - - ); - } - _onToggleButton: () => void; - - /** - * Dispatch actions for starting or stopping transcription, based on - * current state. - * - * @private - * @returns {void} - */ - _onToggleButton() { - const { _requestingSubtitles, dispatch } = this.props; - - sendAnalytics(createToolbarEvent('transcribing.ccButton', - { - 'requesting_subtitles': Boolean(_requestingSubtitles) - })); - - dispatch(toggleRequestingSubtitles()); - } +import { translate } from '../../base/i18n/index'; -} +import { + AbstractClosedCaptionButton, + _abstractMapStateToProps +} from './AbstractClosedCaptionButton'; /** - * Maps (parts of) the Redux state to the associated props for the - * {@code ClosedCaptionButton} component. - * - * @param {Object} state - The Redux state. - * @private - * @returns {{ - * }} + * A button which starts/stops the transcriptions. */ -function _mapStateToProps(state) { - const { _requestingSubtitles } = state['features/subtitles']; +class ClosedCaptionButton + extends AbstractClosedCaptionButton { - return { - _requestingSubtitles - }; + accessibilityLabel = 'toolbar.accessibilityLabel.cc'; + iconName = 'icon-closed_caption'; + toggledIconName = 'icon-closed_caption toggled'; + tooltip = 'transcribing.ccButtonTooltip'; } -export default translate(connect(_mapStateToProps)(ClosedCaptionButton)); +export default translate(connect(_abstractMapStateToProps)( + ClosedCaptionButton)); diff --git a/react/features/toolbox/components/native/OverflowMenu.js b/react/features/toolbox/components/native/OverflowMenu.js index 0011d4721a..89b8debe28 100644 --- a/react/features/toolbox/components/native/OverflowMenu.js +++ b/react/features/toolbox/components/native/OverflowMenu.js @@ -8,6 +8,7 @@ import { AudioRouteButton } from '../../../mobile/audio-mode'; import { PictureInPictureButton } from '../../../mobile/picture-in-picture'; import { LiveStreamButton, RecordButton } from '../../../recording'; import { RoomLockButton } from '../../../room-lock'; +import { ClosedCaptionButton } from '../../../subtitles'; import AudioOnlyButton from './AudioOnlyButton'; import { overflowMenuItemStyles } from './styles'; @@ -69,6 +70,7 @@ class OverflowMenu extends Component { +