소스 검색

[RN] Add ExpandedLabel

j8
Bettenbuk Zoltan 7 년 전
부모
커밋
e5cc732b72
27개의 변경된 파일860개의 추가작업 그리고 75개의 파일을 삭제
  1. 8
    0
      lang/main.json
  2. 2
    2
      package-lock.json
  3. 2
    1
      react/features/base/label/components/AbstractCircularLabel.js
  4. 114
    8
      react/features/base/label/components/CircularLabel.native.js
  5. 1
    1
      react/features/base/label/components/CircularLabel.web.js
  6. 135
    0
      react/features/base/label/components/ExpandedLabel.native.js
  7. 0
    0
      react/features/base/label/components/ExpandedLabel.web.js
  8. 1
    0
      react/features/base/label/components/index.js
  9. 51
    6
      react/features/base/label/components/styles.js
  10. 281
    26
      react/features/large-video/components/Labels.native.js
  11. 14
    8
      react/features/large-video/components/styles.js
  12. 10
    16
      react/features/recording/components/AbstractRecordingLabel.js
  13. 108
    0
      react/features/recording/components/RecordingExpandedLabel.native.js
  14. 0
    0
      react/features/recording/components/RecordingExpandedLabel.web.js
  15. 13
    2
      react/features/recording/components/RecordingLabel.native.js
  16. 1
    2
      react/features/recording/components/RecordingLabel.web.js
  17. 1
    0
      react/features/recording/components/index.js
  18. 5
    2
      react/features/recording/components/styles.js
  19. 14
    0
      react/features/recording/constants.js
  20. 29
    0
      react/features/recording/functions.js
  21. 25
    0
      react/features/transcribing/components/TranscribingExpandedLabel.native.js
  22. 0
    0
      react/features/transcribing/components/TranscribingExpandedLabel.web.js
  23. 3
    0
      react/features/transcribing/components/index.js
  24. 36
    0
      react/features/video-quality/components/VideoQualityExpandedLabel.native.js
  25. 0
    0
      react/features/video-quality/components/VideoQualityExpandedLabel.web.js
  26. 3
    0
      react/features/video-quality/components/index.js
  27. 3
    1
      react/features/video-quality/components/styles.js

+ 8
- 0
lang/main.json 파일 보기

462
         "busyTitle": "All recorders are currently busy",
462
         "busyTitle": "All recorders are currently busy",
463
         "buttonTooltip": "Start / Stop recording",
463
         "buttonTooltip": "Start / Stop recording",
464
         "error": "Recording failed. Please try again.",
464
         "error": "Recording failed. Please try again.",
465
+        "expandedOff": "Recording has stopped",
466
+        "expandedOn": "The meeting is currently being recorded.",
467
+        "expandedPending": "Recording is being started...",
465
         "failedToStart": "Recording failed to start",
468
         "failedToStart": "Recording failed to start",
466
         "live": "LIVE",
469
         "live": "LIVE",
467
         "off": "Recording stopped",
470
         "off": "Recording stopped",
483
         "pending" : "Preparing to transcribe the meeting...",
486
         "pending" : "Preparing to transcribe the meeting...",
484
         "off" : "Transcribing stopped",
487
         "off" : "Transcribing stopped",
485
         "error": "Transcribing failed. Please try again.",
488
         "error": "Transcribing failed. Please try again.",
489
+        "expandedLabel": "Transcribing is currently on",
486
         "failedToStart": "Transcribing failed to start",
490
         "failedToStart": "Transcribing failed to start",
487
         "tr": "TR",
491
         "tr": "TR",
488
         "labelToolTip": "The meeting is being transcribed",
492
         "labelToolTip": "The meeting is being transcribed",
502
         "error": "Live Streaming failed. Please try again.",
506
         "error": "Live Streaming failed. Please try again.",
503
         "errorAPI": "An error occurred while accessing your YouTube broadcasts. Please try logging in again.",
507
         "errorAPI": "An error occurred while accessing your YouTube broadcasts. Please try logging in again.",
504
         "errorLiveStreamNotEnabled": "Live Streaming is not enabled on __email__. Please enable live streaming or log into an account with live streaming enabled.",
508
         "errorLiveStreamNotEnabled": "Live Streaming is not enabled on __email__. Please enable live streaming or log into an account with live streaming enabled.",
509
+        "expandedOff": "The live streaming has stopped",
510
+        "expandedOn": "The meeting is currently being streamed to YouTube.",
511
+        "expandedPending": "The live streaming is being started...",
505
         "failedToStart": "Live Streaming failed to start",
512
         "failedToStart": "Live Streaming failed to start",
506
         "off": "Live Streaming stopped",
513
         "off": "Live Streaming stopped",
507
         "on": "Live Streaming",
514
         "on": "Live Streaming",
546
     },
553
     },
547
     "videoStatus": {
554
     "videoStatus": {
548
         "audioOnly": "AUD",
555
         "audioOnly": "AUD",
556
+        "audioOnlyExpanded": "You are in audio only mode. This mode saves bandwidth but you won't see videos of others.",
549
         "callQuality": "Call Quality",
557
         "callQuality": "Call Quality",
550
         "hd": "HD",
558
         "hd": "HD",
551
         "hdTooltip": "Viewing high definition video",
559
         "hdTooltip": "Viewing high definition video",

+ 2
- 2
package-lock.json 파일 보기

441
     },
441
     },
