Browse Source

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
master
virtuacoplenny 8 years ago
parent
commit
fe411398e3

+ 3
- 1
lang/main.json View File

214
     "notify": {
214
     "notify": {
215
         "disconnected": "disconnected",
215
         "disconnected": "disconnected",
216
         "moderator": "Moderator rights granted!",
216
         "moderator": "Moderator rights granted!",
217
-        "connected": "connected",
217
+        "connectedOneMember": "__name__ connected",
218
+        "connectedTwoMembers": "__first__ and __second__ connected",
219
+        "connectedThreePlusMembers": "__name__ and __count__ others connected",
218
         "somebody": "Somebody",
220
         "somebody": "Somebody",
219
         "me": "Me",
221
         "me": "Me",
220
         "focus": "Conference focus",
222
         "focus": "Conference focus",

+ 5
- 3
modules/UI/UI.js View File

25
 } from '../../react/features/device-selection';
25
 } from '../../react/features/device-selection';
26
 import { updateDeviceList } from '../../react/features/base/devices';
26
 import { updateDeviceList } from '../../react/features/base/devices';
27
 import { JitsiTrackErrors } from '../../react/features/base/lib-jitsi-meet';
27
 import { JitsiTrackErrors } from '../../react/features/base/lib-jitsi-meet';
28
-import { getLocalParticipant } from '../../react/features/base/participants';
28
+import {
29
+    getLocalParticipant,
30
+    showParticipantJoinedNotification
31
+} from '../../react/features/base/participants';
29
 import { openDisplayNamePrompt } from '../../react/features/display-name';
32
 import { openDisplayNamePrompt } from '../../react/features/display-name';
30
 import {
33
 import {
31
     maybeShowNotificationWithDoNotDisplay,
34
     maybeShowNotificationWithDoNotDisplay,
473
     const id = user.getId();
476
     const id = user.getId();
474
     const displayName = user.getDisplayName();
477
     const displayName = user.getDisplayName();
475
 
478
 
476
-    messageHandler.participantNotification(
477
-        displayName, 'notify.somebody', 'connected', 'notify.connected');
479
+    APP.store.dispatch(showParticipantJoinedNotification(displayName));
478
 
480
 
479
     if (!config.startAudioMuted
481
     if (!config.startAudioMuted
480
         || config.startAudioMuted > APP.conference.membersCount) {
482
         || config.startAudioMuted > APP.conference.membersCount) {

+ 2
- 2
modules/UI/util/MessageHandler.js View File

362
         $titleString.attr('data-i18n', titleKey);
362
         $titleString.attr('data-i18n', titleKey);
363
 
363
 
364
         return $('<div>').append($titleString)
364
         return $('<div>').append($titleString)
365
-.html();
365
+            .html();
366
     },
366
     },
367
 
367
 
368
     /**
368
     /**
479
      * @param displayName the display name of the participant that is
479
      * @param displayName the display name of the participant that is
480
      * associated with the notification.
480
      * associated with the notification.
481
      * @param displayNameKey the key from the language file for the display
481
      * @param displayNameKey the key from the language file for the display
482
-     * name. Only used if displayName i not provided.
482
+     * name. Only used if displayName is not provided.
483
      * @param cls css class for the notification
483
      * @param cls css class for the notification
484
      * @param messageKey the key from the language file for the text of the
484
      * @param messageKey the key from the language file for the text of the
485
      * message.
485
      * message.

+ 82
- 0
react/features/base/participants/actions.js View File

1
+/* global interfaceConfig */
2
+
3
+import throttle from 'lodash/throttle';
4
+
5
+import { Notification, showNotification } from '../../notifications';
6
+
1
 import {
7
 import {
2
     DOMINANT_SPEAKER_CHANGED,
8
     DOMINANT_SPEAKER_CHANGED,
3
     KICK_PARTICIPANT,
9
     KICK_PARTICIPANT,
318
         }
324
         }
319
     };
325
     };
320
 }
326
 }
327
+
328
+/**
329
+ * An array of names of participants that have joined the conference. The array
330
+ * is replaced with an empty array as notifications are displayed.
331
+ *
332
+ * @private
333
+ * @type {string[]}
334
+ */
335
+let joinedParticipantsNames = [];
336
+
337
+/**
338
+ * A throttled internal function that takes the internal list of participant
339
+ * names, {@code joinedParticipantsNames}, and triggers the display of a
340
+ * notification informing of their joining.
341
+ *
342
+ * @private
343
+ * @type {Function}
344
+ */
345
+const _throttledNotifyParticipantConnected = throttle(dispatch => {
346
+    const joinedParticipantsCount = joinedParticipantsNames.length;
347
+
348
+    let notificationProps;
349
+
350
+    if (joinedParticipantsCount >= 3) {
351
+        notificationProps = {
352
+            titleArguments: {
353
+                name: joinedParticipantsNames[0],
354
+                count: joinedParticipantsCount - 1
355
+            },
356
+            titleKey: 'notify.connectedThreePlusMembers'
357
+        };
358
+    } else if (joinedParticipantsCount === 2) {
359
+        notificationProps = {
360
+            titleArguments: {
361
+                first: joinedParticipantsNames[0],
362
+                second: joinedParticipantsNames[1]
363
+            },
364
+            titleKey: 'notify.connectedTwoMembers'
365
+        };
366
+    } else if (joinedParticipantsCount) {
367
+        notificationProps = {
368
+            titleArguments: {
369
+                name: joinedParticipantsNames[0]
370
+            },
371
+            titleKey: 'notify.connectedOneMember'
372
+        };
373
+    }
374
+
375
+    if (notificationProps) {
376
+        dispatch(
377
+            showNotification(
378
+                Notification,
379
+                notificationProps,
380
+                2500));
381
+    }
382
+
383
+    joinedParticipantsNames = [];
384
+
385
+}, 500, { leading: false });
386
+
387
+/**
388
+ * Queues the display of a notification of a participant having connected to
389
+ * the meeting. The notifications are batched so that quick consecutive
390
+ * connection events are shown in one notification.
391
+ *
392
+ * @param {string} displayName - The name of the participant that connected.
393
+ * @returns {Function}
394
+ */
395
+export function showParticipantJoinedNotification(displayName) {
396
+    joinedParticipantsNames.push(
397
+        displayName || interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME);
398
+
399
+    return dispatch => {
400
+        _throttledNotifyParticipantConnected(dispatch);
401
+    };
402
+}

+ 10
- 4
react/features/notifications/components/Notification.web.js View File

104
          */
104
          */
105
         title: PropTypes.string,
105
         title: PropTypes.string,
106
 
106
 
107
+        /**
108
+         * The translation arguments that may be necessary for the title.
109
+         */
110
+        titleArguments: PropTypes.object,
111
+
107
         /**
112
         /**
108
          * The translation key to display as the title of the notification if
113
          * The translation key to display as the title of the notification if
109
          * no title is provided.
114
          * no title is provided.
138
      */
143
      */
139
     render() {
144
     render() {
140
         const {
145
         const {
141
-            hideErrorSupportLink,
142
             appearance,
146
             appearance,
143
-            titleKey,
147
+            description,
144
             descriptionArguments,
148
             descriptionArguments,
145
             descriptionKey,
149
             descriptionKey,
146
-            description,
150
+            hideErrorSupportLink,
147
             isDismissAllowed,
151
             isDismissAllowed,
148
             onDismissed,
152
             onDismissed,
149
             t,
153
             t,
150
             title,
154
             title,
155
+            titleArguments,
156
+            titleKey,
151
             uid
157
             uid
152
         } = this.props;
158
         } = this.props;
153
 
159
 
161
                 id = { uid }
167
                 id = { uid }
162
                 isDismissAllowed = { isDismissAllowed }
168
                 isDismissAllowed = { isDismissAllowed }
163
                 onDismissed = { onDismissed }
169
                 onDismissed = { onDismissed }
164
-                title = { title || t(titleKey) } />
170
+                title = { title || t(titleKey, titleArguments) } />
165
         );
171
         );
166
     }
172
     }
167
 
173
 

Loading…
Cancel
Save