mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-12-24 01:10:31 +01:00
part 1 of notif refactor
This commit is contained in:
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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<scores.length; ++i) {
|
||||
bulk.find({_key: key, value: values[i]}).upsert().updateOne({$set: {score: scores[i], value: values[i]}});
|
||||
}
|
||||
|
||||
bulk.execute(function(err, result) {
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
|
||||
module.sortedSetsAdd = function(keys, score, value, callback) {
|
||||
callback = callback || helpers.noop;
|
||||
|
||||
@@ -40,9 +62,12 @@ module.exports = function(db, module) {
|
||||
|
||||
module.sortedSetRemove = function(key, value, callback) {
|
||||
callback = callback || helpers.noop;
|
||||
value = helpers.valueToString(value);
|
||||
if (!Array.isArray(value)) {
|
||||
value = [value];
|
||||
}
|
||||
value = value.map(helpers.valueToString);
|
||||
|
||||
db.collection('objects').remove({_key: key, value: value}, function(err) {
|
||||
db.collection('objects').remove({_key: key, value: {$in: value}}, function(err) {
|
||||
callback(err);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -3,11 +3,27 @@
|
||||
module.exports = function(redisClient, module) {
|
||||
module.sortedSetAdd = function(key, score, value, callback) {
|
||||
callback = callback || function() {};
|
||||
if (Array.isArray(score) && Array.isArray(value)) {
|
||||
return sortedSetAddMulti(key, score, value, callback);
|
||||
}
|
||||
redisClient.zadd(key, score, value, function(err) {
|
||||
callback(err);
|
||||
});
|
||||
};
|
||||
|
||||
function sortedSetAddMulti(key, scores, values, callback) {
|
||||
if (scores.length !== values.length) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
}
|
||||
var multi = redisClient.multi();
|
||||
for(var i=0; i<scores.lenth; ++i) {
|
||||
multi.zadd(key, scores[i], values[i]);
|
||||
}
|
||||
multi.exec(function(err, result) {
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
|
||||
module.sortedSetsAdd = function(keys, score, value, callback) {
|
||||
callback = callback || function() {};
|
||||
var multi = redisClient.multi();
|
||||
@@ -23,7 +39,14 @@ module.exports = function(redisClient, module) {
|
||||
|
||||
module.sortedSetRemove = function(key, value, callback) {
|
||||
callback = callback || function() {};
|
||||
redisClient.zrem(key, value, function(err) {
|
||||
if (!Array.isArray(value)) {
|
||||
value = [value];
|
||||
}
|
||||
var multi = redisClient.multi();
|
||||
for(var i=0; i<value.length; ++i) {
|
||||
multi.zrem(key, value[i]);
|
||||
}
|
||||
multi.exec(function(err) {
|
||||
callback(err);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -102,8 +102,7 @@ var db = require('./database'),
|
||||
getMessages(mids, fromuid, touid, isNew, callback);
|
||||
});
|
||||
|
||||
// Mark any chat notifications pertaining to this chat as read
|
||||
notifications.markReadByUniqueId(fromuid, 'chat_' + touid + '_' + fromuid, function(err) {
|
||||
notifications.markRead('chat_' + touid + '_' + fromuid, fromuid, function(err) {
|
||||
if (err) {
|
||||
winston.error('[messaging] Could not mark notifications related to this chat as read: ' + err.message);
|
||||
}
|
||||
|
||||
@@ -53,8 +53,12 @@ var async = require('async'),
|
||||
notification.text = S(notification.text).escapeHTML().s;
|
||||
}
|
||||
|
||||
if (notification.bodyShort) {
|
||||
notification.bodyShort = S(notification.bodyShort).escapeHTML().s;
|
||||
}
|
||||
if (notification.bodyLong) {
|
||||
notification.bodyLong = S(notification.bodyLong).escapeHTML().s;
|
||||
}
|
||||
|
||||
if (notification.from && !notification.image) {
|
||||
User.getUserField(notification.from, 'picture', function(err, picture) {
|
||||
@@ -80,203 +84,134 @@ var async = require('async'),
|
||||
};
|
||||
|
||||
Notifications.create = function(data, callback) {
|
||||
// Add default values to data Object if not already set
|
||||
var defaults = {
|
||||
bodyShort: '',
|
||||
bodyLong: '',
|
||||
importance: 5,
|
||||
datetime: Date.now(),
|
||||
uniqueId: utils.generateUUID()
|
||||
};
|
||||
|
||||
for(var v in defaults) {
|
||||
if (defaults.hasOwnProperty(v) && !data[v]) {
|
||||
data[v] = defaults[v];
|
||||
if (!data.nid) {
|
||||
return callback(new Error('no-notification-id'));
|
||||
}
|
||||
}
|
||||
|
||||
// Backwards compatibility for old notification schema
|
||||
// Remove this block for NodeBB v0.6.0
|
||||
if (data.hasOwnProperty('text')) {
|
||||
data.bodyShort = data.text;
|
||||
data.bodyLong = '';
|
||||
delete data.text;
|
||||
}
|
||||
|
||||
db.incrObjectField('global', 'nextNid', function(err, nid) {
|
||||
data.importance = data.importance || 5;
|
||||
db.getObject('notifications:' + data.nid, function(err, oldNotification) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
data.nid = nid;
|
||||
db.setAdd('notifications', nid);
|
||||
db.setObject('notifications:' + nid, data, function(err) {
|
||||
callback(err, nid);
|
||||
if (oldNotification) {
|
||||
if (parseInt(oldNotification.pid, 10) === parseInt(data.pid, 10) && parseInt(oldNotification.importance, 10) > 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);
|
||||
}
|
||||
var unreadKeys = [];
|
||||
var readKeys = [];
|
||||
|
||||
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);
|
||||
}
|
||||
uids.filter(Boolean).forEach(function(uid) {
|
||||
unreadKeys.push('uid:' + uid + ':notifications:unread');
|
||||
readKeys.push('uid:' + uid + ':notifications:read');
|
||||
});
|
||||
|
||||
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(next) {
|
||||
db.sortedSetsAdd(unreadKeys, notification.datetime, notification.nid, next);
|
||||
},
|
||||
function(next) {
|
||||
db.sortedSetsRemove(readKeys, notification.nid, next);
|
||||
}
|
||||
], function(err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
User.notifications.getUnreadCount(uid, function(err, count) {
|
||||
if (!err) {
|
||||
websockets.in('uid_' + uid).emit('event:new_notification', notif_data, count);
|
||||
}
|
||||
});
|
||||
plugins.fireHook('action:notification.pushed', {notification: notification, uids: uids});
|
||||
|
||||
// Plugins
|
||||
notif_data.uid = uid;
|
||||
plugins.fireHook('action:notification.pushed', notif_data);
|
||||
next();
|
||||
});
|
||||
});
|
||||
}, callback);
|
||||
for(var i=0; i<uids.length; ++i) {
|
||||
websockets.in('uid_' + uids[i]).emit('event:new_notification', notification);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function shouldPush(uid, newNotifObj, callback) {
|
||||
if (!newNotifObj) {
|
||||
return callback(null, false);
|
||||
}
|
||||
|
||||
hasNotification(newNotifObj.uniqueId, uid, function(err, hasNotification) {
|
||||
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));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
callback(null, results[0] || results[1]);
|
||||
});
|
||||
}
|
||||
|
||||
Notifications.pushGroup = function(nid, groupName, callback) {
|
||||
Notifications.pushGroup = function(notification, groupName, callback) {
|
||||
callback = callback || function() {};
|
||||
groups.get(groupName, {}, function(err, groupObj) {
|
||||
if (err || !groupObj || !Array.isArray(groupObj.members) || !groupObj.members.length) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
Notifications.push(nid, groupObj.members, callback);
|
||||
Notifications.push(notification, groupObj.members, callback);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
Notifications.markRead = function(nid, uid, callback) {
|
||||
callback = callback || function() {};
|
||||
if (!parseInt(uid, 10) || !parseInt(nid, 10)) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
db.getObjectFields('notifications:' + nid, ['uniqueId', 'datetime'], function(err, notificationData) {
|
||||
if (err || !notificationData) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
async.parallel([
|
||||
async.apply(db.sortedSetRemove, 'uid:' + uid + ':notifications:unread', notificationData.uniqueId),
|
||||
async.apply(db.sortedSetAdd, 'uid:' + uid + ':notifications:read', notificationData.datetime, notificationData.uniqueId)
|
||||
], callback);
|
||||
});
|
||||
Notifications.markReadMultiple([nid], uid, callback);
|
||||
};
|
||||
|
||||
Notifications.markReadMultiple = function(nids, uid, callback) {
|
||||
callback = callback || function() {};
|
||||
if (!nids) {
|
||||
return callback(null);
|
||||
}
|
||||
if (!Array.isArray(nids) && parseInt(nids, 10) > 0) {
|
||||
nids = [nids];
|
||||
if (!Array.isArray(nids) || !nids.length) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
async.each(nids, function(nid, next) {
|
||||
Notifications.markRead(nid, uid, next);
|
||||
}, callback);
|
||||
var notificationKeys = nids.map(function(nid) {
|
||||
return 'notifications:' + nid;
|
||||
});
|
||||
|
||||
db.getObjectsFields(notificationKeys, ['datetime'], function(err, notificationData) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
var datetimes = notificationData.map(function(notification) {
|
||||
return notification && notification.datetime;
|
||||
});
|
||||
|
||||
async.parallel([
|
||||
function(next) {
|
||||
db.sortedSetRemove('uid:' + uid + ':notifications:unread', nids, next);
|
||||
},
|
||||
function(next) {
|
||||
db.sortedSetAdd('uid:' + uid + ':notifications:read', datetimes, nids, 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);
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -181,7 +181,10 @@ SocketUser.changePicture = function(socket, data, callback) {
|
||||
};
|
||||
|
||||
SocketUser.follow = function(socket, data, callback) {
|
||||
if (socket.uid && data) {
|
||||
if (!socket.uid || !data) {
|
||||
return;
|
||||
}
|
||||
|
||||
toggleFollow('follow', socket.uid, data.uid, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
@@ -195,17 +198,16 @@ SocketUser.follow = function(socket, data, callback) {
|
||||
notifications.create({
|
||||
bodyShort: '[[notifications:user_started_following_you, ' + userData.username + ']]',
|
||||
path: nconf.get('relative_path') + '/user/' + userData.userslug,
|
||||
uniqueId: 'follow:uid:' + socket.uid,
|
||||
nid: 'follow:uid:' + socket.uid,
|
||||
from: socket.uid
|
||||
}, function(err, nid) {
|
||||
if (!err) {
|
||||
notifications.push(nid, [data.uid]);
|
||||
}, function(err, notification) {
|
||||
if (!err && notification) {
|
||||
notifications.push(notification, [data.uid]);
|
||||
}
|
||||
callback(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
SocketUser.unfollow = function(socket, data, callback) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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!!!
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
|
||||
@@ -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) {
|
||||
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]]);
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
db.getObjectValues('uid:' + uid + ':notifications:uniqueId:nid', function(err, nids) {
|
||||
}, 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,27 +198,17 @@ 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.getUnreadCount = function(uid, callback) {
|
||||
@@ -230,30 +216,21 @@ 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) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
var nids = Object.keys(uniqueIdsToNids).map(function(uniqueId) {
|
||||
return uniqueIdsToNids[uniqueId];
|
||||
});
|
||||
|
||||
UserNotifications.getNotifications(nids, uid, function(err, notifications) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
notifications = notifications.filter(function(notification) {
|
||||
nids = notifications.filter(function(notification) {
|
||||
return notification && notification[field] !== value.toString();
|
||||
}).map(function(notification) {
|
||||
return notification.nid;
|
||||
@@ -262,14 +239,12 @@ var async = require('async'),
|
||||
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){
|
||||
notifications.push(nid, followers);
|
||||
}, function(followers) {
|
||||
if (!followers.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user