442
     "@atlaskit/inline-dialog": {
442
     "@atlaskit/inline-dialog": {
443
       "version": "5.3.0",
443
       "version": "5.3.0",
444
-      "resolved": "http://registry.npmjs.org/@atlaskit/inline-dialog/-/inline-dialog-5.3.0.tgz",
444
+      "resolved": "https://registry.npmjs.org/@atlaskit/inline-dialog/-/inline-dialog-5.3.0.tgz",
445
       "integrity": "sha512-4bEeC5rZwtb4YO9BxW1UCJYCp/dyCVXqcygRW1BDnYVbveAI8wdym6qEi4BRvIwXCT4qgNhsVsqcxSrn0X6CKQ==",
445
       "integrity": "sha512-4bEeC5rZwtb4YO9BxW1UCJYCp/dyCVXqcygRW1BDnYVbveAI8wdym6qEi4BRvIwXCT4qgNhsVsqcxSrn0X6CKQ==",
446
       "requires": {
446
       "requires": {
447
         "@atlaskit/layer": "^2.8.0",
447
         "@atlaskit/layer": "^2.8.0",
451
       "dependencies": {
451
       "dependencies": {
452
         "@atlaskit/layer": {
452
         "@atlaskit/layer": {
453
           "version": "2.9.1",
453
           "version": "2.9.1",
454
-          "resolved": "http://registry.npmjs.org/@atlaskit/layer/-/layer-2.9.1.tgz",
454
+          "resolved": "https://registry.npmjs.org/@atlaskit/layer/-/layer-2.9.1.tgz",
455
           "integrity": "sha512-nyIVGeS2OhuGR5gIMTYUfRmCG8z/9KMgUzTpbpsB70sH6+d4KSFhfkz+KhKNIa8gvKI6zBc+3UBYSlUW1t1qmQ==",
455
           "integrity": "sha512-nyIVGeS2OhuGR5gIMTYUfRmCG8z/9KMgUzTpbpsB70sH6+d4KSFhfkz+KhKNIa8gvKI6zBc+3UBYSlUW1t1qmQ==",
456
           "requires": {
456
           "requires": {
457
             "styled-components": "1.4.6 - 3"
457
             "styled-components": "1.4.6 - 3"

+ 2
- 1
react/features/base/label/components/AbstractCircularLabel.js 파일 보기

12
 /**
12
 /**
13
  * Abstract class for the {@code CircularLabel} component.
13
  * Abstract class for the {@code CircularLabel} component.
14
  */
14
  */
15
-export default class AbstractCircularLabel<P: Props> extends Component<P> {
15
+export default class AbstractCircularLabel<P: Props, S: *>
16
+    extends Component<P, S> {
16
 
17
 
17
 }
18
 }

+ 114
- 8
react/features/base/label/components/CircularLabel.native.js 파일 보기

1
 // @flow
1
 // @flow
2
 import React from 'react';
2
 import React from 'react';
3
-import { Text, View } from 'react-native';
3
+import { Animated, Text } from 'react-native';
4
 
4
 
5
 import { combineStyles, type StyleType } from '../../styles';
5
 import { combineStyles, type StyleType } from '../../styles';
6
 
6
 
9
 } from './AbstractCircularLabel';
9
 } from './AbstractCircularLabel';
10
 import styles from './styles';
10
 import styles from './styles';
11
 
11
 
12
+/**
13
+ * Const for status string 'in progress'.
14
+ */
15
+const STATUS_IN_PROGRESS = 'in_progress';
16
+
17
+/**
18
+ * Const for status string 'off'.
19
+ */
20
+const STATUS_OFF = 'off';
21
+
12
 type Props = AbstractProps & {
22
 type Props = AbstractProps & {
13
 
23
 
24
+    /**
25
+     * Status of the label. This prop adds some additional styles based on its
26
+     * value. E.g. if status = off, it will render the label symbolising that
27
+     * the thing it displays (e.g. recording) is off.
28
+     */
29
+    status: ('in_progress' | 'off' | 'on'),
30
+
14
     /**
31
     /**
15
      * Style of the label.
32
      * Style of the label.
16
      */
33
      */
17
     style?: ?StyleType
34
     style?: ?StyleType
18
 };
35
 };
19
 
36
 
37
+type State = {
38
+
39
+    /**
40
+     * An animation object handling the opacity changes of the in progress
41
+     * label.
42
+     */
43
+    pulseAnimation: Object
44
+}
45
+
20
 /**
46
 /**
21
  * Renders a circular indicator to be used for status icons, such as recording
47
  * Renders a circular indicator to be used for status icons, such as recording
22
  * on, audio-only conference, video quality and similar.
48
  * on, audio-only conference, video quality and similar.
23
  */
49
  */
24
-export default class CircularLabel extends AbstractCircularLabel<Props> {
50
+export default class CircularLabel extends AbstractCircularLabel<Props, State> {
51
+    /**
52
+     * A reference to the started animation of this label.
53
+     */
54
+    animationReference: Object;
55
+
56
+    /**
57
+     * Instantiates a new instance of {@code CircularLabel}.
58
+     *
59
+     * @inheritdoc
60
+     */
61
+    constructor(props: Props) {
62
+        super(props);
63
+
64
+        this.state = {
65
+            pulseAnimation: new Animated.Value(0)
66
+        };
67
+
68
+        this._maybeToggleAnimation({}, props);
69
+    }
70
+
71
+    /**
72
+     * Implements {@code Component#componentWillReceiveProps}.
73
+     *
74
+     * @inheritdoc
75
+     */
76
+    componentWillReceiveProps(newProps: Props) {
77
+        this._maybeToggleAnimation(this.props, newProps);
78
+    }
79
+
25
     /**
80
     /**
26
      * Implements React {@link Component}'s render.
81
      * Implements React {@link Component}'s render.
27
      *
82
      *
28
      * @inheritdoc
83
      * @inheritdoc
29
      */
84
      */
30
     render() {
85
     render() {
31
-        const { label, style } = this.props;
86
+        const { status, label, style } = this.props;
87
+
88
+        let extraStyle = null;
89
+
90
+        switch (status) {
91
+        case STATUS_IN_PROGRESS:
92
+            extraStyle = {
93
+                opacity: this.state.pulseAnimation
94
+            };
95
+            break;
96
+        case STATUS_OFF:
97
+            extraStyle = styles.labelOff;
98
+            break;
99
+        }
32
 
100
 
33
         return (
101
         return (
34
-            <View
35
-                style = {
36
-                    combineStyles(styles.indicatorContainer, style)
37
-                }>
102
+            <Animated.View
103
+                style = { [
104
+                    combineStyles(styles.indicatorContainer, style),
105
+                    extraStyle
106
+                ] }>
38
                 <Text style = { styles.indicatorText }>
107
                 <Text style = { styles.indicatorText }>
39
                     { label }
108
                     { label }
40
                 </Text>
109
                 </Text>
41
-            </View>
110
+            </Animated.View>
42
         );
111
         );
43
     }
112
     }
113
+
114
+    /**
115
+     * Checks if the animation has to be started or stopped and acts
116
+     * accordingly.
117
+     *
118
+     * @param {Props} oldProps - The previous values of the Props.
119
+     * @param {Props} newProps - The new values of the Props.
120
+     * @returns {void}
121
+     */
122
+    _maybeToggleAnimation(oldProps, newProps) {
123
+        const { status: oldStatus } = oldProps;
124
+        const { status: newStatus } = newProps;
125
+        const { pulseAnimation } = this.state;
126
+
127
+        if (newStatus === STATUS_IN_PROGRESS
128
+                && oldStatus !== STATUS_IN_PROGRESS) {
129
+            // Animation must be started
130
+            this.animationReference = Animated.loop(Animated.sequence([
131
+                Animated.timing(pulseAnimation, {
132
+                    delay: 500,
133
+                    toValue: 1,
134
+                    useNativeDriver: true
135
+                }),
136
+                Animated.timing(pulseAnimation, {
137
+                    toValue: 0.3,
138
+                    useNativeDriver: true
139
+                })
140
+            ]));
141
+
142
+            this.animationReference.start();
143
+        } else if (this.animationReference
144
+                && newStatus !== STATUS_IN_PROGRESS
145
+                && oldStatus === STATUS_IN_PROGRESS) {
146
+            // Animation must be stopped
147
+            this.animationReference.stop();
148
+        }
149
+    }
44
 }
150
 }

+ 1
- 1
react/features/base/label/components/CircularLabel.web.js 파일 보기

25
  *
25
  *
26
  * @extends Component
26
  * @extends Component
27
  */
27
  */
28
-export default class CircularLabel extends AbstractCircularLabel<Props> {
28
+export default class CircularLabel extends AbstractCircularLabel<Props, {}> {
29
     /**
29
     /**
30
      * Default values for {@code CircularLabel} component's properties.
30
      * Default values for {@code CircularLabel} component's properties.
31
      *
31
      *

+ 135
- 0
react/features/base/label/components/ExpandedLabel.native.js 파일 보기

1
+// @flow
2
+
3
+import React, { Component } from 'react';
4
+import { Animated, Text, View } from 'react-native';
5
+
6
+import styles, { DEFAULT_COLOR, LABEL_MARGIN, LABEL_SIZE } from './styles';
7
+
8
+export type Props = {
9
+
10
+    /**
11
+     * The position of the parent element (from right to left) to display the
12
+     * arrow.
13
+     */
14
+    parentPosition: number
15
+};
16
+
17
+type State = {
18
+
19
+    /**
20
+     * The opacity animation Object.
21
+     */
22
+    opacityAnimation: Object,
23
+
24
+    /**
25
+     * A boolean to descide to show or not show the arrow. This is required as
26
+     * we can't easily animate this transformed Component so we render it once
27
+     * the animation is done.
28
+     */
29
+    showArrow: boolean
30
+};
31
+
32
+/**
33
+ * Offset to the arrow to be rendered in the right position.
34
+ */
35
+const ARROW_OFFSET = 0;
36
+
37
+/**
38
+ * A react {@code Component} that implements an expanded label as tooltip-like
39
+ * component to explain the meaning of the {@code Label}.
40
+ */
41
+export default class ExpandedLabel<P: Props> extends Component<P, State> {
42
+    /**
43
+     * Instantiates a new {@code ExpandedLabel} instance.
44
+     *
45
+     * @inheritdoc
46
+     */
47
+    constructor(props: P) {
48
+        super(props);
49
+
50
+        this.state = {
51
+            opacityAnimation: new Animated.Value(0),
52
+            showArrow: false
53
+        };
54
+    }
55
+
56
+    /**
57
+     * Implements React {@code Component}'s componentDidMount.
58
+     *
59
+     * @inheritdoc
60
+     */
61
+    componentDidMount() {
62
+        Animated.decay(this.state.opacityAnimation, {
63
+            toValue: 1,
64
+            velocity: 1,
65
+            useNativeDriver: true
66
+        }).start(({ finished }) => {
67
+            finished && this.setState({
68
+                showArrow: true
69
+            });
70
+        });
71
+    }
72
+
73
+    /**
74
+     * Implements React {@code Component}'s render.
75
+     *
76
+     * @inheritdoc
77
+     */
78
+    render() {
79
+        const arrowPosition
80
+            = this.props.parentPosition - LABEL_MARGIN - (LABEL_SIZE / 2);
81
+
82
+        return (
83
+            <Animated.View
84
+                style = { [
85
+                    styles.expandedLabelWrapper,
86
+                    {
87
+                        opacity: this.state.opacityAnimation
88
+                    }
89
+                ] } >
90
+                <View
91
+                    style = { [
92
+                        styles.expandedLabelArrow,
93
+                        {
94
+                            backgroundColor: this._getColor() || DEFAULT_COLOR,
95
+                            marginRight: arrowPosition + ARROW_OFFSET
96
+                        }
97
+                    ] } />
98
+                <View
99
+                    style = { [
100
+                        styles.expandedLabelContainer,
101
+                        {
102
+                            backgroundColor: this._getColor() || DEFAULT_COLOR
103
+                        }
104
+                    ] }>
105
+                    <Text style = { styles.expandedLabelText }>
106
+                        { this._getLabel() }
107
+                    </Text>
108
+                </View>
109
+            </Animated.View>
110
+        );
111
+    }
112
+
113
+    /**
114
+     * Returns the label that needs to be rendered in the box. To be implemented
115
+     * by its overriding classes.
116
+     *
117
+     * @returns {string}
118
+     */
119
+    _getLabel: () => string
120
+
121
+    _getColor: () => string
122
+
123
+    /**
124
+     * Defines the color of the expanded label. This function returns a default
125
+     * value if implementing classes don't override it, but the goal is to have
126
+     * expanded labels matching to circular labels in color.
127
+     * If implementing classes return a falsy value, it also uses the default
128
+     * color.
129
+     *
130
+     * @returns {string}
131
+     */
132
+    _getColor() {
133
+        return DEFAULT_COLOR;
134
+    }
135
+}

+ 0
- 0
react/features/base/label/components/ExpandedLabel.web.js 파일 보기


+ 1
- 0
react/features/base/label/components/index.js 파일 보기

1
 export { default as CircularLabel } from './CircularLabel';
1
 export { default as CircularLabel } from './CircularLabel';
2
+export { default as ExpandedLabel } from './ExpandedLabel';

+ 51
- 6
react/features/base/label/components/styles.js 파일 보기

1
 // @flow
1
 // @flow
2
 
2
 
3
-import { ColorPalette, createStyleSheet } from '../../styles';
3
+import { BoxModel, ColorPalette, createStyleSheet } from '../../styles';
4
+
5
+/**
6
+ * The default color of the {@code Label} and {@code ExpandedLabel}.
7
+ */
8
+export const DEFAULT_COLOR = '#808080';
9
+
10
+/**
11
+ * Margin of the {@Label} - to be reused when rendering the
12
+ * {@code ExpandedLabel}.
13
+ */
14
+export const LABEL_MARGIN = 5;
15
+
16
+/**
17
+ * Size of the {@Label} - to be reused when rendering the
18
+ * {@code ExpandedLabel}.
19
+ */
20
+export const LABEL_SIZE = 36;
4
 
21
 
5
 /**
22
 /**
6
  * The styles of the native base/label feature.
23
  * The styles of the native base/label feature.
7
  */
24
  */
8
 export default createStyleSheet({
25
 export default createStyleSheet({
9
 
26
 
27
+    expandedLabelArrow: {
28
+        backgroundColor: ColorPalette.blue,
29
+        height: 15,
30
+        transform: [ { rotate: '45deg' }, { translateX: 10 } ],
31
+        width: 15
32
+    },
33
+
34
+    expandedLabelContainer: {
35
+        backgroundColor: ColorPalette.blue,
36
+        borderColor: ColorPalette.blue,
37
+        borderRadius: 6,
38
+        marginHorizontal: BoxModel.margin,
39
+        padding: BoxModel.padding
40
+    },
41
+
42
+    expandedLabelText: {
43
+        color: ColorPalette.white
44
+    },
45
+
46
+    expandedLabelWrapper: {
47
+        alignItems: 'flex-end',
48
+        flexDirection: 'column'
49
+    },
50
+
10
     /**
51
     /**
11
      * The outermost view.
52
      * The outermost view.
12
      */
53
      */
13
     indicatorContainer: {
54
     indicatorContainer: {
14
         alignItems: 'center',
55
         alignItems: 'center',
15
-        backgroundColor: '#808080',
16
-        borderRadius: 18,
56
+        backgroundColor: DEFAULT_COLOR,
57
+        borderRadius: LABEL_SIZE / 2,
17
         borderWidth: 0,
58
         borderWidth: 0,
18
         flex: 0,
59
         flex: 0,
19
-        height: 36,
60
+        height: LABEL_SIZE,
20
         justifyContent: 'center',
61
         justifyContent: 'center',
21
-        margin: 5,
62
+        margin: LABEL_MARGIN,
22
         opacity: 0.6,
63
         opacity: 0.6,
23
-        width: 36
64
+        width: LABEL_SIZE
24
     },
65
     },
25
 
66
 
26
     indicatorText: {
67
     indicatorText: {
27
         color: ColorPalette.white,
68
         color: ColorPalette.white,
28
         fontSize: 12
69
         fontSize: 12
70
+    },
71
+
72
+    labelOff: {
73
+        opacity: 0.3
29
     }
74
     }
30
 });
75
 });

+ 281
- 26
react/features/large-video/components/Labels.native.js 파일 보기

1
 // @flow
1
 // @flow
2
 
2
 
3
 import React from 'react';
3
 import React from 'react';
4
-import { View } from 'react-native';
4
+import { TouchableOpacity, View } from 'react-native';
5
 import { connect } from 'react-redux';
5
 import { connect } from 'react-redux';
6
 
6
 
7
 import { JitsiRecordingConstants } from '../../base/lib-jitsi-meet';
7
 import { JitsiRecordingConstants } from '../../base/lib-jitsi-meet';
8
+import {
9
+    RecordingExpandedLabel
10
+} from '../../recording';
8
 import {
11
 import {
9
     isNarrowAspectRatio,
12
     isNarrowAspectRatio,
10
     makeAspectRatioAware
13
     makeAspectRatioAware
11
 } from '../../base/responsive-ui';
14
 } from '../../base/responsive-ui';
15
+import { TranscribingExpandedLabel } from '../../transcribing';
16
+import { VideoQualityExpandedLabel } from '../../video-quality';
12
 
17
 
13
 import AbstractLabels, {
18
 import AbstractLabels, {
14
     _abstractMapStateToProps,
19
     _abstractMapStateToProps,
21
  */
26
  */
22
 type Props = AbstractLabelsProps & {
27
 type Props = AbstractLabelsProps & {
23
 
28
 
29
+    /**
30
+     * Function to translate i18n labels.
31
+     */
32
+    t: Function,
33
+
24
     /**
34
     /**
25
      * The indicator which determines whether the UI is reduced (to accommodate
35
      * The indicator which determines whether the UI is reduced (to accommodate
26
      * smaller display areas).
36
      * smaller display areas).
30
     _reducedUI: boolean
40
     _reducedUI: boolean
31
 };
41
 };
32
 
42
 
43
+type State = {
44
+
45
+    /**
46
+     * Layout object of the outermost container. For stucture please see:
47
+     * https://facebook.github.io/react-native/docs/view#onlayout
48
+     */
49
+    containerLayout: ?Object,
50
+
51
+    /**
52
+     * Layout objects of the individual labels. This data type contains the same
53
+     * structure as the layout is defined here:
54
+     * https://facebook.github.io/react-native/docs/view#onlayout
55
+     * but keyed with the ID of the label its layout it contains. E.g.
56
+     *
57
+     * {
58
+     *   transcribing: {
59
+     *     { layout: { x, y, width, height } }
60
+     *   },
61
+     *   ...
62
+     * }
63
+     */
64
+    labelLayouts: Object,
65
+
66
+    /**
67
+     * Position of the label to render the {@code ExpandedLabel} to.
68
+     */
69
+    parentPosition: ?number,
70
+
71
+    /**
72
+     * String to show which {@code ExpandedLabel} to be shown. (Equals to the
73
+     * label IDs below.)
74
+     */
75
+    visibleExpandedLabel: ?string
76
+}
77
+
78
+const LABEL_ID_QUALITY = 'quality';
79
+const LABEL_ID_RECORDING = 'recording';
80
+const LABEL_ID_STREAMING = 'streaming';
81
+const LABEL_ID_TRANSCRIBING = 'transcribing';
82
+
83
+/**
84
+ * The {@code ExpandedLabel} components to be rendered for the individual
85
+ * {@code Label}s.
86
+ */
87
+const EXPANDED_LABELS = {
88
+    quality: VideoQualityExpandedLabel,
89
+    recording: {
90
+        component: RecordingExpandedLabel,
91
+        props: {
92
+            mode: JitsiRecordingConstants.mode.FILE
93
+        }
94
+    },
95
+    streaming: {
96
+        component: RecordingExpandedLabel,
97
+        props: {
98
+            mode: JitsiRecordingConstants.mode.STREAM
99
+        }
100
+    },
101
+    transcribing: TranscribingExpandedLabel
102
+};
103
+
104
+/**
105
+ * Timeout to hide the {@ExpandedLabel}.
106
+ */
107
+const EXPANDED_LABEL_TIMEOUT = 5000;
108
+
33
 /**
109
 /**
34
  * A container that renders the conference indicators, if any.
110
  * A container that renders the conference indicators, if any.
35
  */
111
  */
36
-class Labels extends AbstractLabels<Props, *> {
112
+class Labels extends AbstractLabels<Props, State> {
113
+    /**
114
+     * Timeout for the expanded labels to disappear.
115
+     */
116
+    expandedLabelTimeout: TimeoutID;
117
+
118
+    /**
119
+     * Instantiates a new instance of {@code Labels}.
120
+     *
121
+     * @inheritdoc
122
+     */
123
+    constructor(props: Props) {
124
+        super(props);
125
+
126
+        this.state = {
127
+            containerLayout: undefined,
128
+            labelLayouts: {},
129
+            parentPosition: undefined,
130
+            visibleExpandedLabel: undefined
131
+        };
132
+
133
+        this._onTopViewLayout = this._onTopViewLayout.bind(this);
134
+    }
135
+
136
+    /**
137
+     * Implements React {@code Component}'s componentWillUnmount.
138
+     *
139
+     * @inheritdoc
140
+     */
141
+    componentWillUnmount() {
142
+        clearTimeout(this.expandedLabelTimeout);
143
+    }
144
+
37
     /**
145
     /**
38
      * Implements React {@code Component}'s render.
146
      * Implements React {@code Component}'s render.
39
      *
147
      *
46
         return (
154
         return (
47
             <View
155
             <View
48
                 pointerEvents = 'box-none'
156
                 pointerEvents = 'box-none'
49
-                style = { [
50
-                    styles.indicatorContainer,
51
-                    wide && _filmstripVisible && styles.indicatorContainerWide
52
-                ] }>
53
-                {
54
-                    this._renderRecordingLabel(
55
-                        JitsiRecordingConstants.mode.FILE)
56
-                }
57
-                {
58
-                    this._renderRecordingLabel(
59
-                        JitsiRecordingConstants.mode.STREAM)
60
-                }
61
-                {
62
-                    this._renderTranscribingLabel()
63
-                }
64
-                {/*
65
-                  * Emil, Lyubomir, Nichole, and Zoli said that the Labels
66
-                  * should not be rendered in Picture-in-Picture. Saul argued
67
-                  * that the recording Labels should be rendered. As a temporary
68
-                  * compromise, don't render the VideoQualityLabel at least
69
-                  * because it's not that important.
70
-                  */
71
-                    _reducedUI || this._renderVideoQualityLabel()
72
-                }
157
+                style = { styles.labelWrapper }>
158
+                <View
159
+                    onLayout = { this._onTopViewLayout }
160
+                    pointerEvents = 'box-none'
161
+                    style = { [
162
+                        styles.indicatorContainer,
163
+                        wide && _filmstripVisible
164
+                            && styles.indicatorContainerWide
165
+                    ] }>
166
+                    <TouchableOpacity
167
+                        onLayout = { this._createOnLayout(LABEL_ID_RECORDING) }
168
+                        onPress = { this._createOnPress(LABEL_ID_RECORDING) } >
169
+                        {
170
+                            this._renderRecordingLabel(
171
+                                JitsiRecordingConstants.mode.FILE)
172
+                        }
173
+                    </TouchableOpacity>
174
+                    <TouchableOpacity
175
+                        onLayout = { this._createOnLayout(LABEL_ID_STREAMING) }
176
+                        onPress = { this._createOnPress(LABEL_ID_STREAMING) } >
177
+                        {
178
+                            this._renderRecordingLabel(
179
+                                JitsiRecordingConstants.mode.STREAM)
180
+                        }
181
+                    </TouchableOpacity>
182
+                    <TouchableOpacity
183
+                        onLayout = {
184
+                            this._createOnLayout(LABEL_ID_TRANSCRIBING)
185
+                        }
186
+                        onPress = {
187
+                            this._createOnPress(LABEL_ID_TRANSCRIBING)
188
+                        } >
189
+                        {
190
+                            this._renderTranscribingLabel()
191
+                        }
192
+                    </TouchableOpacity>
193
+                    {/*
194
+                      * Emil, Lyubomir, Nichole, and Zoli said that the Labels
195
+                      * should not be rendered in Picture-in-Picture. Saul
196
+                      * argued that the recording Labels should be rendered. As
197
+                      * a temporary compromise, don't render the
198
+                      * VideoQualityLabel at least because it's not that
199
+                      * important.
200
+                      */
201
+                        _reducedUI || (
202
+                            <TouchableOpacity
203
+                                onLayout = {
204
+                                    this._createOnLayout(LABEL_ID_QUALITY) }
205
+                                onPress = {
206
+                                    this._createOnPress(LABEL_ID_QUALITY) } >
207
+                                { this._renderVideoQualityLabel() }
208
+                            </TouchableOpacity>
209
+                        )
210
+                    }
211
+                </View>
212
+                <View
213
+                    style = { [
214
+                        styles.indicatorContainer,
215
+                        wide && _filmstripVisible
216
+                            && styles.indicatorContainerWide
217
+                    ] }>
218
+                    {
219
+                        this._renderExpandedLabel()
220
+                    }
221
+                </View>
73
             </View>
222
             </View>
74
         );
223
         );
75
     }
224
     }
76
 
225
 
226
+    /**
227
+     * Creates a function to be invoked when the onLayout of the touchables are
228
+     * triggered.
229
+     *
230
+     * @param {string} label - The identifier of the label that's onLayout is
231
+     * triggered.
232
+     * @returns {Function}
233
+     */
234
+    _createOnLayout(label) {
235
+        return ({ nativeEvent: { layout } }) => {
236
+            const { labelLayouts } = this.state;
237
+            const updatedLayout = {};
238
+
239
+            updatedLayout[label] = layout;
240
+
241
+            this.setState({
242
+                labelLayouts: {
243
+                    ...labelLayouts,
244
+                    ...updatedLayout
245
+                }
246
+            });
247
+        };
248
+    }
249
+
250
+    /**
251
+     * Creates a function to be invoked when the onPress of the touchables are
252
+     * triggered.
253
+     *
254
+     * @param {string} label - The identifier of the label that's onLayout is
255
+     * triggered.
256
+     * @returns {Function}
257
+     */
258
+    _createOnPress(label) {
259
+        return () => {
260
+            const {
261
+                containerLayout,
262
+                labelLayouts
263
+            } = this.state;
264
+            let { visibleExpandedLabel } = this.state;
265
+
266
+            if (containerLayout) {
267
+                const labelLayout = labelLayouts[label];
268
+
269
+                // This calculation has to be changed if the labels are not
270
+                // positioned right anymore.
271
+                const right = containerLayout.width - labelLayout.x;
272
+
273
+                visibleExpandedLabel
274
+                    = visibleExpandedLabel === label ? undefined : label;
275
+
276
+                clearTimeout(this.expandedLabelTimeout);
277
+                this.setState({
278
+                    parentPosition: right,
279
+                    visibleExpandedLabel
280
+                });
281
+
282
+                if (visibleExpandedLabel) {
283
+                    this.expandedLabelTimeout = setTimeout(() => {
284
+                        this.setState({
285
+                            visibleExpandedLabel: undefined
286
+                        });
287
+                    }, EXPANDED_LABEL_TIMEOUT);
288
+                }
289
+            }
290
+        };
291
+    }
292
+
293
+    _onTopViewLayout: Object => void
294
+
295
+    /**
296
+     * Invoked when the View containing the {@code Label}s is laid out.
297
+     *
298
+     * @param {Object} layout - The native layout object.
299
+     * @returns {void}
300
+     */
301
+    _onTopViewLayout({ nativeEvent: { layout } }) {
302
+        this.setState({
303
+            containerLayout: layout
304
+        });
305
+    }
306
+
307
+    /**
308
+     * Rendes the expanded (explaining) label for the label that was touched.
309
+     *
310
+     * @returns {React$Element}
311
+     */
312
+    _renderExpandedLabel() {
313
+        const { parentPosition, visibleExpandedLabel } = this.state;
314
+
315
+        if (visibleExpandedLabel) {
316
+            const expandedLabel = EXPANDED_LABELS[visibleExpandedLabel];
317
+
318
+            if (expandedLabel) {
319
+                const component = expandedLabel.component || expandedLabel;
320
+                const expandedLabelProps = expandedLabel.props || {};
321
+
322
+                return React.createElement(component, {
323
+                    ...expandedLabelProps,
324
+                    parentPosition
325
+                });
326
+            }
327
+        }
328
+
329
+        return null;
330
+    }
331
+
77
     _renderRecordingLabel: string => React$Element<*>;
332
     _renderRecordingLabel: string => React$Element<*>;
78
 
333
 
79
     _renderTranscribingLabel: () => React$Element<*>
334
     _renderTranscribingLabel: () => React$Element<*>

+ 14
- 8
react/features/large-video/components/styles.js 파일 보기

9
 export const AVATAR_SIZE = 200;
9
 export const AVATAR_SIZE = 200;
10
 
10
 
11
 export default createStyleSheet({
11
 export default createStyleSheet({
12
+
12
     /**
13
     /**
13
      * View that contains the indicators.
14
      * View that contains the indicators.
14
      */
15
      */
15
     indicatorContainer: {
16
     indicatorContainer: {
16
         flex: 1,
17
         flex: 1,
17
         flexDirection: 'row',
18
         flexDirection: 'row',
18
-        margin: BoxModel.margin,
19
+        justifyContent: 'flex-end',
20
+        margin: BoxModel.margin
21
+    },
22
+
23
+    /**
24
+     * Indicator container for wide aspect ratio.
25
+     */
26
+    indicatorContainerWide: {
27
+        marginRight: FILMSTRIP_SIZE + BoxModel.margin
28
+    },
29
+
30
+    labelWrapper: {
31
+        flexDirection: 'column',
19
         position: 'absolute',
32
         position: 'absolute',
20
         right: 0,
33
         right: 0,
21
 
34
 
25
         top: BoxModel.margin * 3
38
         top: BoxModel.margin * 3
26
     },
39
     },
27
 
40
 
28
-    /**
29
-     * Indicator container for wide aspect ratio.
30
-     */
31
-    indicatorContainerWide: {
32
-        right: FILMSTRIP_SIZE
33
-    },
34
-
35
     /**
41
     /**
36
      * Large video container style.
42
      * Large video container style.
37
      */
43
      */

+ 10
- 16
react/features/recording/components/AbstractRecordingLabel.js 파일 보기

4
 
4
 
5
 import { JitsiRecordingConstants } from '../../base/lib-jitsi-meet';
5
 import { JitsiRecordingConstants } from '../../base/lib-jitsi-meet';
6
 
6
 
7
+import { getSessionStatusToShow } from '../functions';
8
+
7
 /**
9
 /**
8
  * NOTE: Web currently renders multiple indicators if multiple recording
10
  * NOTE: Web currently renders multiple indicators if multiple recording
9
  * sessions are running. This is however may not be a good UX as it's not
11
  * sessions are running. This is however may not be a good UX as it's not
12
  * running. These boolean are shared across the two components to make it
14
  * running. These boolean are shared across the two components to make it
13
  * easier to align web's behaviour to mobile's later if necessary.
15
  * easier to align web's behaviour to mobile's later if necessary.
14
  */
16
  */
15
-export type Props = {
17
+type Props = {
16
 
18
 
17
     /**
19
     /**
18
-     * True if there is an active recording with the provided mode therefore the
19
-     * component must be rendered.
20
+     * The status of the highermost priority session.
20
      */
21
      */
21
-    _visible: boolean,
22
+    _status: ?string,
22
 
23
 
23
     /**
24
     /**
24
      * The recording mode this indicator should display.
25
      * The recording mode this indicator should display.
34
 /**
35
 /**
35
  * Abstract class for the {@code RecordingLabel} component.
36
  * Abstract class for the {@code RecordingLabel} component.
36
  */
37
  */
37
-export default class AbstractRecordingLabel<P: Props>
38
-    extends Component<P> {
38
+export default class AbstractRecordingLabel
39
+    extends Component<Props> {
39
 
40
 
40
     /**
41
     /**
41
      * Implements React {@code Component}'s render.
42
      * Implements React {@code Component}'s render.
43
      * @inheritdoc
44
      * @inheritdoc
44
      */
45
      */
45
     render() {
46
     render() {
46
-        return this.props._visible ? this._renderLabel() : null;
47
+        return this.props._status ? this._renderLabel() : null;
47
     }
48
     }
48
 
49
 
49
     _getLabelKey: () => ?string
50
     _getLabelKey: () => ?string
84
  * @param {Props} ownProps - The component's own props.
85
  * @param {Props} ownProps - The component's own props.
85
  * @private
86
  * @private
86
  * @returns {{
87
  * @returns {{
87
- *     _visible: boolean
88
+ *     _status: ?string
88
  * }}
89
  * }}
89
  */
90
  */
90
 export function _mapStateToProps(state: Object, ownProps: Props) {
91
 export function _mapStateToProps(state: Object, ownProps: Props) {
91
     const { mode } = ownProps;
92
     const { mode } = ownProps;
92
-    const _recordingSessions = state['features/recording'].sessionDatas;
93
-    const _visible
94
-        = Array.isArray(_recordingSessions)
95
-        && _recordingSessions.some(
96
-            session => session.status === JitsiRecordingConstants.status.ON
97
-            && session.mode === mode
98
-        );
99
 
93
 
100
     return {
94
     return {
101
-        _visible
95
+        _status: getSessionStatusToShow(state, mode)
102
     };
96
     };
103
 }
97
 }

+ 108
- 0
react/features/recording/components/RecordingExpandedLabel.native.js 파일 보기

1
+// @flow
2
+
3
+import { connect } from 'react-redux';
4
+
5
+import { translate } from '../../base/i18n';
6
+import { JitsiRecordingConstants } from '../../base/lib-jitsi-meet';
7
+import {
8
+    ExpandedLabel,
9
+    type Props as AbstractProps
10
+} from '../../base/label';
11
+
12
+import { getSessionStatusToShow } from '../functions';
13
+
14
+import { LIVE_LABEL_COLOR, REC_LABEL_COLOR } from './styles';
15
+
16
+type Props = AbstractProps & {
17
+
18
+    /**
19
+     * The status of the highermost priority session.
20
+     */
21
+    _status: ?string,
22
+
23
+    /**
24
+     * The recording mode this indicator should display.
25
+     */
26
+    mode: string,
27
+
28
+    /**
29
+     * Function to be used to translate i18n labels.
30
+     */
31
+    t: Function
32
+}
33
+
34
+/**
35
+ * A react {@code Component} that implements an expanded label as tooltip-like
36
+ * component to explain the meaning of the {@code RecordingLabel}.
37
+ */
38
+class RecordingExpandedLabel extends ExpandedLabel<Props> {
39
+    /**
40
+     * Returns the color this expanded label should be rendered with.
41
+     *
42
+     * @returns {string}
43
+     */
44
+    _getColor() {
45
+        switch (this.props.mode) {
46
+        case JitsiRecordingConstants.mode.STREAM:
47
+            return LIVE_LABEL_COLOR;
48
+        case JitsiRecordingConstants.mode.FILE:
49
+            return REC_LABEL_COLOR;
50
+        default:
51
+            return null;
52
+        }
53
+    }
54
+
55
+    /**
56
+     * Returns the label specific text of this {@code ExpandedLabel}.
57
+     *
58
+     * @returns {string}
59
+     */
60
+    _getLabel() {
61
+        const { _status, mode, t } = this.props;
62
+        let postfix = 'recording', prefix = 'expandedOn'; // Default values.
63
+
64
+        switch (mode) {
65
+        case JitsiRecordingConstants.mode.STREAM:
66
+            prefix = 'liveStreaming';
67
+            break;
68
+        case JitsiRecordingConstants.mode.FILE:
69
+            prefix = 'recording';
70
+            break;
71
+        }
72
+
73
+        switch (_status) {
74
+        case JitsiRecordingConstants.status.OFF:
75
+            postfix = 'expandedOff';
76
+            break;
77
+        case JitsiRecordingConstants.status.PENDING:
78
+            postfix = 'expandedPending';
79
+            break;
80
+        case JitsiRecordingConstants.status.ON:
81
+            postfix = 'expandedOn';
82
+            break;
83
+        }
84
+
85
+        return t(`${prefix}.${postfix}`);
86
+    }
87
+}
88
+
89
+/**
90
+ * Maps (parts of) the Redux state to the associated
91
+ * {@code RecordingExpandedLabel}'s props.
92
+ *
93
+ * @param {Object} state - The Redux state.
94
+ * @param {Props} ownProps - The component's own props.
95
+ * @private
96
+ * @returns {{
97
+ *     _status: ?string
98
+ * }}
99
+ */
100
+function _mapStateToProps(state: Object, ownProps: Props) {
101
+    const { mode } = ownProps;
102
+
103
+    return {
104
+        _status: getSessionStatusToShow(state, mode)
105
+    };
106
+}
107
+
108
+export default translate(connect(_mapStateToProps)(RecordingExpandedLabel));

+ 0
- 0
react/features/recording/components/RecordingExpandedLabel.web.js 파일 보기


+ 13
- 2
react/features/recording/components/RecordingLabel.native.js 파일 보기

8
 import { JitsiRecordingConstants } from '../../base/lib-jitsi-meet';
8
 import { JitsiRecordingConstants } from '../../base/lib-jitsi-meet';
9
 
9
 
10
 import AbstractRecordingLabel, {
10
 import AbstractRecordingLabel, {
11
-    type Props,
12
     _mapStateToProps
11
     _mapStateToProps
13
 } from './AbstractRecordingLabel';
12
 } from './AbstractRecordingLabel';
14
 import styles from './styles';
13
 import styles from './styles';
19
  *
18
  *
20
  * @extends {Component}
19
  * @extends {Component}
21
  */
20
  */
22
-class RecordingLabel extends AbstractRecordingLabel<Props> {
21
+class RecordingLabel extends AbstractRecordingLabel {
23
 
22
 
24
     /**
23
     /**
25
      * Renders the platform specific label component.
24
      * Renders the platform specific label component.
41
             return null;
40
             return null;
42
         }
41
         }
43
 
42
 
43
+        let status = 'on';
44
+
45
+        switch (this.props._status) {
46
+        case JitsiRecordingConstants.status.PENDING:
47
+            status = 'in_progress';
48
+            break;
49
+        case JitsiRecordingConstants.status.OFF:
50
+            status = 'off';
51
+            break;
52
+        }
53
+
44
         return (
54
         return (
45
             <CircularLabel
55
             <CircularLabel
46
                 label = { this.props.t(this._getLabelKey()) }
56
                 label = { this.props.t(this._getLabelKey()) }
57
+                status = { status }
47
                 style = { indicatorStyle } />
58
                 style = { indicatorStyle } />
48
         );
59
         );
49
     }
60
     }

+ 1
- 2
react/features/recording/components/RecordingLabel.web.js 파일 보기

7
 import { translate } from '../../base/i18n';
7
 import { translate } from '../../base/i18n';
8
 
8
 
9
 import AbstractRecordingLabel, {
9
 import AbstractRecordingLabel, {
10
-    type Props,
11
     _mapStateToProps
10
     _mapStateToProps
12
 } from './AbstractRecordingLabel';
11
 } from './AbstractRecordingLabel';
13
 
12
 
17
  *
16
  *
18
  * @extends {Component}
17
  * @extends {Component}
19
  */
18
  */
20
-class RecordingLabel extends AbstractRecordingLabel<Props> {
19
+class RecordingLabel extends AbstractRecordingLabel {
21
     /**
20
     /**
22
      * Renders the platform specific label component.
21
      * Renders the platform specific label component.
23
      *
22
      *

+ 1
- 0
react/features/recording/components/index.js 파일 보기

9
     StopRecordingDialog
9
     StopRecordingDialog
10
 } from './Recording';
10
 } from './Recording';
11
 export { default as RecordingLabel } from './RecordingLabel';
11
 export { default as RecordingLabel } from './RecordingLabel';
12
+export { default as RecordingExpandedLabel } from './RecordingExpandedLabel';

+ 5
- 2
react/features/recording/components/styles.js 파일 보기

2
 
2
 
3
 import { ColorPalette, createStyleSheet } from '../../base/styles';
3
 import { ColorPalette, createStyleSheet } from '../../base/styles';
4
 
4
 
5
+export const LIVE_LABEL_COLOR = ColorPalette.blue;
6
+export const REC_LABEL_COLOR = ColorPalette.red;
7
+
5
 /**
8
 /**
6
  * The styles of the React {@code Components} of the feature recording.
9
  * The styles of the React {@code Components} of the feature recording.
7
  */
10
  */
11
      * Style for the recording indicator.
14
      * Style for the recording indicator.
12
      */
15
      */
13
     indicatorLive: {
16
     indicatorLive: {
14
-        backgroundColor: ColorPalette.blue
17
+        backgroundColor: LIVE_LABEL_COLOR
15
     },
18
     },
16
 
19
 
17
     /**
20
     /**
18
      * Style for the recording indicator.
21
      * Style for the recording indicator.
19
      */
22
      */
20
     indicatorRecording: {
23
     indicatorRecording: {
21
-        backgroundColor: ColorPalette.red
24
+        backgroundColor: REC_LABEL_COLOR
22
     }
25
     }
23
 });
26
 });

+ 14
- 0
react/features/recording/constants.js 파일 보기

1
 // @flow
1
 // @flow
2
 
2
 
3
+import { JitsiRecordingConstants } from '../base/lib-jitsi-meet';
4
+
3
 /**
5
 /**
4
  * The identifier of the sound to be played when a recording or live streaming
6
  * The identifier of the sound to be played when a recording or live streaming
5
  * session is stopped.
7
  * session is stopped.
26
     JIBRI: 'jibri',
28
     JIBRI: 'jibri',
27
     JIRECON: 'jirecon'
29
     JIRECON: 'jirecon'
28
 };
30
 };
31
+
32
+/**
33
+ * An array defining the priorities of the recording (or live streaming)
34
+ * statuses, where the index of the array is the priority itself.
35
+ *
36
+ * @type {Array<string>}
37
+ */
38
+export const RECORDING_STATUS_PRIORITIES = [
39
+    JitsiRecordingConstants.status.OFF,
40
+    JitsiRecordingConstants.status.PENDING,
41
+    JitsiRecordingConstants.status.ON
42
+];

+ 29
- 0
react/features/recording/functions.js 파일 보기

2
 
2
 
3
 import { JitsiRecordingConstants } from '../base/lib-jitsi-meet';
3
 import { JitsiRecordingConstants } from '../base/lib-jitsi-meet';
4
 
4
 
5
+import { RECORDING_STATUS_PRIORITIES } from './constants';
6
+
5
 /**
7
 /**
6
  * Searches in the passed in redux state for an active recording session of the
8
  * Searches in the passed in redux state for an active recording session of the
7
  * passed in mode.
9
  * passed in mode.
43
     return state['features/recording'].sessionDatas.find(
45
     return state['features/recording'].sessionDatas.find(
44
         sessionData => sessionData.id === id);
46
         sessionData => sessionData.id === id);
45
 }
47
 }
48
+
49
+/**
50
+ * Returns the recording session status that is to be shown in a label. E.g. if
51
+ * there is a session with the status OFF and one with PENDING, then the PENDING
52
+ * one will be shown, because that is likely more important for the user to see.
53
+ *
54
+ * @param {Object} state - The redux state to search in.
55
+ * @param {string} mode - The recording mode to get status for.
56
+ * @returns {string|undefined}
57
+ */
58
+export function getSessionStatusToShow(state: Object, mode: string): ?string {
59
+    const recordingSessions = state['features/recording'].sessionDatas;
60
+    let status;
61
+
62
+    if (Array.isArray(recordingSessions)) {
63
+        for (const session of recordingSessions) {
64
+            if (session.mode === mode
65
+                    && (!status
66
+                        || (RECORDING_STATUS_PRIORITIES.indexOf(session.status)
67
+                            > RECORDING_STATUS_PRIORITIES.indexOf(status)))) {
68
+                status = session.status;
69
+            }
70
+        }
71
+    }
72
+
73
+    return status;
74
+}

+ 25
- 0
react/features/transcribing/components/TranscribingExpandedLabel.native.js 파일 보기

1
+// @flow
2
+
3
+import { translate } from '../../base/i18n';
4
+import { ExpandedLabel, type Props as AbstractProps } from '../../base/label';
5
+
6
+type Props = AbstractProps & {
7
+    t: Function
8
+}
9
+
10
+/**
11
+ * A react {@code Component} that implements an expanded label as tooltip-like
12
+ * component to explain the meaning of the {@code TranscribingLabel}.
13
+ */
14
+class TranscribingExpandedLabel extends ExpandedLabel<Props> {
15
+    /**
16
+     * Returns the label specific text of this {@code ExpandedLabel}.
17
+     *
18
+     * @returns {string}
19
+     */
20
+    _getLabel() {
21
+        return this.props.t('transcribing.expandedLabel');
22
+    }
23
+}
24
+
25
+export default translate(TranscribingExpandedLabel);

+ 0
- 0
react/features/transcribing/components/TranscribingExpandedLabel.web.js 파일 보기


+ 3
- 0
react/features/transcribing/components/index.js 파일 보기

1
 export { default as TranscribingLabel } from './TranscribingLabel';
1
 export { default as TranscribingLabel } from './TranscribingLabel';
2
+export {
3
+    default as TranscribingExpandedLabel
4
+} from './TranscribingExpandedLabel';

+ 36
- 0
react/features/video-quality/components/VideoQualityExpandedLabel.native.js 파일 보기

1
+// @flow
2
+
3
+import { translate } from '../../base/i18n';
4
+import { ExpandedLabel, type Props as AbstractProps } from '../../base/label';
5
+
6
+import { AUD_LABEL_COLOR } from './styles';
7
+
8
+type Props = AbstractProps & {
9
+    t: Function
10
+}
11
+
12
+/**
13
+ * A react {@code Component} that implements an expanded label as tooltip-like
14
+ * component to explain the meaning of the {@code VideoQualityLabel}.
15
+ */
16
+class VideoQualityExpandedLabel extends ExpandedLabel<Props> {
17
+    /**
18
+     * Returns the color this expanded label should be rendered with.
19
+     *
20
+     * @returns {string}
21
+     */
22
+    _getColor() {
23
+        return AUD_LABEL_COLOR;
24
+    }
25
+
26
+    /**
27
+     * Returns the label specific text of this {@code ExpandedLabel}.
28
+     *
29
+     * @returns {string}
30
+     */
31
+    _getLabel() {
32
+        return this.props.t('videoStatus.audioOnlyExpanded');
33
+    }
34
+}
35
+
36
+export default translate(VideoQualityExpandedLabel);

+ 0
- 0
react/features/video-quality/components/VideoQualityExpandedLabel.web.js 파일 보기


+ 3
- 0
react/features/video-quality/components/index.js 파일 보기

3
 } from './OverflowMenuVideoQualityItem';
3
 } from './OverflowMenuVideoQualityItem';
4
 export { default as VideoQualityDialog } from './VideoQualityDialog';
4
 export { default as VideoQualityDialog } from './VideoQualityDialog';
5
 export { default as VideoQualityLabel } from './VideoQualityLabel';
5
 export { default as VideoQualityLabel } from './VideoQualityLabel';
6
+export {
7
+    default as VideoQualityExpandedLabel
8
+} from './VideoQualityExpandedLabel';

+ 3
- 1
react/features/video-quality/components/styles.js 파일 보기

2
 
2
 
3
 import { ColorPalette, createStyleSheet } from '../../base/styles';
3
 import { ColorPalette, createStyleSheet } from '../../base/styles';
4
 
4
 
5
+export const AUD_LABEL_COLOR = ColorPalette.green;
6
+
5
 /**
7
 /**
6
  * The styles of the React {@code Components} of the feature video-quality.
8
  * The styles of the React {@code Components} of the feature video-quality.
7
  */
9
  */
11
      * Style for the audio-only indicator.
13
      * Style for the audio-only indicator.
12
      */
14
      */
13
     indicatorAudioOnly: {
15
     indicatorAudioOnly: {
14
-        backgroundColor: ColorPalette.green
16
+        backgroundColor: AUD_LABEL_COLOR
15
     }
17
     }
16
 });
18
 });

Loading…
취소
저장