Browse Source

feat(RN): displays transcription subtitles

master
paweldomas 7 years ago
parent
commit
26d906fa46

+ 5
- 0
react/features/base/conference/actions.js View File

17
     participantRoleChanged,
17
     participantRoleChanged,
18
     participantUpdated
18
     participantUpdated
19
 } from '../participants';
19
 } from '../participants';
20
+import { endpointMessageReceived } from '../../subtitles';
20
 import { getLocalTracks, trackAdded, trackRemoved } from '../tracks';
21
 import { getLocalTracks, trackAdded, trackRemoved } from '../tracks';
21
 import { getJitsiMeetGlobalNS } from '../util';
22
 import { getJitsiMeetGlobalNS } from '../util';
22
 
23
 
137
         JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED,
138
         JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED,
138
         id => dispatch(dominantSpeakerChanged(id, conference)));
139
         id => dispatch(dominantSpeakerChanged(id, conference)));
139
 
140
 
141
+    conference.on(
142
+        JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
143
+        (...args) => dispatch(endpointMessageReceived(...args)));
144
+
140
     conference.on(
145
     conference.on(
141
         JitsiConferenceEvents.PARTICIPANT_CONN_STATUS_CHANGED,
146
         JitsiConferenceEvents.PARTICIPANT_CONN_STATUS_CHANGED,
142
         (...args) => dispatch(participantConnectionStatusChanged(...args)));
147
         (...args) => dispatch(participantConnectionStatusChanged(...args)));

+ 25
- 24
react/features/conference/components/Conference.native.js View File

22
 import { LargeVideo } from '../../large-video';
22
 import { LargeVideo } from '../../large-video';
23
 import { CalleeInfoContainer } from '../../invite';
23
 import { CalleeInfoContainer } from '../../invite';
24
 import { NotificationsContainer } from '../../notifications';
24
 import { NotificationsContainer } from '../../notifications';
25
+import { Captions } from '../../subtitles';
25
 import { setToolboxVisible, Toolbox } from '../../toolbox';
26
 import { setToolboxVisible, Toolbox } from '../../toolbox';
26
 
27
 
27
 import styles from './styles';
28
 import styles from './styles';
283
                 <View
284
                 <View
284
                     pointerEvents = 'box-none'
285
                     pointerEvents = 'box-none'
285
                     style = { styles.toolboxAndFilmstripContainer }>
286
                     style = { styles.toolboxAndFilmstripContainer }>
286
-                    {
287
-
288
-                        /**
289
-                         * Notifications are rendered on the very top of other
290
-                         * components like subtitles, toolbox and filmstrip.
291
-                         */
287
+                    {/*
288
+                      * Notifications are rendered on the very top of other
289
+                      * components like subtitles, toolbox and filmstrip.
290
+                      */
292
                         this._renderNotificationsContainer()
291
                         this._renderNotificationsContainer()
293
                     }
292
                     }
293
+
294
+                    <Captions onPress = { this._onClick } />
295
+
294
                     {/*
296
                     {/*
295
                       * The Toolbox is in a stacking layer bellow the Filmstrip.
297
                       * The Toolbox is in a stacking layer bellow the Filmstrip.
296
                       */}
298
                       */}
297
                     <Toolbox />
299
                     <Toolbox />
