浏览代码

feat(title-bar) Updated title bar (#10752)

Only display Picture-in-Picture button when feature is available
Moved conference timer before title
Created new always-on container for labels
Moved recording labels to always-on
Updated expanded label to support new always-on labels
Added raised hands counter label
Added speaker - earpiece toggle button
Lifted state up
master
Robert Pintilii 3 年前
父节点
当前提交
eb010061e0
没有帐户链接到提交者的电子邮件

+ 10
- 2
react/features/base/label/components/ExpandedLabel.native.js 查看文件

@@ -11,7 +11,12 @@ export type Props = {
11 11
      * The position of the parent element (from right to left) to display the
12 12
      * arrow.
13 13
      */
14
-    parentPosition: number
14
+    parentPosition: number,
15
+
16
+    /**
17
+     * Custom styles.
18
+     */
19
+    style: ?Object
15 20
 };
16 21
 
17 22
 type State = {
@@ -61,7 +66,10 @@ export default class ExpandedLabel<P: Props> extends Component<P, State> {
61 66
     render() {
62 67
         return (
63 68
             <Animated.View
64
-                style = { [ styles.expandedLabelContainer, { opacity: this.state.opacityAnimation } ] }>
69
+                style = { [ styles.expandedLabelContainer,
70
+                    this.props.style,
71
+                    { opacity: this.state.opacityAnimation }
72
+                ] }>
65 73
                 <View
66 74
                     style = { [ styles.expandedLabelTextContainer,
67 75
                         { backgroundColor: this._getColor() || DEFAULT_COLOR } ] }>

+ 14
- 3
react/features/base/label/components/Label.native.js 查看文件

@@ -22,6 +22,11 @@ const STATUS_OFF = 'off';
22 22
 
23 23
 type Props = AbstractProps & {
24 24
 
25
+    /**
26
+     * Color for the icon.
27
+     */
28
+    iconColor?: ?string,
29
+
25 30
     /**
26 31
      * Status of the label. This prop adds some additional styles based on its
27 32
      * value. E.g. If status = off, it will render the label symbolising that
@@ -32,7 +37,12 @@ type Props = AbstractProps & {
32 37
     /**
33 38
      * Style of the label.
34 39
      */
35
-    style?: ?StyleType
40
+    style?: ?StyleType,
41
+
42
+    /**
43
+     * Custom styles for the text.
44
+     */
45
+    textStyle?: ?StyleType
36 46
 };
37 47
 
38 48
 type State = {
@@ -91,7 +101,7 @@ export default class Label extends AbstractLabel<Props, State> {
91 101
      * @inheritdoc
92 102
      */
93 103
     render() {
94
-        const { icon, text, status, style } = this.props;
104
+        const { icon, text, status, style, iconColor, textStyle } = this.props;
95 105
 
96 106
         let extraStyle = null;
97 107
 
@@ -113,9 +123,10 @@ export default class Label extends AbstractLabel<Props, State> {
113 123
                     extraStyle
114 124
                 ] }>
115 125
                 { icon && <Icon
126
+                    color = { iconColor }
116 127
                     size = '18'
117 128
                     src = { icon } /> }
118
-                { text && <Text style = { styles.labelText }>
129
+                { text && <Text style = { [ styles.labelText, textStyle ] }>
119 130
                     { text }
120 131
                 </Text>}
121 132
             </Animated.View>

+ 4
- 2
react/features/base/label/components/styles.js 查看文件

@@ -1,6 +1,7 @@
1 1
 // @flow
2 2
 
3 3
 import { ColorPalette } from '../../styles';
4
+import BaseTheme from '../../ui/components/BaseTheme';
4 5
 
5 6
 /**
6 7
  * The default color of the {@code Label} and {@code ExpandedLabel}.
@@ -29,7 +30,8 @@ export default {
29 30
         right: 0,
30 31
         top: 36,
31 32
         flexDirection: 'row',
32
-        justifyContent: 'center'
33
+        justifyContent: 'center',
34
+        zIndex: 1
33 35
     },
34 36
 
35 37
     expandedLabelTextContainer: {
@@ -59,7 +61,7 @@ export default {
59 61
 
60 62
     labelText: {
61 63
         color: ColorPalette.white,
62
-        fontSize: 12
64
+        ...BaseTheme.typography.labelBold
63 65
     },
64 66
 
65 67
     labelOff: {

+ 44
- 0
react/features/conference/components/native/AlwaysOnLabels.js 查看文件

@@ -0,0 +1,44 @@
1
+// @flow
2
+
3
+import React from 'react';
4
+import { TouchableOpacity } from 'react-native';
5
+
6
+import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
7
+import { RecordingLabel } from '../../../recording';
8
+
9
+import RaisedHandsCountLabel from './RaisedHandsCountLabel';
10
+import {
11
+    LabelHitSlop,
12
+    LABEL_ID_RAISED_HANDS_COUNT,
13
+    LABEL_ID_RECORDING,
14
+    LABEL_ID_STREAMING
15
+} from './constants';
16
+
17
+type Props = {
18
+
19
+    /**
20
+     * Creates a function to be invoked when the onPress of the touchables are
21
+     * triggered.
22
+     */
23
+    createOnPress: Function
24
+}
25
+
26
+const AlwaysOnLabels = ({ createOnPress }: Props) => (<>
27
+    <TouchableOpacity
28
+        hitSlop = { LabelHitSlop }
29
+        onPress = { createOnPress(LABEL_ID_RECORDING) } >
30
+        <RecordingLabel mode = { JitsiRecordingConstants.mode.FILE } />
31
+    </TouchableOpacity>
32
+    <TouchableOpacity
33
+        hitSlop = { LabelHitSlop }
34
+        onPress = { createOnPress(LABEL_ID_STREAMING) } >
35
+        <RecordingLabel mode = { JitsiRecordingConstants.mode.STREAM } />
36
+    </TouchableOpacity>
37
+    <TouchableOpacity
38
+        hitSlop = { LabelHitSlop }
39
+        onPress = { createOnPress(LABEL_ID_RAISED_HANDS_COUNT) } >
40
+        <RaisedHandsCountLabel />
41
+    </TouchableOpacity>
42
+</>);
43
+
44
+export default AlwaysOnLabels;

+ 90
- 8
react/features/conference/components/native/Conference.js 查看文件

@@ -2,6 +2,7 @@
2 2
 
3 3
 import React from 'react';
4 4
 import { NativeModules, SafeAreaView, StatusBar, View } from 'react-native';
5
+import { withSafeAreaInsets } from 'react-native-safe-area-context';
5 6
 
6 7
 import { appNavigate } from '../../../app/actions';
7 8
 import { PIP_ENABLED, FULLSCREEN_ENABLED, getFeatureFlag } from '../../../base/flags';
@@ -32,9 +33,12 @@ import {
32 33
 } from '../AbstractConference';
33 34
 import type { AbstractProps } from '../AbstractConference';
34 35
 
36
+import AlwaysOnLabels from './AlwaysOnLabels';
35 37
 import { navigate } from './ConferenceNavigationContainerRef';
38
+import ExpandedLabelPopup from './ExpandedLabelPopup';
36 39
 import LonelyMeetingExperience from './LonelyMeetingExperience';
37
-import NavigationBar from './NavigationBar';
40
+import TitleBar from './TitleBar';
41
+import { EXPANDED_LABEL_TIMEOUT } from './constants';
38 42
 import { screen } from './routes';
39 43
 import styles from './styles';
40 44
 
@@ -106,13 +110,31 @@ type Props = AbstractProps & {
106 110
     /**
107 111
      * The redux {@code dispatch} function.
108 112
      */
109
-    dispatch: Function
113
+    dispatch: Function,
114
+
115
+    /**
116
+    * Object containing the safe area insets.
117
+    */
118
+    insets: Object
110 119
 };
111 120
 
121
+type State = {
122
+
123
+    /**
124
+     * The label that is currently expanded.
125
+     */
126
+    visibleExpandedLabel: ?string
127
+}
128
+
112 129
 /**
113 130
  * The conference page of the mobile (i.e. React Native) application.
114 131
  */
115
-class Conference extends AbstractConference<Props, *> {
132
+class Conference extends AbstractConference<Props, State> {
133
+    /**
134
+     * Timeout ref.
135
+     */
136
+    _expandedLabelTimeout: Object;
137
+
116 138
     /**
117 139
      * Initializes a new Conference instance.
118 140
      *
@@ -122,10 +144,17 @@ class Conference extends AbstractConference<Props, *> {
122 144
     constructor(props) {
123 145
         super(props);
124 146
 
147
+        this.state = {
148
+            visibleExpandedLabel: undefined
149
+        };
150
+
151
+        this._expandedLabelTimeout = React.createRef();
152
+
125 153
         // Bind event handlers so they are only bound once per instance.
126 154
         this._onClick = this._onClick.bind(this);
127 155
         this._onHardwareBackPress = this._onHardwareBackPress.bind(this);
128 156
         this._setToolboxVisible = this._setToolboxVisible.bind(this);
157
+        this._createOnPress = this._createOnPress.bind(this);
129 158
     }
130 159
 
131 160
     /**
@@ -167,6 +196,8 @@ class Conference extends AbstractConference<Props, *> {
167 196
     componentWillUnmount() {
168 197
         // Tear handling any hardware button presses for back navigation down.
169 198
         BackButtonRegistry.removeListener(this._onHardwareBackPress);
199
+
200
+        clearTimeout(this._expandedLabelTimeout.current);
170 201
     }
171 202
 
172 203
     /**
@@ -243,6 +274,38 @@ class Conference extends AbstractConference<Props, *> {
243 274
                 : undefined);
244 275
     }
245 276
 
277
+    _createOnPress: (string) => void;
278
+
279
+    /**
280
+     * Creates a function to be invoked when the onPress of the touchables are
281
+     * triggered.
282
+     *
283
+     * @param {string} label - The identifier of the label that's onLayout is
284
+     * triggered.
285
+     * @returns {Function}
286
+     */
287
+    _createOnPress(label) {
288
+        return () => {
289
+            const { visibleExpandedLabel } = this.state;
290
+
291
+            const newVisibleExpandedLabel
292
+                = visibleExpandedLabel === label ? undefined : label;
293
+
294
+            clearTimeout(this._expandedLabelTimeout.current);
295
+            this.setState({
296
+                visibleExpandedLabel: newVisibleExpandedLabel
297
+            });
298
+
299
+            if (newVisibleExpandedLabel) {
300
+                this._expandedLabelTimeout.current = setTimeout(() => {
301
+                    this.setState({
302
+                        visibleExpandedLabel: undefined
303
+                    });
304
+                }, EXPANDED_LABEL_TIMEOUT);
305
+            }
306
+        };
307
+    }
308
+
246 309
     /**
247 310
      * Renders the content for the Conference container.
248 311
      *
@@ -307,10 +370,29 @@ class Conference extends AbstractConference<Props, *> {
307 370
                     pointerEvents = 'box-none'
308 371
                     style = {
309 372
                         _toolboxVisible
310
-                            ? styles.navBarSafeViewColor
311
-                            : styles.navBarSafeViewTransparent }>
312
-                    <NavigationBar />
313
-                    { this._renderNotificationsContainer() }
373
+                            ? styles.titleBarSafeViewColor
374
+                            : styles.titleBarSafeViewTransparent }>
375
+                    <TitleBar _createOnPress = { this._createOnPress } />
376
+                </SafeAreaView>
377
+                <SafeAreaView
378
+                    pointerEvents = 'box-none'
379
+                    style = {
380
+                        _toolboxVisible
381
+                            ? [ styles.titleBarSafeViewTransparent, { top: this.props.insets.top + 50 } ]
382
+                            : styles.titleBarSafeViewTransparent
383
+                    }>
384
+                    <View
385
+                        pointerEvents = 'box-none'
386
+                        style = { styles.expandedLabelWrapper }>
387
+                        <ExpandedLabelPopup visibleExpandedLabel = { this.state.visibleExpandedLabel } />
388
+                    </View>
389
+                    <View
390
+                        pointerEvents = 'box-none'
391
+                        style = { styles.alwaysOnTitleBar }>
392
+                        {/* eslint-disable-next-line react/jsx-no-bind */}
393
+                        <AlwaysOnLabels createOnPress = { this._createOnPress } />
394
+                    </View>
395
+                    {this._renderNotificationsContainer()}
314 396
                     <KnockingParticipantList />
315 397
                 </SafeAreaView>
316 398
 
@@ -439,4 +521,4 @@ function _mapStateToProps(state) {
439 521
     };
440 522
 }
441 523
 
442
-export default connect(_mapStateToProps)(Conference);
524
+export default withSafeAreaInsets(connect(_mapStateToProps)(Conference));

+ 37
- 0
react/features/conference/components/native/ExpandedLabelPopup.js 查看文件

@@ -0,0 +1,37 @@
1
+// @flow
2
+
3
+import React from 'react';
4
+
5
+import BaseTheme from '../../../base/ui/components/BaseTheme';
6
+
7
+import { EXPANDED_LABELS } from './constants';
8
+
9
+type Props = {
10
+
11
+    /**
12
+     * The selected label to show details.
13
+     */
14
+    visibleExpandedLabel: ?string
15
+}
16
+
17
+const ExpandedLabelPopup = ({ visibleExpandedLabel }: Props) => {
18
+    if (visibleExpandedLabel) {
19
+        const expandedLabel = EXPANDED_LABELS[visibleExpandedLabel];
20
+
21
+        if (expandedLabel) {
22
+            const LabelComponent = expandedLabel.component || expandedLabel;
23
+            const { props, alwaysOn } = expandedLabel || {};
24
+            const style = {
25
+                top: alwaysOn ? BaseTheme.spacing[6] : BaseTheme.spacing[1]
26
+            };
27
+
28
+            return (<LabelComponent
29
+                { ...props }
30
+                style = { style } />);
31
+        }
32
+    }
33
+
34
+    return null;
35
+};
36
+
37
+export default ExpandedLabelPopup;

+ 33
- 167
react/features/conference/components/native/Labels.js 查看文件

@@ -3,96 +3,27 @@
3 3
 import React, { Component } from 'react';
4 4
 import { TouchableOpacity, View } from 'react-native';
5 5
 
6
-import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
7
-import { RecordingLabel, RecordingExpandedLabel } from '../../../recording';
8
-import { TranscribingExpandedLabel, TranscribingLabel } from '../../../transcribing';
9
-import { VideoQualityExpandedLabel, VideoQualityLabel } from '../../../video-quality';
6
+import { TranscribingLabel } from '../../../transcribing';
7
+import { VideoQualityLabel } from '../../../video-quality';
10 8
 
11
-import InsecureRoomNameExpandedLabel from './InsecureRoomNameExpandedLabel';
9
+import { LabelHitSlop, LABEL_ID_INSECURE_ROOM_NAME, LABEL_ID_QUALITY, LABEL_ID_TRANSCRIBING } from './constants';
12 10
 import styles from './styles';
13 11
 
14 12
 import { InsecureRoomNameLabel } from './';
15 13
 
16
-type Props = {}
17
-
18
-type State = {
14
+type Props = {
19 15
 
20 16
     /**
21
-     * String to show which {@code ExpandedLabel} to be shown. (Equals to the
22
-     * label IDs below.).
17
+     * Creates a function to be invoked when the onPress of the touchables are
18
+     * triggered.
23 19
      */
24
-    visibleExpandedLabel: ?string
20
+    createOnPress: Function
25 21
 }
26 22
 
27
-const LABEL_ID_QUALITY = 'quality';
28
-const LABEL_ID_RECORDING = 'recording';
29
-const LABEL_ID_STREAMING = 'streaming';
30
-const LABEL_ID_TRANSCRIBING = 'transcribing';
31
-const LABEL_ID_INSECURE_ROOM_NAME = 'insecure-room-name';
32
-
33
-const LabelHitSlop = {
34
-    top: 10,
35
-    bottom: 10,
36
-    left: 0,
37
-    right: 0
38
-};
39
-
40
-/**
41
- * The {@code ExpandedLabel} components to be rendered for the individual
42
- * {@code Label}s.
43
- */
44
-const EXPANDED_LABELS = {
45
-    [LABEL_ID_QUALITY]: VideoQualityExpandedLabel,
46
-    [LABEL_ID_RECORDING]: {
47
-        component: RecordingExpandedLabel,
48
-        props: {
49
-            mode: JitsiRecordingConstants.mode.FILE
50
-        }
51
-    },
52
-    [LABEL_ID_STREAMING]: {
53
-        component: RecordingExpandedLabel,
54
-        props: {
55
-            mode: JitsiRecordingConstants.mode.STREAM
56
-        }
57
-    },
58
-    [LABEL_ID_TRANSCRIBING]: TranscribingExpandedLabel,
59
-    [LABEL_ID_INSECURE_ROOM_NAME]: InsecureRoomNameExpandedLabel
60
-};
61
-
62
-/**
63
- * Timeout to hide the {@ExpandedLabel}.
64
- */
65
-const EXPANDED_LABEL_TIMEOUT = 5000;
66
-
67 23
 /**
68 24
  * A container that renders the conference indicators, if any.
69 25
  */
70
-class Labels extends Component<Props, State> {
71
-    /**
72
-     * Timeout for the expanded labels to disappear.
73
-     */
74
-    expandedLabelTimeout: TimeoutID;
75
-
76
-    /**
77
-     * Instantiates a new instance of {@code Labels}.
78
-     *
79
-     * @inheritdoc
80
-     */
81
-    constructor(props: Props) {
82
-        super(props);
83
-        this.state = {
84
-            visibleExpandedLabel: undefined
85
-        };
86
-    }
87
-
88
-    /**
89
-     * Implements React {@code Component}'s componentWillUnmount.
90
-     *
91
-     * @inheritdoc
92
-     */
93
-    componentWillUnmount() {
94
-        clearTimeout(this.expandedLabelTimeout);
95
-    }
26
+class Labels extends Component<Props> {
96 27
 
97 28
     /**
98 29
      * Implements React {@code Component}'s render.
@@ -101,99 +32,34 @@ class Labels extends Component<Props, State> {
101 32
      */
102 33
     render() {
103 34
         return (
104
-            <>
105
-                <View pointerEvents = 'box-none'>
106
-                    <View
107
-                        pointerEvents = 'box-none'
108
-                        style = { styles.indicatorContainer }>
109
-                        <TouchableOpacity
110
-                            hitSlop = { LabelHitSlop }
111
-                            onPress = { this._createOnPress(LABEL_ID_RECORDING) } >
112
-                            <RecordingLabel mode = { JitsiRecordingConstants.mode.FILE } />
113
-                        </TouchableOpacity>
114
-                        <TouchableOpacity
115
-                            hitSlop = { LabelHitSlop }
116
-                            onPress = { this._createOnPress(LABEL_ID_STREAMING) } >
117
-                            <RecordingLabel mode = { JitsiRecordingConstants.mode.STREAM } />
118
-                        </TouchableOpacity>
119
-                        <TouchableOpacity
120
-                            hitSlop = { LabelHitSlop }
121
-                            onPress = {
122
-                                this._createOnPress(LABEL_ID_TRANSCRIBING)
123
-                            } >
124
-                            <TranscribingLabel />
125
-                        </TouchableOpacity>
126
-                        <TouchableOpacity
127
-                            hitSlop = { LabelHitSlop }
128
-                            onPress = {
129
-                                this._createOnPress(LABEL_ID_INSECURE_ROOM_NAME)
130
-                            } >
131
-                            <InsecureRoomNameLabel />
132
-                        </TouchableOpacity>
133
-                        <TouchableOpacity
134
-                            hitSlop = { LabelHitSlop }
135
-                            onPress = {
136
-                                this._createOnPress(LABEL_ID_QUALITY) } >
137
-                            <VideoQualityLabel />
138
-                        </TouchableOpacity>
139
-                    </View>
35
+            <View pointerEvents = 'box-none'>
36
+                <View
37
+                    pointerEvents = 'box-none'
38
+                    style = { styles.indicatorContainer }>
39
+                    <TouchableOpacity
40
+                        hitSlop = { LabelHitSlop }
41
+                        onPress = {
42
+                            this.props.createOnPress(LABEL_ID_TRANSCRIBING)
43
+                        } >
44
+                        <TranscribingLabel />
45
+                    </TouchableOpacity>
46
+                    <TouchableOpacity
47
+                        hitSlop = { LabelHitSlop }
48
+                        onPress = {
49
+                            this.props.createOnPress(LABEL_ID_INSECURE_ROOM_NAME)
50
+                        } >
51
+                        <InsecureRoomNameLabel />
52
+                    </TouchableOpacity>
53
+                    <TouchableOpacity
54
+                        hitSlop = { LabelHitSlop }
55
+                        onPress = {
56
+                            this.props.createOnPress(LABEL_ID_QUALITY) } >
57
+                        <VideoQualityLabel />
58
+                    </TouchableOpacity>
140 59
                 </View>
141
-                { this._renderExpandedLabel() }
142
-            </>
60
+            </View>
143 61
         );
144 62
     }
145
-
146
-    /**
147
-     * Creates a function to be invoked when the onPress of the touchables are
148
-     * triggered.
149
-     *
150
-     * @param {string} label - The identifier of the label that's onLayout is
151
-     * triggered.
152
-     * @returns {Function}
153
-     */
154
-    _createOnPress(label) {
155
-        return () => {
156
-            let { visibleExpandedLabel } = this.state;
157
-
158
-            visibleExpandedLabel
159
-                = visibleExpandedLabel === label ? undefined : label;
160
-
161
-            clearTimeout(this.expandedLabelTimeout);
162
-            this.setState({
163
-                visibleExpandedLabel
164
-            });
165
-
166
-            if (visibleExpandedLabel) {
167
-                this.expandedLabelTimeout = setTimeout(() => {
168
-                    this.setState({
169
-                        visibleExpandedLabel: undefined
170
-                    });
171
-                }, EXPANDED_LABEL_TIMEOUT);
172
-            }
173
-        };
174
-    }
175
-
176
-    /**
177
-     * Rendes the expanded (explaining) label for the label that was touched.
178
-     *
179
-     * @returns {React$Element}
180
-     */
181
-    _renderExpandedLabel() {
182
-        const { visibleExpandedLabel } = this.state;
183
-
184
-        if (visibleExpandedLabel) {
185
-            const expandedLabel = EXPANDED_LABELS[visibleExpandedLabel];
186
-
187
-            if (expandedLabel) {
188
-                const LabelComponent = expandedLabel.component || expandedLabel;
189
-                const { props } = expandedLabel || {};
190
-
191
-                return <LabelComponent { ...props } />;
192
-            }
193
-        }
194
-
195
-        return null;
196
-    }
197 63
 }
198 64
 
199 65
 export default Labels;

+ 26
- 0
react/features/conference/components/native/RaisedHandsCountExpandedLabel.js 查看文件

@@ -0,0 +1,26 @@
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 RaisedHandsCountExpandedLabel}.
13
+ */
14
+class RaisedHandsCountExpandedLabel extends ExpandedLabel<Props> {
15
+
16
+    /**
17
+     * Returns the label specific text of this {@code ExpandedLabel}.
18
+     *
19
+     * @returns {string}
20
+     */
21
+    _getLabel() {
22
+        return this.props.t('raisedHandsLabel');
23
+    }
24
+}
25
+
26
+export default translate(RaisedHandsCountExpandedLabel);

+ 26
- 0
react/features/conference/components/native/RaisedHandsCountLabel.js 查看文件

@@ -0,0 +1,26 @@
1
+// @flow
2
+
3
+import React from 'react';
4
+import { useSelector } from 'react-redux';
5
+
6
+import { IconRaisedHand } from '../../../base/icons';
7
+import { Label } from '../../../base/label';
8
+import BaseTheme from '../../../base/ui/components/BaseTheme';
9
+
10
+import styles from './styles';
11
+
12
+const RaisedHandsCountLabel = () => {
13
+    const raisedHandsCount = useSelector(state =>
14
+        (state['features/base/participants'].raisedHandsQueue || []).length);
15
+
16
+    return raisedHandsCount > 0 && (
17
+        <Label
18
+            icon = { IconRaisedHand }
19
+            iconColor = { BaseTheme.palette.uiBackground }
20
+            style = { styles.raisedHandsCountLabel }
21
+            text = { raisedHandsCount }
22
+            textStyle = { styles.raisedHandsCountLabelText } />
23
+    );
24
+};
25
+
26
+export default RaisedHandsCountLabel;

react/features/conference/components/native/NavigationBar.js → react/features/conference/components/native/TitleBar.js 查看文件

@@ -7,6 +7,7 @@ import { getConferenceName, getConferenceTimestamp } from '../../../base/confere
7 7
 import { getFeatureFlag, CONFERENCE_TIMER_ENABLED, MEETING_NAME_ENABLED } from '../../../base/flags';
8 8
 import { connect } from '../../../base/redux';
9 9
 import InviteButton from '../../../invite/components/add-people-dialog/native/InviteButton';
10
+import AudioDeviceToggleButton from '../../../mobile/audio-mode/components/AudioDeviceToggleButton';
10 11
 import { PictureInPictureButton } from '../../../mobile/picture-in-picture';
11 12
 import { isToolboxVisible } from '../../../toolbox/functions.native';
12 13
 import ConferenceTimer from '../ConferenceTimer';
@@ -17,6 +18,12 @@ import styles from './styles';
17 18
 
18 19
 type Props = {
19 20
 
21
+    /**
22
+     * Creates a function to be invoked when the onPress of the touchables are
23
+     * triggered.
24
+     */
25
+    _createOnPress: Function,
26
+
20 27
     /**
21 28
      * Whether displaying the current conference timer is enabled or not.
22 29
      */
@@ -45,23 +52,24 @@ type Props = {
45 52
  * @param {Props} props - The React props passed to this component.
46 53
  * @returns {React.Node}
47 54
  */
48
-const NavigationBar = (props: Props) => {
49
-    if (!props._visible) {
50
-        return null;
51
-    }
52
-
53
-    return (
55
+const TitleBar = (props: Props) => (<>
56
+    {props._visible && <View
57
+        pointerEvents = 'box-none'
58
+        style = { styles.titleBarWrapper }>
59
+        <View style = { styles.pipButtonContainer }>
60
+            <PictureInPictureButton styles = { styles.pipButton } />
61
+        </View>
54 62
         <View
55 63
             pointerEvents = 'box-none'
56
-            style = { styles.navBarWrapper }>
57
-            <View style = { styles.pipButtonContainer }>
58
-                <PictureInPictureButton styles = { styles.pipButton } />
59
-            </View>
60
-            <View
61
-                pointerEvents = 'box-none'
62
-                style = { styles.roomNameWrapper }>
63
-                {
64
-                    props._meetingNameEnabled
64
+            style = { styles.roomNameWrapper }>
65
+            {
66
+                props._conferenceTimerEnabled
67
+                    && <View style = { styles.roomTimerView }>
68
+                        <ConferenceTimer textStyle = { styles.roomTimer } />
69
+                    </View>
70
+            }
71
+            {
72
+                props._meetingNameEnabled
65 73
                         && <View style = { styles.roomNameView }>
66 74
                             <Text
67 75
                                 numberOfLines = { 1 }
@@ -69,21 +77,18 @@ const NavigationBar = (props: Props) => {
69 77
                                 { props._meetingName }
70 78
                             </Text>
71 79
                         </View>
72
-                }
73
-                {
74
-                    props._conferenceTimerEnabled
75
-                            && <View style = { styles.roomTimerView }>
76
-                                <ConferenceTimer textStyle = { styles.roomTimer } />
77
-                            </View>
78
-                }
79
-                <Labels />
80
-            </View>
81
-            <View style = { styles.inviteButtonContainer }>
82
-                <InviteButton styles = { styles.inviteButton } />
83
-            </View>
80
+            }
81
+            {/* eslint-disable-next-line react/jsx-no-bind */}
82
+            <Labels createOnPress = { props._createOnPress } />
84 83
         </View>
85
-    );
86
-};
84
+        <View style = { styles.titleBarButtonContainer }>
85
+            <AudioDeviceToggleButton styles = { styles.inviteButton } />
86
+        </View>
87
+        <View style = { styles.titleBarButtonContainer }>
88
+            <InviteButton styles = { styles.inviteButton } />
89
+        </View>
90
+    </View>}
91
+</>);
87 92
 
88 93
 /**
89 94
  * Maps part of the Redux store to the props of this component.
@@ -105,4 +110,4 @@ function _mapStateToProps(state) {
105 110
     };
106 111
 }
107 112
 
108
-export default connect(_mapStateToProps)(NavigationBar);
113
+export default connect(_mapStateToProps)(TitleBar);

+ 56
- 0
react/features/conference/components/native/constants.js 查看文件

@@ -0,0 +1,56 @@
1
+// @flow
2
+
3
+import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
4
+import { RecordingExpandedLabel } from '../../../recording';
5
+import { TranscribingExpandedLabel } from '../../../transcribing';
6
+import { VideoQualityExpandedLabel } from '../../../video-quality';
7
+
8
+import InsecureRoomNameExpandedLabel from './InsecureRoomNameExpandedLabel';
9
+import RaisedHandsCountExpandedLabel from './RaisedHandsCountExpandedLabel';
10
+
11
+export const LabelHitSlop = {
12
+    top: 10,
13
+    bottom: 10,
14
+    left: 0,
15
+    right: 0
16
+};
17
+
18
+/**
19
+ * Timeout to hide the {@ExpandedLabel}.
20
+ */
21
+export const EXPANDED_LABEL_TIMEOUT = 5000;
22
+
23
+export const LABEL_ID_QUALITY = 'quality';
24
+export const LABEL_ID_RECORDING = 'recording';
25
+export const LABEL_ID_STREAMING = 'streaming';
26
+export const LABEL_ID_TRANSCRIBING = 'transcribing';
27
+export const LABEL_ID_INSECURE_ROOM_NAME = 'insecure-room-name';
28
+export const LABEL_ID_RAISED_HANDS_COUNT = 'raised-hands-count';
29
+
30
+/**
31
+ * The {@code ExpandedLabel} components to be rendered for the individual
32
+ * {@code Label}s.
33
+ */
34
+export const EXPANDED_LABELS = {
35
+    [LABEL_ID_QUALITY]: VideoQualityExpandedLabel,
36
+    [LABEL_ID_RECORDING]: {
37
+        component: RecordingExpandedLabel,
38
+        props: {
39
+            mode: JitsiRecordingConstants.mode.FILE
40
+        },
41
+        alwaysOn: true
42
+    },
43
+    [LABEL_ID_STREAMING]: {
44
+        component: RecordingExpandedLabel,
45
+        props: {
46
+            mode: JitsiRecordingConstants.mode.STREAM
47
+        },
48
+        alwaysOn: true
49
+    },
50
+    [LABEL_ID_TRANSCRIBING]: TranscribingExpandedLabel,
51
+    [LABEL_ID_INSECURE_ROOM_NAME]: InsecureRoomNameExpandedLabel,
52
+    [LABEL_ID_RAISED_HANDS_COUNT]: {
53
+        component: RaisedHandsCountExpandedLabel,
54
+        alwaysOn: true
55
+    }
56
+};

+ 61
- 33
react/features/conference/components/native/styles.js 查看文件

@@ -4,13 +4,13 @@ import BaseTheme from '../../../base/ui/components/BaseTheme.native';
4 4
 
5 5
 export const INSECURE_ROOM_NAME_LABEL_COLOR = BaseTheme.palette.actionDanger;
6 6
 
7
-const NAVBAR_BUTTON_SIZE = 24;
7
+const TITLE_BAR_BUTTON_SIZE = 24;
8 8
 
9 9
 
10 10
 /**
11
- * The styles of the safe area view that contains the navigation bar.
11
+ * The styles of the safe area view that contains the title bar.
12 12
  */
13
-const navBarSafeView = {
13
+const titleBarSafeView = {
14 14
     left: 0,
15 15
     position: 'absolute',
16 16
     right: 0,
@@ -53,14 +53,11 @@ export default {
53 53
         flexDirection: 'row'
54 54
     },
55 55
 
56
-    inviteButtonContainer: {
56
+    titleBarButtonContainer: {
57 57
         borderRadius: 3,
58 58
         height: BaseTheme.spacing[7],
59
-        position: 'absolute',
60 59
         marginTop: BaseTheme.spacing[1],
61 60
         marginRight: BaseTheme.spacing[1],
62
-        top: 0,
63
-        right: 0,
64 61
         zIndex: 1,
65 62
         width: BaseTheme.spacing[7]
66 63
     },
@@ -69,7 +66,7 @@ export default {
69 66
         iconStyle: {
70 67
             color: BaseTheme.palette.icon01,
71 68
             padding: 12,
72
-            fontSize: NAVBAR_BUTTON_SIZE
69
+            fontSize: TITLE_BAR_BUTTON_SIZE
73 70
         },
74 71
         underlayColor: BaseTheme.spacing.underlay01
75 72
     },
@@ -98,36 +95,35 @@ export default {
98 95
     },
99 96
 
100 97
     pipButtonContainer: {
101
-        borderRadius: 3,
102
-        height: BaseTheme.spacing[7],
103
-        position: 'absolute',
104
-        marginTop: BaseTheme.spacing[1],
105
-        marginLeft: BaseTheme.spacing[1],
106
-        top: 0,
107
-        left: 0,
108
-        zIndex: 1,
109
-        width: BaseTheme.spacing[7]
98
+        '&:not(:empty)': {
99
+            borderRadius: 3,
100
+            height: BaseTheme.spacing[7],
101
+            marginTop: BaseTheme.spacing[1],
102
+            marginLeft: BaseTheme.spacing[1],
103
+            zIndex: 1,
104
+            width: BaseTheme.spacing[7]
105
+        }
110 106
     },
111 107
 
112 108
     pipButton: {
113 109
         iconStyle: {
114 110
             color: BaseTheme.palette.icon01,
115 111
             padding: 12,
116
-            fontSize: NAVBAR_BUTTON_SIZE
112
+            fontSize: TITLE_BAR_BUTTON_SIZE
117 113
         },
118 114
         underlayColor: BaseTheme.spacing.underlay01
119 115
     },
120 116
 
121
-    navBarSafeViewColor: {
122
-        ...navBarSafeView,
117
+    titleBarSafeViewColor: {
118
+        ...titleBarSafeView,
123 119
         backgroundColor: BaseTheme.palette.uiBackground
124 120
     },
125 121
 
126
-    navBarSafeViewTransparent: {
127
-        ...navBarSafeView
122
+    titleBarSafeViewTransparent: {
123
+        ...titleBarSafeView
128 124
     },
129 125
 
130
-    navBarWrapper: {
126
+    titleBarWrapper: {
131 127
         alignItems: 'center',
132 128
         flex: 1,
133 129
         flexDirection: 'row',
@@ -135,25 +131,42 @@ export default {
135 131
         justifyContent: 'center'
136 132
     },
137 133
 
134
+    alwaysOnTitleBar: {
135
+        padding: 4,
136
+        paddingRight: 0,
137
+        borderRadius: 6,
138
+        backgroundColor: 'rgba(0, 0, 0, .5)',
139
+        marginLeft: BaseTheme.spacing[2],
140
+        flexDirection: 'row',
141
+        alignSelf: 'flex-start',
142
+        alignItems: 'center',
143
+        justifyContent: 'center',
144
+        marginTop: BaseTheme.spacing[2]
145
+    },
146
+
147
+    expandedLabelWrapper: {
148
+        zIndex: 1
149
+    },
150
+
138 151
     roomTimer: {
139 152
         color: BaseTheme.palette.text01,
140
-        fontSize: 12,
141
-        fontWeight: '400',
142
-        paddingHorizontal: 8
153
+        ...BaseTheme.typography.bodyShortBold,
154
+        paddingHorizontal: 8,
155
+        paddingVertical: 6,
156
+        textAlign: 'center'
143 157
     },
144 158
 
145 159
     roomTimerView: {
146 160
         backgroundColor: BaseTheme.palette.action02,
147 161
         borderRadius: 3,
148
-        height: 28,
149 162
         justifyContent: 'center',
150 163
         minWidth: 50
151 164
     },
152 165
 
153 166
     roomName: {
154 167
         color: BaseTheme.palette.text01,
155
-        fontSize: 14,
156
-        fontWeight: '400'
168
+        ...BaseTheme.typography.bodyShortBold,
169
+        paddingVertical: 6
157 170
     },
158 171
 
159 172
     roomNameView: {
@@ -161,15 +174,16 @@ export default {
161 174
         borderBottomLeftRadius: 3,
162 175
         borderTopLeftRadius: 3,
163 176
         flexShrink: 1,
164
-        height: 28,
165 177
         justifyContent: 'center',
166
-        paddingHorizontal: 10,
167
-        maxWidth: 168
178
+        paddingHorizontal: 10
168 179
     },
169 180
 
170 181
     roomNameWrapper: {
171 182
         flexDirection: 'row',
172
-        marginHorizontal: 35
183
+        marginRight: 10,
184
+        marginLeft: 8,
185
+        flexShrink: 1,
186
+        flexGrow: 1
173 187
     },
174 188
 
175 189
     /**
@@ -189,6 +203,20 @@ export default {
189 203
 
190 204
     insecureRoomNameLabel: {
191 205
         backgroundColor: INSECURE_ROOM_NAME_LABEL_COLOR
206
+    },
207
+
208
+    raisedHandsCountLabel: {
209
+        backgroundColor: BaseTheme.palette.warning02,
210
+        flexDirection: 'row',
211
+        alignItems: 'center',
212
+        marginLeft: BaseTheme.spacing[0],
213
+        marginBottom: BaseTheme.spacing[0],
214
+        marginRight: BaseTheme.spacing[1]
215
+    },
216
+
217
+    raisedHandsCountLabelText: {
218
+        color: BaseTheme.palette.uiBackground,
219
+        paddingLeft: BaseTheme.spacing[2]
192 220
     }
193 221
 };
194 222
 

+ 40
- 0
react/features/mobile/audio-mode/components/AudioDeviceToggleButton.js 查看文件

@@ -0,0 +1,40 @@
1
+// @flow
2
+import type { Dispatch } from 'redux';
3
+
4
+import { openDialog } from '../../../base/dialog';
5
+import { translate } from '../../../base/i18n';
6
+import { IconVolumeEmpty } from '../../../base/icons';
7
+import { connect } from '../../../base/redux';
8
+import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
9
+
10
+import AudioRoutePickerDialog from './AudioRoutePickerDialog';
11
+
12
+type Props = AbstractButtonProps & {
13
+
14
+    /**
15
+     * The Redux dispatch function.
16
+     */
17
+    dispatch: Dispatch<any>
18
+};
19
+
20
+/**
21
+ * Implements an {@link AbstractButton} to open the audio device list.
22
+ */
23
+class AudioDeviceToggleButton extends AbstractButton<Props, *> {
24
+    accessibilityLabel = 'toolbar.accessibilityLabel.audioRoute';
25
+    icon = IconVolumeEmpty;
26
+    label = 'toolbar.accessibilityLabel.audioRoute';
27
+
28
+    /**
29
+     * Handles clicking / pressing the button, and opens the appropriate dialog.
30
+     *
31
+     * @private
32
+     * @returns {void}
33
+     */
34
+    _handleClick() {
35
+        this.props.dispatch(openDialog(AudioRoutePickerDialog));
36
+    }
37
+}
38
+
39
+
40
+export default translate(connect()(AudioDeviceToggleButton));

+ 0
- 17
react/features/recording/components/native/RecordingExpandedLabel.js 查看文件

@@ -9,8 +9,6 @@ import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
9 9
 import { connect } from '../../../base/redux';
10 10
 import { getSessionStatusToShow } from '../../functions';
11 11
 
12
-import { LIVE_LABEL_COLOR, REC_LABEL_COLOR } from './styles';
13
-
14 12
 type Props = AbstractProps & {
15 13
 
16 14
     /**
@@ -34,21 +32,6 @@ type Props = AbstractProps & {
34 32
  * component to explain the meaning of the {@code RecordingLabel}.
35 33
  */
36 34
 class RecordingExpandedLabel extends ExpandedLabel<Props> {
37
-    /**
38
-     * Returns the color this expanded label should be rendered with.
39
-     *
40
-     * @returns {string}
41
-     */
42
-    _getColor() {
43
-        switch (this.props.mode) {
44
-        case JitsiRecordingConstants.mode.STREAM:
45
-            return LIVE_LABEL_COLOR;
46
-        case JitsiRecordingConstants.mode.FILE:
47
-            return REC_LABEL_COLOR;
48
-        default:
49
-            return null;
50
-        }
51
-    }
52 35
 
53 36
     /**
54 37
      * Returns the label specific text of this {@code ExpandedLabel}.

+ 9
- 3
react/features/recording/components/native/RecordingLabel.js 查看文件

@@ -26,14 +26,20 @@ class RecordingLabel extends AbstractRecordingLabel {
26 26
      * @inheritdoc
27 27
      */
28 28
     _renderLabel() {
29
-        let indicatorStyle;
29
+        let indicatorStyle = styles.indicatorStyle;
30 30
 
31 31
         switch (this.props.mode) {
32 32
         case JitsiRecordingConstants.mode.STREAM:
33
-            indicatorStyle = styles.indicatorLive;
33
+            indicatorStyle = {
34
+                ...indicatorStyle,
35
+                ...styles.indicatorLive
36
+            };
34 37
             break;
35 38
         case JitsiRecordingConstants.mode.FILE:
36
-            indicatorStyle = styles.indicatorRecording;
39
+            indicatorStyle = {
40
+                ...indicatorStyle,
41
+                ...styles.indicatorRecording
42
+            };
37 43
             break;
38 44
         default:
39 45
             // Invalid mode is passed to the component.

+ 11
- 2
react/features/recording/components/native/styles.js 查看文件

@@ -1,15 +1,24 @@
1 1
 // @flow
2 2
 
3 3
 import { ColorPalette, createStyleSheet } from '../../../base/styles';
4
+import BaseTheme from '../../../base/ui/components/BaseTheme';
4 5
 
5 6
 export const LIVE_LABEL_COLOR = ColorPalette.blue;
6
-export const REC_LABEL_COLOR = ColorPalette.red;
7 7
 
8 8
 /**
9 9
  * The styles of the React {@code Components} of the feature recording.
10 10
  */
11 11
 export default createStyleSheet({
12 12
 
13
+    /**
14
+     * Style for the recording indicator.
15
+     */
16
+    indicatorStyle: {
17
+        marginRight: 4,
18
+        marginLeft: 0,
19
+        marginBottom: 0
20
+    },
21
+
13 22
     /**
14 23
      * Style for the recording indicator.
15 24
      */
@@ -21,6 +30,6 @@ export default createStyleSheet({
21 30
      * Style for the recording indicator.
22 31
      */
23 32
     indicatorRecording: {
24
-        backgroundColor: REC_LABEL_COLOR
33
+        backgroundColor: BaseTheme.palette.iconError
25 34
     }
26 35
 });

正在加载...
取消
保存