From 493d0dea1e48f8091ccb57a897a66e826ad9e72e Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 8 Sep 2014 23:03:37 -0400 Subject: [PATCH] part 1 of notif refactor --- public/src/modules/notifications.js | 10 +- src/controllers/accounts.js | 5 +- src/database/level/sorted.js | 12 +- src/database/mongo/sorted.js | 29 +++- src/database/redis/sorted.js | 25 +++- src/messaging.js | 3 +- src/notifications.js | 219 ++++++++++------------------ src/socket.io/modules.js | 8 +- src/socket.io/posts.js | 16 +- src/socket.io/topics.js | 8 +- src/socket.io/user.js | 38 ++--- src/topics/follow.js | 8 +- src/topics/unread.js | 3 + src/upgrade.js | 52 ++++++- src/user/create.js | 7 +- src/user/delete.js | 3 - src/user/notifications.js | 154 +++++++++---------- 17 files changed, 319 insertions(+), 281 deletions(-) diff --git a/public/src/modules/notifications.js b/public/src/modules/notifications.js index 750449fc72..95f7baacda 100644 --- a/public/src/modules/notifications.js +++ b/public/src/modules/notifications.js @@ -71,6 +71,11 @@ define('notifications', ['sounds'], function(sound) { Tinycon.setBubble(count); }; + function increaseNotifCount() { + var count = parseInt(notifIcon.attr('data-content'), 10) + 1; + updateNotifCount(count); + } + socket.emit('notifications.getCount', function(err, count) { if (!err) { updateNotifCount(count); @@ -79,7 +84,7 @@ define('notifications', ['sounds'], function(sound) { } }); - socket.on('event:new_notification', function(notifData, notifCount) { + socket.on('event:new_notification', function(notifData) { app.alert({ alert_id: 'new_notif', title: '[[notifications:new_notification]]', @@ -93,10 +98,11 @@ define('notifications', ['sounds'], function(sound) { ajaxify.refresh(); } - updateNotifCount(notifCount); + increaseNotifCount(); sound.play('notification'); }); + socket.on('event:notifications.updateCount', function(count) { updateNotifCount(count); }); diff --git a/src/controllers/accounts.js b/src/controllers/accounts.js index 903853d562..616cfc50d3 100644 --- a/src/controllers/accounts.js +++ b/src/controllers/accounts.js @@ -480,7 +480,10 @@ accountsController.uploadPicture = function (req, res, next) { }; accountsController.getNotifications = function(req, res, next) { - user.notifications.getAll(req.user.uid, 25, function(err, notifications) { + user.notifications.getAll(req.user.uid, 40, function(err, notifications) { + if (err) { + return next(err); + } res.render('notifications', { notifications: notifications }); diff --git a/src/database/level/sorted.js b/src/database/level/sorted.js index ac681bb65b..b0cbcb415a 100644 --- a/src/database/level/sorted.js +++ b/src/database/level/sorted.js @@ -7,6 +7,9 @@ module.exports = function(db, module) { var helpers = module.helpers.level; module.sortedSetAdd = function(key, score, value, callback) { + if (Array.isArray(score) && Array.isArray(value)) { + return sortedSetAddMulti(key, score, value, callback); + } module.getListRange(key, 0, -1, function(err, set) { set = set.filter(function(a) {return a.value !== value.toString();}); @@ -20,6 +23,10 @@ module.exports = function(db, module) { }); }; + function sortedSetAddMulti(key, scores, values, callback) { + throw new Error('not implemented'); + } + module.sortedSetsAdd = function(keys, score, value, callback) { async.each(keys, function(key, next) { module.sortedSetAdd(key, score, value, next); @@ -29,8 +36,11 @@ module.exports = function(db, module) { }; module.sortedSetRemove = function(key, value, callback) { + if (!Array.isArray(value)) { + value = [value]; + } module.getListRange(key, 0, -1, function(err, set) { - set = set.filter(function(a) {return a.value !== value.toString();}); + set = set.filter(function(a) { return value.indexOf(a) === -1;}); module.set(key, set, callback); }); }; diff --git a/src/database/mongo/sorted.js b/src/database/mongo/sorted.js index 9e31335308..ead67b6115 100644 --- a/src/database/mongo/sorted.js +++ b/src/database/mongo/sorted.js @@ -7,6 +7,10 @@ module.exports = function(db, module) { module.sortedSetAdd = function(key, score, value, callback) { callback = callback || helpers.noop; + if (Array.isArray(score) && Array.isArray(value)) { + return sortedSetAddBulk(key, score, value, callback); + } + value = helpers.valueToString(value); var data = { score: parseInt(score, 10), @@ -18,6 +22,24 @@ module.exports = function(db, module) { }); }; + function sortedSetAddBulk(key, scores, values, callback) { + if (scores.length !== values.length) { + return callback(new Error('[[error:invalid-data]]')); + } + + values = values.map(helpers.valueToString); + + var bulk = db.collection('objects').initializeUnorderedBulkOp(); + + for(var i=0; i parseInt(data.importance, 10)) { + return callback(null, null); + } + } + + var now = Date.now(); + data.datetime = now; + async.parallel([ + function(next) { + db.sortedSetAdd('notifications', now, data.nid, next); + }, + function(next) { + db.setObject('notifications:' + data.nid, data, next); + } + ], function(err) { + callback(err, data); }); }); }; - Notifications.push = function(nid, uids, callback) { + Notifications.push = function(notification, uids, callback) { callback = callback || function() {}; var websockets = require('./socket.io'); if (!Array.isArray(uids)) { uids = [uids]; } - Notifications.get(nid, function(err, notif_data) { - if (err) { - return callback(err); - } - - async.eachLimit(uids, 10, function(uid, next) { - if (!parseInt(uid, 10)) { - return next(); - } - - shouldPush(uid, notif_data, function(err, shouldPush) { - if (err || !shouldPush) { - return callback(err); - } - - async.parallel([ - async.apply(db.setObjectField, 'uid:' + uid + ':notifications:uniqueId:nid', notif_data.uniqueId, nid), - async.apply(db.sortedSetAdd, 'uid:' + uid + ':notifications:unread', notif_data.datetime, notif_data.uniqueId), - async.apply(db.sortedSetRemove, 'uid:' + uid + ':notifications:read', notif_data.uniqueId) - ], function(err) { - if (err) { - return next(err); - } + var unreadKeys = []; + var readKeys = []; - User.notifications.getUnreadCount(uid, function(err, count) { - if (!err) { - websockets.in('uid_' + uid).emit('event:new_notification', notif_data, count); - } - }); - - // Plugins - notif_data.uid = uid; - plugins.fireHook('action:notification.pushed', notif_data); - next(); - }); - }); - }, callback); + uids.filter(Boolean).forEach(function(uid) { + unreadKeys.push('uid:' + uid + ':notifications:unread'); + readKeys.push('uid:' + uid + ':notifications:read'); }); - }; - - function shouldPush(uid, newNotifObj, callback) { - if (!newNotifObj) { - return callback(null, false); - } - hasNotification(newNotifObj.uniqueId, uid, function(err, hasNotification) { + async.parallel([ + function(next) { + db.sortedSetsAdd(unreadKeys, notification.datetime, notification.nid, next); + }, + function(next) { + db.sortedSetsRemove(readKeys, notification.nid, next); + } + ], function(err) { if (err) { return callback(err); } - if (!hasNotification) { - return callback(null, true); - } - - db.getObjectField('uid:' + uid + ':notifications:uniqueId:nid', newNotifObj.uniqueId, function(err, nid) { - if (err) { - return callback(err); - } - - db.getObjectFields('notifications:' + nid, ['nid', 'uniqueId', 'importance'], function(err, oldNotifObj) { - if (err) { - return callback(err); - } - - if (!oldNotifObj || newNotifObj.uniqueId !== oldNotifObj.uniqueId) { - return callback(null, true); - } - - callback(null, parseInt(newNotifObj.importance, 10) >= parseInt(oldNotifObj.importance, 10)); - }); - }); - }); - } + plugins.fireHook('action:notification.pushed', {notification: notification, uids: uids}); - function hasNotification(uniqueId, uid, callback) { - async.parallel([ - async.apply(db.isSortedSetMember, 'uid:' + uid + ':notifications:unread', uniqueId), - async.apply(db.isSortedSetMember, 'uid:' + uid + ':notifications:read', uniqueId) - ], function(err, results) { - if (err) { - return callback(err); + for(var i=0; i 0) { - nids = [nids]; - } - - async.each(nids, function(nid, next) { - Notifications.markRead(nid, uid, next); - }, callback); - }; - Notifications.markAllRead = function(uid, callback) { - db.getObjectValues('uid:' + uid + ':notifications:uniqueId:nid', function(err, nids) { + db.getSortedSetRange('uid:' + uid + ':notifications:unread', 0, 99, function(err, nids) { if (err) { return callback(err); } if (!Array.isArray(nids) || !nids.length) { - return callback(err); + return callback(); } Notifications.markReadMultiple(nids, uid, callback); }); }; - Notifications.markReadByUniqueId = function(uid, uniqueId, callback) { - async.waterfall([ - async.apply(db.getObjectField, 'uid:' + uid + ':notifications:uniqueId:nid', uniqueId), - function(nid, next) { - Notifications.markRead(nid, uid, next); - } - ], callback); - }; - Notifications.prune = function() { var start = process.hrtime(); @@ -289,12 +224,14 @@ var async = require('async'), var cutoffTime = Date.now() - week; - db.getSetMembers('notifications', function(err, nids) { + db.getSortedSetRange('notifications', 0, 499, function(err, nids) { if (err) { return winston.error(err.message); } + if (!Array.isArray(nids) || !nids.length) { + return; + } var totalNidCount = nids.length; - nids = _.sortBy(nids, function(num) { return parseInt(num, 10); }).slice(0, 500); var keys = nids.map(function(nid) { return 'notifications:' + nid; @@ -319,7 +256,7 @@ var async = require('async'), async.parallel([ function(next) { - db.setRemove('notifications', expiredNids, next); + db.sortedSetRemove('notifications', expiredNids, next); }, function(next) { db.deleteAll(keys, next); diff --git a/src/socket.io/modules.js b/src/socket.io/modules.js index 1ad5c45e12..54e6b4585d 100644 --- a/src/socket.io/modules.js +++ b/src/socket.io/modules.js @@ -224,11 +224,11 @@ function sendChatNotification(fromuid, touid, messageObj) { bodyShort: '[[notifications:new_message_from, ' + messageObj.fromUser.username + ']]', bodyLong: messageObj.content, path: nconf.get('relative_path') + '/chats/' + utils.slugify(messageObj.fromUser.username), - uniqueId: 'chat_' + fromuid + '_' + touid, + nid: 'chat_' + fromuid + '_' + touid, from: fromuid - }, function(err, nid) { - if (!err) { - notifications.push(nid, [touid]); + }, function(err, notification) { + if (!err && notification) { + notifications.push(notification, [touid]); } }); } diff --git a/src/socket.io/posts.js b/src/socket.io/posts.js index 8683906988..36ae2934a8 100644 --- a/src/socket.io/posts.js +++ b/src/socket.io/posts.js @@ -135,11 +135,11 @@ SocketPosts.sendNotificationToPostOwner = function(pid, fromuid, notification) { bodyShort: '[[' + notification + ', ' + results.username + ']]', bodyLong: results.postContent, pid: pid, - uniqueId: 'post:' + pid + ':uid:' + fromuid, + nid: 'post:' + pid + ':uid:' + fromuid, from: fromuid - }, function(err, nid) { - if (!err) { - notifications.push(nid, [postData.uid]); + }, function(err, notification) { + if (!err && notification) { + notifications.push(notification, [postData.uid]); } }); }); @@ -310,13 +310,13 @@ SocketPosts.flag = function(socket, pid, callback) { bodyShort: message, bodyLong: post.content, pid: pid, - uniqueId: 'post_flag:' + pid, + nid: 'post_flag:' + pid + ':uid:' + socket.uid, from: socket.uid - }, function(err, nid) { - if (err) { + }, function(err, notification) { + if (err || !notification) { return next(err); } - notifications.push(nid, adminGroup.members, next); + notifications.push(notification, adminGroup.members, next); }); }, function(next) { diff --git a/src/socket.io/topics.js b/src/socket.io/topics.js index 47958809f1..666b62d244 100644 --- a/src/socket.io/topics.js +++ b/src/socket.io/topics.js @@ -353,11 +353,11 @@ SocketTopics.sendNotificationToTopicOwner = function(tid, fromuid, notification) notifications.create({ bodyShort: '[[' + notification + ', ' + results.username + ']]', path: nconf.get('relative_path') + '/topic/' + results.topicData.slug, - uniqueId: 'topic:' + tid + ':uid:' + fromuid, + nid: 'topic:' + tid + ':uid:' + fromuid, from: fromuid - }, function(err, nid) { - if (!err) { - notifications.push(nid, [results.topicData.uid]); + }, function(err, notification) { + if (!err && notification) { + notifications.push(notification, [results.topicData.uid]); } }); }); diff --git a/src/socket.io/user.js b/src/socket.io/user.js index 538edac1d9..43bb934bdb 100644 --- a/src/socket.io/user.js +++ b/src/socket.io/user.js @@ -181,31 +181,33 @@ SocketUser.changePicture = function(socket, data, callback) { }; SocketUser.follow = function(socket, data, callback) { - if (socket.uid && data) { - toggleFollow('follow', socket.uid, data.uid, function(err) { + if (!socket.uid || !data) { + return; + } + + toggleFollow('follow', socket.uid, data.uid, function(err) { + if (err) { + return callback(err); + } + + user.getUserFields(socket.uid, ['username', 'userslug'], function(err, userData) { if (err) { return callback(err); } - user.getUserFields(socket.uid, ['username', 'userslug'], function(err, userData) { - if (err) { - return callback(err); + notifications.create({ + bodyShort: '[[notifications:user_started_following_you, ' + userData.username + ']]', + path: nconf.get('relative_path') + '/user/' + userData.userslug, + nid: 'follow:uid:' + socket.uid, + from: socket.uid + }, function(err, notification) { + if (!err && notification) { + notifications.push(notification, [data.uid]); } - - notifications.create({ - bodyShort: '[[notifications:user_started_following_you, ' + userData.username + ']]', - path: nconf.get('relative_path') + '/user/' + userData.userslug, - uniqueId: 'follow:uid:' + socket.uid, - from: socket.uid - }, function(err, nid) { - if (!err) { - notifications.push(nid, [data.uid]); - } - callback(err); - }); + callback(err); }); }); - } + }); }; SocketUser.unfollow = function(socket, data, callback) { diff --git a/src/topics/follow.js b/src/topics/follow.js index 0ea48620be..9c711ad440 100644 --- a/src/topics/follow.js +++ b/src/topics/follow.js @@ -56,12 +56,12 @@ module.exports = function(Topics) { bodyShort: '[[notifications:user_posted_to, ' + results.username + ', ' + results.title + ']]', bodyLong: results.postContent, pid: pid, - uniqueId: 'topic:' + tid + ':uid:' + exceptUid, + nid: 'topic:' + tid + ':uid:' + exceptUid, tid: tid, from: exceptUid - }, function(err, nid) { - if (!err) { - notifications.push(nid, followers); + }, function(err, notification) { + if (!err && notification) { + notifications.push(notification, followers); } }); }); diff --git a/src/topics/unread.js b/src/topics/unread.js index 1c44f8cc5f..b25fd83bd6 100644 --- a/src/topics/unread.js +++ b/src/topics/unread.js @@ -234,6 +234,9 @@ module.exports = function(Topics) { Topics.markTopicNotificationsRead = function(tid, uid) { user.notifications.getUnreadByField(uid, 'tid', tid, function(err, nids) { + if (err) { + return winston.error(err.stack); + } notifications.markReadMultiple(nids, uid, function() { user.notifications.pushCount(uid); }); diff --git a/src/upgrade.js b/src/upgrade.js index db91530ada..ace94ed833 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -19,7 +19,7 @@ var db = require('./database'), schemaDate, thisSchemaDate, // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema - latestSchema = Date.UTC(2014, 6, 24); + latestSchema = Date.UTC(2014, 8, 8); Upgrade.check = function(callback) { db.get('schemaDate', function(err, value) { @@ -963,6 +963,56 @@ Upgrade.upgrade = function(callback) { winston.info('[2014/7/24] Upgrading chats to sorted set - skipped'); next(); } + }, + function(next) { + thisSchemaDate = Date.UTC(2014, 8, 8); + + if (schemaDate < thisSchemaDate) { + winston.info('[2014/9/8] Deleting old notifications...'); + + async.parallel({ + uids: function(next) { + db.getSortedSetRange('users:joindate', 0, -1, next); + }, + nids: function(next) { + db.getSetMembers('notifications', next); + } + }, function(err, results) { + if (err) { + return next(err); + } + var uidKeys = results.uids.map(function(uid) { + return 'uid:' + uid + ':notifications:uniqueId:nid'; + }); + + var nidKeys = results.nids.filter(Boolean).map(function(nid) { + return 'notifications:' + nid; + }); + + async.series([ + function(next) { + db.deleteAll(nidKeys, next); + }, + function(next) { + db.deleteAll(uidKeys, next); + }, + function(next) { + db.delete('notifications', next); + } + ], function(err, results) { + if (err) { + winston.error('[2014/9/8] Error encountered while deleting notifications'); + return next(err); + } + + winston.info('[2014/9/8] Deleted old notifications'); + Upgrade.update(thisSchemaDate, next); + }); + }); + } else { + winston.info('[2014/9/8] Deleting old notifications skipped'); + next(); + } } // Add new schema updates here // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema IN LINE 22!!! diff --git a/src/user/create.js b/src/user/create.js index 80a5877d7f..c6ecc17289 100644 --- a/src/user/create.js +++ b/src/user/create.js @@ -165,10 +165,11 @@ module.exports = function(User) { bodyShort: '[[user:username_taken_workaround, ' + userData.username + ']]', bodyLong: '', image: 'brand:logo', + nid: 'username_taken:' + uid, datetime: Date.now() - }, function(err, nid) { - if (!err) { - notifications.push(nid, uid); + }, function(err, notification) { + if (!err && notification) { + notifications.push(notification, uid); } }); } diff --git a/src/user/delete.js b/src/user/delete.js index 460a8a1df5..ac800d0836 100644 --- a/src/user/delete.js +++ b/src/user/delete.js @@ -65,9 +65,6 @@ module.exports = function(User) { function(next) { db.delete('uid:' + uid + ':notifications:unread', next); }, - function(next) { - db.delete('uid:' + uid + ':notifications:uniqueId:nid', next); - }, function(next) { db.sortedSetRemove('users:joindate', uid, next); }, diff --git a/src/user/notifications.js b/src/user/notifications.js index 16feb87d19..e7dfd97f97 100644 --- a/src/user/notifications.js +++ b/src/user/notifications.js @@ -45,63 +45,61 @@ var async = require('async'), }; function getNotificationsFromSet(set, uid, start, stop, max, callback) { - db.getSortedSetRevRange(set, start, stop, function(err, uniqueIds) { + db.getSortedSetRevRange(set, start, stop, function(err, nids) { if (err) { return callback(err); } - if(!Array.isArray(uniqueIds) || !uniqueIds.length) { + if(!Array.isArray(nids) || !nids.length) { return callback(null, []); } - if (uniqueIds.length > max) { - uniqueIds.length = max; + if (nids.length > max) { + nids.length = max; } - db.getObjectFields('uid:' + uid + ':notifications:uniqueId:nid', uniqueIds, function(err, uniqueIdToNids) { + UserNotifications.getNotifications(nids, uid, function(err, notifications) { if (err) { return callback(err); } - var nidsToUniqueIds = {}; - var nids = []; - uniqueIds.forEach(function(uniqueId) { - nidsToUniqueIds[uniqueIdToNids[uniqueId]] = uniqueId; - nids.push(uniqueIdToNids[uniqueId]); - }); - - UserNotifications.getNotifications(nids, uid, function(err, notifications) { - if (err) { - return callback(err); - } + var deletedNids = []; - notifications.forEach(function(notification, index) { - if (!notification) { - if (process.env.NODE_ENV === 'development') { - winston.info('[notifications.get] nid ' + nids[index] + ' not found. Removing.'); - } - - db.sortedSetRemove(set, nidsToUniqueIds[nids[index]]); - db.deleteObjectField('uid:' + uid + ':notifications:uniqueId:nid', nidsToUniqueIds[nids[index]]); + notifications.forEach(function(notification, index) { + if (!notification) { + if (process.env.NODE_ENV === 'development') { + winston.info('[notifications.get] nid ' + nids[index] + ' not found. Removing.'); } - }); - callback(null, notifications); + if (nids[index]) { + deletedNids.push(nids[index]); + } + } }); + + if (deletedNids.length) { + db.sortedSetRemove(set, deletedNids); + } + + callback(null, notifications); }); }); } - UserNotifications.getAll = function(uid, limit, callback) { - if (!limit || parseInt(limit, 10) <= 0) { - limit = 25; - } - - db.getObjectValues('uid:' + uid + ':notifications:uniqueId:nid', function(err, nids) { + UserNotifications.getAll = function(uid, count, callback) { + async.parallel({ + unread: function(next) { + db.getSortedSetRevRange('uid:' + uid + ':notifications:unread', 0, count, next); + }, + read: function(next) { + db.getSortedSetRevRange('uid:' + uid + ':notifications:read', 0, count, next); + } + }, function(err, results) { if (err) { return callback(err); } + var nids = results.unread.concat(results.read); UserNotifications.getNotifications(nids, uid, function(err, notifs) { if (err) { return callback(err); @@ -122,11 +120,7 @@ var async = require('async'), return callback(err); } - var uniqueIds = notifications.map(function(notification) { - return notification ? notification.uniqueId : null; - }); - - db.isSortedSetMembers('uid:' + uid + ':notifications:read', uniqueIds, function(err, hasRead) { + db.isSortedSetMembers('uid:' + uid + ':notifications:read', nids, function(err, hasRead) { if (err) { return callback(err); } @@ -159,7 +153,8 @@ var async = require('async'), }; function generatePostPaths(pids, uid, callback) { - var postKeys = pids.filter(Boolean).map(function(pid) { + pids = pids.filter(Boolean); + var postKeys = pids.map(function(pid) { return 'post:' + pid; }); @@ -193,6 +188,7 @@ var async = require('async'), pidToPaths[pid] = nconf.get('relative_path') + '/topic/' + slug + '/' + postIndex; } }); + callback(null, pidToPaths); }); }); @@ -202,26 +198,16 @@ var async = require('async'), var now = Date.now(), yesterday = now - (1000*60*60*24); // Approximate, can be more or less depending on time changes, makes no difference really. - db.getSortedSetRangeByScore('uid:' + uid + ':notifications:unread', 0, 20, yesterday, now, function(err, uniqueIds) { + db.getSortedSetRangeByScore('uid:' + uid + ':notifications:unread', 0, 20, yesterday, now, function(err, nids) { if (err) { return callback(err); } - if (!Array.isArray(uniqueIds) || !uniqueIds.length) { + if (!Array.isArray(nids) || !nids.length) { return callback(null, []); } - db.getObjectFields('uid:' + uid + ':notifications:uniqueId:nid', uniqueIds, function(err, uniqueIdToNids) { - if (err) { - return callback(err); - } - - var nids = Object.keys(uniqueIdToNids).map(function(uniqueId) { - return uniqueIdToNids[uniqueId]; - }); - - UserNotifications.getNotifications(nids, uid, callback); - }); + UserNotifications.getNotifications(nids, uid, callback); }); }; @@ -230,46 +216,35 @@ var async = require('async'), }; UserNotifications.getUnreadByField = function(uid, field, value, callback) { - db.getSortedSetRange('uid:' + uid + ':notifications:unread', 0, -1, function(err, uniqueIds) { + db.getSortedSetRange('uid:' + uid + ':notifications:unread', 0, -1, function(err, nids) { if (err) { return callback(err); } - if (!Array.isArray(uniqueIds) || !uniqueIds.length) { + if (!Array.isArray(nids) || !nids.length) { return callback(null, []); } - db.getObjectFields('uid:' + uid + ':notifications:uniqueId:nid', uniqueIds, function(err, uniqueIdsToNids) { + UserNotifications.getNotifications(nids, uid, function(err, notifications) { if (err) { return callback(err); } - var nids = Object.keys(uniqueIdsToNids).map(function(uniqueId) { - return uniqueIdsToNids[uniqueId]; + nids = notifications.filter(function(notification) { + return notification && notification[field] !== value.toString(); + }).map(function(notification) { + return notification.nid; }); - UserNotifications.getNotifications(nids, uid, function(err, notifications) { - if (err) { - return callback(err); - } - - notifications = notifications.filter(function(notification) { - return notification && notification[field] !== value.toString(); - }).map(function(notification) { - return notification.nid; - }); - - callback(null, nids); - }); + callback(null, nids); }); }); }; UserNotifications.sendPostNotificationToFollowers = function(uid, tid, pid) { - return; db.getSetMembers('followers:' + uid, function(err, followers) { - if (err || !followers || !followers.length) { + if (err || !Array.isArray(followers) || !followers.length) { return; } @@ -296,23 +271,30 @@ var async = require('async'), return !results.topicFollowers[index]; }); - notifications.create({ - bodyShort: '[[notifications:user_posted_to, ' + results.username + ', ' + results.topic.title + ']]', - bodyLong: results.postContent, - pid: pid, - uniqueId: 'topic:' + tid + ':uid:' + uid, - tid: tid, - from: uid - }, function(err, nid) { - if (err) { + if (!followers.length) { + return; + } + + async.filter(followers, function(uid, next) { + privileges.categories.can('read', results.topic.cid, uid, function(err, canRead) { + next(!err && canRead); + }); + }, function(followers) { + if (!followers.length) { return; } - async.filter(followers, function(uid, next) { - privileges.categories.can('read', results.topic.cid, uid, function(err, canRead) { - next(!err && canRead); - }); - }, function(followers){ - notifications.push(nid, followers); + + notifications.create({ + bodyShort: '[[notifications:user_posted_to, ' + results.username + ', ' + results.topic.title + ']]', + bodyLong: results.postContent, + pid: pid, + nid: 'topic:' + tid + ':uid:' + uid, + tid: tid, + from: uid + }, function(err, notification) { + if (!err && notification) { + notifications.push(notification, followers); + } }); }); });