fix(notifications): throttle and batch join notifications (#2182)

* fix(notifications): throttle and batch join notifications

Instead of directly calling to show a join notification,
go through a specific method. This method will queue
names for display while a throttled function pulls
the names and shows a notification.

* squash: remove unused translation key

* squash: use default display name

* squash: move into participant actions
This commit is contained in:
virtuacoplenny 2017-11-21 09:38:40 -08:00 committed by yanas
parent 0aa377fcfc
commit fe411398e3
5 changed files with 102 additions and 10 deletions

View File

@ -214,7 +214,9 @@
"notify": { "notify": {
"disconnected": "disconnected", "disconnected": "disconnected",
"moderator": "Moderator rights granted!", "moderator": "Moderator rights granted!",
"connected": "connected", "connectedOneMember": "__name__ connected",
"connectedTwoMembers": "__first__ and __second__ connected",
"connectedThreePlusMembers": "__name__ and __count__ others connected",
"somebody": "Somebody", "somebody": "Somebody",
"me": "Me", "me": "Me",
"focus": "Conference focus", "focus": "Conference focus",

View File

@ -25,7 +25,10 @@ import {
} from '../../react/features/device-selection'; } from '../../react/features/device-selection';
import { updateDeviceList } from '../../react/features/base/devices'; import { updateDeviceList } from '../../react/features/base/devices';
import { JitsiTrackErrors } from '../../react/features/base/lib-jitsi-meet'; import { JitsiTrackErrors } from '../../react/features/base/lib-jitsi-meet';
import { getLocalParticipant } from '../../react/features/base/participants'; import {
getLocalParticipant,
showParticipantJoinedNotification
} from '../../react/features/base/participants';
import { openDisplayNamePrompt } from '../../react/features/display-name'; import { openDisplayNamePrompt } from '../../react/features/display-name';
import { import {
maybeShowNotificationWithDoNotDisplay, maybeShowNotificationWithDoNotDisplay,
@ -473,8 +476,7 @@ UI.addUser = function(user) {
const id = user.getId(); const id = user.getId();
const displayName = user.getDisplayName(); const displayName = user.getDisplayName();
messageHandler.participantNotification( APP.store.dispatch(showParticipantJoinedNotification(displayName));
displayName, 'notify.somebody', 'connected', 'notify.connected');
if (!config.startAudioMuted if (!config.startAudioMuted
|| config.startAudioMuted > APP.conference.membersCount) { || config.startAudioMuted > APP.conference.membersCount) {

View File

@ -362,7 +362,7 @@ const messageHandler = {
$titleString.attr('data-i18n', titleKey); $titleString.attr('data-i18n', titleKey);
return $('<div>').append($titleString) return $('<div>').append($titleString)
.html(); .html();
}, },
/** /**
@ -479,7 +479,7 @@ const messageHandler = {
* @param displayName the display name of the participant that is * @param displayName the display name of the participant that is
* associated with the notification. * associated with the notification.
* @param displayNameKey the key from the language file for the display * @param displayNameKey the key from the language file for the display
* name. Only used if displayName i not provided. * name. Only used if displayName is not provided.
* @param cls css class for the notification * @param cls css class for the notification
* @param messageKey the key from the language file for the text of the * @param messageKey the key from the language file for the text of the
* message. * message.

View File

@ -1,3 +1,9 @@
/* global interfaceConfig */
import throttle from 'lodash/throttle';
import { Notification, showNotification } from '../../notifications';
import { import {
DOMINANT_SPEAKER_CHANGED, DOMINANT_SPEAKER_CHANGED,
KICK_PARTICIPANT, KICK_PARTICIPANT,
@ -318,3 +324,79 @@ export function pinParticipant(id) {
} }
}; };
} }
/**
* An array of names of participants that have joined the conference. The array
* is replaced with an empty array as notifications are displayed.
*
* @private
* @type {string[]}
*/
let joinedParticipantsNames = [];
/**
* A throttled internal function that takes the internal list of participant
* names, {@code joinedParticipantsNames}, and triggers the display of a
* notification informing of their joining.
*
* @private
* @type {Function}
*/
const _throttledNotifyParticipantConnected = throttle(dispatch => {
const joinedParticipantsCount = joinedParticipantsNames.length;
let notificationProps;
if (joinedParticipantsCount >= 3) {
notificationProps = {
titleArguments: {
name: joinedParticipantsNames[0],
count: joinedParticipantsCount - 1
},
titleKey: 'notify.connectedThreePlusMembers'
};
} else if (joinedParticipantsCount === 2) {
notificationProps = {
titleArguments: {
first: joinedParticipantsNames[0],
second: joinedParticipantsNames[1]
},
titleKey: 'notify.connectedTwoMembers'
};
} else if (joinedParticipantsCount) {
notificationProps = {
titleArguments: {
name: joinedParticipantsNames[0]
},
titleKey: 'notify.connectedOneMember'
};
}
if (notificationProps) {
dispatch(
showNotification(
Notification,
notificationProps,
2500));
}
joinedParticipantsNames = [];
}, 500, { leading: false });
/**
* Queues the display of a notification of a participant having connected 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 connected.
* @returns {Function}
*/
export function showParticipantJoinedNotification(displayName) {
joinedParticipantsNames.push(
displayName || interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME);
return dispatch => {
_throttledNotifyParticipantConnected(dispatch);
};
}

View File

@ -104,6 +104,11 @@ class Notification extends Component<*> {
*/ */
title: PropTypes.string, title: PropTypes.string,
/**
* The translation arguments that may be necessary for the title.
*/
titleArguments: PropTypes.object,
/** /**
* The translation key to display as the title of the notification if * The translation key to display as the title of the notification if
* no title is provided. * no title is provided.
@ -138,16 +143,17 @@ class Notification extends Component<*> {
*/ */
render() { render() {
const { const {
hideErrorSupportLink,
appearance, appearance,
titleKey, description,
descriptionArguments, descriptionArguments,
descriptionKey, descriptionKey,
description, hideErrorSupportLink,
isDismissAllowed, isDismissAllowed,
onDismissed, onDismissed,
t, t,
title, title,
titleArguments,
titleKey,
uid uid
} = this.props; } = this.props;
@ -161,7 +167,7 @@ class Notification extends Component<*> {
id = { uid } id = { uid }
isDismissAllowed = { isDismissAllowed } isDismissAllowed = { isDismissAllowed }
onDismissed = { onDismissed } onDismissed = { onDismissed }
title = { title || t(titleKey) } /> title = { title || t(titleKey, titleArguments) } />
); );
} }