Skip to content

Commit

Permalink
feat: Additional setting to order participants in speaker stats (jits…
Browse files Browse the repository at this point in the history
…i#9751)

* Additional setting to order participants in speaker stats jitsi#9742

* Setting to order speaker stats optimisations jitsi#9742

* Lint fixes jitsi#9742

* Replace APP references jitsi#9742

* Lint fixes jitsi#9742

* Setting to order speaker stats optimisations 2 jitsi#9742

* Lint fixes jitsi#9742

* Remove unnecessary param jitsi#9742

* Add more speaker-stats reducer _updateStats docs  jitsi#9742
  • Loading branch information
dimitardelchev93 authored Sep 9, 2021
1 parent db473df commit 5e152b4
Show file tree
Hide file tree
Showing 12 changed files with 494 additions and 64 deletions.
7 changes: 7 additions & 0 deletions config.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,13 @@ var config = {
// Specifies whether there will be a search field in speaker stats or not
// disableSpeakerStatsSearch: false,

// Specifies whether participants in speaker stats should be ordered or not, and with what priority
// speakerStatsOrder: [
// 'role', <- Moderators on top
// 'name', <- Alphabetically by name
// 'hasLeft', <- The ones that have left in the bottom
// ] <- the order of the array elements determines priority

// How many participants while in the tile view mode, before the receiving video quality is reduced from HD to SD.
// Use -1 to disable.
// maxFullResolutionParticipants: 2,
Expand Down
1 change: 1 addition & 0 deletions react/features/app/middlewares.any.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import '../recording/middleware';
import '../rejoin/middleware';
import '../room-lock/middleware';
import '../rtcstats/middleware';
import '../speaker-stats/middleware';
import '../subtitles/middleware';
import '../toolbox/middleware';
import '../transcribing/middleware';
Expand Down
1 change: 1 addition & 0 deletions react/features/app/reducers.any.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import '../reactions/reducer';
import '../recent-list/reducer';
import '../recording/reducer';
import '../settings/reducer';
import '../speaker-stats/reducer';
import '../subtitles/reducer';
import '../screen-share/reducer';
import '../toolbox/reducer';
Expand Down
1 change: 1 addition & 0 deletions react/features/base/config/configWhitelist.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ export default [
'disableShowMoreStats',
'disableRemoveRaisedHandOnFocus',
'disableSpeakerStatsSearch',
'speakerStatsOrder',
'disableSimulcast',
'disableThirdPartyRequests',
'disableTileView',
Expand Down
16 changes: 16 additions & 0 deletions react/features/base/util/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,3 +185,19 @@ function parseShorthandColor(color) {

return [ r, g, b ];
}

/**
* Sorts an object by a sort function, same functionality as array.sort().
*
* @param {Object} object - The data object.
* @param {Function} callback - The sort function.
* @returns {void}
*/
export function objectSort(object: Object, callback: Function) {
return Object.entries(object)
.sort(([ , a ], [ , b ]) => callback(a, b))
.reduce((row, [ key, value ]) => {
return { ...row,
[key]: value };
}, {});
}
39 changes: 39 additions & 0 deletions react/features/speaker-stats/actionTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// @flow

/**
* Action type to start search.
*
* {
* type: INIT_SEARCH
* }
*/
export const INIT_SEARCH = 'INIT_SEARCH';

/**
* Action type to start stats retrieval.
*
* {
* type: INIT_UPDATE_STATS,
* getSpeakerStats: Function
* }
*/
export const INIT_UPDATE_STATS = 'INIT_UPDATE_STATS';

/**
* Action type to update stats.
*
* {
* type: UPDATE_STATS,
* stats: Object
* }
*/
export const UPDATE_STATS = 'UPDATE_STATS';

/**
* Action type to initiate reordering of the stats.
*
* {
* type: INIT_REORDER_STATS
* }
*/
export const INIT_REORDER_STATS = 'INIT_REORDER_STATS';
58 changes: 58 additions & 0 deletions react/features/speaker-stats/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// @flow

import {
INIT_SEARCH,
INIT_UPDATE_STATS,
UPDATE_STATS,
INIT_REORDER_STATS
} from './actionTypes';

/**
* Starts a search by criteria.
*
* @param {string} criteria - The search criteria.
* @returns {Object}
*/
export function initSearch(criteria: string) {
return {
type: INIT_SEARCH,
criteria
};
}

/**
* Gets the new stats and triggers update.
*
* @param {Function} getSpeakerStats - Function to get the speaker stats.
* @returns {Object}
*/
export function initUpdateStats(getSpeakerStats: Function) {
return {
type: INIT_UPDATE_STATS,
getSpeakerStats
};
}

/**
* Updates the stats with new stats.
*
* @param {Object} stats - The new stats.
* @returns {Object}
*/
export function updateStats(stats: Object) {
return {
type: UPDATE_STATS,
stats
};
}

/**
* Initiates reordering of the stats.
*
* @returns {Object}
*/
export function initReorderStats() {
return {
type: INIT_REORDER_STATS
};
}
98 changes: 34 additions & 64 deletions react/features/speaker-stats/components/SpeakerStats.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
// @flow

import React, { Component } from 'react';
import type { Dispatch } from 'redux';

import { Dialog } from '../../base/dialog';
import { translate } from '../../base/i18n';
import { getLocalParticipant } from '../../base/participants';
import { connect } from '../../base/redux';
import { escapeRegexp } from '../../base/util';
import { initUpdateStats, initSearch } from '../actions';
import { SPEAKER_STATS_RELOAD_INTERVAL } from '../constants';
import { getSpeakerStats, getSearchCriteria } from '../functions';

import SpeakerStatsItem from './SpeakerStatsItem';
import SpeakerStatsLabels from './SpeakerStatsLabels';
Expand All @@ -25,38 +29,37 @@ type Props = {
_localDisplayName: string,

/**
* The JitsiConference from which stats will be pulled.
* The speaker paricipant stats.
*/
conference: Object,
_stats: Object,

/**
* The function to translate human-readable text.
* The search criteria.
*/
t: Function
};
_criteria: string,

/**
* The type of the React {@code Component} state of {@link SpeakerStats}.
*/
type State = {
/**
* The JitsiConference from which stats will be pulled.
*/
conference: Object,

/**
* The stats summary provided by the JitsiConference.
* Redux store dispatch method.
*/
stats: Object,
dispatch: Dispatch<any>,

/**
* The search input criteria.
* The function to translate human-readable text.
*/
criteria: string,
t: Function
};

/**
* React component for displaying a list of speaker stats.
*
* @extends Component
*/
class SpeakerStats extends Component<Props, State> {
class SpeakerStats extends Component<Props> {
_updateInterval: IntervalID;

/**
Expand All @@ -68,14 +71,11 @@ class SpeakerStats extends Component<Props, State> {
constructor(props) {
super(props);

this.state = {
stats: this._getSpeakerStats(),
criteria: ''
};

// Bind event handlers so they are only bound once per instance.
this._updateStats = this._updateStats.bind(this);
this._onSearch = this._onSearch.bind(this);

this._updateStats();
}

/**
Expand All @@ -84,7 +84,7 @@ class SpeakerStats extends Component<Props, State> {
* @inheritdoc
*/
componentDidMount() {
this._updateInterval = setInterval(this._updateStats, 1000);
this._updateInterval = setInterval(() => this._updateStats(), SPEAKER_STATS_RELOAD_INTERVAL);
}

/**
Expand All @@ -104,14 +104,14 @@ class SpeakerStats extends Component<Props, State> {
* @returns {ReactElement}
*/
render() {
const userIds = Object.keys(this.state.stats);
const userIds = Object.keys(this.props._stats);
const items = userIds.map(userId => this._createStatsItem(userId));

return (
<Dialog
cancelKey = { 'dialog.close' }
cancelKey = 'dialog.close'
submitDisabled = { true }
titleKey = { 'speakerStats.speakerStats' }>
titleKey = 'speakerStats.speakerStats'>
<div className = 'speaker-stats'>
<SpeakerStatsSearch onSearch = { this._onSearch } />
<SpeakerStatsLabels />
Expand All @@ -121,32 +121,6 @@ class SpeakerStats extends Component<Props, State> {
);
}

/**
* Update the internal state with the latest speaker stats.
*
* @returns {void}
* @private
*/
_getSpeakerStats() {
const stats = { ...this.props.conference.getSpeakerStats() };

if (this.state?.criteria) {
const searchRegex = new RegExp(this.state.criteria, 'gi');

for (const id in stats) {
if (stats[id].hasOwnProperty('_isLocalStats')) {
const name = stats[id].isLocalStats() ? this.props._localDisplayName : stats[id].getDisplayName();

if (!name || !name.match(searchRegex)) {
delete stats[id];
}
}
}
}

return stats;
}

/**
* Create a SpeakerStatsItem instance for the passed in user id.
*
Expand All @@ -156,9 +130,9 @@ class SpeakerStats extends Component<Props, State> {
* @private
*/
_createStatsItem(userId) {
const statsModel = this.state.stats[userId];
const statsModel = this.props._stats[userId];

if (!statsModel) {
if (!statsModel || statsModel.hidden) {
return null;
}

Expand All @@ -177,7 +151,7 @@ class SpeakerStats extends Component<Props, State> {
= displayName ? `${displayName} (${meString})` : meString;
} else {
displayName
= this.state.stats[userId].getDisplayName()
= this.props._stats[userId].getDisplayName()
|| interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME;
}

Expand All @@ -201,10 +175,7 @@ class SpeakerStats extends Component<Props, State> {
* @protected
*/
_onSearch(criteria = '') {
this.setState({
...this.state,
criteria: escapeRegexp(criteria)
});
this.props.dispatch(initSearch(escapeRegexp(criteria)));
}

_updateStats: () => void;
Expand All @@ -216,12 +187,7 @@ class SpeakerStats extends Component<Props, State> {
* @private
*/
_updateStats() {
const stats = this._getSpeakerStats();

this.setState({
...this.state,
stats
});
this.props.dispatch(initUpdateStats(() => this.props.conference.getSpeakerStats()));
}
}

Expand All @@ -231,7 +197,9 @@ class SpeakerStats extends Component<Props, State> {
* @param {Object} state - The redux state.
* @private
* @returns {{
* _localDisplayName: ?string
* _localDisplayName: ?string,
* _stats: Object,
* _criteria: string,
* }}
*/
function _mapStateToProps(state) {
Expand All @@ -244,7 +212,9 @@ function _mapStateToProps(state) {
* @private
* @type {string|undefined}
*/
_localDisplayName: localParticipant && localParticipant.name
_localDisplayName: localParticipant && localParticipant.name,
_stats: getSpeakerStats(state),
_criteria: getSearchCriteria(state)
};
}

Expand Down
1 change: 1 addition & 0 deletions react/features/speaker-stats/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const SPEAKER_STATS_RELOAD_INTERVAL = 1000;
Loading

0 comments on commit 5e152b4

Please sign in to comment.