feat(notifications) coalesce participant left and raised hand notifications

This commit is contained in:
Tudor D. Pop 2021-12-02 15:17:07 +02:00 committed by GitHub
parent 40a76940ac
commit c172a27f24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 126 additions and 12 deletions

View File

@ -585,6 +585,9 @@
"invitedThreePlusMembers": "{{name}} and {{count}} others have been invited",
"invitedTwoMembers": "{{first}} and {{second}} have been invited",
"kickParticipant": "{{kicked}} was kicked by {{kicker}}",
"leftOneMember": "{{name}} left the meeting",
"leftThreePlusMembers": "{{name}} and many others left the meeting",
"leftTwoMembers": "{{first}} and {{second}} left the meeting",
"me": "Me",
"moderator": "You're now a moderator",
"muted": "You have started the conversation muted.",
@ -596,6 +599,7 @@
"passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) removed by another participant",
"passwordSetRemotely": "$t(lockRoomPasswordUppercase) set by another participant",
"raisedHand": "Would like to speak.",
"raisedHands": "{{participantName}} and {{raisedHands}} more people",
"screenShareNoAudio": " Share audio box was not checked in the window selection screen.",
"screenShareNoAudioTitle": "Couldn't share system audio!",
"somebody": "Somebody",

View File

@ -1,12 +1,17 @@
// @flow
import i18n from 'i18next';
import { batch } from 'react-redux';
import UIEvents from '../../../../service/UI/UIEvents';
import { approveParticipant } from '../../av-moderation/actions';
import { toggleE2EE } from '../../e2ee/actions';
import { MAX_MODE } from '../../e2ee/constants';
import { NOTIFICATION_TIMEOUT_TYPE, showNotification } from '../../notifications';
import {
NOTIFICATION_TIMEOUT_TYPE,
RAISE_HAND_NOTIFICATION_ID,
showNotification
} from '../../notifications';
import { isForceMuted } from '../../participants-pane/functions';
import { CALLING, INVITED } from '../../presence-status';
import { RAISE_HAND_SOUND_ID } from '../../reactions/constants';
@ -555,12 +560,27 @@ function _raiseHandUpdated({ dispatch, getState }, conference, participantId, ne
} : {};
if (raisedHandTimestamp) {
let notificationTitle;
const participantName = getParticipantDisplayName(state, participantId);
const { raisedHandsQueue } = state['features/base/participants'];
if (raisedHandsQueue.length > 1) {
const raisedHands = raisedHandsQueue.length - 1;
notificationTitle = i18n.t('notify.raisedHands', {
participantName,
raisedHands
});
} else {
notificationTitle = participantName;
}
dispatch(showNotification({
titleKey: 'notify.somebody',
title: getParticipantDisplayName(state, participantId),
title: notificationTitle,
descriptionKey: 'notify.raisedHand',
raiseHandNotification: true,
concatText: true,
uid: RAISE_HAND_NOTIFICATION_ID,
...action
}, shouldDisplayAllowAction ? NOTIFICATION_TIMEOUT_TYPE.MEDIUM : NOTIFICATION_TIMEOUT_TYPE.SHORT));
dispatch(playSound(RAISE_HAND_SOUND_ID));

View File

@ -17,7 +17,8 @@ import {
NOTIFICATION_TIMEOUT_TYPE,
NOTIFICATION_TIMEOUT,
NOTIFICATION_TYPE,
SILENT_JOIN_THRESHOLD
SILENT_JOIN_THRESHOLD,
SILENT_LEFT_THRESHOLD
} from './constants';
/**
@ -219,6 +220,70 @@ const _throttledNotifyParticipantConnected = throttle((dispatch: Dispatch<any>,
}, 2000, { leading: false });
/**
* An array of names of participants that have left the conference. The array
* is replaced with an empty array as notifications are displayed.
*
* @private
* @type {string[]}
*/
let leftParticipantsNames = [];
/**
* A throttled internal function that takes the internal list of participant
* names, {@code leftParticipantsNames}, and triggers the display of a
* notification informing of their leaving.
*
* @private
* @type {Function}
*/
const _throttledNotifyParticipantLeft = throttle((dispatch: Dispatch<any>, getState: Function) => {
const participantCount = getParticipantCount(getState());
// Skip left notifications altogether for large meetings.
if (participantCount > SILENT_LEFT_THRESHOLD) {
leftParticipantsNames = [];
return;
}
const leftParticipantsCount = leftParticipantsNames.length;
let notificationProps;
if (leftParticipantsCount >= 3) {
notificationProps = {
titleArguments: {
name: leftParticipantsNames[0]
},
titleKey: 'notify.leftThreePlusMembers'
};
} else if (leftParticipantsCount === 2) {
notificationProps = {
titleArguments: {
first: leftParticipantsNames[0],
second: leftParticipantsNames[1]
},
titleKey: 'notify.leftTwoMembers'
};
} else if (leftParticipantsCount) {
notificationProps = {
titleArguments: {
name: leftParticipantsNames[0]
},
titleKey: 'notify.leftOneMember'
};
}
if (notificationProps) {
dispatch(
showNotification(notificationProps, NOTIFICATION_TIMEOUT_TYPE.SHORT));
}
leftParticipantsNames = [];
}, 2000, { leading: false });
/**
* Queues the display of a notification of a participant having connected to
* the meeting. The notifications are batched so that quick consecutive
@ -228,7 +293,21 @@ const _throttledNotifyParticipantConnected = throttle((dispatch: Dispatch<any>,
* @returns {Function}
*/
export function showParticipantJoinedNotification(displayName: string) {
joinedParticipantsNames.push(displayName);
leftParticipantsNames.push(displayName);
return (dispatch: Dispatch<any>, getState: Function) => _throttledNotifyParticipantConnected(dispatch, getState);
}
/**
* Queues the display of a notification of a participant having left to
* the meeting. The notifications are batched so that quick consecutive
* connection events are shown in one notification.
*
* @param {string} displayName - The name of the participant that left.
* @returns {Function}
*/
export function showParticipantLeftNotification(displayName: string) {
joinedParticipantsNames.push(displayName);
return (dispatch: Dispatch<any>, getState: Function) => _throttledNotifyParticipantLeft(dispatch, getState);
}

View File

@ -46,7 +46,19 @@ export const NOTIFICATION_TYPE_PRIORITIES = {
[NOTIFICATION_TYPE.WARNING]: 4
};
/**
* The identifier of the raise hand notification.
*
* @type {string}
*/
export const RAISE_HAND_NOTIFICATION_ID = 'RAISE_HAND_NOTIFICATION';
/**
* Amount of participants beyond which no join notification will be emitted.
*/
export const SILENT_JOIN_THRESHOLD = 30;
/**
* Amount of participants beyond which no left notification will be emitted.
*/
export const SILENT_LEFT_THRESHOLD = 30;

View File

@ -17,13 +17,12 @@ import {
clearNotifications,
hideRaiseHandNotifications,
showNotification,
showParticipantJoinedNotification
showParticipantJoinedNotification,
showParticipantLeftNotification
} from './actions';
import { NOTIFICATION_TIMEOUT_TYPE } from './constants';
import { joinLeaveNotificationsDisabled } from './functions';
declare var interfaceConfig: Object;
/**
* Middleware that captures actions to display notifications.
*
@ -49,17 +48,17 @@ MiddlewareRegistry.register(store => next => action => {
}
case PARTICIPANT_LEFT: {
if (!joinLeaveNotificationsDisabled()) {
const { dispatch, getState } = store;
const state = getState();
const participant = getParticipantById(
store.getState(),
action.participant.id
);
if (participant && !participant.local && !action.participant.isReplaced) {
store.dispatch(showNotification({
descriptionKey: 'notify.disconnected',
titleKey: 'notify.somebody',
title: participant.name
}, NOTIFICATION_TIMEOUT_TYPE.SHORT));
dispatch(showParticipantLeftNotification(
getParticipantDisplayName(state, participant.id)
));
}
}