feat: (speaker-stats) fix refresh and minor refactoring

This commit is contained in:
Andrei Oltean 2021-11-29 11:33:38 +02:00 committed by Calinteodor
parent 827e8201d4
commit 8ef48a8a1d
8 changed files with 54 additions and 23 deletions

View File

@ -99,10 +99,10 @@ export const IOS_SCREENSHARING_ENABLED = 'ios.screensharing.enabled';
export const ANDROID_SCREENSHARING_ENABLED = 'android.screensharing.enabled'; export const ANDROID_SCREENSHARING_ENABLED = 'android.screensharing.enabled';
/** /**
* Flag indicating if speaker statistics should be enabled in android. * Flag indicating if speaker statistics should be enabled.
* Default: enabled (true). * Default: enabled (true).
*/ */
export const ANDROID_SPEAKERSTATS_ENABLED = 'android.speakerstats.enabled'; export const SPEAKERSTATS_ENABLED = 'speakerstats.enabled';
/** /**
* Flag indicating if kickout is enabled. * Flag indicating if kickout is enabled.

View File

@ -14,17 +14,17 @@ import {
* Component that renders the list of speaker stats. * Component that renders the list of speaker stats.
* *
* @param {Function} speakerStatsItem - React element tu use when rendering. * @param {Function} speakerStatsItem - React element tu use when rendering.
* @param {boolean} [isWeb=false] - Is for web in browser.
* @returns {Function} * @returns {Function}
*/ */
const abstractSpeakerStatsList = (speakerStatsItem: Function, isWeb: boolean = true): Function[] => { const abstractSpeakerStatsList = (speakerStatsItem: Function): Function[] => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
const conference = useSelector(state => state['features/base/conference'].conference); const conference = useSelector(state => state['features/base/conference'].conference);
const speakerStats = useSelector(state => state['features/speaker-stats'].stats);
const localParticipant = useSelector(getLocalParticipant); const localParticipant = useSelector(getLocalParticipant);
const { enableFacialRecognition } = isWeb ? useSelector(state => state['features/base/config']) : {}; const { enableFacialRecognition } = useSelector(state => state['features/base/config']) || {};
const { facialExpressions: localFacialExpressions } = isWeb const { facialExpressions: localFacialExpressions } = useSelector(
? useSelector(state => state['features/facial-recognition']) : {}; state => state['features/facial-recognition']) || {};
/** /**
* Update the internal state with the latest speaker stats. * Update the internal state with the latest speaker stats.
@ -62,21 +62,22 @@ const abstractSpeakerStatsList = (speakerStatsItem: Function, isWeb: boolean = t
}); });
const updateStats = useCallback( const updateStats = useCallback(
() => dispatch(initUpdateStats( () => dispatch(initUpdateStats(getLocalSpeakerStats)),
() => getLocalSpeakerStats())), [ dispatch, initUpdateStats() ]); [ dispatch, initUpdateStats ]);
useEffect(() => { useEffect(() => {
const updateInterval = setInterval(() => updateStats(), SPEAKER_STATS_RELOAD_INTERVAL); const intervalId = setInterval(() => {
updateStats();
}, SPEAKER_STATS_RELOAD_INTERVAL);
return () => { return () => clearInterval(intervalId);
clearInterval(updateInterval); }, []);
};
}, [ dispatch, conference ]);
const userIds = Object.keys(getLocalSpeakerStats()); const localSpeakerStats = Object.keys(speakerStats).length === 0 ? getLocalSpeakerStats() : speakerStats;
const userIds = Object.keys(localSpeakerStats);
return userIds.map(userId => { return userIds.map(userId => {
const statsModel = getLocalSpeakerStats()[userId]; const statsModel = localSpeakerStats[userId];
if (!statsModel || statsModel.hidden) { if (!statsModel || statsModel.hidden) {
return null; return null;
@ -85,6 +86,7 @@ const abstractSpeakerStatsList = (speakerStatsItem: Function, isWeb: boolean = t
props.isDominantSpeaker = statsModel.isDominantSpeaker(); props.isDominantSpeaker = statsModel.isDominantSpeaker();
props.dominantSpeakerTime = statsModel.getTotalDominantSpeakerTime(); props.dominantSpeakerTime = statsModel.getTotalDominantSpeakerTime();
props.participantId = userId;
props.hasLeft = statsModel.hasLeft(); props.hasLeft = statsModel.hasLeft();
if (enableFacialRecognition) { if (enableFacialRecognition) {
props.facialExpressions = statsModel.getFacialExpressions(); props.facialExpressions = statsModel.getFacialExpressions();

View File

@ -1,6 +1,7 @@
// @flow // @flow
import { createToolbarEvent, sendAnalytics } from '../../../analytics'; import { createToolbarEvent, sendAnalytics } from '../../../analytics';
import { getFeatureFlag, SPEAKERSTATS_ENABLED } from '../../../base/flags';
import { translate } from '../../../base/i18n'; import { translate } from '../../../base/i18n';
import { connect } from '../../../base/redux'; import { connect } from '../../../base/redux';
import { navigate } from '../../../conference/components/native/ConferenceNavigationContainerRef'; import { navigate } from '../../../conference/components/native/ConferenceNavigationContainerRef';
@ -25,4 +26,23 @@ class SpeakerStatsButton extends AbstractSpeakerStatsButton {
} }
} }
export default translate(connect()(SpeakerStatsButton)); /**
* Maps (parts of) the redux state to the associated props for the
* {@code SpeakerStatsButton} component.
*
* @param {Object} state - The Redux state.
* @private
* @returns {{
* visible: boolean
* }}
*/
function _mapStateToProps(state): Object {
const enabled = getFeatureFlag(state, SPEAKERSTATS_ENABLED, true);
return {
visible: enabled
};
}
export default translate(connect(_mapStateToProps)(SpeakerStatsButton));

View File

@ -20,6 +20,11 @@ type Props = {
*/ */
dominantSpeakerTime: number, dominantSpeakerTime: number,
/**
* The id of the user.
*/
participantId: string,
/** /**
* True if the participant is no longer in the meeting. * True if the participant is no longer in the meeting.
*/ */
@ -41,7 +46,7 @@ const SpeakerStatsItem = (props: Props) => {
return ( return (
<View <View
key = { props.displayName + props.dominantSpeakerTime } key = { props.participantId }
style = { style.speakerStatsItemContainer }> style = { style.speakerStatsItemContainer }>
<View style = { style.speakerStatsItemStatus }> <View style = { style.speakerStatsItemStatus }>
<View style = { [ style.speakerStatsItemStatusDot, { backgroundColor: dotColor } ] } /> <View style = { [ style.speakerStatsItemStatusDot, { backgroundColor: dotColor } ] } />

View File

@ -13,7 +13,7 @@ import SpeakerStatsItem from './SpeakerStatsItem';
* @returns {React$Element<any>} * @returns {React$Element<any>}
*/ */
const SpeakerStatsList = () => { const SpeakerStatsList = () => {
const items = abstractSpeakerStatsList(SpeakerStatsItem, false); const items = abstractSpeakerStatsList(SpeakerStatsItem);
return ( return (
<View> <View>

View File

@ -35,6 +35,11 @@ type Props = {
*/ */
dominantSpeakerTime: number, dominantSpeakerTime: number,
/**
* The id of the user.
*/
participantId: string,
/** /**
* True if the participant is no longer in the meeting. * True if the participant is no longer in the meeting.
*/ */
@ -68,7 +73,7 @@ const SpeakerStatsItem = (props: Props) => {
return ( return (
<div <div
className = { rowDisplayClass } className = { rowDisplayClass }
key = { props.displayName } > key = { props.participantId } >
<div className = 'speaker-stats-item__status'> <div className = 'speaker-stats-item__status'>
<span className = { speakerStatusClass } /> <span className = { speakerStatusClass } />
</div> </div>

View File

@ -1,3 +1 @@
export const SPEAKER_STATS_RELOAD_INTERVAL = 1000; export const SPEAKER_STATS_RELOAD_INTERVAL = 1000;
export const SPEAKER_STATS_VIEW_MODEL_ID = 'speakerStats';

View File

@ -10,7 +10,8 @@ import {
createToolbarEvent, createToolbarEvent,
sendAnalytics sendAnalytics
} from '../../../analytics'; } from '../../../analytics';
import { getToolbarButtons, isToolbarButtonEnabled } from '../../../base/config'; import { getToolbarButtons } from '../../../base/config';
import { isToolbarButtonEnabled } from '../../../base/config/functions.web';
import { openDialog, toggleDialog } from '../../../base/dialog'; import { openDialog, toggleDialog } from '../../../base/dialog';
import { isIosMobileBrowser, isMobileBrowser } from '../../../base/environment/utils'; import { isIosMobileBrowser, isMobileBrowser } from '../../../base/environment/utils';
import { translate } from '../../../base/i18n'; import { translate } from '../../../base/i18n';