Ver código fonte

feat: Add search to speaker stats (#9510)

* Additional setting to add search to speaker stats

* Add translation for speaker stats search placeholder

* Unset speaker stats search input autocomplete

* Fix lint errors for speaker stats search

* Change setting to disableSpeakerStatsSearch

* Better Object.prototype.hasOwnProperty.call alternative

* Make SpeakerStatsSearch a functional component

* Align header with input and use material-ui styles instead of scss and remove SpeakerStats header and fix dialog close

* Resolve code style remark in SpeakerStats constructor

* Resolve component empty return value remark in SpeakerStatsSearch

* Resolve get config property in outside function remark in SpeakerStatsSearch

* Resolve unnecessary anonymous function remark in SpeakerStatsSearch
master
dimitardelchev93 3 anos atrás
pai
commit
c123ff9e15
Nenhuma conta vinculada ao e-mail do autor do commit

+ 3
- 0
config.js Ver arquivo

@@ -144,6 +144,9 @@ var config = {
144 144
     // Sets the preferred resolution (height) for local video. Defaults to 720.
145 145
     // resolution: 720,
146 146
 
147
+    // Specifies whether there will be a search field in speaker stats or not
148
+    // disableSpeakerStatsSearch: false,
149
+
147 150
     // How many participants while in the tile view mode, before the receiving video quality is reduced from HD to SD.
148 151
     // Use -1 to disable.
149 152
     // maxFullResolutionParticipants: 2,

+ 1
- 0
lang/main-bg.json Ver arquivo

@@ -586,6 +586,7 @@
586 586
     },
587 587
     "speaker": "Говорещ",
588 588
     "speakerStats": {
589
+        "search": "Търсене",
589 590
         "hours": "{{count}}ч",
590 591
         "minutes": "{{count}}мин",
591 592
         "name": "Име",

+ 1
- 0
lang/main-de.json Ver arquivo

@@ -802,6 +802,7 @@
802 802
     },
803 803
     "speaker": "Sprecher/-in",
804 804
     "speakerStats": {
805
+        "search": "Suche",
805 806
         "hours": "{{count}} Std. ",
806 807
         "minutes": "{{count}} Min. ",
807 808
         "name": "Name",

+ 1
- 0
lang/main.json Ver arquivo

@@ -802,6 +802,7 @@
802 802
     },
803 803
     "speaker": "Speaker",
804 804
     "speakerStats": {
805
+        "search": "Search",
805 806
         "hours": "{{count}}h",
806 807
         "minutes": "{{count}}m",
807 808
         "name": "Name",

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

@@ -98,6 +98,7 @@ export default [
98 98
     'disableRtx',
99 99
     'disableShortcuts',
100 100
     'disableShowMoreStats',
101
+    'disableSpeakerStatsSearch',
101 102
     'disableSimulcast',
102 103
     'disableThirdPartyRequests',
103 104
     'disableTileView',

+ 60
- 5
react/features/speaker-stats/components/SpeakerStats.js Ver arquivo

@@ -6,9 +6,11 @@ import { Dialog } from '../../base/dialog';
6 6
 import { translate } from '../../base/i18n';
7 7
 import { getLocalParticipant } from '../../base/participants';
8 8
 import { connect } from '../../base/redux';
9
+import { escapeRegexp } from '../../base/util';
9 10
 
10 11
 import SpeakerStatsItem from './SpeakerStatsItem';
11 12
 import SpeakerStatsLabels from './SpeakerStatsLabels';
13
+import SpeakerStatsSearch from './SpeakerStatsSearch';
12 14
 
13 15
 declare var interfaceConfig: Object;
14 16
 
@@ -41,7 +43,12 @@ type State = {
41 43
     /**
42 44
      * The stats summary provided by the JitsiConference.
43 45
      */
44
-    stats: Object
46
+    stats: Object,
47
+
48
+    /**
49
+     * The search input criteria.
50
+     */
51
+    criteria: string,
45 52
 };
46 53
 
47 54
 /**
@@ -62,11 +69,13 @@ class SpeakerStats extends Component<Props, State> {
62 69
         super(props);
63 70
 
64 71
         this.state = {
65
-            stats: this.props.conference.getSpeakerStats()
72
+            stats: this._getSpeakerStats(),
73
+            criteria: ''
66 74
         };
67 75
 
68 76
         // Bind event handlers so they are only bound once per instance.
69 77
         this._updateStats = this._updateStats.bind(this);
78
+        this._onSearch = this._onSearch.bind(this);
70 79
     }
71 80
 
72 81
     /**
@@ -102,8 +111,9 @@ class SpeakerStats extends Component<Props, State> {
102 111
             <Dialog
103 112
                 cancelKey = { 'dialog.close' }
104 113
                 submitDisabled = { true }
105
-                titleKey = 'speakerStats.speakerStats'>
114
+                titleKey = { 'speakerStats.speakerStats' }>
106 115
                 <div className = 'speaker-stats'>
116
+                    <SpeakerStatsSearch onSearch = { this._onSearch } />
107 117
                     <SpeakerStatsLabels />
108 118
                     { items }
109 119
                 </div>
@@ -111,6 +121,32 @@ class SpeakerStats extends Component<Props, State> {
111 121
         );
112 122
     }
113 123
 
124
+    /**
125
+     * Update the internal state with the latest speaker stats.
126
+     *
127
+     * @returns {void}
128
+     * @private
129
+     */
130
+    _getSpeakerStats() {
131
+        const stats = { ...this.props.conference.getSpeakerStats() };
132
+
133
+        if (this.state?.criteria) {
134
+            const searchRegex = new RegExp(this.state.criteria, 'gi');
135
+
136
+            for (const id in stats) {
137
+                if (stats[id].hasOwnProperty('_isLocalStats')) {
138
+                    const name = stats[id].isLocalStats() ? this.props._localDisplayName : stats[id].getDisplayName();
139
+
140
+                    if (!name || !name.match(searchRegex)) {
141
+                        delete stats[id];
142
+                    }
143
+                }
144
+            }
145
+        }
146
+
147
+        return stats;
148
+    }
149
+
114 150
     /**
115 151
      * Create a SpeakerStatsItem instance for the passed in user id.
116 152
      *
@@ -155,6 +191,22 @@ class SpeakerStats extends Component<Props, State> {
155 191
         );
156 192
     }
157 193
 
194
+    _onSearch: () => void;
195
+
196
+    /**
197
+     * Search the existing participants by name.
198
+     *
199
+     * @returns {void}
200
+     * @param {string} criteria - The search parameter.
201
+     * @protected
202
+     */
203
+    _onSearch(criteria = '') {
204
+        this.setState({
205
+            ...this.state,
206
+            criteria: escapeRegexp(criteria)
207
+        });
208
+    }
209
+
158 210
     _updateStats: () => void;
159 211
 
160 212
     /**
@@ -164,9 +216,12 @@ class SpeakerStats extends Component<Props, State> {
164 216
      * @private
165 217
      */
166 218
     _updateStats() {
167
-        const stats = this.props.conference.getSpeakerStats();
219
+        const stats = this._getSpeakerStats();
168 220
 
169
-        this.setState({ stats });
221
+        this.setState({
222
+            ...this.state,
223
+            stats
224
+        });
170 225
     }
171 226
 }
172 227
 

+ 73
- 0
react/features/speaker-stats/components/SpeakerStatsSearch.js Ver arquivo

@@ -0,0 +1,73 @@
1
+/* @flow */
2
+
3
+import { FieldTextStateless as TextField } from '@atlaskit/field-text';
4
+import { makeStyles } from '@material-ui/core/styles';
5
+import React, { useCallback, useState } from 'react';
6
+import { useTranslation } from 'react-i18next';
7
+import { useSelector } from 'react-redux';
8
+
9
+import { getFieldValue } from '../../base/react';
10
+import { isSpeakerStatsSearchDisabled } from '../functions';
11
+
12
+const useStyles = makeStyles(() => {
13
+    return {
14
+        speakerStatsSearch: {
15
+            position: 'absolute',
16
+            right: '50px',
17
+            top: '-5px'
18
+        }
19
+    };
20
+});
21
+
22
+/**
23
+ * The type of the React {@code Component} props of {@link SpeakerStatsSearch}.
24
+ */
25
+type Props = {
26
+
27
+    /**
28
+     * The function to initiate the change in the speaker stats table.
29
+     */
30
+    onSearch: Function,
31
+
32
+};
33
+
34
+/**
35
+ * React component for display an individual user's speaker stats.
36
+ *
37
+ * @returns {React$Element<any>}
38
+ */
39
+function SpeakerStatsSearch({ onSearch }: Props) {
40
+    const classes = useStyles();
41
+    const { t } = useTranslation();
42
+    const [ searchValue, setSearchValue ] = useState<string>('');
43
+    const onChange = useCallback((evt: Event) => {
44
+        const value = getFieldValue(evt);
45
+
46
+        setSearchValue(value);
47
+
48
+        onSearch && onSearch(value);
49
+    }, []);
50
+    const disableSpeakerStatsSearch = useSelector(isSpeakerStatsSearchDisabled);
51
+
52
+    if (disableSpeakerStatsSearch) {
53
+        return null;
54
+    }
55
+
56
+    return (
57
+        <div className = { classes.speakerStatsSearch }>
58
+            <TextField
59
+                autoComplete = 'off'
60
+                autoFocus = { false }
61
+                compact = { true }
62
+                name = 'speakerStatsSearch'
63
+                onChange = { onChange }
64
+                placeholder = { t('speakerStats.search') }
65
+                shouldFitContainer = { false }
66
+                type = 'text'
67
+                value = { searchValue } />
68
+        </div>
69
+    );
70
+}
71
+
72
+export default SpeakerStatsSearch;
73
+

+ 11
- 0
react/features/speaker-stats/functions.js Ver arquivo

@@ -0,0 +1,11 @@
1
+// @flow
2
+
3
+/**
4
+ * Checks if the speaker stats search is disabled.
5
+ *
6
+ * @param {*} state - The redux state.
7
+ * @returns {boolean} - True if the speaker stats search is disabled and false otherwise.
8
+ */
9
+export function isSpeakerStatsSearchDisabled(state: Object) {
10
+    return state['features/base/config']?.disableSpeakerStatsSearch;
11
+}

Carregando…
Cancelar
Salvar