From a61d9472956ca4d23bc70919ff32fb628c38433d Mon Sep 17 00:00:00 2001 From: Ben Lubar Date: Fri, 25 Mar 2016 22:18:49 -0500 Subject: [PATCH 1/3] allow filtering /unread to only topics that have not yet been seen --- src/controllers/unread.js | 5 +++-- src/topics/unread.js | 27 +++++++++++++++++++++------ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/controllers/unread.js b/src/controllers/unread.js index d81774661f..d07c2d16a1 100644 --- a/src/controllers/unread.js +++ b/src/controllers/unread.js @@ -16,6 +16,7 @@ unreadController.get = function(req, res, next) { var stop = (parseInt(meta.config.topicsPerList, 10) || 20) - 1; var results; var cid = req.query.cid; + var seen = !req.query.unseen; async.waterfall([ function(next) { @@ -24,7 +25,7 @@ unreadController.get = function(req, res, next) { user.getWatchedCategories(req.uid, next); }, unreadTopics: function(next) { - topics.getUnreadTopics(cid, req.uid, 0, stop, next); + topics.getUnreadTopics(cid, req.uid, 0, stop, seen, next); } }, next); }, @@ -64,7 +65,7 @@ unreadController.get = function(req, res, next) { unreadController.unreadTotal = function(req, res, next) { - topics.getTotalUnread(req.uid, function (err, data) { + topics.getTotalUnread(req.uid, !req.query.unseen, function (err, data) { if (err) { return next(err); } diff --git a/src/topics/unread.js b/src/topics/unread.js index 28846640b7..713f4732fa 100644 --- a/src/topics/unread.js +++ b/src/topics/unread.js @@ -14,13 +14,23 @@ var utils = require('../../public/src/utils'); module.exports = function(Topics) { - Topics.getTotalUnread = function(uid, callback) { - Topics.getUnreadTids(0, uid, 0, 99, function(err, tids) { + Topics.getTotalUnread = function(uid, allowSeen, callback) { + if (!callback) { + callback = allowSeen; + allowSeen = true; + } + + Topics.getUnreadTids(0, uid, 0, 99, allowSeen, function(err, tids) { callback(err, tids ? tids.length : 0); }); }; - Topics.getUnreadTopics = function(cid, uid, start, stop, callback) { + Topics.getUnreadTopics = function(cid, uid, start, stop, allowSeen, callback) { + if (!callback) { + callback = allowSeen; + allowSeen = true; + } + var unreadTopics = { showSelect: true, @@ -30,7 +40,7 @@ module.exports = function(Topics) { async.waterfall([ function(next) { - Topics.getUnreadTids(cid, uid, start, stop, next); + Topics.getUnreadTids(cid, uid, start, stop, allowSeen, next); }, function(tids, next) { if (!tids.length) { @@ -54,7 +64,12 @@ module.exports = function(Topics) { return Date.now() - (parseInt(meta.config.unreadCutoff, 10) || 2) * 86400000; }; - Topics.getUnreadTids = function(cid, uid, start, stop, callback) { + Topics.getUnreadTids = function(cid, uid, start, stop, allowSeen, callback) { + if (!callback) { + callback = allowSeen; + allowSeen = true; + } + uid = parseInt(uid, 10); if (uid === 0) { return callback(null, []); @@ -95,7 +110,7 @@ module.exports = function(Topics) { }); var tids = results.recentTids.filter(function(recentTopic) { - return !userRead[recentTopic.value] || recentTopic.score > userRead[recentTopic.value]; + return !userRead[recentTopic.value] || allowSeen && recentTopic.score > userRead[recentTopic.value]; }).map(function(topic) { return topic.value; }).filter(function(tid, index, array) { From 217f0c5652cd313db5489874791c5c5b4d2b8f8a Mon Sep 17 00:00:00 2001 From: Ben Lubar Date: Fri, 8 Apr 2016 13:42:47 -0500 Subject: [PATCH 2/3] Switch to a named filter for unread to allow future expansion. Don't use the query string as it is not passed to infinite scroll. --- src/controllers/unread.js | 18 +++++++++++++++--- src/routes/api.js | 2 +- src/routes/index.js | 2 +- src/topics/unread.js | 29 +++++++++++++++++------------ 4 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/controllers/unread.js b/src/controllers/unread.js index d07c2d16a1..e7aacb2465 100644 --- a/src/controllers/unread.js +++ b/src/controllers/unread.js @@ -12,11 +12,17 @@ var plugins = require('../plugins'); var unreadController = {}; +var validFilter = {'': true, 'new': true}; + unreadController.get = function(req, res, next) { var stop = (parseInt(meta.config.topicsPerList, 10) || 20) - 1; var results; var cid = req.query.cid; - var seen = !req.query.unseen; + var filter = req.params.filter || ''; + + if (!validFilter[filter]) { + return next(); + } async.waterfall([ function(next) { @@ -25,7 +31,7 @@ unreadController.get = function(req, res, next) { user.getWatchedCategories(req.uid, next); }, unreadTopics: function(next) { - topics.getUnreadTopics(cid, req.uid, 0, stop, seen, next); + topics.getUnreadTopics(cid, req.uid, 0, stop, filter, next); } }, next); }, @@ -65,7 +71,13 @@ unreadController.get = function(req, res, next) { unreadController.unreadTotal = function(req, res, next) { - topics.getTotalUnread(req.uid, !req.query.unseen, function (err, data) { + var filter = req.params.filter || ''; + + if (!validFilter[filter]) { + return next(); + } + + topics.getTotalUnread(req.uid, filter, function (err, data) { if (err) { return next(err); } diff --git a/src/routes/api.js b/src/routes/api.js index 34cf142b37..4806663460 100644 --- a/src/routes/api.js +++ b/src/routes/api.js @@ -22,7 +22,7 @@ module.exports = function(app, middleware, controllers) { router.get('/categories/:cid/moderators', controllers.api.getModerators); router.get('/recent/posts/:term?', controllers.api.getRecentPosts); - router.get('/unread/total', middleware.authenticate, controllers.unread.unreadTotal); + router.get('/unread/:filter?/total', middleware.authenticate, controllers.unread.unreadTotal); router.get('/topic/teaser/:topic_id', controllers.topics.teaser); var multipart = require('connect-multiparty'); diff --git a/src/routes/index.js b/src/routes/index.js index a80eb2e46a..539f26b3db 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -55,7 +55,7 @@ function categoryRoutes(app, middleware, controllers) { setupPageRoute(app, '/categories', middleware, [], controllers.categories.list); setupPageRoute(app, '/popular/:term?', middleware, [], controllers.popular.get); setupPageRoute(app, '/recent', middleware, [], controllers.recent.get); - setupPageRoute(app, '/unread', middleware, [middleware.authenticate], controllers.unread.get); + setupPageRoute(app, '/unread/:filter?', middleware, [middleware.authenticate], controllers.unread.get); setupPageRoute(app, '/category/:category_id/:slug/:topic_index', middleware, [], controllers.category.get); setupPageRoute(app, '/category/:category_id/:slug?', middleware, [], controllers.category.get); diff --git a/src/topics/unread.js b/src/topics/unread.js index 713f4732fa..63941cd01a 100644 --- a/src/topics/unread.js +++ b/src/topics/unread.js @@ -14,21 +14,21 @@ var utils = require('../../public/src/utils'); module.exports = function(Topics) { - Topics.getTotalUnread = function(uid, allowSeen, callback) { + Topics.getTotalUnread = function(uid, filter, callback) { if (!callback) { - callback = allowSeen; - allowSeen = true; + callback = filter; + filter = ''; } - Topics.getUnreadTids(0, uid, 0, 99, allowSeen, function(err, tids) { + Topics.getUnreadTids(0, uid, 0, 99, filter, function(err, tids) { callback(err, tids ? tids.length : 0); }); }; - Topics.getUnreadTopics = function(cid, uid, start, stop, allowSeen, callback) { + Topics.getUnreadTopics = function(cid, uid, start, stop, filter, callback) { if (!callback) { - callback = allowSeen; - allowSeen = true; + callback = filter; + filter = ''; } @@ -40,7 +40,7 @@ module.exports = function(Topics) { async.waterfall([ function(next) { - Topics.getUnreadTids(cid, uid, start, stop, allowSeen, next); + Topics.getUnreadTids(cid, uid, start, stop, filter, next); }, function(tids, next) { if (!tids.length) { @@ -64,10 +64,10 @@ module.exports = function(Topics) { return Date.now() - (parseInt(meta.config.unreadCutoff, 10) || 2) * 86400000; }; - Topics.getUnreadTids = function(cid, uid, start, stop, allowSeen, callback) { + Topics.getUnreadTids = function(cid, uid, start, stop, filter, callback) { if (!callback) { - callback = allowSeen; - allowSeen = true; + callback = filter; + filter = ''; } uid = parseInt(uid, 10); @@ -110,7 +110,12 @@ module.exports = function(Topics) { }); var tids = results.recentTids.filter(function(recentTopic) { - return !userRead[recentTopic.value] || allowSeen && recentTopic.score > userRead[recentTopic.value]; + switch (filter) { + default: + return !userRead[recentTopic.value] || recentTopic.score > userRead[recentTopic.value]; + case 'new': + return !userRead[recentTopic.value]; + } }).map(function(topic) { return topic.value; }).filter(function(tid, index, array) { From 114e957b0f4a1ae50ecd4d84bb44fc64d6874dc5 Mon Sep 17 00:00:00 2001 From: Ben Lubar Date: Fri, 8 Apr 2016 13:54:51 -0500 Subject: [PATCH 3/3] add client-side unread count support for the "new" filter --- public/src/client/footer.js | 7 +++++++ src/socket.io/user.js | 1 + 2 files changed, 8 insertions(+) diff --git a/public/src/client/footer.js b/public/src/client/footer.js index d8e54d4fc3..f1d05d4bfe 100644 --- a/public/src/client/footer.js +++ b/public/src/client/footer.js @@ -13,6 +13,12 @@ define('forum/footer', ['notifications', 'chat', 'components', 'translator'], fu .attr('data-content', count > 99 ? '99+' : count); } + function updateUnreadNewTopicCount(count) { + $('#unread-new-count i') + .toggleClass('unread-count', count > 0) + .attr('data-content', count > 99 ? '99+' : count); + } + function updateUnreadChatCount(count) { components.get('chat/icon') .toggleClass('unread-count', count > 0) @@ -61,6 +67,7 @@ define('forum/footer', ['notifications', 'chat', 'components', 'translator'], fu } updateUnreadTopicCount(data.unreadTopicCount); + updateUnreadNewTopicCount(data.unreadNewTopicCount); updateUnreadChatCount(data.unreadChatCount); Notifications.updateNotifCount(data.unreadNotificationCount); }); diff --git a/src/socket.io/user.js b/src/socket.io/user.js index 06acacd520..8ca3e4231e 100644 --- a/src/socket.io/user.js +++ b/src/socket.io/user.js @@ -245,6 +245,7 @@ SocketUser.getUnreadCounts = function(socket, data, callback) { } async.parallel({ unreadTopicCount: async.apply(topics.getTotalUnread, socket.uid), + unreadNewTopicCount: async.apply(topics.getTotalUnread, socket.uid, 'new'), unreadChatCount: async.apply(messaging.getUnreadCount, socket.uid), unreadNotificationCount: async.apply(user.notifications.getUnreadCount, socket.uid) }, callback);