300
+
298
                     {/*
301
                     {/*
299
                       * The Filmstrip is in a stacking layer above the
302
                       * The Filmstrip is in a stacking layer above the
300
                       * LargeVideo. The LargeVideo and the Filmstrip form what
303
                       * LargeVideo. The LargeVideo and the Filmstrip form what
369
     }
372
     }
370
 
373
 
371
     /**
374
     /**
372
-     * Renders a container for notifications to be displayed by
373
-     * the base/notifications feature.
375
+     * Renders a container for notifications to be displayed by the
376
+     * base/notifications feature.
374
      *
377
      *
375
-     * @returns {React$Element}
376
      * @private
378
      * @private
379
+     * @returns {React$Element}
377
      */
380
      */
378
     _renderNotificationsContainer() {
381
     _renderNotificationsContainer() {
379
-        const notificationsStyle = { };
380
-
381
-        /**
382
-         * In the landscape mode (wide) there's problem with notifications being
383
-         * shadowed by the filmstrip rendered on the right. This makes the "x"
384
-         * button not clickable. In order to avoid that a margin of
385
-         * the filmstrip's size is added to the right.
386
-         *
387
-         * Pawel: after many attempts I failed to make notifications adjust to
388
-         * their contents width because of column and rows being used in
389
-         * the flex layout. The only option that seemed to limit
390
-         * the notification's size was explicit 'width' value which is not
391
-         * better than the margin added here.
392
-         */
393
-        if (!isNarrowAspectRatio(this) && this.props._filmstripVisible) {
382
+        const notificationsStyle = {};
383
+
384
+        // In the landscape mode (wide) there's problem with notifications being
385
+        // shadowed by the filmstrip rendered on the right. This makes the "x"
386
+        // button not clickable. In order to avoid that a margin of the
387
+        // filmstrip's size is added to the right.
388
+        //
389
+        // Pawel: after many attempts I failed to make notifications adjust to
390
+        // their contents width because of column and rows being used in the
391
+        // flex layout. The only option that seemed to limit the notification's
392
+        // size was explicit 'width' value which is not better than the margin
393
+        // added here.
394
+        if (this.props._filmstripVisible && !isNarrowAspectRatio(this)) {
394
             notificationsStyle.marginRight = FILMSTRIP_SIZE;
395
             notificationsStyle.marginRight = FILMSTRIP_SIZE;
395
         }
396
         }
396
 
397
 

+ 25
- 25
react/features/large-video/components/LargeVideo.web.js View File

1
-/* @flow */
1
+// @flow
2
 
2
 
3
-import PropTypes from 'prop-types';
4
 import React, { Component } from 'react';
3
 import React, { Component } from 'react';
5
 
4
 
6
 import { Watermarks } from '../../base/react';
5
 import { Watermarks } from '../../base/react';
7
-import { TranscriptionSubtitles } from '../../subtitles/';
6
+import { Captions } from '../../subtitles/';
8
 
7
 
9
 import Labels from './Labels';
8
 import Labels from './Labels';
10
 
9
 
11
 declare var interfaceConfig: Object;
10
 declare var interfaceConfig: Object;
12
 
11
 
12
+/**
13
+ * The type of the React {@code Component} props of {@link LargeVideo}.
14
+ */
15
+type Props = {
16
+
17
+    /**
18
+     * True if the {@code VideoQualityLabel} should not be displayed.
19
+     */
20
+    hideVideoQualityLabel: boolean
21
+};
22
+
13
 /**
23
 /**
14
  * Implements a React {@link Component} which represents the large video (a.k.a.
24
  * Implements a React {@link Component} which represents the large video (a.k.a.
15
  * the conference participant who is on the local stage) on Web/React.
25
  * the conference participant who is on the local stage) on Web/React.
16
  *
26
  *
17
  * @extends Component
27
  * @extends Component
18
  */
28
  */
19
-export default class LargeVideo extends Component<*> {
20
-    static propTypes = {
21
-        /**
22
-         * True if the {@code VideoQualityLabel} should not be displayed.
23
-         */
24
-        hideVideoQualityLabel: PropTypes.bool
25
-    };
26
-
29
+export default class LargeVideo extends Component<Props> {
27
     /**
30
     /**
28
      * Implements React's {@link Component#render()}.
31
      * Implements React's {@link Component#render()}.
29
      *
32
      *
30
      * @inheritdoc
33
      * @inheritdoc
31
-     * @returns {ReactElement}
34
+     * @returns {React$Element}
32
      */
35
      */
33
     render() {
36
     render() {
34
         return (
37
         return (
52
                 <span id = 'remoteConnectionMessage' />
55
                 <span id = 'remoteConnectionMessage' />
53
                 <div id = 'largeVideoElementsContainer'>
56
                 <div id = 'largeVideoElementsContainer'>
54
                     <div id = 'largeVideoBackgroundContainer' />
57
                     <div id = 'largeVideoBackgroundContainer' />
55
-                    {
56
 
58
 
57
-                        /**
58
-                         * FIXME: the architecture of elements related to the
59
-                         * large video and  the naming. The background is not
60
-                         * part of largeVideoWrapper because we are controlling
61
-                         * the size of the video through largeVideoWrapper.
62
-                         * That's why we need another container for the the
63
-                         * background and the largeVideoWrapper in order to
64
-                         * hide/show them.
65
-                         */
66
-                    }
59
+                    {/*
60
+                      * FIXME: the architecture of elements related to the large
61
+                      * video and the naming. The background is not part of
62
+                      * largeVideoWrapper because we are controlling the size of
63
+                      * the video through largeVideoWrapper. That's why we need
64
+                      * another container for the background and the
65
+                      * largeVideoWrapper in order to hide/show them.
66
+                      */}
67
                     <div id = 'largeVideoWrapper'>
67
                     <div id = 'largeVideoWrapper'>
68
                         <video
68
                         <video
69
                             autoPlay = { true }
69
                             autoPlay = { true }
72
                     </div>
72
                     </div>
73
                 </div>
73
                 </div>
74
                 { interfaceConfig.DISABLE_TRANSCRIPTION_SUBTITLES
74
                 { interfaceConfig.DISABLE_TRANSCRIPTION_SUBTITLES
75
-                    ? null : <TranscriptionSubtitles /> }
75
+                    || <Captions /> }
76
                 <span id = 'localConnectionMessage' />
76
                 <span id = 'localConnectionMessage' />
77
                 { this.props.hideVideoQualityLabel
77
                 { this.props.hideVideoQualityLabel
78
-                    ? null : <Labels /> }
78
+                    || <Labels /> }
79
             </div>
79
             </div>
80
         );
80
         );
81
     }
81
     }

+ 128
- 0
react/features/subtitles/components/AbstractCaptions.js View File

1
+// @flow
2
+
3
+import { Component } from 'react';
4
+
5
+/**
6
+ * {@code AbstractCaptions} properties.
7
+ */
8
+export type AbstractCaptionsProps = {
9
+
10
+    /**
11
+     * Whether local participant is requesting to see subtitles.
12
+     */
13
+    _requestingSubtitles: boolean,
14
+
15
+    /**
16
+     * Transcript texts formatted with participant's name and final content.
17
+     * Mapped by id just to have the keys for convenience during the rendering
18
+     * process.
19
+     */
20
+    _transcripts: Map<string, string>
21
+};
22
+
23
+/**
24
+ * Abstract React {@code Component} which can display speech-to-text results
25
+ * from Jigasi as subtitles.
26
+ */
27
+export class AbstractCaptions<P: AbstractCaptionsProps>
28
+    extends Component<P> {
29
+
30
+    /**
31
+     * Implements React's {@link Component#render()}.
32
+     *
33
+     * @inheritdoc
34
+     * @returns {React$Element}
35
+     */
36
+    render() {
37
+        const { _requestingSubtitles, _transcripts } = this.props;
38
+
39
+        if (!_requestingSubtitles || !_transcripts.size) {
40
+            return null;
41
+        }
42
+
43
+        const paragraphs = [];
44
+
45
+        for (const [ id, text ] of _transcripts) {
46
+            paragraphs.push(this._renderParagraph(id, text));
47
+        }
48
+
49
+        return this._renderSubtitlesContainer(paragraphs);
50
+    }
51
+
52
+    /**
53
+     * Renders the transcription text.
54
+     *
55
+     * @abstract
56
+     * @param {string} id - The ID of the transcript message from which the
57
+     * {@code text} has been created.
58
+     * @param {string} text - Subtitles text formatted with the participant's
59
+     * name.
60
+     * @protected
61
+     * @returns {React$Element} - The React element which displays the text.
62
+     */
63
+    _renderParagraph: (id: string, text: string) => React$Element<*>;
64
+
65
+    /**
66
+     * Renders the subtitles container.
67
+     *
68
+     * @abstract
69
+     * @param {Array<React$Element>} paragraphs - An array of elements created
70
+     * for each subtitle using the {@link _renderParagraph} method.
71
+     * @protected
72
+     * @returns {React$Element} - The subtitles container.
73
+     */
74
+    _renderSubtitlesContainer: (Array<React$Element<*>>) => React$Element<*>;
75
+}
76
+
77
+/**
78
+ * Formats the transcript messages into text by prefixing participant's name to
79
+ * avoid duplicating the effort on platform specific component.
80
+ *
81
+ * @param {Object} state - The redux state.
82
+ * @private
83
+ * @returns {Map<string, string>} - Formatted transcript subtitles mapped by
84
+ * transcript message IDs.
85
+ */
86
+function _constructTranscripts(state: Object): Map<string, string> {
87
+    const { _transcriptMessages } = state['features/subtitles'];
88
+    const transcripts = new Map();
89
+
90
+    for (const [ id, transcriptMessage ] of _transcriptMessages) {
91
+        if (transcriptMessage) {
92
+            let text = `${transcriptMessage.participantName}: `;
93
+
94
+            if (transcriptMessage.final) {
95
+                text += transcriptMessage.final;
96
+            } else {
97
+                const stable = transcriptMessage.stable || '';
98
+                const unstable = transcriptMessage.unstable || '';
99
+
100
+                text += stable + unstable;
101
+            }
102
+
103
+            transcripts.set(id, text);
104
+        }
105
+    }
106
+
107
+    return transcripts;
108
+}
109
+
110
+/**
111
+ * Maps the transcriptionSubtitles in the redux state to the associated props of
112
+ * {@code AbstractCaptions}.
113
+ *
114
+ * @param {Object} state - The redux state.
115
+ * @private
116
+ * @returns {{
117
+ *     _requestingSubtitles: boolean,
118
+ *     _transcripts: Map<string, string>
119
+ * }}
120
+ */
121
+export function _abstractMapStateToProps(state: Object) {
122
+    const { _requestingSubtitles } = state['features/subtitles'];
123
+
124
+    return {
125
+        _requestingSubtitles,
126
+        _transcripts: _constructTranscripts(state)
127
+    };
128
+}

+ 68
- 0
react/features/subtitles/components/Captions.native.js View File

1
+// @flow
2
+
3
+import React from 'react';
4
+import { connect } from 'react-redux';
5
+
6
+import { Container, Text } from '../../base/react';
7
+
8
+import {
9
+    _abstractMapStateToProps,
10
+    AbstractCaptions,
11
+    type AbstractCaptionsProps
12
+} from './AbstractCaptions';
13
+import styles from './styles';
14
+
15
+/**
16
+ * The type of the React {@code Component} props of {@link Captions}.
17
+ */
18
+type Props = AbstractCaptionsProps & {
19
+    onPress: Function
20
+};
21
+
22
+/**
23
+ * React {@code Component} which can display speech-to-text results from
24
+ * Jigasi as subtitles.
25
+ */
26
+class Captions
27
+    extends AbstractCaptions<Props> {
28
+
29
+    /**
30
+     * Renders the transcription text.
31
+     *
32
+     * @param {string} id - The ID of the transcript message from which the
33
+     * {@code text} has been created.
34
+     * @param {string} text - Subtitles text formatted with the participant's
35
+     * name.
36
+     * @protected
37
+     * @returns {React$Element} - The React element which displays the text.
38
+     */
39
+    _renderParagraph(id: string, text: string): React$Element<*> {
40
+        return (
41
+            <Text
42
+                key = { id }
43
+                onPress = { this.props.onPress }
44
+                style = { styles.subtitle } >
45
+                { text }
46
+            </Text>
47
+        );
48
+    }
49
+
50
+    /**
51
+     * Renders the subtitles container.
52
+     *
53
+     * @param {Array<React$Element>} paragraphs - An array of elements created
54
+     * for each subtitle using the {@link _renderParagraph} method.
55
+     * @protected
56
+     * @returns {React$Element} - The subtitles container.
57
+     */
58
+    _renderSubtitlesContainer(
59
+            paragraphs: Array<React$Element<*>>): React$Element<*> {
60
+        return (
61
+            <Container style = { styles.subtitlesContainer } >
62
+                { paragraphs }
63
+            </Container>
64
+        );
65
+    }
66
+}
67
+
68
+export default connect(_abstractMapStateToProps)(Captions);

+ 55
- 0
react/features/subtitles/components/Captions.web.js View File

1
+// @flow
2
+
3
+import React from 'react';
4
+import { connect } from 'react-redux';
5
+
6
+import {
7
+    _abstractMapStateToProps,
8
+    AbstractCaptions,
9
+    type AbstractCaptionsProps as Props
10
+} from './AbstractCaptions';
11
+
12
+/**
13
+ * React {@code Component} which can display speech-to-text results from
14
+ * Jigasi as subtitles.
15
+ */
16
+class Captions
17
+    extends AbstractCaptions<Props> {
18
+
19
+    /**
20
+     * Renders the transcription text.
21
+     *
22
+     * @param {string} id - The ID of the transcript message from which the
23
+     * {@code text} has been created.
24
+     * @param {string} text - Subtitles text formatted with the participant's
25
+     * name.
26
+     * @protected
27
+     * @returns {React$Element} - The React element which displays the text.
28
+     */
29
+    _renderParagraph(id: string, text: string): React$Element<*> {
30
+        return (
31
+            <p key = { id }>
32
+                <span>{ text }</span>
33
+            </p>
34
+        );
35
+    }
36
+
37
+    /**
38
+     * Renders the subtitles container.
39
+     *
40
+     * @param {Array<React$Element>} paragraphs - An array of elements created
41
+     * for each subtitle using the {@link _renderParagraph} method.
42
+     * @protected
43
+     * @returns {React$Element} - The subtitles container.
44
+     */
45
+    _renderSubtitlesContainer(
46
+            paragraphs: Array<React$Element<*>>): React$Element<*> {
47
+        return (
48
+            <div className = 'transcription-subtitles' >
49
+                { paragraphs }
50
+            </div>
51
+        );
52
+    }
53
+}
54
+
55
+export default connect(_abstractMapStateToProps)(Captions);

+ 0
- 0
react/features/subtitles/components/TranscriptionSubtitles.native.js View File


+ 0
- 96
react/features/subtitles/components/TranscriptionSubtitles.web.js View File

1
-// @flow
2
-
3
-import React, { Component } from 'react';
4
-import { connect } from 'react-redux';
5
-
6
-/**
7
- * The type of the React {@code Component} props of
8
- * {@link TranscriptionSubtitles}.
9
- */
10
-type Props = {
11
-
12
-    /**
13
-     * Map of transcriptMessageID's with corresponding transcriptMessage.
14
-     */
15
-    _transcriptMessages: Map<string, Object>,
16
-
17
-    /**
18
-     * Whether local participant is requesting to see subtitles
19
-     */
20
-    _requestingSubtitles: Boolean
21
-};
22
-
23
-/**
24
- * React {@code Component} which can display speech-to-text results from
25
- * Jigasi as subtitles.
26
- */
27
-class TranscriptionSubtitles extends Component<Props> {
28
-
29
-    /**
30
-     * Implements React's {@link Component#render()}.
31
-     *
32
-     * @inheritdoc
33
-     * @returns {ReactElement}
34
-     */
35
-    render() {
36
-        if (!this.props._requestingSubtitles
37
-             || !this.props._transcriptMessages) {
38
-            return null;
39
-        }
40
-
41
-        const paragraphs = [];
42
-
43
-        for (const [ transcriptMessageID, transcriptMessage ]
44
-            of this.props._transcriptMessages) {
45
-            let text;
46
-
47
-            if (transcriptMessage) {
48
-                text = `${transcriptMessage.participantName}: `;
49
-
50
-                if (transcriptMessage.final) {
51
-                    text += transcriptMessage.final;
52
-                } else {
53
-                    const stable = transcriptMessage.stable || '';
54
-                    const unstable = transcriptMessage.unstable || '';
55
-
56
-                    text += stable + unstable;
57
-                }
58
-                paragraphs.push(
59
-                    <p key = { transcriptMessageID }>
60
-                        <span>{ text }</span>
61
-                    </p>
62
-                );
63
-            }
64
-        }
65
-
66
-        return (
67
-            <div className = 'transcription-subtitles' >
68
-                { paragraphs }
69
-            </div>
70
-        );
71
-    }
72
-}
73
-
74
-
75
-/**
76
- * Maps the transcriptionSubtitles in the Redux state to the associated
77
- * props of {@code TranscriptionSubtitles}.
78
- *
79
- * @param {Object} state - The Redux state.
80
- * @private
81
- * @returns {{
82
- *     _transcriptMessages: Map
83
- * }}
84
- */
85
-function _mapStateToProps(state) {
86
-    const {
87
-        _transcriptMessages,
88
-        _requestingSubtitles
89
-    } = state['features/subtitles'];
90
-
91
-    return {
92
-        _transcriptMessages,
93
-        _requestingSubtitles
94
-    };
95
-}
96
-export default connect(_mapStateToProps)(TranscriptionSubtitles);

+ 1
- 1
react/features/subtitles/components/index.js View File

1
-export { default as TranscriptionSubtitles } from './TranscriptionSubtitles';
1
+export { default as Captions } from './Captions';
2
 export { default as ClosedCaptionButton } from './ClosedCaptionButton';
2
 export { default as ClosedCaptionButton } from './ClosedCaptionButton';

+ 31
- 0
react/features/subtitles/components/styles.js View File

1
+// @flow
2
+
3
+import { BoxModel, ColorPalette, createStyleSheet } from '../../base/styles';
4
+
5
+/**
6
+ * The styles of the React {@code Component}s of the feature subtitles.
7
+ */
8
+export default createStyleSheet({
9
+
10
+    /**
11
+     * Style for subtitle paragraph.
12
+     */
13
+    subtitle: {
14
+        backgroundColor: ColorPalette.black,
15
+        borderRadius: BoxModel.margin / 4,
16
+        color: ColorPalette.white,
17
+        marginBottom: BoxModel.margin,
18
+        padding: BoxModel.padding / 2
19
+    },
20
+
21
+    /**
22
+     * Style for the subtitles container.
23
+     */
24
+    subtitlesContainer: {
25
+        alignItems: 'center',
26
+        flexDirection: 'column',
27
+        flexGrow: 0,
28
+        justifyContent: 'flex-end',
29
+        margin: BoxModel.margin
30
+    }
31
+});

+ 63
- 63
react/features/subtitles/middleware.js View File

2
 
2
 
3
 import { MiddlewareRegistry } from '../base/redux';
3
 import { MiddlewareRegistry } from '../base/redux';
4
 
4
 
5
-import {
6
-    ENDPOINT_MESSAGE_RECEIVED,
7
-    TOGGLE_REQUESTING_SUBTITLES
8
-} from './actionTypes';
9
 import {
5
 import {
10
     removeTranscriptMessage,
6
     removeTranscriptMessage,
11
     updateTranscriptMessage
7
     updateTranscriptMessage
12
 } from './actions';
8
 } from './actions';
9
+import {
10
+    ENDPOINT_MESSAGE_RECEIVED,
11
+    TOGGLE_REQUESTING_SUBTITLES
12
+} from './actionTypes';
13
 
13
 
14
 const logger = require('jitsi-meet-logger').getLogger(__filename);
14
 const logger = require('jitsi-meet-logger').getLogger(__filename);
15
 
15
 
25
  */
25
  */
26
 const JSON_TYPE_TRANSLATION_RESULT = 'translation-result';
26
 const JSON_TYPE_TRANSLATION_RESULT = 'translation-result';
27
 
27
 
28
-/**
29
- * The local participant property which is used to store the language
30
- * preference for translation for a participant.
31
- */
32
-const P_NAME_TRANSLATION_LANGUAGE = 'translation_language';
33
-
34
 /**
28
 /**
35
  * The local participant property which is used to set whether the local
29
  * The local participant property which is used to set whether the local
36
  * participant wants to have a transcriber in the room.
30
  * participant wants to have a transcriber in the room.
37
  */
31
  */
38
 const P_NAME_REQUESTING_TRANSCRIPTION = 'requestingTranscription';
32
 const P_NAME_REQUESTING_TRANSCRIPTION = 'requestingTranscription';
39
 
33
 
34
+/**
35
+ * The local participant property which is used to store the language
36
+ * preference for translation for a participant.
37
+ */
38
+const P_NAME_TRANSLATION_LANGUAGE = 'translation_language';
39
+
40
 /**
40
 /**
41
 * Time after which the rendered subtitles will be removed.
41
 * Time after which the rendered subtitles will be removed.
42
 */
42
 */
43
 const REMOVE_AFTER_MS = 3000;
43
 const REMOVE_AFTER_MS = 3000;
44
 
44
 
45
 /**
45
 /**
46
- * Middleware that catches actions related to transcript messages
47
- * to be rendered in {@link TranscriptionSubtitles }
46
+ * Middleware that catches actions related to transcript messages to be rendered
47
+ * in {@link Captions}.
48
  *
48
  *
49
- * @param {Store} store - Redux store.
49
+ * @param {Store} store - The redux store.
50
  * @returns {Function}
50
  * @returns {Function}
51
  */
51
  */
52
 MiddlewareRegistry.register(store => next => action => {
52
 MiddlewareRegistry.register(store => next => action => {
53
     switch (action.type) {
53
     switch (action.type) {
54
     case ENDPOINT_MESSAGE_RECEIVED:
54
     case ENDPOINT_MESSAGE_RECEIVED:
55
         return _endpointMessageReceived(store, next, action);
55
         return _endpointMessageReceived(store, next, action);
56
+
56
     case TOGGLE_REQUESTING_SUBTITLES:
57
     case TOGGLE_REQUESTING_SUBTITLES:
57
         _requestingSubtitlesToggled(store);
58
         _requestingSubtitlesToggled(store);
58
         break;
59
         break;
61
     return next(action);
62
     return next(action);
62
 });
63
 });
63
 
64
 
64
-/**
65
- * Toggle the local property 'requestingTranscription'. This will cause Jicofo
66
- * and Jigasi to decide whether the transcriber needs to be in the room.
67
- *
68
- * @param {Store} store - The redux store.
69
- * @private
70
- * @returns {void}
71
- */
72
-function _requestingSubtitlesToggled({ getState }) {
73
-    const { _requestingSubtitles } = getState()['features/subtitles'];
74
-    const { conference } = getState()['features/base/conference'];
75
-
76
-    conference.setLocalParticipantProperty(P_NAME_REQUESTING_TRANSCRIPTION,
77
-        !_requestingSubtitles);
78
-}
79
-
80
 /**
65
 /**
81
  * Notifies the feature transcription that the action
66
  * Notifies the feature transcription that the action
82
  * {@code ENDPOINT_MESSAGE_RECEIVED} is being dispatched within a specific redux
67
  * {@code ENDPOINT_MESSAGE_RECEIVED} is being dispatched within a specific redux
92
  * @returns {Object} The value returned by {@code next(action)}.
77
  * @returns {Object} The value returned by {@code next(action)}.
93
  */
78
  */
94
 function _endpointMessageReceived({ dispatch, getState }, next, action) {
79
 function _endpointMessageReceived({ dispatch, getState }, next, action) {
95
-    if (!(action.json
96
-        && (action.json.type === JSON_TYPE_TRANSCRIPTION_RESULT
97
-            || action.json.type === JSON_TYPE_TRANSLATION_RESULT))) {
80
+    const { json } = action;
81
+
82
+    if (!(json
83
+            && (json.type === JSON_TYPE_TRANSCRIPTION_RESULT
84
+                || json.type === JSON_TYPE_TRANSLATION_RESULT))) {
98
         return next(action);
85
         return next(action);
99
     }
86
     }
100
 
87
 
101
-    const json = action.json;
88
+    const state = getState();
102
     const translationLanguage
89
     const translationLanguage
103
-        = getState()['features/base/conference'].conference
90
+        = state['features/base/conference'].conference
104
             .getLocalParticipantProperty(P_NAME_TRANSLATION_LANGUAGE);
91
             .getLocalParticipantProperty(P_NAME_TRANSLATION_LANGUAGE);
105
 
92
 
106
     try {
93
     try {
107
         const transcriptMessageID = json.message_id;
94
         const transcriptMessageID = json.message_id;
108
         const participantName = json.participant.name;
95
         const participantName = json.participant.name;
109
-        const isInterim = json.is_interim;
110
-        const stability = json.stability;
111
 
96
 
112
         if (json.type === JSON_TYPE_TRANSLATION_RESULT
97
         if (json.type === JSON_TYPE_TRANSLATION_RESULT
113
-            && json.language === translationLanguage) {
98
+                && json.language === translationLanguage) {
114
             // Displays final results in the target language if translation is
99
             // Displays final results in the target language if translation is
115
             // enabled.
100
             // enabled.
116
 
101
 
117
             const newTranscriptMessage = {
102
             const newTranscriptMessage = {
118
-                participantName,
103
+                clearTimeOut: undefined,
119
                 final: json.text,
104
                 final: json.text,
120
-                clearTimeOut: undefined
105
+                participantName
121
             };
106
             };
122
 
107
 
123
-            setClearerOnTranscriptMessage(dispatch,
108
+            _setClearerOnTranscriptMessage(dispatch,
124
                 transcriptMessageID, newTranscriptMessage);
109
                 transcriptMessageID, newTranscriptMessage);
125
             dispatch(updateTranscriptMessage(transcriptMessageID,
110
             dispatch(updateTranscriptMessage(transcriptMessageID,
126
                 newTranscriptMessage));
111
                 newTranscriptMessage));
127
 
112
 
128
         } else if (json.type === JSON_TYPE_TRANSCRIPTION_RESULT
113
         } else if (json.type === JSON_TYPE_TRANSCRIPTION_RESULT
129
-            && !translationLanguage) {
114
+                && !translationLanguage) {
130
             // Displays interim and final results without any translation if
115
             // Displays interim and final results without any translation if
131
             // translations are disabled.
116
             // translations are disabled.
132
 
117
 
133
-            const text = json.transcript[0].text;
118
+            const { text } = json.transcript[0];
134
 
119
 
135
             // We update the previous transcript message with the same
120
             // We update the previous transcript message with the same
136
             // message ID or adds a new transcript message if it does not
121
             // message ID or adds a new transcript message if it does not
137
             // exist in the map.
122
             // exist in the map.
138
-            const newTranscriptMessage
139
-                = { ...getState()['features/subtitles']._transcriptMessages
140
-                    .get(transcriptMessageID) || { participantName } };
123
+            const newTranscriptMessage = {
124
+                ...state['features/subtitles']._transcriptMessages
125
+                        .get(transcriptMessageID)
126
+                    || { participantName }
127
+            };
141
 
128
 
142
-            setClearerOnTranscriptMessage(dispatch,
129
+            _setClearerOnTranscriptMessage(dispatch,
143
                 transcriptMessageID, newTranscriptMessage);
130
                 transcriptMessageID, newTranscriptMessage);
144
 
131
 
145
             // If this is final result, update the state as a final result
132
             // If this is final result, update the state as a final result
146
             // and start a count down to remove the subtitle from the state
133
             // and start a count down to remove the subtitle from the state
147
-            if (!isInterim) {
148
-
134
+            if (!json.is_interim) {
149
                 newTranscriptMessage.final = text;
135
                 newTranscriptMessage.final = text;
150
 
136
 
151
-                dispatch(updateTranscriptMessage(transcriptMessageID,
152
-                    newTranscriptMessage));
153
-            } else if (stability > 0.85) {
154
-
137
+            } else if (json.stability > 0.85) {
155
                 // If the message has a high stability, we can update the
138
                 // If the message has a high stability, we can update the
156
                 // stable field of the state and remove the previously
139
                 // stable field of the state and remove the previously
157
                 // unstable results
140
                 // unstable results
158
-
159
                 newTranscriptMessage.stable = text;
141
                 newTranscriptMessage.stable = text;
160
                 newTranscriptMessage.unstable = undefined;
142
                 newTranscriptMessage.unstable = undefined;
161
 
143
 
162
-                dispatch(updateTranscriptMessage(transcriptMessageID,
163
-                    newTranscriptMessage));
164
             } else {
144
             } else {
165
                 // Otherwise, this result has an unstable result, which we
145
                 // Otherwise, this result has an unstable result, which we
166
                 // add to the state. The unstable result will be appended
146
                 // add to the state. The unstable result will be appended
167
                 // after the stable part.
147
                 // after the stable part.
168
-
169
                 newTranscriptMessage.unstable = text;
148
                 newTranscriptMessage.unstable = text;
170
-                dispatch(updateTranscriptMessage(transcriptMessageID,
171
-                    newTranscriptMessage));
172
             }
149
             }
150
+
151
+            dispatch(
152
+                updateTranscriptMessage(
153
+                    transcriptMessageID,
154
+                    newTranscriptMessage));
173
         }
155
         }
174
     } catch (error) {
156
     } catch (error) {
175
         logger.error('Error occurred while updating transcriptions\n', error);
157
         logger.error('Error occurred while updating transcriptions\n', error);
178
     return next(action);
160
     return next(action);
179
 }
161
 }
180
 
162
 
163
+/**
164
+ * Toggle the local property 'requestingTranscription'. This will cause Jicofo
165
+ * and Jigasi to decide whether the transcriber needs to be in the room.
166
+ *
167
+ * @param {Store} store - The redux store.
168
+ * @private
169
+ * @returns {void}
170
+ */
171
+function _requestingSubtitlesToggled({ getState }) {
172
+    const state = getState();
173
+    const { _requestingSubtitles } = state['features/subtitles'];
174
+    const { conference } = state['features/base/conference'];
175
+
176
+    conference.setLocalParticipantProperty(
177
+        P_NAME_REQUESTING_TRANSCRIPTION,
178
+        !_requestingSubtitles);
179
+}
180
+
181
 /**
181
 /**
182
  * Set a timeout on a TranscriptMessage object so it clears itself when it's not
182
  * Set a timeout on a TranscriptMessage object so it clears itself when it's not
183
  * updated.
183
  * updated.
185
  * @param {Function} dispatch - Dispatch remove action to store.
185
  * @param {Function} dispatch - Dispatch remove action to store.
186
  * @param {string} transcriptMessageID - The id of the message to remove.
186
  * @param {string} transcriptMessageID - The id of the message to remove.
187
  * @param {Object} transcriptMessage - The message to remove.
187
  * @param {Object} transcriptMessage - The message to remove.
188
- *
189
  * @returns {void}
188
  * @returns {void}
190
  */
189
  */
191
-function setClearerOnTranscriptMessage(
190
+function _setClearerOnTranscriptMessage(
192
         dispatch,
191
         dispatch,
193
         transcriptMessageID,
192
         transcriptMessageID,
194
         transcriptMessage) {
193
         transcriptMessage) {
196
         clearTimeout(transcriptMessage.clearTimeOut);
195
         clearTimeout(transcriptMessage.clearTimeOut);
197
     }
196
     }
198
 
197
 
199
-    transcriptMessage.clearTimeOut = setTimeout(() => {
200
-        dispatch(removeTranscriptMessage(transcriptMessageID));
201
-    }, REMOVE_AFTER_MS);
198
+    transcriptMessage.clearTimeOut
199
+        = setTimeout(
200
+            () => dispatch(removeTranscriptMessage(transcriptMessageID)),
201
+            REMOVE_AFTER_MS);
202
 }
202
 }

Loading…
Cancel
Save