From 3c27d2ee5450a45a5ef1634853409491101176a1 Mon Sep 17 00:00:00 2001 From: Nik Date: Wed, 22 Aug 2018 19:49:58 +0200 Subject: [PATCH] independently display subtitles based on participants choice --- react/features/subtitles/actionTypes.js | 12 ++++++ react/features/subtitles/actions.js | 14 +++++++ .../components/ClosedCaptionButton.native.js | 0 .../components/ClosedCaptionButton.web.js | 38 ++++++------------- .../components/TranscriptionSubtitles.web.js | 20 +++++++++- react/features/subtitles/components/index.js | 1 + react/features/subtitles/middleware.js | 35 +++++++++++++++-- react/features/subtitles/reducer.js | 16 +++++--- .../toolbox/components/web/Toolbox.js | 10 ++--- .../features/transcribing/components/index.js | 1 - 10 files changed, 102 insertions(+), 45 deletions(-) rename react/features/{transcribing => subtitles}/components/ClosedCaptionButton.native.js (100%) rename react/features/{transcribing => subtitles}/components/ClosedCaptionButton.web.js (69%) diff --git a/react/features/subtitles/actionTypes.js b/react/features/subtitles/actionTypes.js index c535b7f72..6a0e3282d 100644 --- a/react/features/subtitles/actionTypes.js +++ b/react/features/subtitles/actionTypes.js @@ -33,3 +33,15 @@ export const REMOVE_TRANSCRIPT_MESSAGE = Symbol('REMOVE_TRANSCRIPT_MESSAGE'); * } */ export const UPDATE_TRANSCRIPT_MESSAGE = Symbol('UPDATE_TRANSCRIPT_MESSAGE'); + +/** + * The type of (redux) action which indicates that the user pressed the + * ClosedCaption button, to either enable or disable subtitles based on the + * current state. + * + * { + * type: TOGGLE_REQUESTING_SUBTITLES + * } + */ +export const TOGGLE_REQUESTING_SUBTITLES + = Symbol('TOGGLE_REQUESTING_SUBTITLES'); diff --git a/react/features/subtitles/actions.js b/react/features/subtitles/actions.js index 5a8d629cf..11366f5ae 100644 --- a/react/features/subtitles/actions.js +++ b/react/features/subtitles/actions.js @@ -3,6 +3,7 @@ import { ENDPOINT_MESSAGE_RECEIVED, REMOVE_TRANSCRIPT_MESSAGE, + TOGGLE_REQUESTING_SUBTITLES, UPDATE_TRANSCRIPT_MESSAGE } from './actionTypes'; @@ -61,3 +62,16 @@ export function updateTranscriptMessage(transcriptMessageID: string, newTranscriptMessage }; } + +/** + * Signals that the local user has toggled the ClosedCaption button. + * + * @returns {{ + * type: TOGGLE_REQUESTING_SUBTITLES + * }} + */ +export function toggleRequestingSubtitles() { + return { + type: TOGGLE_REQUESTING_SUBTITLES + }; +} diff --git a/react/features/transcribing/components/ClosedCaptionButton.native.js b/react/features/subtitles/components/ClosedCaptionButton.native.js similarity index 100% rename from react/features/transcribing/components/ClosedCaptionButton.native.js rename to react/features/subtitles/components/ClosedCaptionButton.native.js diff --git a/react/features/transcribing/components/ClosedCaptionButton.web.js b/react/features/subtitles/components/ClosedCaptionButton.web.js similarity index 69% rename from react/features/transcribing/components/ClosedCaptionButton.web.js rename to react/features/subtitles/components/ClosedCaptionButton.web.js index a64404681..3b85ccd7b 100644 --- a/react/features/transcribing/components/ClosedCaptionButton.web.js +++ b/react/features/subtitles/components/ClosedCaptionButton.web.js @@ -6,7 +6,7 @@ import { translate } from '../../base/i18n/index'; import { ToolbarButton } from '../../toolbox/'; -import { dialTranscriber, stopTranscribing } from '../actions'; +import { toggleRequestingSubtitles } from '../actions'; import { createToolbarEvent, sendAnalytics } from '../../analytics'; @@ -26,14 +26,9 @@ type Props = { dispatch: Function, /** - * Boolean value indicating current transcribing status + * Whether the local participant is currently requesting subtitles. */ - _transcribing: boolean, - - /** - * Boolean value indicating current dialing status - */ - _dialing: boolean + _requestingSubtitles: Boolean }; /** @@ -64,8 +59,8 @@ class ClosedCaptionButton extends Component { * @returns {ReactElement} */ render() { - const { _dialing, _transcribing, t } = this.props; - const iconClass = `icon-closed_caption ${_dialing || _transcribing + const { _requestingSubtitles, t } = this.props; + const iconClass = `icon-closed_caption ${_requestingSubtitles ? 'toggled' : ''}`; return ( @@ -88,24 +83,14 @@ class ClosedCaptionButton extends Component { * @returns {void} */ _onToggleButton() { - const { _transcribing, _dialing, dispatch } = this.props; + const { _requestingSubtitles, dispatch } = this.props; - sendAnalytics(createToolbarEvent( - 'transcribing.ccButton', + sendAnalytics(createToolbarEvent('transcribing.ccButton', { - 'is_transcribing': Boolean(_transcribing), - 'is_dialing': Boolean(_dialing) + 'requesting_subtitles': Boolean(_requestingSubtitles) })); - if (_dialing) { - return; - } - - if (_transcribing) { - dispatch(stopTranscribing()); - } else { - dispatch(dialTranscriber()); - } + dispatch(toggleRequestingSubtitles()); } } @@ -120,11 +105,10 @@ class ClosedCaptionButton extends Component { * }} */ function _mapStateToProps(state) { - const { isTranscribing, isDialing } = state['features/transcribing']; + const { _requestingSubtitles } = state['features/subtitles']; return { - _transcribing: isTranscribing, - _dialing: isDialing + _requestingSubtitles }; } diff --git a/react/features/subtitles/components/TranscriptionSubtitles.web.js b/react/features/subtitles/components/TranscriptionSubtitles.web.js index 28b1dcc46..96e3e103e 100644 --- a/react/features/subtitles/components/TranscriptionSubtitles.web.js +++ b/react/features/subtitles/components/TranscriptionSubtitles.web.js @@ -12,7 +12,12 @@ type Props = { /** * Map of transcriptMessageID's with corresponding transcriptMessage. */ - _transcriptMessages: Map + _transcriptMessages: Map, + + /** + * Whether local participant is requesting to see subtitles + */ + _requestingSubtitles: Boolean }; /** @@ -28,6 +33,11 @@ class TranscriptionSubtitles extends Component { * @returns {ReactElement} */ render() { + if (!this.props._requestingSubtitles + || !this.props._transcriptMessages) { + return null; + } + const paragraphs = []; for (const [ transcriptMessageID, transcriptMessage ] @@ -73,8 +83,14 @@ class TranscriptionSubtitles extends Component { * }} */ function _mapStateToProps(state) { + const { + _transcriptMessages, + _requestingSubtitles + } = state['features/subtitles']; + return { - _transcriptMessages: state['features/subtitles'].transcriptMessages + _transcriptMessages, + _requestingSubtitles }; } export default connect(_mapStateToProps)(TranscriptionSubtitles); diff --git a/react/features/subtitles/components/index.js b/react/features/subtitles/components/index.js index 531c68d72..175052d43 100644 --- a/react/features/subtitles/components/index.js +++ b/react/features/subtitles/components/index.js @@ -1 +1,2 @@ export { default as TranscriptionSubtitles } from './TranscriptionSubtitles'; +export { default as ClosedCaptionButton } from './ClosedCaptionButton'; diff --git a/react/features/subtitles/middleware.js b/react/features/subtitles/middleware.js index 8cbf497cc..2a7ea040c 100644 --- a/react/features/subtitles/middleware.js +++ b/react/features/subtitles/middleware.js @@ -2,7 +2,10 @@ import { MiddlewareRegistry } from '../base/redux'; -import { ENDPOINT_MESSAGE_RECEIVED } from './actionTypes'; +import { + ENDPOINT_MESSAGE_RECEIVED, + TOGGLE_REQUESTING_SUBTITLES +} from './actionTypes'; import { removeTranscriptMessage, updateTranscriptMessage @@ -28,6 +31,12 @@ const JSON_TYPE_TRANSLATION_RESULT = 'translation-result'; */ const P_NAME_TRANSLATION_LANGUAGE = 'translation_language'; +/** + * The local participant property which is used to set whether the local + * participant wants to have a transcriber in the room. + */ +const P_NAME_REQUESTING_TRANSCRIPTION = 'requestingTranscription'; + /** * Time after which the rendered subtitles will be removed. */ @@ -41,15 +50,33 @@ const REMOVE_AFTER_MS = 3000; * @returns {Function} */ MiddlewareRegistry.register(store => next => action => { - switch (action.type) { case ENDPOINT_MESSAGE_RECEIVED: return _endpointMessageReceived(store, next, action); + case TOGGLE_REQUESTING_SUBTITLES: + _requestingSubtitlesToggled(store); + break; } return next(action); }); +/** + * Toggle the local property 'requestingTranscription'. This will cause Jicofo + * and Jigasi to decide whether the transcriber needs to be in the room. + * + * @param {Store} store - The redux store. + * @private + * @returns {void} + */ +function _requestingSubtitlesToggled({ getState }) { + const { _requestingSubtitles } = getState()['features/subtitles']; + const { conference } = getState()['features/base/conference']; + + conference.setLocalParticipantProperty(P_NAME_REQUESTING_TRANSCRIPTION, + !_requestingSubtitles); +} + /** * Notifies the feature transcription that the action * {@code ENDPOINT_MESSAGE_RECEIVED} is being dispatched within a specific redux @@ -109,7 +136,7 @@ function _endpointMessageReceived({ dispatch, getState }, next, action) { // message ID or adds a new transcript message if it does not // exist in the map. const newTranscriptMessage - = { ...getState()['features/subtitles'].transcriptMessages + = { ...getState()['features/subtitles']._transcriptMessages .get(transcriptMessageID) || { participantName } }; setClearerOnTranscriptMessage(dispatch, @@ -120,6 +147,7 @@ function _endpointMessageReceived({ dispatch, getState }, next, action) { if (!isInterim) { newTranscriptMessage.final = text; + dispatch(updateTranscriptMessage(transcriptMessageID, newTranscriptMessage)); } else if (stability > 0.85) { @@ -130,6 +158,7 @@ function _endpointMessageReceived({ dispatch, getState }, next, action) { newTranscriptMessage.stable = text; newTranscriptMessage.unstable = undefined; + dispatch(updateTranscriptMessage(transcriptMessageID, newTranscriptMessage)); } else { diff --git a/react/features/subtitles/reducer.js b/react/features/subtitles/reducer.js index c1cdd9b5a..30fbc723d 100644 --- a/react/features/subtitles/reducer.js +++ b/react/features/subtitles/reducer.js @@ -1,7 +1,7 @@ import { ReducerRegistry } from '../base/redux'; import { - REMOVE_TRANSCRIPT_MESSAGE, + REMOVE_TRANSCRIPT_MESSAGE, TOGGLE_REQUESTING_SUBTITLES, UPDATE_TRANSCRIPT_MESSAGE } from './actionTypes'; @@ -9,7 +9,8 @@ import { * Default State for 'features/transcription' feature */ const defaultState = { - transcriptMessages: new Map() + _transcriptMessages: new Map(), + _requestingSubtitles: false }; /** @@ -21,9 +22,14 @@ ReducerRegistry.register('features/subtitles', ( switch (action.type) { case REMOVE_TRANSCRIPT_MESSAGE: return _removeTranscriptMessage(state, action); - case UPDATE_TRANSCRIPT_MESSAGE: return _updateTranscriptMessage(state, action); + + case TOGGLE_REQUESTING_SUBTITLES: + return { + ...state, + _requestingSubtitles: !state._requestingSubtitles + }; } return state; @@ -46,7 +52,7 @@ function _removeTranscriptMessage(state, { transcriptMessageID }) { return { ...state, - transcriptMessages: newTranscriptMessages + _transcriptMessages: newTranscriptMessages }; } @@ -68,6 +74,6 @@ function _updateTranscriptMessage(state, return { ...state, - transcriptMessages: newTranscriptMessages + _transcriptMessages: newTranscriptMessages }; } diff --git a/react/features/toolbox/components/web/Toolbox.js b/react/features/toolbox/components/web/Toolbox.js index 147f9ef8c..9fa0db775 100644 --- a/react/features/toolbox/components/web/Toolbox.js +++ b/react/features/toolbox/components/web/Toolbox.js @@ -14,8 +14,7 @@ import { translate } from '../../../base/i18n'; import { getLocalParticipant, getParticipants, - participantUpdated, - isLocalParticipantModerator + participantUpdated } from '../../../base/participants'; import { getLocalVideoTrack, toggleScreensharing } from '../../../base/tracks'; import { ChatCounter } from '../../../chat'; @@ -64,7 +63,7 @@ import ToolbarButton from './ToolbarButton'; import VideoMuteButton from '../VideoMuteButton'; import { ClosedCaptionButton -} from '../../../transcribing'; +} from '../../../subtitles'; /** * The type of the React {@code Component} props of {@link Toolbox}. @@ -1040,7 +1039,7 @@ function _mapStateToProps(state) { callStatsID, iAmRecorder } = state['features/base/config']; - let { + const { transcribingEnabled } = state['features/base/config']; const sharedVideoStatus = state['features/shared-video'].status; @@ -1060,9 +1059,6 @@ function _mapStateToProps(state) { let desktopSharingDisabledTooltipKey; - transcribingEnabled - = isLocalParticipantModerator(state) && transcribingEnabled; - if (state['features/base/config'].enableFeaturesBasedOnToken) { // we enable desktop sharing if any participant already have this // feature enabled diff --git a/react/features/transcribing/components/index.js b/react/features/transcribing/components/index.js index 651bc819c..171f9b002 100644 --- a/react/features/transcribing/components/index.js +++ b/react/features/transcribing/components/index.js @@ -1,2 +1 @@ export { default as TranscribingLabel } from './TranscribingLabel'; -export { default as ClosedCaptionButton } from './ClosedCaptionButton';