diff --git a/public/language/en_GB/category.json b/public/language/en_GB/category.json index db742469f9..22d72c3064 100644 --- a/public/language/en_GB/category.json +++ b/public/language/en_GB/category.json @@ -5,5 +5,6 @@ "browsing": "browsing", "no_replies": "No one has replied", - "share_this_category": "Share this category" + "share_this_category": "Share this category", + "ignore": "Ignore" } diff --git a/public/src/forum/category.js b/public/src/forum/category.js index d62719fb9d..21cceb55aa 100644 --- a/public/src/forum/category.js +++ b/public/src/forum/category.js @@ -48,8 +48,26 @@ define('forum/category', ['composer', 'forum/pagination', 'forum/infinitescroll' } }); }); + + handleIgnoreWatch(cid); }; + function handleIgnoreWatch(cid) { + $('.watch, .ignore').on('click', function() { + $this = $(this); + var command = $this.hasClass('watch') ? 'watch' : 'ignore'; + + socket.emit('categories.' + command, cid, function(err) { + if (err) { + return app.alertError(err.message); + } + + $('.watch').toggleClass('hidden', command === 'watch'); + $('.ignore').toggleClass('hidden', command === 'ignore'); + }); + }) + } + Category.toTop = function() { navigator.scrollTop(0); }; diff --git a/src/categories.js b/src/categories.js index 2245517d5b..39affb9d1e 100644 --- a/src/categories.js +++ b/src/categories.js @@ -83,6 +83,9 @@ var db = require('./database'), }, pageCount: function(next) { Categories.getPageCount(cid, uid, next); + }, + isIgnored: function(next) { + Categories.isIgnored([cid], uid, next); } }, function(err, results) { if(err) { @@ -92,6 +95,7 @@ var db = require('./database'), category.topics = results.topics.topics; category.nextStart = results.topics.nextStart; category.pageCount = results.pageCount; + category.isIgnored = results.isIgnored[0]; category.topic_row_size = 'col-md-9'; plugins.fireHook('filter:category.get', category, uid, callback); @@ -141,6 +145,19 @@ var db = require('./database'), ], callback); }; + Categories.isIgnored = function(cids, uid, callback) { + user.getIgnoredCategories(uid, function(err, ignoredCids) { + if (err) { + return callback(err); + } + + cids = cids.map(function(cid) { + return ignoredCids.indexOf(cid.toString()) !== -1; + }); + callback(null, cids); + }); + }; + Categories.getTopicIds = function(cid, start, stop, callback) { db.getSortedSetRevRange('categories:' + cid + ':tid', start, stop, callback); }; diff --git a/src/socket.io/categories.js b/src/socket.io/categories.js index 554ea486a9..d92f18ca19 100644 --- a/src/socket.io/categories.js +++ b/src/socket.io/categories.js @@ -5,6 +5,7 @@ var async = require('async'), categories = require('../categories'), privileges = require('../privileges'), user = require('../user'), + topics = require('../topics'), websockets = require('./index'), SocketCategories = {}; @@ -83,4 +84,22 @@ SocketCategories.getCategoriesByPrivilege = function(socket, privilege, callback categories.getCategoriesByPrivilege(socket.uid, privilege, callback); }; +SocketCategories.watch = function(socket, cid, callback) { + user.watchCategory(socket.uid, cid, function(err) { + if (err) { + return callback(err); + } + topics.pushUnreadCount(socket.uid, callback); + }); +}; + +SocketCategories.ignore = function(socket, cid, callback) { + user.ignoreCategory(socket.uid, cid, function(err) { + if (err) { + return callback(err); + } + topics.pushUnreadCount(socket.uid, callback); + }); +}; + module.exports = SocketCategories; diff --git a/src/topics/unread.js b/src/topics/unread.js index 0ff51edc21..41385318a4 100644 --- a/src/topics/unread.js +++ b/src/topics/unread.js @@ -27,7 +27,11 @@ module.exports = function(Topics) { topics: [] }; - function sendUnreadTopics(tids) { + function sendUnreadTopics(tids, callback) { + if (!tids.length) { + return callback(null, unreadTopics); + } + Topics.getTopicsByTids(tids, uid, function(err, topicData) { if (err) { return callback(err); @@ -55,11 +59,7 @@ module.exports = function(Topics) { return callback(err); } - if (unreadTids.length) { - sendUnreadTopics(unreadTids); - } else { - callback(null, unreadTopics); - } + sendUnreadTopics(unreadTids, callback); }); }; @@ -75,50 +75,86 @@ module.exports = function(Topics) { var count = 0; if (stop === -1) { count = Infinity; - } else { + } else { count = stop - start + 1; } - async.whilst(function() { - return unreadTids.length < count && !done; - }, function(next) { - Topics.getLatestTids(start, stop, 'month', function(err, tids) { - if (err) { - return next(err); - } - if (tids && !tids.length) { - done = true; - return next(); - } + user.getIgnoredCategories(uid, function(err, ignoredCids) { + if (err) { + return callback(err); + } - Topics.hasReadTopics(tids, uid, function(err, read) { + async.whilst(function() { + return unreadTids.length < count && !done; + }, function(next) { + Topics.getLatestTids(start, stop, 'month', function(err, tids) { if (err) { return next(err); } - var newtids = tids.filter(function(tid, index) { - return !read[index]; - }); + if (tids && !tids.length) { + done = true; + return next(); + } - privileges.topics.filter('read', newtids, uid, function(err, newtids) { + Topics.hasReadTopics(tids, uid, function(err, read) { if (err) { return next(err); } - unreadTids.push.apply(unreadTids, newtids); - start = stop + 1; - stop = start + 19; + var newtids = tids.filter(function(tid, index) { + return !read[index]; + }); + + privileges.topics.filter('read', newtids, uid, function(err, newtids) { + if (err) { + return next(err); + } + + filterTopicsFromIgnoredCategories(newtids, ignoredCids, function(err, newtids) { + if (err) { + return next(err); + } - next(); + unreadTids.push.apply(unreadTids, newtids); + + start = stop + 1; + stop = start + 19; + + next(); + }); + }); }); }); + }, function(err) { + callback(err, unreadTids.slice(0, count)); }); - }, function(err) { - callback(err, unreadTids.slice(0, count)); + }); }; + function filterTopicsFromIgnoredCategories(tids, ignoredCids, callback) { + if (!Array.isArray(ignoredCids) || !ignoredCids.length || !tids.length) { + return callback(null, tids); + } + + var keys = tids.map(function(tid) { + return 'topic:' + tid; + }); + db.getObjectsFields(keys, ['tid', 'cid'], function(err, topics) { + if (err) { + return callback(err); + } + topics = topics.filter(function(topic) { + return topic && ignoredCids.indexOf(topic.cid.toString()) === -1; + }).map(function(topic) { + return topic.tid; + }); + + callback(null, topics); + }); + } Topics.pushUnreadCount = function(uids, callback) { var websockets = require('./../socket.io'); diff --git a/src/user.js b/src/user.js index 7abee910cb..16ce0fe537 100644 --- a/src/user.js +++ b/src/user.js @@ -438,5 +438,17 @@ var }); }; + User.getIgnoredCategories = function(uid, callback) { + db.getSortedSetRange('uid:' + uid + ':ignored:cids', 0, -1, callback); + }; + + User.ignoreCategory = function(uid, cid, callback) { + db.sortedSetAdd('uid:' + uid + ':ignored:cids', Date.now(), cid, callback); + }; + + User.watchCategory = function(uid, cid, callback) { + db.sortedSetRemove('uid:' + uid + ':ignored:cids', cid, callback); + }; + }(exports));