Explorar el Código

feat(conference-info-header) Make conference info header configurable. (#9638)

master
Horatiu Muresan hace 4 años
padre
commit
5514be630d
No account linked to committer's email address

+ 20
- 3
config.js Ver fichero

@@ -904,15 +904,32 @@ var config = {
904 904
     // If true, tile view will not be enabled automatically when the participants count threshold is reached.
905 905
     // disableTileView: true,
906 906
 
907
+    // Controls the visibility and behavior of the top header conference info labels.
908
+    // If a label's id is not in any of the 2 arrays, it will not be visible at all on the header.
909
+    // conferenceInfo: {
910
+    //     // those labels will not be hidden in tandem with the toolbox.
911
+    //     alwaysVisible: ['recording', 'local-recording'],
912
+    //     // those labels will be auto-hidden in tandem with the toolbox buttons.
913
+    //     autoHide: [
914
+    //         'subject',
915
+    //         'conference-timer',
916
+    //         'participants-count',
917
+    //         'e2ee',
918
+    //         'transcribing',
919
+    //         'video-quality',
920
+    //         'insecure-room'
921
+    //     ]
922
+    // },
923
+
907 924
     // Hides the conference subject
908 925
     // hideConferenceSubject: true,
909 926
 
910
-    // Hides the recording label
911
-    // hideRecordingLabel: false,
912
-
913 927
     // Hides the conference timer.
914 928
     // hideConferenceTimer: true,
915 929
 
930
+    // Hides the recording label
931
+    // hideRecordingLabel: false,
932
+
916 933
     // Hides the participants stats
917 934
     // hideParticipantsStats: true,
918 935
 

+ 39
- 45
css/_subject.scss Ver fichero

@@ -1,54 +1,22 @@
1 1
 .subject {
2
-    box-sizing: border-box;
3 2
     color: #fff;
4
-    margin-top: 20px;
5
-    position: absolute;
6
-    top: -120px;
7
-    transition: top .3s ease-in;
8
-    width: 100%;
3
+    margin-top: -120px;
4
+    transition: margin-top .3s ease-in;
9 5
     z-index: $zindex3;
10 6
 
11 7
     &.visible {
12
-        top: 0;
13
-    }
14
-
15
-    &.recording {
16
-        top: 0;
17
-
18
-        .subject-details-container {
19
-            opacity: 0;
20
-            transition: opacity .3s ease-in;
21
-        }
22
-
23
-        .subject-info-container .show-always {
24
-            transition: margin-left .3s ease-in;
25
-        }
26
-
27
-        &.visible {
28
-            .subject-details-container {
29
-                opacity: 1;
30
-            }
31
-        }
8
+        margin-top: 20px;
32 9
     }
33 10
 }
34 11
 
35
-.subject-details-container {
36
-    display: flex;
37
-}
38
-
39 12
 .subject-info-container {
40 13
     display: flex;
41 14
     justify-content: center;
42
-    max-width: calc(100% - 280px);
43 15
     margin: 0 auto;
44
-
45
-    &--full-width {
46
-        max-width: 100%;
47
-    }
16
+    height: 28px;
48 17
 
49 18
     @media (max-width: 500px) {
50 19
         flex-wrap: wrap;
51
-        max-width: 100%;
52 20
     }
53 21
 }
54 22
 
@@ -63,21 +31,47 @@
63 31
 .subject-text {
64 32
     background: rgba(0, 0, 0, 0.6);
65 33
     border-radius: 3px 0px 0px 3px;
34
+    box-sizing: border-box;
66 35
     font-size: 14px;
67
-    line-height: 24px;
68
-    padding: 2px 16px;
69
-    height: 24px;
70
-    overflow: hidden;
71
-    text-overflow: ellipsis;
72
-    white-space: nowrap;
36
+    line-height: 28px;
37
+    padding: 0 16px;
38
+    height: 28px;
39
+
40
+    @media (max-width: 700px) {
41
+        max-width: 100px;
42
+    }
43
+
44
+    @media (max-width: 300px) {
45
+        display: none;
46
+    }
47
+
48
+    &--content {
49
+        overflow: hidden;
50
+        text-overflow: ellipsis;
51
+        white-space: nowrap;
52
+    }
73 53
 }
74 54
 
75 55
 .subject-timer {
76 56
     background: rgba(0, 0, 0, 0.8);
77 57
     border-radius: 0px 3px 3px 0px;
78
-    display: inline-block;
58
+    box-sizing: border-box;
79 59
     font-size: 12px;
80
-    line-height: 16px;
60
+    line-height: 28px;
81 61
     min-width: 34px;
82
-    padding: 6px 8px;
62
+    padding: 0 8px;
63
+    height: 28px;
64
+
65
+    @media (max-width: 300px) {
66
+        display: none;
67
+    }
68
+}
69
+
70
+.details-container {
71
+    width: 100%;
72
+    display: flex;
73
+    justify-content: center;
74
+    position: absolute;
75
+    top: 0;
76
+    height: 48px;
83 77
 }

+ 1
- 0
react/features/base/config/configWhitelist.js Ver fichero

@@ -70,6 +70,7 @@ export default [
70 70
      */
71 71
     'callUUID',
72 72
 
73
+    'conferenceInfo',
73 74
     'channelLastN',
74 75
     'connectionIndicators',
75 76
     'constraints',

+ 54
- 0
react/features/base/config/reducer.js Ver fichero

@@ -2,6 +2,7 @@
2 2
 
3 3
 import _ from 'lodash';
4 4
 
5
+import { CONFERENCE_INFO } from '../../conference/components/constants';
5 6
 import { equals, ReducerRegistry } from '../redux';
6 7
 
7 8
 import {
@@ -56,6 +57,17 @@ const INITIAL_RN_STATE = {
56 57
     }
57 58
 };
58 59
 
60
+/**
61
+ * Mapping between old configs controlling the conference info headers visibility and the
62
+ * new configs. Needed in order to keep backwards compatibility.
63
+ */
64
+const CONFERENCE_HEADER_MAPPING = {
65
+    hideConferenceTimer: [ 'conference-timer' ],
66
+    hideConferenceSubject: [ 'subject' ],
67
+    hideParticipantsStats: [ 'participants-count' ],
68
+    hideRecordingLabel: [ 'recording', 'local-recording' ]
69
+};
70
+
59 71
 ReducerRegistry.register('features/base/config', (state = _getInitialState(), action) => {
60 72
     switch (action.type) {
61 73
     case UPDATE_CONFIG:
@@ -172,6 +184,27 @@ function _setConfig(state, { config }) {
172 184
     return equals(state, newState) ? state : newState;
173 185
 }
174 186
 
187
+/**
188
+ * Processes the conferenceInfo object against the defaults.
189
+ *
190
+ * @param {Object} config - The old config.
191
+ * @returns {Object} The processed conferenceInfo object.
192
+ */
193
+function _getConferenceInfo(config) {
194
+    const { conferenceInfo } = config;
195
+
196
+    if (conferenceInfo) {
197
+        return {
198
+            alwaysVisible: conferenceInfo.alwaysVisible ?? [ ...CONFERENCE_INFO.alwaysVisible ],
199
+            autoHide: conferenceInfo.autoHide ?? [ ...CONFERENCE_INFO.autoHide ]
200
+        };
201
+    }
202
+
203
+    return {
204
+        ...CONFERENCE_INFO
205
+    };
206
+}
207
+
175 208
 /**
176 209
  * Constructs a new config {@code Object}, if necessary, out of a specific
177 210
  * config {@code Object} which is in the latest format supported by jitsi-meet.
@@ -194,6 +227,27 @@ function _translateLegacyConfig(oldValue: Object) {
194 227
         newValue.toolbarButtons = interfaceConfig.TOOLBAR_BUTTONS;
195 228
     }
196 229
 
230
+    const filteredConferenceInfo = Object.keys(CONFERENCE_HEADER_MAPPING).filter(key => oldValue[key]);
231
+
232
+    if (filteredConferenceInfo.length) {
233
+        newValue.conferenceInfo = _getConferenceInfo(oldValue);
234
+
235
+        filteredConferenceInfo.forEach(key => {
236
+            // hideRecordingLabel does not mean not render it at all, but autoHide it
237
+            if (key === 'hideRecordingLabel') {
238
+                newValue.conferenceInfo.alwaysVisible
239
+                    = newValue.conferenceInfo.alwaysVisible.filter(c => !CONFERENCE_HEADER_MAPPING[key].includes(c));
240
+                newValue.conferenceInfo.autoHide
241
+                    = _.union(newValue.conferenceInfo.autoHide, CONFERENCE_HEADER_MAPPING[key]);
242
+            } else {
243
+                newValue.conferenceInfo.alwaysVisible
244
+                    = newValue.conferenceInfo.alwaysVisible.filter(c => !CONFERENCE_HEADER_MAPPING[key].includes(c));
245
+                newValue.conferenceInfo.autoHide
246
+                    = newValue.conferenceInfo.autoHide.filter(c => !CONFERENCE_HEADER_MAPPING[key].includes(c));
247
+            }
248
+        });
249
+    }
250
+
197 251
     if (!oldValue.connectionIndicators
198 252
             && typeof interfaceConfig === 'object'
199 253
             && (interfaceConfig.hasOwnProperty('CONNECTION_INDICATOR_DISABLED')

+ 12
- 0
react/features/conference/components/constants.js Ver fichero

@@ -0,0 +1,12 @@
1
+export const CONFERENCE_INFO = {
2
+    alwaysVisible: [ 'recording', 'local-recording' ],
3
+    autoHide: [
4
+        'subject',
5
+        'conference-timer',
6
+        'participants-count',
7
+        'e2ee',
8
+        'transcribing',
9
+        'video-quality',
10
+        'insecure-room'
11
+    ]
12
+};

+ 22
- 0
react/features/conference/components/functions.js Ver fichero

@@ -0,0 +1,22 @@
1
+// @flow
2
+
3
+import { CONFERENCE_INFO } from './constants';
4
+
5
+/**
6
+ * Retrieves the conference info labels based on config values and defaults.
7
+ *
8
+ * @param {Object} state - The redux state.
9
+ * @returns {Object} The conferenceInfo object.
10
+ */
11
+export const getConferenceInfo = (state: Object) => {
12
+    const { conferenceInfo } = state['features/base/config'];
13
+
14
+    if (conferenceInfo) {
15
+        return {
16
+            alwaysVisible: conferenceInfo.alwaysVisible ?? CONFERENCE_INFO.alwaysVisible,
17
+            autoHide: conferenceInfo.autoHide ?? CONFERENCE_INFO.autoHide
18
+        };
19
+    }
20
+
21
+    return CONFERENCE_INFO;
22
+};

+ 134
- 126
react/features/conference/components/web/ConferenceInfo.js Ver fichero

@@ -1,22 +1,22 @@
1 1
 /* @flow */
2 2
 
3
-import React from 'react';
3
+import React, { Component } from 'react';
4 4
 
5
-import { getConferenceName } from '../../../base/conference/functions';
6 5
 import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
7
-import { getParticipantCount } from '../../../base/participants/functions';
8 6
 import { connect } from '../../../base/redux';
9 7
 import { E2EELabel } from '../../../e2ee';
10 8
 import { LocalRecordingLabel } from '../../../local-recording';
11
-import { getSessionStatusToShow, RecordingLabel } from '../../../recording';
9
+import { RecordingLabel } from '../../../recording';
12 10
 import { isToolboxVisible } from '../../../toolbox/functions.web';
13 11
 import { TranscribingLabel } from '../../../transcribing';
14 12
 import { VideoQualityLabel } from '../../../video-quality';
15 13
 import ConferenceTimer from '../ConferenceTimer';
14
+import { getConferenceInfo } from '../functions';
16 15
 
16
+import ConferenceInfoContainer from './ConferenceInfoContainer';
17
+import InsecureRoomNameLabel from './InsecureRoomNameLabel';
17 18
 import ParticipantsCount from './ParticipantsCount';
18
-
19
-import { InsecureRoomNameLabel } from '.';
19
+import SubjectText from './SubjectText';
20 20
 
21 21
 /**
22 22
  * The type of the React {@code Component} props of {@link Subject}.
@@ -24,116 +24,150 @@ import { InsecureRoomNameLabel } from '.';
24 24
 type Props = {
25 25
 
26 26
     /**
27
-     * Whether the info should span across the full width.
27
+     * The conference info labels to be shown in the conference header.
28 28
      */
29
-    _fullWidth: boolean,
29
+    _conferenceInfo: Object,
30 30
 
31 31
     /**
32
-     * Whether the conference name and timer should be displayed or not.
32
+     * Indicates whether the component should be visible or not.
33 33
      */
34
-    _hideConferenceNameAndTimer: boolean,
34
+    _visible: boolean
35
+};
35 36
 
36
-    /**
37
-     * Whether the conference timer should be shown or not.
38
-     */
39
-    _hideConferenceTimer: boolean,
37
+const COMPONENTS = [
38
+    {
39
+        Component: SubjectText,
40
+        id: 'subject'
41
+    },
42
+    {
43
+        Component: ConferenceTimer,
44
+        id: 'conference-timer'
45
+    },
46
+    {
47
+        Component: ParticipantsCount,
48
+        id: 'participants-count'
49
+    },
50
+    {
51
+        Component: E2EELabel,
52
+        id: 'e2ee'
53
+    },
54
+    {
55
+        Component: () => (
56
+            <>
57
+                <RecordingLabel mode = { JitsiRecordingConstants.mode.FILE } />
58
+                <RecordingLabel mode = { JitsiRecordingConstants.mode.STREAM } />
59
+            </>
60
+        ),
61
+        id: 'recording'
62
+    },
63
+    {
64
+        Component: LocalRecordingLabel,
65
+        id: 'local-recording'
66
+    },
67
+    {
68
+        Component: TranscribingLabel,
69
+        id: 'transcribing'
70
+    },
71
+    {
72
+        Component: VideoQualityLabel,
73
+        id: 'video-quality'
74
+    },
75
+    {
76
+        Component: InsecureRoomNameLabel,
77
+        id: 'insecure-room'
78
+    }
79
+];
40 80
 
81
+/**
82
+ * The upper band of the meeing containing the conference name, timer and labels.
83
+ *
84
+ * @param {Object} props - The props of the component.
85
+ * @returns {React$None}
86
+ */
87
+class ConferenceInfo extends Component<Props> {
41 88
     /**
42
-     * Whether the recording label should be shown or not.
89
+     * Initializes a new {@code ConferenceInfo} instance.
90
+     *
91
+     * @param {Props} props - The read-only React {@code Component} props with
92
+     * which the new instance is to be initialized.
43 93
      */
44
-    _hideRecordingLabel: boolean,
94
+    constructor(props: Props) {
95
+        super(props);
45 96
 
46
-    /**
47
-     * Whether the participant count should be shown or not.
48
-     */
49
-    _showParticipantCount: boolean,
97
+        this._renderAutoHide = this._renderAutoHide.bind(this);
98
+        this._renderAlwaysVisible = this._renderAlwaysVisible.bind(this);
99
+    }
100
+
101
+    _renderAutoHide: () => void;
50 102
 
51 103
     /**
52
-     * The subject or the of the conference.
53
-     * Falls back to conference name.
104
+     * Renders auto-hidden info header labels.
105
+     *
106
+     * @returns {void}
54 107
      */
55
-    _subject: string,
108
+    _renderAutoHide() {
109
+        const { autoHide } = this.props._conferenceInfo;
110
+
111
+        if (!autoHide || !autoHide.length) {
112
+            return null;
113
+        }
114
+
115
+        return (
116
+            <ConferenceInfoContainer visible = { this.props._visible } >
117
+                {
118
+                    COMPONENTS
119
+                        .filter(comp => autoHide.includes(comp.id))
120
+                        .map(c =>
121
+                            <c.Component key = { c.id } />
122
+                        )
123
+                }
124
+            </ConferenceInfoContainer>
125
+        );
126
+    }
127
+
128
+    _renderAlwaysVisible: () => void;
56 129
 
57 130
     /**
58
-     * Indicates whether the component should be visible or not.
131
+     * Renders the always visible info header labels.
132
+     *
133
+     * @returns {void}
59 134
      */
60
-    _visible: boolean,
135
+    _renderAlwaysVisible() {
136
+        const { alwaysVisible } = this.props._conferenceInfo;
137
+
138
+        if (!alwaysVisible || !alwaysVisible.length) {
139
+            return null;
140
+        }
141
+
142
+        return (
143
+            <ConferenceInfoContainer visible = { true } >
144
+                {
145
+                    COMPONENTS
146
+                        .filter(comp => alwaysVisible.includes(comp.id))
147
+                        .map(c =>
148
+                            <c.Component key = { c.id } />
149
+                        )
150
+                }
151
+            </ConferenceInfoContainer>
152
+        );
153
+    }
61 154
 
62 155
     /**
63
-     * Whether or not the recording label is visible.
156
+     * Implements React's {@link Component#render()}.
157
+     *
158
+     * @inheritdoc
159
+     * @returns {ReactElement}
64 160
      */
65
-    _recordingLabel: boolean
66
-};
67
-
68
-const getLeftMargin = () => {
69
-    const subjectContainerWidth = document.getElementById('subject-container')?.clientWidth ?? 0;
70
-    const recContainerWidth = document.getElementById('rec-container')?.clientWidth ?? 0;
71
-    const subjectDetailsContainer = document.getElementById('subject-details-container')?.clientWidth ?? 0;
72
-
73
-    return (subjectContainerWidth - recContainerWidth - subjectDetailsContainer) / 2;
74
-};
75
-
76
-/**
77
- * The upper band of the meeing containing the conference name, timer and labels.
78
- *
79
- * @param {Object} props - The props of the component.
80
- * @returns {React$None}
81
- */
82
-function ConferenceInfo(props: Props) {
83
-    const {
84
-        _hideConferenceNameAndTimer,
85
-        _hideConferenceTimer,
86
-        _showParticipantCount,
87
-        _hideRecordingLabel,
88
-        _subject,
89
-        _fullWidth,
90
-        _visible,
91
-        _recordingLabel
92
-    } = props;
93
-
94
-    return (
95
-        <div className = { `subject ${_recordingLabel ? 'recording' : ''} ${_visible ? 'visible' : ''}` }>
96
-            <div
97
-                className = { `subject-info-container${_fullWidth ? ' subject-info-container--full-width' : ''}` }
98
-                id = 'subject-container'>
99
-                {!_hideRecordingLabel && <div
100
-                    className = 'show-always'
101
-                    id = 'rec-container'
102
-                    // eslint-disable-next-line react-native/no-inline-styles
103
-                    style = {{
104
-                        marginLeft: !_recordingLabel || _visible ? 0 : getLeftMargin()
105
-                    }}>
106
-                    <RecordingLabel mode = { JitsiRecordingConstants.mode.FILE } />
107
-                    <RecordingLabel mode = { JitsiRecordingConstants.mode.STREAM } />
108
-                    <LocalRecordingLabel />
109
-                </div>
110
-                }
111
-                <div
112
-                    className = 'subject-details-container'
113
-                    id = 'subject-details-container'>
114
-                    {
115
-                        !_hideConferenceNameAndTimer
116
-                            && <div className = 'subject-info'>
117
-                                { _subject && <span className = 'subject-text'>{ _subject }</span>}
118
-                                { !_hideConferenceTimer && <ConferenceTimer /> }
119
-                            </div>
120
-                    }
121
-                    { _showParticipantCount && <ParticipantsCount /> }
122
-                    <E2EELabel />
123
-                    {_hideRecordingLabel && (
124
-                        <>
125
-                            <RecordingLabel mode = { JitsiRecordingConstants.mode.FILE } />
126
-                            <RecordingLabel mode = { JitsiRecordingConstants.mode.STREAM } />
127
-                            <LocalRecordingLabel />
128
-                        </>
129
-                    )}
130
-                    <TranscribingLabel />
131
-                    <VideoQualityLabel />
132
-                    <InsecureRoomNameLabel />
133
-                </div>
161
+    render() {
162
+        return (
163
+            <div className = 'details-container' >
164
+                { [
165
+                    this._renderAlwaysVisible(),
166
+                    this._renderAutoHide()
167
+                ] }
134 168
             </div>
135
-        </div>
136
-    );
169
+        );
170
+    }
137 171
 }
138 172
 
139 173
 /**
@@ -143,40 +177,14 @@ function ConferenceInfo(props: Props) {
143 177
  * @param {Object} state - The Redux state.
144 178
  * @private
145 179
  * @returns {{
146
- *     _hideConferenceTimer: boolean,
147
- *     _showParticipantCount: boolean,
148
- *     _subject: string,
149
- *     _visible: boolean
180
+ *     _visible: boolean,
181
+ *     _conferenceInfo: Object
150 182
  * }}
151 183
  */
152 184
 function _mapStateToProps(state) {
153
-    const participantCount = getParticipantCount(state);
154
-    const {
155
-        hideConferenceTimer,
156
-        hideConferenceSubject,
157
-        hideParticipantsStats,
158
-        hideRecordingLabel,
159
-        iAmRecorder
160
-    } = state['features/base/config'];
161
-    const { clientWidth } = state['features/base/responsive-ui'];
162
-
163
-    const shouldHideRecordingLabel = hideRecordingLabel || iAmRecorder;
164
-    const fileRecordingStatus = getSessionStatusToShow(state, JitsiRecordingConstants.mode.FILE);
165
-    const streamRecordingStatus = getSessionStatusToShow(state, JitsiRecordingConstants.mode.STREAM);
166
-    const isFileRecording = fileRecordingStatus ? fileRecordingStatus !== JitsiRecordingConstants.status.OFF : false;
167
-    const isStreamRecording = streamRecordingStatus
168
-        ? streamRecordingStatus !== JitsiRecordingConstants.status.OFF : false;
169
-    const { isEngaged } = state['features/local-recording'];
170
-
171 185
     return {
172
-        _hideConferenceNameAndTimer: clientWidth < 300,
173
-        _hideConferenceTimer: Boolean(hideConferenceTimer),
174
-        _hideRecordingLabel: shouldHideRecordingLabel,
175
-        _fullWidth: state['features/video-layout'].tileViewEnabled,
176
-        _showParticipantCount: participantCount > 2 && !hideParticipantsStats,
177
-        _subject: hideConferenceSubject ? '' : getConferenceName(state),
178 186
         _visible: isToolboxVisible(state),
179
-        _recordingLabel: (isFileRecording || isStreamRecording || isEngaged) && !shouldHideRecordingLabel
187
+        _conferenceInfo: getConferenceInfo(state)
180 188
     };
181 189
 }
182 190
 

+ 24
- 0
react/features/conference/components/web/ConferenceInfoContainer.js Ver fichero

@@ -0,0 +1,24 @@
1
+/* @flow */
2
+
3
+import React from 'react';
4
+
5
+type Props = {
6
+
7
+    /**
8
+     * The children components.
9
+     */
10
+     children: React$Node,
11
+
12
+     /**
13
+      * Whether this conference info container should be visible or not.
14
+      */
15
+     visible: boolean
16
+}
17
+
18
+export default ({ visible, children }: Props) => (
19
+    <div className = { `subject${visible ? ' visible' : ''}` }>
20
+        <div className = { 'subject-info-container' }>
21
+            {children}
22
+        </div>
23
+    </div>
24
+);

+ 8
- 2
react/features/conference/components/web/ParticipantsCount.js Ver fichero

@@ -19,7 +19,7 @@ type Props = {
19 19
     /**
20 20
      * Number of the conference participants.
21 21
      */
22
-    count: string,
22
+    count: number,
23 23
 
24 24
     /**
25 25
      * Conference data.
@@ -72,6 +72,12 @@ class ParticipantsCount extends PureComponent<Props> {
72 72
      * @returns {ReactElement}
73 73
      */
74 74
     render() {
75
+        const { count } = this.props;
76
+
77
+        if (count <= 2) {
78
+            return null;
79
+        }
80
+
75 81
         return (
76 82
             <div
77 83
                 className = 'participants-count'
@@ -79,7 +85,7 @@ class ParticipantsCount extends PureComponent<Props> {
79 85
                 <Label
80 86
                     className = 'label--white'
81 87
                     icon = { IconUserGroups }
82
-                    text = { this.props.count } />
88
+                    text = { count } />
83 89
             </div>
84 90
         );
85 91
     }

+ 50
- 0
react/features/conference/components/web/SubjectText.js Ver fichero

@@ -0,0 +1,50 @@
1
+/* @flow */
2
+
3
+import React from 'react';
4
+
5
+import { getConferenceName } from '../../../base/conference/functions';
6
+import { connect } from '../../../base/redux';
7
+import { Tooltip } from '../../../base/tooltip';
8
+
9
+type Props = {
10
+
11
+    /**
12
+     * The conference display name.
13
+     */
14
+    _subject: string
15
+}
16
+
17
+/**
18
+ * Label for the conference name.
19
+ *
20
+ * @param {Props} props - The props of the component.
21
+ * @returns {ReactElement}
22
+ */
23
+const SubjectText = ({ _subject }: Props) => (
24
+    <div className = 'subject-text'>
25
+        <Tooltip
26
+            content = { _subject }
27
+            position = 'bottom'>
28
+            <div className = 'subject-text--content'>{ _subject }</div>
29
+        </Tooltip>
30
+    </div>
31
+);
32
+
33
+
34
+/**
35
+ * Maps (parts of) the Redux state to the associated
36
+ * {@code Subject}'s props.
37
+ *
38
+ * @param {Object} state - The Redux state.
39
+ * @private
40
+ * @returns {{
41
+ *     _subject: string,
42
+ * }}
43
+ */
44
+function _mapStateToProps(state) {
45
+    return {
46
+        _subject: getConferenceName(state)
47
+    };
48
+}
49
+
50
+export default connect(_mapStateToProps)(SubjectText);

+ 1
- 0
react/features/conference/components/web/index.js Ver fichero

@@ -5,3 +5,4 @@ export { default as renderConferenceTimer } from './ConferenceTimerDisplay';
5 5
 export { default as InsecureRoomNameLabel } from './InsecureRoomNameLabel';
6 6
 export { default as InviteMore } from './InviteMore';
7 7
 export { default as ConferenceInfo } from './ConferenceInfo';
8
+export { default as SubjectText } from './SubjectText';

+ 13
- 6
react/features/local-recording/components/LocalRecordingLabel.web.js Ver fichero

@@ -14,14 +14,19 @@ import { Tooltip } from '../../base/tooltip';
14 14
 type Props = {
15 15
 
16 16
     /**
17
-     * Invoked to obtain translated strings.
17
+     * Whether this is the Jibri recorder participant.
18 18
      */
19
-    t: Function,
19
+     _iAmRecorder: boolean,
20
+
21
+     /**
22
+      * Whether local recording is engaged or not.
23
+      */
24
+     _isEngaged: boolean,
20 25
 
21 26
     /**
22
-     * Whether local recording is engaged or not.
27
+     * Invoked to obtain translated strings.
23 28
      */
24
-    isEngaged: boolean
29
+    t: Function,
25 30
 };
26 31
 
27 32
 /**
@@ -38,7 +43,7 @@ class LocalRecordingLabel extends Component<Props> {
38 43
      * @returns {ReactElement}
39 44
      */
40 45
     render() {
41
-        if (!this.props.isEngaged) {
46
+        if (!this.props._isEngaged || this.props._iAmRecorder) {
42 47
             return null;
43 48
         }
44 49
 
@@ -66,9 +71,11 @@ class LocalRecordingLabel extends Component<Props> {
66 71
  */
67 72
 function _mapStateToProps(state) {
68 73
     const { isEngaged } = state['features/local-recording'];
74
+    const { iAmRecorder } = state['features/base/config'];
69 75
 
70 76
     return {
71
-        isEngaged
77
+        _isEngaged: isEngaged,
78
+        _iAmRecorder: iAmRecorder
72 79
     };
73 80
 }
74 81
 

+ 7
- 1
react/features/recording/components/AbstractRecordingLabel.js Ver fichero

@@ -15,6 +15,11 @@ import { getSessionStatusToShow } from '../functions';
15 15
  */
16 16
 type Props = {
17 17
 
18
+    /**
19
+     * Whether this is the Jibri recorder participant.
20
+     */
21
+    _iAmRecorder: boolean,
22
+
18 23
     /**
19 24
      * The status of the highermost priority session.
20 25
      */
@@ -100,7 +105,7 @@ export default class AbstractRecordingLabel
100 105
      * @inheritdoc
101 106
      */
102 107
     render() {
103
-        return this.props._status && !this.state.staleLabel
108
+        return this.props._status && !this.state.staleLabel && !this.props._iAmRecorder
104 109
             ? this._renderLabel() : null;
105 110
     }
106 111
 
@@ -172,6 +177,7 @@ export function _mapStateToProps(state: Object, ownProps: Props) {
172 177
     const { mode } = ownProps;
173 178
 
174 179
     return {
180
+        _iAmRecorder: state['features/base/config'].iAmRecorder,
175 181
         _status: getSessionStatusToShow(state, mode)
176 182
     };
177 183
 }

Loading…
Cancelar
Guardar