feat(config) add ability to hide speaker stats

This commit is contained in:
bogdandarie 2022-10-17 23:40:13 +03:00 committed by GitHub
parent ceb1cd9673
commit 485c875ee5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 126 additions and 17 deletions

View File

@ -224,9 +224,29 @@ var config = {
// Specifies whether the raised hand will hide when someone becomes a dominant speaker or not // Specifies whether the raised hand will hide when someone becomes a dominant speaker or not
// disableRemoveRaisedHandOnFocus: false, // disableRemoveRaisedHandOnFocus: false,
// speakerStats: {
// // Specifies whether the speaker stats is enable or not.
// disabled: false,
// // Specifies whether there will be a search field in speaker stats or not.
// disableSearch: false,
// // Specifies whether participants in speaker stats should be ordered or not, and with what priority.
// // 'role', <- Moderators on top.
// // 'name', <- Alphabetically by name.
// // 'hasLeft', <- The ones that have left in the bottom.
// order: [
// 'role',
// 'name',
// 'hasLeft',
// ],
// },
// DEPRECATED. Please use speakerStats.disableSearch instead.
// Specifies whether there will be a search field in speaker stats or not // Specifies whether there will be a search field in speaker stats or not
// disableSpeakerStatsSearch: false, // disableSpeakerStatsSearch: false,
// DEPRECATED. Please use speakerStats.order .
// Specifies whether participants in speaker stats should be ordered or not, and with what priority // Specifies whether participants in speaker stats should be ordered or not, and with what priority
// speakerStatsOrder: [ // speakerStatsOrder: [
// 'role', <- Moderators on top // 'role', <- Moderators on top

View File

@ -12,7 +12,6 @@ import { toggleDialog } from '../../react/features/base/dialog';
import { clickOnVideo } from '../../react/features/filmstrip/actions'; import { clickOnVideo } from '../../react/features/filmstrip/actions';
import { KeyboardShortcutsDialog } import { KeyboardShortcutsDialog }
from '../../react/features/keyboard-shortcuts'; from '../../react/features/keyboard-shortcuts';
import { SpeakerStats } from '../../react/features/speaker-stats';
const logger = Logger.getLogger(__filename); const logger = Logger.getLogger(__filename);
@ -249,13 +248,6 @@ const KeyboardShortcut = {
}); });
this._addShortcutToHelp('SPACE', 'keyboardShortcuts.pushToTalk'); this._addShortcutToHelp('SPACE', 'keyboardShortcuts.pushToTalk');
this.registerShortcut('T', null, () => {
sendAnalytics(createShortcutEvent('speaker.stats'));
APP.store.dispatch(toggleDialog(SpeakerStats, {
conference: APP.conference
}));
}, 'keyboardShortcuts.showSpeakerStats');
/** /**
* FIXME: Currently focus keys are directly implemented below in * FIXME: Currently focus keys are directly implemented below in
* onkeyup. They should be moved to the SmallVideo instead. * onkeyup. They should be moved to the SmallVideo instead.

View File

@ -428,6 +428,11 @@ export interface IConfig {
mode?: 'always' | 'recording'; mode?: 'always' | 'recording';
}; };
serviceUrl?: string; serviceUrl?: string;
speakerStats?: {
disableSearch?: boolean;
disabled?: boolean;
order?: Array<'role' | 'name' | 'hasLeft'>;
};
speakerStatsOrder?: Array<'role' | 'name' | 'hasLeft'>; speakerStatsOrder?: Array<'role' | 'name' | 'hasLeft'>;
startAudioMuted?: number; startAudioMuted?: number;
startAudioOnly?: boolean; startAudioOnly?: boolean;

View File

@ -210,6 +210,7 @@ export default [
'resolution', 'resolution',
'salesforceUrl', 'salesforceUrl',
'screenshotCapture', 'screenshotCapture',
'speakerStats',
'startAudioMuted', 'startAudioMuted',
'startAudioOnly', 'startAudioOnly',
'startLastN', 'startLastN',

View File

@ -450,6 +450,25 @@ function _translateLegacyConfig(oldValue: IConfig) {
}; };
} }
newValue.speakerStats = newValue.speakerStats || {};
if (oldValue.disableSpeakerStatsSearch !== undefined
&& newValue.speakerStats.disableSearch === undefined
) {
newValue.speakerStats = {
...newValue.speakerStats,
disableSearch: oldValue.disableSpeakerStatsSearch
};
}
if (oldValue.speakerStatsOrder !== undefined
&& newValue.speakerStats.order === undefined) {
newValue.speakerStats = {
...newValue.speakerStats,
order: oldValue.speakerStatsOrder
};
}
return newValue; return newValue;
} }

View File

@ -10,6 +10,7 @@ import { COLORS } from '../../../base/label/constants';
import { getParticipantCount } from '../../../base/participants'; import { getParticipantCount } from '../../../base/participants';
import { connect } from '../../../base/redux'; import { connect } from '../../../base/redux';
import { SpeakerStats } from '../../../speaker-stats'; import { SpeakerStats } from '../../../speaker-stats';
import { isSpeakerStatsDisabled } from '../../../speaker-stats/functions';
/** /**
@ -31,6 +32,11 @@ type Props = {
* Invoked to open Speaker stats. * Invoked to open Speaker stats.
*/ */
dispatch: Dispatch<any>, dispatch: Dispatch<any>,
/**
* Weather or not the speaker stats is disabled.
*/
_isSpeakerStatsDisabled: Boolean,
}; };
/** /**
@ -83,7 +89,7 @@ class ParticipantsCount extends PureComponent<Props> {
<Label <Label
color = { COLORS.white } color = { COLORS.white }
icon = { IconUserGroups } icon = { IconUserGroups }
onClick = { this._onClick } onClick = { !this.props._isSpeakerStatsDisabled && this._onClick }
text = { count } /> text = { count } />
); );
} }
@ -101,7 +107,8 @@ class ParticipantsCount extends PureComponent<Props> {
function mapStateToProps(state) { function mapStateToProps(state) {
return { return {
conference: state['features/base/conference'].conference, conference: state['features/base/conference'].conference,
count: getParticipantCount(state) count: getParticipantCount(state),
_isSpeakerStatsDisabled: isSpeakerStatsDisabled(state)
}; };
} }

View File

@ -14,7 +14,17 @@ import {
* @returns {boolean} - True if the speaker stats search is disabled and false otherwise. * @returns {boolean} - True if the speaker stats search is disabled and false otherwise.
*/ */
export function isSpeakerStatsSearchDisabled(state: Object) { export function isSpeakerStatsSearchDisabled(state: Object) {
return state['features/base/config']?.disableSpeakerStatsSearch; return state['features/base/config']?.speakerStats.disableSearch;
}
/**
* Checks if the speaker stats is disabled.
*
* @param {*} state - The redux state.
* @returns {boolean} - True if the speaker stats search is disabled and false otherwise.
*/
export function isSpeakerStatsDisabled(state: Object) {
return state['features/base/config']?.speakerStats?.disabled;
} }
/** /**
@ -24,7 +34,7 @@ export function isSpeakerStatsSearchDisabled(state: Object) {
* @returns {Array<string>} - The speaker stats order array or an empty array. * @returns {Array<string>} - The speaker stats order array or an empty array.
*/ */
export function getSpeakerStatsOrder(state: Object) { export function getSpeakerStatsOrder(state: Object) {
return state['features/base/config']?.speakerStatsOrder ?? [ return state['features/base/config']?.speakerStats.order ?? [
'role', 'role',
'name', 'name',
'hasLeft' 'hasLeft'

View File

@ -15,6 +15,7 @@ import SecurityDialogButton
from '../../../security/components/security-dialog/native/SecurityDialogButton'; from '../../../security/components/security-dialog/native/SecurityDialogButton';
import { SharedVideoButton } from '../../../shared-video/components'; import { SharedVideoButton } from '../../../shared-video/components';
import SpeakerStatsButton from '../../../speaker-stats/components/native/SpeakerStatsButton'; import SpeakerStatsButton from '../../../speaker-stats/components/native/SpeakerStatsButton';
import { isSpeakerStatsDisabled } from '../../../speaker-stats/functions';
import { ClosedCaptionButton } from '../../../subtitles'; import { ClosedCaptionButton } from '../../../subtitles';
import { TileViewButton } from '../../../video-layout'; import { TileViewButton } from '../../../video-layout';
import styles from '../../../video-menu/components/native/styles'; import styles from '../../../video-menu/components/native/styles';
@ -54,7 +55,12 @@ type Props = {
/** /**
* Used for hiding the dialog when the selection was completed. * Used for hiding the dialog when the selection was completed.
*/ */
dispatch: Function dispatch: Function,
/**
* Whether or not speaker stats is disable.
*/
_isSpeakerStatsDisabled: boolean
}; };
type State = { type State = {
@ -95,6 +101,7 @@ class OverflowMenu extends PureComponent<Props, State> {
*/ */
render() { render() {
const { const {
_isSpeakerStatsDisabled,
_reactionsEnabled, _reactionsEnabled,
_width _width
} = this.props; } = this.props;
@ -135,7 +142,7 @@ class OverflowMenu extends PureComponent<Props, State> {
<Divider style = { styles.divider } /> <Divider style = { styles.divider } />
<SharedVideoButton { ...buttonProps } /> <SharedVideoButton { ...buttonProps } />
{!toolbarButtons.has('screensharing') && <ScreenSharingButton { ...buttonProps } />} {!toolbarButtons.has('screensharing') && <ScreenSharingButton { ...buttonProps } />}
<SpeakerStatsButton { ...buttonProps } /> {!_isSpeakerStatsDisabled && <SpeakerStatsButton { ...buttonProps } />}
{!toolbarButtons.has('tileview') && <TileViewButton { ...buttonProps } />} {!toolbarButtons.has('tileview') && <TileViewButton { ...buttonProps } />}
<Divider style = { styles.divider } /> <Divider style = { styles.divider } />
<ClosedCaptionButton { ...buttonProps } /> <ClosedCaptionButton { ...buttonProps } />
@ -176,6 +183,7 @@ class OverflowMenu extends PureComponent<Props, State> {
*/ */
function _mapStateToProps(state) { function _mapStateToProps(state) {
return { return {
_isSpeakerStatsDisabled: isSpeakerStatsDisabled(state),
_reactionsEnabled: isReactionsEnabled(state), _reactionsEnabled: isReactionsEnabled(state),
_width: state['features/base/responsive-ui'].clientWidth _width: state['features/base/responsive-ui'].clientWidth
}; };

View File

@ -6,6 +6,8 @@ import { batch } from 'react-redux';
// @ts-ignore // @ts-ignore
import keyboardShortcut from '../../../../../modules/keyboardshortcut/keyboardshortcut'; import keyboardShortcut from '../../../../../modules/keyboardshortcut/keyboardshortcut';
// @ts-ignore
import { isSpeakerStatsDisabled } from '../../../../features/speaker-stats/functions';
import { ACTION_SHORTCUT_TRIGGERED, createShortcutEvent, createToolbarEvent } from '../../../analytics/AnalyticsEvents'; import { ACTION_SHORTCUT_TRIGGERED, createShortcutEvent, createToolbarEvent } from '../../../analytics/AnalyticsEvents';
import { sendAnalytics } from '../../../analytics/functions'; import { sendAnalytics } from '../../../analytics/functions';
import { IState } from '../../../app/types'; import { IState } from '../../../app/types';
@ -81,6 +83,7 @@ import { SettingsButton } from '../../../settings';
import { SharedVideoButton } from '../../../shared-video/components'; import { SharedVideoButton } from '../../../shared-video/components';
// @ts-ignore // @ts-ignore
import { SpeakerStatsButton } from '../../../speaker-stats/components/web'; import { SpeakerStatsButton } from '../../../speaker-stats/components/web';
import SpeakerStats from '../../../speaker-stats/components/web/SpeakerStats';
import { import {
ClosedCaptionButton ClosedCaptionButton
// @ts-ignore // @ts-ignore
@ -241,6 +244,12 @@ interface Props extends WithTranslation {
_isProfileDisabled: boolean; _isProfileDisabled: boolean;
/** /**
* Whether or not speaker stats is disable.
*/
_isSpeakerStatsDisabled: boolean;
/**
* Whether or not the current meeting belongs to a JaaS user. * Whether or not the current meeting belongs to a JaaS user.
*/ */
_isVpaasMeeting: boolean; _isVpaasMeeting: boolean;
@ -399,6 +408,7 @@ class Toolbox extends Component<Props> {
this._onToolbarToggleRaiseHand = this._onToolbarToggleRaiseHand.bind(this); this._onToolbarToggleRaiseHand = this._onToolbarToggleRaiseHand.bind(this);
this._onToolbarToggleScreenshare = this._onToolbarToggleScreenshare.bind(this); this._onToolbarToggleScreenshare = this._onToolbarToggleScreenshare.bind(this);
this._onShortcutToggleTileView = this._onShortcutToggleTileView.bind(this); this._onShortcutToggleTileView = this._onShortcutToggleTileView.bind(this);
this._onShortcutSpeakerStats = this._onShortcutSpeakerStats.bind(this);
this._onEscKey = this._onEscKey.bind(this); this._onEscKey = this._onEscKey.bind(this);
} }
@ -409,7 +419,8 @@ class Toolbox extends Component<Props> {
* @returns {void} * @returns {void}
*/ */
componentDidMount() { componentDidMount() {
const { _toolbarButtons, t, dispatch, _reactionsEnabled, _gifsEnabled } = this.props; const { _toolbarButtons, t, dispatch, _reactionsEnabled, _gifsEnabled, _isSpeakerStatsDisabled } = this.props;
const KEYBOARD_SHORTCUTS = [ const KEYBOARD_SHORTCUTS = [
isToolbarButtonEnabled('videoquality', _toolbarButtons) && { isToolbarButtonEnabled('videoquality', _toolbarButtons) && {
character: 'A', character: 'A',
@ -445,6 +456,11 @@ class Toolbox extends Component<Props> {
character: 'W', character: 'W',
exec: this._onShortcutToggleTileView, exec: this._onShortcutToggleTileView,
helpDescription: 'toolbar.tileViewToggle' helpDescription: 'toolbar.tileViewToggle'
},
!_isSpeakerStatsDisabled && isToolbarButtonEnabled('stats', _toolbarButtons) && {
character: 'T',
exec: this._onShortcutSpeakerStats,
helpDescription: 'keyboardShortcuts.showSpeakerStats'
} }
]; ];
@ -698,9 +714,10 @@ class Toolbox extends Component<Props> {
_getAllButtons() { _getAllButtons() {
const { const {
_feedbackConfigured, _feedbackConfigured,
_hasSalesforce,
_isIosMobile, _isIosMobile,
_isMobile, _isMobile,
_hasSalesforce, _isSpeakerStatsDisabled,
_multiStreamModeEnabled, _multiStreamModeEnabled,
_screenSharing, _screenSharing,
_whiteboardEnabled _whiteboardEnabled
@ -864,7 +881,7 @@ class Toolbox extends Component<Props> {
group: 3 group: 3
}; };
const speakerStats = { const speakerStats = !_isSpeakerStatsDisabled && {
key: 'stats', key: 'stats',
Content: SpeakerStatsButton, Content: SpeakerStatsButton,
group: 3 group: 3
@ -1204,6 +1221,34 @@ class Toolbox extends Component<Props> {
this._doToggleScreenshare(); this._doToggleScreenshare();
} }
/**
* Creates an analytics keyboard shortcut event and dispatches an action for
* toggling speaker stats.
*
* @private
* @returns {void}
*/
_onShortcutSpeakerStats() {
sendAnalytics(createShortcutEvent(
'speaker.stats'
));
this._doToggleSpekearStats();
}
/**
* Dispatches an action to toggle speakerStats.
*
* @private
* @returns {void}
*/
_doToggleSpekearStats() {
const { dispatch } = this.props;
dispatch(toggleDialog(SpeakerStats, {
conference: APP.conference
}));
}
/** /**
* Toggle the toolbar visibility when tabbing into it. * Toggle the toolbar visibility when tabbing into it.
@ -1481,6 +1526,7 @@ class Toolbox extends Component<Props> {
function _mapStateToProps(state: IState, ownProps: Partial<Props>) { function _mapStateToProps(state: IState, ownProps: Partial<Props>) {
const { conference } = state['features/base/conference']; const { conference } = state['features/base/conference'];
const endConferenceSupported = conference?.isEndConferenceSupported(); const endConferenceSupported = conference?.isEndConferenceSupported();
const { const {
buttonsWithNotifyClick, buttonsWithNotifyClick,
callStatsID, callStatsID,
@ -1516,6 +1562,7 @@ function _mapStateToProps(state: IState, ownProps: Partial<Props>) {
_isProfileDisabled: Boolean(disableProfile), _isProfileDisabled: Boolean(disableProfile),
_isIosMobile: isIosMobileBrowser(), _isIosMobile: isIosMobileBrowser(),
_isMobile: isMobileBrowser(), _isMobile: isMobileBrowser(),
_isSpeakerStatsDisabled: isSpeakerStatsDisabled(state),
_isVpaasMeeting: isVpaasMeeting(state), _isVpaasMeeting: isVpaasMeeting(state),
_jwtDisabledButons: getJwtDisabledButtons(state), _jwtDisabledButons: getJwtDisabledButtons(state),
_hasSalesforce: isSalesforceEnabled(state), _hasSalesforce: isSalesforceEnabled(state),