feat: (speaker-stats) fix refresh and minor refactoring
This commit is contained in:
parent
827e8201d4
commit
8ef48a8a1d
|
@ -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.
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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 } ] } />
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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';
|
|
||||||
|
|
|
@ -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';
|
||||||
|
|
Loading…
Reference in New Issue