fix(speaker-stats) decouple sort order from speaker stats (#12197)
This commit is contained in:
parent
2e6f14f872
commit
5d6aec3f3c
|
@ -29,6 +29,14 @@ export const INIT_UPDATE_STATS = 'INIT_UPDATE_STATS';
|
|||
*/
|
||||
export const UPDATE_STATS = 'UPDATE_STATS';
|
||||
|
||||
/**
|
||||
* Action type to update the speaker stats order.
|
||||
* {
|
||||
* type: UPDATE_SORTED_SPEAKER_STATS_IDS
|
||||
* }
|
||||
*/
|
||||
export const UPDATE_SORTED_SPEAKER_STATS_IDS = 'UPDATE_SORTED_SPEAKER_STATS_IDS'
|
||||
|
||||
/**
|
||||
* Action type to initiate reordering of the stats.
|
||||
*
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
INIT_UPDATE_STATS,
|
||||
RESET_SEARCH_CRITERIA,
|
||||
TOGGLE_FACE_EXPRESSIONS,
|
||||
UPDATE_SORTED_SPEAKER_STATS_IDS,
|
||||
UPDATE_STATS
|
||||
} from './actionTypes';
|
||||
|
||||
|
@ -48,6 +49,19 @@ export function updateStats(stats: Object) {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the speaker stats order.
|
||||
*
|
||||
* @param {Object} participantIds - Participant ids.
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function updateSortedSpeakerStatsIds(participantIds: Array<string>) {
|
||||
return {
|
||||
type: UPDATE_SORTED_SPEAKER_STATS_IDS,
|
||||
participantIds
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates reordering of the stats.
|
||||
*
|
||||
|
|
|
@ -21,7 +21,11 @@ const abstractSpeakerStatsList = (speakerStatsItem: Function, itemStyles?: Objec
|
|||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
const conference = useSelector(state => state['features/base/conference'].conference);
|
||||
const { stats: speakerStats, showFaceExpressions } = useSelector(state => state['features/speaker-stats']);
|
||||
const {
|
||||
stats: speakerStats,
|
||||
showFaceExpressions,
|
||||
sortedSpeakerStatsIds
|
||||
} = useSelector(state => state['features/speaker-stats']);
|
||||
const localParticipant = useSelector(getLocalParticipant);
|
||||
const { defaultRemoteDisplayName } = useSelector(
|
||||
state => state['features/base/config']) || {};
|
||||
|
@ -80,7 +84,10 @@ const abstractSpeakerStatsList = (speakerStatsItem: Function, itemStyles?: Objec
|
|||
}, [ faceExpressions ]);
|
||||
|
||||
const localSpeakerStats = Object.keys(speakerStats).length === 0 ? getSpeakerStats() : speakerStats;
|
||||
const userIds = Object.keys(localSpeakerStats).filter(id => localSpeakerStats[id] && !localSpeakerStats[id].hidden);
|
||||
const localSortedSpeakerStatsIds
|
||||
= sortedSpeakerStatsIds.length === 0 ? Object.keys(localSpeakerStats) : sortedSpeakerStatsIds;
|
||||
|
||||
const userIds = localSortedSpeakerStatsIds.filter(id => localSpeakerStats[id] && !localSpeakerStats[id].hidden);
|
||||
|
||||
return userIds.map(userId => {
|
||||
const statsModel = localSpeakerStats[userId];
|
||||
|
|
|
@ -6,7 +6,6 @@ import {
|
|||
PARTICIPANT_ROLE,
|
||||
getParticipantById
|
||||
} from '../base/participants';
|
||||
import { objectSort } from '../base/util';
|
||||
|
||||
/**
|
||||
* Checks if the speaker stats search is disabled.
|
||||
|
@ -63,56 +62,67 @@ export function getPendingReorder(state: Object) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get sorted speaker stats based on a configuration setting.
|
||||
* Get sorted speaker stats ids based on a configuration setting.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @param {Object} stats - The current speaker stats.
|
||||
* @returns {Object} - Ordered speaker stats.
|
||||
* @returns {Object} - Ordered speaker stats ids.
|
||||
* @public
|
||||
*/
|
||||
export function getSortedSpeakerStats(state: Object, stats: Object) {
|
||||
export function getSortedSpeakerStatsIds(state: Object, stats: Object) {
|
||||
const orderConfig = getSpeakerStatsOrder(state);
|
||||
|
||||
if (orderConfig) {
|
||||
const enhancedStats = getEnhancedStatsForOrdering(state, stats, orderConfig);
|
||||
const sortedStats = objectSort(enhancedStats, (currentParticipant, nextParticipant) => {
|
||||
if (orderConfig.includes('hasLeft')) {
|
||||
if (nextParticipant.hasLeft() && !currentParticipant.hasLeft()) {
|
||||
return -1;
|
||||
} else if (currentParticipant.hasLeft() && !nextParticipant.hasLeft()) {
|
||||
return 1;
|
||||
|
||||
return Object.entries(enhancedStats)
|
||||
.sort(([ , a ], [ , b ]) => compareFn(a, b))
|
||||
.map(el => el[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Compares the order of two participants in the speaker stats list.
|
||||
*
|
||||
* @param {Object} currentParticipant - The first participant for comparison.
|
||||
* @param {Object} nextParticipant - The second participant for comparison.
|
||||
* @returns {number} - The sort order of the two participants.
|
||||
*/
|
||||
function compareFn(currentParticipant, nextParticipant) {
|
||||
if (orderConfig.includes('hasLeft')) {
|
||||
if (nextParticipant.hasLeft() && !currentParticipant.hasLeft()) {
|
||||
return -1;
|
||||
} else if (currentParticipant.hasLeft() && !nextParticipant.hasLeft()) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
let result;
|
||||
|
||||
for (const sortCriteria of orderConfig) {
|
||||
switch (sortCriteria) {
|
||||
case 'role':
|
||||
if (!nextParticipant.isModerator && currentParticipant.isModerator) {
|
||||
result = -1;
|
||||
} else if (!currentParticipant.isModerator && nextParticipant.isModerator) {
|
||||
result = 1;
|
||||
} else {
|
||||
result = 0;
|
||||
}
|
||||
break;
|
||||
case 'name':
|
||||
result = (currentParticipant.displayName || '').localeCompare(
|
||||
nextParticipant.displayName || ''
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
let result;
|
||||
|
||||
for (const sortCriteria of orderConfig) {
|
||||
switch (sortCriteria) {
|
||||
case 'role':
|
||||
if (!nextParticipant.isModerator && currentParticipant.isModerator) {
|
||||
result = -1;
|
||||
} else if (!currentParticipant.isModerator && nextParticipant.isModerator) {
|
||||
result = 1;
|
||||
} else {
|
||||
result = 0;
|
||||
}
|
||||
break;
|
||||
case 'name':
|
||||
result = (currentParticipant.displayName || '').localeCompare(
|
||||
nextParticipant.displayName || ''
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
if (result !== 0) {
|
||||
break;
|
||||
}
|
||||
if (result !== 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
return sortedStats;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,8 +13,8 @@ import {
|
|||
INIT_UPDATE_STATS,
|
||||
RESET_SEARCH_CRITERIA
|
||||
} from './actionTypes';
|
||||
import { initReorderStats, updateStats } from './actions';
|
||||
import { filterBySearchCriteria, getPendingReorder, getSortedSpeakerStats, resetHiddenStats } from './functions';
|
||||
import { initReorderStats, updateSortedSpeakerStatsIds, updateStats } from './actions';
|
||||
import { filterBySearchCriteria, getPendingReorder, getSortedSpeakerStatsIds, resetHiddenStats } from './functions';
|
||||
|
||||
MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
|
||||
const result = next(action);
|
||||
|
@ -35,8 +35,13 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
|
|||
const stats = filterBySearchCriteria(state, speakerStats);
|
||||
const pendingReorder = getPendingReorder(state);
|
||||
|
||||
dispatch(updateStats(pendingReorder ? getSortedSpeakerStats(state, stats) : stats));
|
||||
if (pendingReorder) {
|
||||
dispatch(updateSortedSpeakerStatsIds(getSortedSpeakerStatsIds(state, stats)));
|
||||
}
|
||||
|
||||
dispatch(updateStats(stats));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case RESET_SEARCH_CRITERIA: {
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
INIT_SEARCH,
|
||||
RESET_SEARCH_CRITERIA,
|
||||
TOGGLE_FACE_EXPRESSIONS,
|
||||
UPDATE_SORTED_SPEAKER_STATS_IDS,
|
||||
UPDATE_STATS
|
||||
} from './actionTypes';
|
||||
|
||||
|
@ -20,7 +21,8 @@ const INITIAL_STATE = {
|
|||
isOpen: false,
|
||||
pendingReorder: true,
|
||||
criteria: null,
|
||||
showFaceExpressions: false
|
||||
showFaceExpressions: false,
|
||||
sortedSpeakerStatsIds: []
|
||||
};
|
||||
|
||||
export interface ISpeakerStatsState {
|
||||
|
@ -28,6 +30,7 @@ export interface ISpeakerStatsState {
|
|||
isOpen: boolean;
|
||||
pendingReorder: boolean;
|
||||
showFaceExpressions: boolean;
|
||||
sortedSpeakerStatsIds: Array<string>;
|
||||
stats: Object;
|
||||
}
|
||||
|
||||
|
@ -40,6 +43,8 @@ ReducerRegistry.register<ISpeakerStatsState>('features/speaker-stats',
|
|||
return _updateStats(state, action);
|
||||
case INIT_REORDER_STATS:
|
||||
return _initReorderStats(state);
|
||||
case UPDATE_SORTED_SPEAKER_STATS_IDS:
|
||||
return _updateSortedSpeakerStats(state, action);
|
||||
case RESET_SEARCH_CRITERIA:
|
||||
return _updateCriteria(state, { criteria: null });
|
||||
case TOGGLE_FACE_EXPRESSIONS: {
|
||||
|
@ -71,13 +76,7 @@ function _updateCriteria(state: ISpeakerStatsState, { criteria }: { criteria: st
|
|||
}
|
||||
|
||||
/**
|
||||
* Reduces a specific Redux action UPDATE_STATS of the feature
|
||||
* speaker-stats.
|
||||
* The speaker stats order is based on the stats object properties.
|
||||
* When updating without reordering, the new stats object properties are reordered
|
||||
* as the last in state, otherwise the order would be lost on each update.
|
||||
* If there was already a pending reorder, the stats object properties already have
|
||||
* the correct order, so the property order is not changing.
|
||||
* Reduces a specific Redux action UPDATE_STATS of the feature speaker-stats.
|
||||
*
|
||||
* @param {Object} state - The Redux state of the feature speaker-stats.
|
||||
* @param {Action} action - The Redux action UPDATE_STATS to reduce.
|
||||
|
@ -85,31 +84,26 @@ function _updateCriteria(state: ISpeakerStatsState, { criteria }: { criteria: st
|
|||
* @returns {Object} - The new state after the reduction of the specified action.
|
||||
*/
|
||||
function _updateStats(state: ISpeakerStatsState, { stats }: { stats: any; }) {
|
||||
const finalStats = state.pendingReorder ? stats : state.stats;
|
||||
return {
|
||||
...state,
|
||||
stats
|
||||
};
|
||||
}
|
||||
|
||||
if (!state.pendingReorder) {
|
||||
// Avoid reordering the speaker stats object properties
|
||||
const finalKeys = Object.keys(stats);
|
||||
|
||||
finalKeys.forEach(newStatId => {
|
||||
finalStats[newStatId] = _.clone(stats[newStatId]);
|
||||
});
|
||||
|
||||
Object.keys(finalStats).forEach(key => {
|
||||
if (!finalKeys.includes(key)) {
|
||||
delete finalStats[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return _.assign(
|
||||
{},
|
||||
state,
|
||||
{
|
||||
stats: { ...finalStats },
|
||||
pendingReorder: false
|
||||
}
|
||||
);
|
||||
/**
|
||||
* Reduces a specific Redux action UPDATE_SORTED_SPEAKER_STATS_IDS of the feature speaker-stats.
|
||||
*
|
||||
* @param {Object} state - The Redux state of the feature speaker-stats.
|
||||
* @param {Action} action - The Redux action UPDATE_SORTED_SPEAKER_STATS_IDS to reduce.
|
||||
* @private
|
||||
* @returns {Object} The new state after the reduction of the specified action.
|
||||
*/
|
||||
function _updateSortedSpeakerStats(state: ISpeakerStatsState, { participantIds }: { participantIds: Array<string>; }) {
|
||||
return {
|
||||
...state,
|
||||
sortedSpeakerStatsIds: participantIds,
|
||||
pendingReorder: false
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue