Merge branch 'hotfix/21.56.9' into master
[gitter.git] / server / services / notifications / push-notification-generator.js
blob175f933dfefeee91af3f8751139986ef88fc7147
1 'use strict';
3 var pushNotificationFilter = require('gitter-web-push-notification-filter');
4 var pushNotificationGateway = require('../../gateways/push-notification-gateway');
5 var serializer = require('../../serializers/notification-serializer');
6 var unreadItemService = require('gitter-web-unread-items');
7 var debug = require('debug')('gitter:app:push-notification-generator');
8 var Promise = require('bluebird');
9 var _ = require('lodash');
11 var MAX_MESSAGES_FOR_NOTIFICATION = 3;
13 function serializeItems(troupeId, recipientUserId, chatIds) {
14   var troupeStrategy = new serializer.TroupeIdStrategy({ recipientUserId: recipientUserId });
15   var chatStrategy = new serializer.ChatIdStrategy();
17   return Promise.all([
18     serializer.serializeObject(troupeId, troupeStrategy),
19     serializer.serialize(chatIds, chatStrategy)
20   ]);
23 /**
24  * Given a set of unread items and mentions, return `n` items
25  * which will be presented to the user in the push notification
26  */
27 function selectChatsForNotification(unreadItems, mentions) {
28   // Trivial case: there are three or less unread items, send them immediately
29   if (unreadItems.length <= MAX_MESSAGES_FOR_NOTIFICATION) return unreadItems;
31   unreadItems.sort(); // Default sorting works fine with mongoids
33   // Simple case: there are no mentions. Just send the first three unread items
34   if (!mentions.length) {
35     return unreadItems.slice(0, MAX_MESSAGES_FOR_NOTIFICATION);
36   }
38   // Mentions case: get the first mention and then get the two closest unread items
39   // to that mention (ie, the two before, the two around or the two after)
40   var firstMention = _.min(mentions);
41   var indexOfFirstMention = unreadItems.indexOf(firstMention);
42   if (indexOfFirstMention <= 0) {
43     /* This should never happen. Things are broken. Failback */
44     return unreadItems.slice(0, MAX_MESSAGES_FOR_NOTIFICATION);
45   }
47   if (indexOfFirstMention === unreadItems.length - 1) {
48     /* Mention is the last message */
49     return unreadItems.slice(-MAX_MESSAGES_FOR_NOTIFICATION);
50   }
52   return unreadItems
53     .slice(indexOfFirstMention - Math.floor(MAX_MESSAGES_FOR_NOTIFICATION / 2))
54     .slice(0, MAX_MESSAGES_FOR_NOTIFICATION);
57 function notifyUserOfActivitySince(userId, troupeId, since, notificationNumber) {
58   debug(
59     'notifyUserOfActivitySince userId=%s, troupeId=%s, since=%s, notificationNumber=%s',
60     userId,
61     troupeId,
62     since,
63     notificationNumber
64   );
66   return unreadItemService
67     .getUnreadItemsForUserTroupeSince(userId, troupeId, since)
68     .spread(function(unreadItems, mentions) {
69       // mentions should always be a subset of unreadItems
70       if (!unreadItems.length) {
71         debug('User %s has no unread items since %s in troupeId=%s', userId, since, troupeId);
72         return;
73       }
75       var chatIdsForNotification = selectChatsForNotification(unreadItems, mentions);
77       return serializeItems(troupeId, userId, chatIdsForNotification).spread(function(
78         troupe,
79         chats
80       ) {
81         if (!troupe || !chats || !chats.length) return;
83         return pushNotificationGateway.sendUserNotification('new_chat', userId, {
84           room: troupe,
85           chats: chats,
86           hasMentions: !!(mentions && mentions.length)
87         });
88       });
89     });
92 function sendUserTroupeNotification(
93   userId,
94   troupeId,
95   notificationNumber,
96   userNotifySetting /*, mentioned*/
97 ) {
98   return pushNotificationFilter
99     .canUnlockForNotification(userId, troupeId, notificationNumber)
100     .then(function(startTime) {
101       if (!startTime) {
102         debug(
103           'Unable to obtain lock to notify %s for user %s troupe %s. Skipping',
104           notificationNumber,
105           userId,
106           troupeId
107         );
108         return;
109       }
111       // TODO: remove this....
112       if (userNotifySetting === 'mute') {
113         /* Mute this troupe for this user */
114         return;
115       }
117       return notifyUserOfActivitySince(
118         userId,
119         troupeId,
120         startTime,
121         notificationNumber,
122         userNotifySetting
123       );
124     });
127 module.exports.sendUserTroupeNotification = sendUserTroupeNotification;
128 module.exports.testOnly = {
129   selectChatsForNotification: selectChatsForNotification,
130   serializeItems: serializeItems