소스 검색

feat(RN): displays transcription subtitles

master
paweldomas 7 년 전
부모
커밋
26d906fa46

+ 5
- 0
react/features/base/conference/actions.js 파일 보기

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

+ 25
- 24
react/features/conference/components/Conference.native.js 파일 보기

@@ -22,6 +22,7 @@ import { FILMSTRIP_SIZE, Filmstrip, isFilmstripVisible } from '../../filmstrip';
22 22
 import { LargeVideo } from '../../large-video';
23 23
 import { CalleeInfoContainer } from '../../invite';
24 24
 import { NotificationsContainer } from '../../notifications';
25
+import { Captions } from '../../subtitles';
25 26
 import { setToolboxVisible, Toolbox } from '../../toolbox';
26 27
 
27 28
 import styles from './styles';
@@ -283,18 +284,20 @@ class Conference extends Component<Props> {
283 284
                 <View
284 285
                     pointerEvents = 'box-none'
285 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 291
                         this._renderNotificationsContainer()
293 292
                     }
293
+
294
+                    <Captions onPress = { this._onClick } />
295
+
294 296
                     {/*
295 297
                       * The Toolbox is in a stacking layer bellow the Filmstrip.
296 298
                       */}
297 299
                     <Toolbox />
300
+
298 301
                     {/*
299 302
                       * The Filmstrip is in a stacking layer above the
300 303
                       * LargeVideo. The LargeVideo and the Filmstrip form what
@@ -369,28 +372,26 @@ class Conference extends Component<Props> {
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 378
      * @private
379
+     * @returns {React$Element}
377 380
      */
378 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 395
             notificationsStyle.marginRight = FILMSTRIP_SIZE;
395 396
         }
396 397
 

+ 25
- 25
react/features/large-video/components/LargeVideo.web.js 파일 보기

@@ -1,34 +1,37 @@
1
-/* @flow */
1
+// @flow
2 2
 
3
-import PropTypes from 'prop-types';
4 3
 import React, { Component } from 'react';
5 4
 
6 5
 import { Watermarks } from '../../base/react';
7
-import { TranscriptionSubtitles } from '../../subtitles/';
6
+import { Captions } from '../../subtitles/';
8 7
 
9 8
 import Labels from './Labels';
10 9
 
11 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 24
  * Implements a React {@link Component} which represents the large video (a.k.a.
15 25
  * the conference participant who is on the local stage) on Web/React.
16 26
  *
17 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 31
      * Implements React's {@link Component#render()}.
29 32
      *
30 33
      * @inheritdoc
31
-     * @returns {ReactElement}
34
+     * @returns {React$Element}
32 35
      */
33 36
     render() {
34 37
         return (
@@ -52,18 +55,15 @@ export default class LargeVideo extends Component<*> {
52 55
                 <span id = 'remoteConnectionMessage' />
53 56
                 <div id = 'largeVideoElementsContainer'>
54 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 67
                     <div id = 'largeVideoWrapper'>
68 68
                         <video
69 69
                             autoPlay = { true }
@@ -72,10 +72,10 @@ export default class LargeVideo extends Component<*> {
72 72
                     </div>
73 73
                 </div>
74 74
                 { interfaceConfig.DISABLE_TRANSCRIPTION_SUBTITLES
75
-                    ? null : <TranscriptionSubtitles /> }
75
+                    || <Captions /> }
76 76
                 <span id = 'localConnectionMessage' />
77 77
                 { this.props.hideVideoQualityLabel
78
-                    ? null : <Labels /> }
78
+                    || <Labels /> }
79 79
             </div>
80 80
         );
81 81
     }

+ 128
- 0
react/features/subtitles/components/AbstractCaptions.js 파일 보기

@@ -0,0 +1,128 @@
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 파일 보기

@@ -0,0 +1,68 @@
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 파일 보기

@@ -0,0 +1,55 @@
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 파일 보기


+ 0
- 96
react/features/subtitles/components/TranscriptionSubtitles.web.js 파일 보기

@@ -1,96 +0,0 @@
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 파일 보기

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

+ 31
- 0
react/features/subtitles/components/styles.js 파일 보기

@@ -0,0 +1,31 @@
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 파일 보기

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