2021-11-10 17:49:53 +00:00
|
|
|
// @flow
|
|
|
|
|
2022-04-07 10:02:43 +00:00
|
|
|
import { useCallback, useEffect, useRef } from 'react';
|
2021-11-10 17:49:53 +00:00
|
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
import { useDispatch, useSelector } from 'react-redux';
|
|
|
|
|
|
|
|
import { getLocalParticipant } from '../../base/participants';
|
2022-10-26 06:59:21 +00:00
|
|
|
import { initUpdateStats } from '../actions';
|
2021-11-10 17:49:53 +00:00
|
|
|
import {
|
|
|
|
SPEAKER_STATS_RELOAD_INTERVAL
|
|
|
|
} from '../constants';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Component that renders the list of speaker stats.
|
|
|
|
*
|
|
|
|
* @param {Function} speakerStatsItem - React element tu use when rendering.
|
2021-12-28 14:35:21 +00:00
|
|
|
* @param {Object} itemStyles - Styles for the speaker stats item.
|
2021-11-10 17:49:53 +00:00
|
|
|
* @returns {Function}
|
|
|
|
*/
|
2021-12-28 14:35:21 +00:00
|
|
|
const abstractSpeakerStatsList = (speakerStatsItem: Function, itemStyles?: Object): Function[] => {
|
2021-11-10 17:49:53 +00:00
|
|
|
const dispatch = useDispatch();
|
|
|
|
const { t } = useTranslation();
|
|
|
|
const conference = useSelector(state => state['features/base/conference'].conference);
|
2022-09-28 07:51:53 +00:00
|
|
|
const {
|
|
|
|
stats: speakerStats,
|
|
|
|
showFaceExpressions,
|
|
|
|
sortedSpeakerStatsIds
|
|
|
|
} = useSelector(state => state['features/speaker-stats']);
|
2021-11-10 17:49:53 +00:00
|
|
|
const localParticipant = useSelector(getLocalParticipant);
|
2021-12-28 14:35:21 +00:00
|
|
|
const { defaultRemoteDisplayName } = useSelector(
|
2021-12-14 15:02:22 +00:00
|
|
|
state => state['features/base/config']) || {};
|
2022-04-06 09:10:31 +00:00
|
|
|
const { faceLandmarks } = useSelector(state => state['features/base/config']) || {};
|
2022-05-02 09:28:52 +00:00
|
|
|
const { faceExpressions } = useSelector(state => state['features/face-landmarks']) || {};
|
2022-04-07 10:02:43 +00:00
|
|
|
const reloadInterval = useRef(null);
|
2021-11-10 17:49:53 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Update the internal state with the latest speaker stats.
|
|
|
|
*
|
|
|
|
* @returns {Object}
|
|
|
|
* @private
|
|
|
|
*/
|
2022-04-07 10:02:43 +00:00
|
|
|
const getSpeakerStats = useCallback(() => {
|
2021-11-10 17:49:53 +00:00
|
|
|
const stats = conference.getSpeakerStats();
|
|
|
|
|
|
|
|
for (const userId in stats) {
|
|
|
|
if (stats[userId]) {
|
|
|
|
if (stats[userId].isLocalStats()) {
|
|
|
|
const meString = t('me');
|
|
|
|
|
|
|
|
stats[userId].setDisplayName(
|
|
|
|
localParticipant.name
|
|
|
|
? `${localParticipant.name} (${meString})`
|
|
|
|
: meString
|
|
|
|
);
|
2022-04-06 09:10:31 +00:00
|
|
|
if (faceLandmarks?.enableDisplayFaceExpressions) {
|
2022-04-07 10:02:43 +00:00
|
|
|
stats[userId].setFaceExpressions(faceExpressions);
|
2021-11-10 17:49:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!stats[userId].getDisplayName()) {
|
|
|
|
stats[userId].setDisplayName(
|
|
|
|
conference.getParticipantById(userId)?.name
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return stats;
|
2022-04-07 10:02:43 +00:00
|
|
|
}, [ faceExpressions ]);
|
2021-11-10 17:49:53 +00:00
|
|
|
|
|
|
|
const updateStats = useCallback(
|
2022-04-07 10:02:43 +00:00
|
|
|
() => dispatch(initUpdateStats(getSpeakerStats)),
|
|
|
|
[ dispatch, initUpdateStats, getSpeakerStats ]);
|
2021-11-10 17:49:53 +00:00
|
|
|
|
|
|
|
useEffect(() => {
|
2022-04-07 10:02:43 +00:00
|
|
|
if (reloadInterval.current) {
|
|
|
|
clearInterval(reloadInterval.current);
|
|
|
|
}
|
|
|
|
reloadInterval.current = setInterval(() => {
|
2021-11-29 09:33:38 +00:00
|
|
|
updateStats();
|
|
|
|
}, SPEAKER_STATS_RELOAD_INTERVAL);
|
2021-11-10 17:49:53 +00:00
|
|
|
|
2022-04-07 10:02:43 +00:00
|
|
|
return () => clearInterval(reloadInterval.current);
|
|
|
|
}, [ faceExpressions ]);
|
2021-11-10 17:49:53 +00:00
|
|
|
|
2022-04-07 10:02:43 +00:00
|
|
|
const localSpeakerStats = Object.keys(speakerStats).length === 0 ? getSpeakerStats() : speakerStats;
|
2022-09-28 07:51:53 +00:00
|
|
|
const localSortedSpeakerStatsIds
|
|
|
|
= sortedSpeakerStatsIds.length === 0 ? Object.keys(localSpeakerStats) : sortedSpeakerStatsIds;
|
|
|
|
|
|
|
|
const userIds = localSortedSpeakerStatsIds.filter(id => localSpeakerStats[id] && !localSpeakerStats[id].hidden);
|
2021-11-10 17:49:53 +00:00
|
|
|
|
|
|
|
return userIds.map(userId => {
|
2021-11-29 09:33:38 +00:00
|
|
|
const statsModel = localSpeakerStats[userId];
|
2021-11-10 17:49:53 +00:00
|
|
|
const props = {};
|
|
|
|
|
|
|
|
props.isDominantSpeaker = statsModel.isDominantSpeaker();
|
|
|
|
props.dominantSpeakerTime = statsModel.getTotalDominantSpeakerTime();
|
2021-11-29 09:33:38 +00:00
|
|
|
props.participantId = userId;
|
2021-11-10 17:49:53 +00:00
|
|
|
props.hasLeft = statsModel.hasLeft();
|
2022-04-06 09:10:31 +00:00
|
|
|
if (showFaceExpressions) {
|
|
|
|
props.faceExpressions = statsModel.getFaceExpressions();
|
2021-11-10 17:49:53 +00:00
|
|
|
}
|
2021-12-28 14:35:21 +00:00
|
|
|
props.hidden = statsModel.hidden;
|
2022-04-06 09:10:31 +00:00
|
|
|
props.showFaceExpressions = showFaceExpressions;
|
2021-12-14 15:02:22 +00:00
|
|
|
props.displayName = statsModel.getDisplayName() || defaultRemoteDisplayName;
|
2021-12-28 14:35:21 +00:00
|
|
|
if (itemStyles) {
|
|
|
|
props.styles = itemStyles;
|
|
|
|
}
|
2021-11-10 17:49:53 +00:00
|
|
|
props.t = t;
|
|
|
|
|
|
|
|
return speakerStatsItem(props);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export default abstractSpeakerStatsList;
|