From fb47bf38893a0ccf4ee61dc978381722c3cad158 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 3 Nov 2016 13:06:21 +0300 Subject: [PATCH] unread/recent changes closes #4774 --- package.json | 4 +- public/src/client/recent.js | 10 ++--- src/controllers/helpers.js | 47 ++++++++++++++++++++++ src/controllers/recent.js | 78 ++++++++++++++++++++++++++++++++++++- src/controllers/unread.js | 53 ++----------------------- src/routes/index.js | 2 +- src/topics/follow.js | 24 ++++++++++++ src/topics/unread.js | 25 ++++++------ 8 files changed, 171 insertions(+), 72 deletions(-) diff --git a/package.json b/package.json index 537079b392..18001516fe 100644 --- a/package.json +++ b/package.json @@ -63,8 +63,8 @@ "nodebb-plugin-spam-be-gone": "0.4.10", "nodebb-rewards-essentials": "0.0.9", "nodebb-theme-lavender": "3.0.15", - "nodebb-theme-persona": "4.1.79", - "nodebb-theme-vanilla": "5.1.52", + "nodebb-theme-persona": "4.1.80", + "nodebb-theme-vanilla": "5.1.53", "nodebb-widget-essentials": "2.0.13", "nodemailer": "2.6.4", "nodemailer-sendmail-transport": "1.0.0", diff --git a/public/src/client/recent.js b/public/src/client/recent.js index 15b37eaf19..346eaaaaa0 100644 --- a/public/src/client/recent.js +++ b/public/src/client/recent.js @@ -43,7 +43,7 @@ define('forum/recent', ['forum/infinitescroll', 'components'], function (infinit return; } - if (ajaxify.data.selectedFilter && ajaxify.data.selectedFilter.url === 'unread/watched') { + if (ajaxify.data.selectedFilter && ajaxify.data.selectedFilter.filter === 'watched') { return; } @@ -69,11 +69,11 @@ define('forum/recent', ['forum/infinitescroll', 'components'], function (infinit return; } - if (ajaxify.data.selectedFilter && ajaxify.data.selectedFilter.url === 'unread/new') { + if (ajaxify.data.selectedFilter && ajaxify.data.selectedFilter.filter === 'new') { return; } - if (ajaxify.data.selectedFilter && ajaxify.data.selectedFilter.url === 'unread/watched') { + if (ajaxify.data.selectedFilter && ajaxify.data.selectedFilter.filter === 'watched') { socket.emit('topics.isFollowed', post.tid, function (err, isFollowed) { if (err) { app.alertError(err.message); @@ -127,13 +127,13 @@ define('forum/recent', ['forum/infinitescroll', 'components'], function (infinit }; Recent.loadMoreTopics = function (direction) { - if(direction < 0 || !$('[component="category"]').length) { + if (direction < 0 || !$('[component="category"]').length) { return; } infinitescroll.loadMore('topics.loadMoreFromSet', { after: $('[component="category"]').attr('data-nextstart'), - set: $('[component="category"]').attr('data-set') ? $('[component="category"]').attr('data-set') : 'topics:recent' + set: $('[component="category"]').attr('data-set') ? $('[component="category"]').attr('data-set') : 'topics:recent' }, function (data, done) { if (data.topics && data.topics.length) { Recent.onTopicsLoaded('recent', data.topics, false, done); diff --git a/src/controllers/helpers.js b/src/controllers/helpers.js index 13a91aaa4c..98b2e9225a 100644 --- a/src/controllers/helpers.js +++ b/src/controllers/helpers.js @@ -5,6 +5,8 @@ var async = require('async'); var validator = require('validator'); var winston = require('winston'); +var user = require('../user'); +var privileges = require('../privileges'); var categories = require('../categories'); var plugins = require('../plugins'); var meta = require('../meta'); @@ -129,4 +131,49 @@ helpers.buildTitle = function (pageTitle) { return title; }; +helpers.getWatchedCategories = function(uid, selectedCid, callback) { + async.waterfall([ + function (next) { + user.getWatchedCategories(uid, next); + }, + function (cids, next) { + privileges.categories.filterCids('read', cids, uid, next); + }, + function (cids, next) { + categories.getCategoriesFields(cids, ['cid', 'name', 'slug', 'icon', 'link', 'color', 'bgColor', 'parentCid'], next); + }, + function (categoryData, next) { + categoryData = categoryData.filter(function (category) { + return category && !category.link; + }); + + var selectedCategory; + categoryData.forEach(function (category) { + category.selected = parseInt(category.cid, 10) === parseInt(selectedCid, 10); + if (category.selected) { + selectedCategory = category; + } + }); + + var categoriesData = []; + var tree = categories.getTree(categoryData, 0); + + tree.forEach(function (category) { + recursive(category, categoriesData, ''); + }); + + next(null, {categories: categoriesData, selectedCategory: selectedCategory}); + } + ], callback); +}; + +function recursive(category, categoriesData, level) { + category.level = level; + categoriesData.push(category); + + category.children.forEach(function (child) { + recursive(child, categoriesData, '    ' + level); + }); +} + module.exports = helpers; diff --git a/src/controllers/recent.js b/src/controllers/recent.js index 72c0f45721..607bd5d9f4 100644 --- a/src/controllers/recent.js +++ b/src/controllers/recent.js @@ -3,6 +3,7 @@ var async = require('async'); var nconf = require('nconf'); +var validator = require('validator'); var db = require('../database'); var privileges = require('../privileges'); @@ -14,12 +15,21 @@ var pagination = require('../pagination'); var recentController = {}; +var validFilter = {'': true, 'new': true, 'watched': true}; + recentController.get = function (req, res, next) { var page = parseInt(req.query.page, 10) || 1; var pageCount = 1; var stop = 0; var topicCount = 0; var settings; + var cid = req.query.cid; + var filter = req.params.filter || ''; + var categoryData; + + if (!validFilter[filter]) { + return next(); + } async.waterfall([ function (next) { @@ -29,12 +39,16 @@ recentController.get = function (req, res, next) { }, tids: function (next) { db.getSortedSetRevRange('topics:recent', 0, 199, next); + }, + watchedCategories: function (next) { + helpers.getWatchedCategories(req.uid, cid, next); } }, next); }, function (results, next) { settings = results.settings; - privileges.topics.filterTids('read', results.tids, req.uid, next); + categoryData = results.watchedCategories; + filterTids(results.tids, req.uid, cid, categoryData.categories, filter, next); }, function (tids, next) { var start = Math.max(0, (page - 1) * settings.topicsPerPage); @@ -53,18 +67,78 @@ recentController.get = function (req, res, next) { var data = {}; data.topics = topics; + data.categories = categoryData.categories; + data.selectedCategory = categoryData.selectedCategory; data.nextStart = stop + 1; data.set = 'topics:recent'; data['feeds:disableRSS'] = parseInt(meta.config['feeds:disableRSS'], 10) === 1; data.rssFeedUrl = nconf.get('relative_path') + '/recent.rss'; data.title = '[[pages:recent]]'; + data.filters = [{ + name: '[[unread:all-topics]]', + url: 'recent', + selected: filter === '', + filter: '' + }, { + name: '[[unread:new-topics]]', + url: 'recent/new', + selected: filter === 'new', + filter: 'new' + }, { + name: '[[unread:watched-topics]]', + url: 'recent/watched', + selected: filter === 'watched', + filter: 'watched' + }]; + + data.selectedFilter = data.filters.find(function (filter) { + return filter && filter.selected; + }); + data.pagination = pagination.create(page, pageCount); if (req.path.startsWith('/api/recent') || req.path.startsWith('/recent')) { data.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[recent:title]]'}]); } - + data.querystring = cid ? ('?cid=' + validator.escape(String(cid))) : ''; res.render('recent', data); }); }; +function filterTids(tids, uid, cid, watchedCategories, filter, callback) { + async.waterfall([ + function (next) { + if (filter === 'watched') { + topics.filterWatchedTids(tids, uid, next); + } else if (filter === 'new') { + topics.filterNewTids(tids, uid, next); + } else { + topics.filterNotIgnoredTids(tids, uid, next); + } + }, + function (tids, next) { + privileges.topics.filterTids('read', tids, uid, next); + }, + function (tids, next) { + topics.getTopicsFields(tids, ['tid', 'cid'], next); + }, + function (topicData, next) { + var watchedCids = watchedCategories.map(function (category) { + return category && parseInt(category.cid, 10); + }); + + tids = topicData.filter(function (topic, index) { + if (topic) { + var topicCid = parseInt(topic.cid, 10); + return watchedCids.indexOf(topicCid) !== -1 && (!cid || parseInt(cid, 10) === topicCid); + } else { + return false; + } + }).map(function (topic) { + return topic.tid; + }); + next(null, tids); + } + ], callback); +} + module.exports = recentController; \ No newline at end of file diff --git a/src/controllers/unread.js b/src/controllers/unread.js index 1c5bafcbae..83de85a67e 100644 --- a/src/controllers/unread.js +++ b/src/controllers/unread.js @@ -5,8 +5,6 @@ var async = require('async'); var querystring = require('querystring'); var validator = require('validator'); -var categories = require('../categories'); -var privileges = require('../privileges'); var pagination = require('../pagination'); var user = require('../user'); var topics = require('../topics'); @@ -30,7 +28,7 @@ unreadController.get = function (req, res, next) { function (next) { async.parallel({ watchedCategories: function (next) { - getWatchedCategories(req.uid, cid, next); + helpers.getWatchedCategories(req.uid, cid, next); }, settings: function (next) { user.getSettings(req.uid, next); @@ -82,9 +80,9 @@ unreadController.get = function (req, res, next) { filter: 'watched' }]; - data.selectedFilter = data.filters.filter(function (filter) { + data.selectedFilter = data.filters.find(function (filter) { return filter && filter.selected; - })[0]; + }); data.querystring = cid ? ('?cid=' + validator.escape(String(cid))) : ''; @@ -92,51 +90,6 @@ unreadController.get = function (req, res, next) { }); }; -function getWatchedCategories(uid, selectedCid, callback) { - async.waterfall([ - function (next) { - user.getWatchedCategories(uid, next); - }, - function (cids, next) { - privileges.categories.filterCids('read', cids, uid, next); - }, - function (cids, next) { - categories.getCategoriesFields(cids, ['cid', 'name', 'slug', 'icon', 'link', 'color', 'bgColor', 'parentCid'], next); - }, - function (categoryData, next) { - categoryData = categoryData.filter(function (category) { - return category && !category.link; - }); - - var selectedCategory; - categoryData.forEach(function (category) { - category.selected = parseInt(category.cid, 10) === parseInt(selectedCid, 10); - if (category.selected) { - selectedCategory = category; - } - }); - - var categoriesData = []; - var tree = categories.getTree(categoryData, 0); - - tree.forEach(function (category) { - recursive(category, categoriesData, ''); - }); - - next(null, {categories: categoriesData, selectedCategory: selectedCategory}); - } - ], callback); -} - -function recursive(category, categoriesData, level) { - category.level = level; - categoriesData.push(category); - - category.children.forEach(function (child) { - recursive(child, categoriesData, '    ' + level); - }); -} - unreadController.unreadTotal = function (req, res, next) { var filter = req.params.filter || ''; diff --git a/src/routes/index.js b/src/routes/index.js index ccf8b5efd0..0433377303 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -64,7 +64,7 @@ function tagRoutes(app, middleware, controllers) { 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, '/recent/:filter?', middleware, [], controllers.recent.get); setupPageRoute(app, '/unread/:filter?', middleware, [middleware.authenticate], controllers.unread.get); setupPageRoute(app, '/category/:category_id/:slug/:topic_index', middleware, [], controllers.category.get); diff --git a/src/topics/follow.js b/src/topics/follow.js index 49feeb50c1..6993920fc0 100644 --- a/src/topics/follow.js +++ b/src/topics/follow.js @@ -159,6 +159,30 @@ module.exports = function (Topics) { ], callback); }; + Topics.filterWatchedTids = function (tids, uid, callback) { + db.sortedSetScores('uid:' + uid + ':followed_tids', tids, function (err, scores) { + if (err) { + return callback(err); + } + tids = tids.filter(function (tid, index) { + return tid && !!scores[index]; + }); + callback(null, tids); + }); + }; + + Topics.filterNotIgnoredTids = function (tids, uid, callback) { + db.sortedSetScores('uid:' + uid + ':ignored_tids', tids, function (err, scores) { + if (err) { + return callback(err); + } + tids = tids.filter(function (tid, index) { + return tid && !scores[index]; + }); + callback(null, tids); + }); + }; + Topics.notifyFollowers = function (postData, exceptUid, callback) { callback = callback || function () {}; var followers; diff --git a/src/topics/unread.js b/src/topics/unread.js index b4a5d07514..c7cabb2881 100644 --- a/src/topics/unread.js +++ b/src/topics/unread.js @@ -135,7 +135,7 @@ module.exports = function (Topics) { }); if (filter === 'watched') { - filterWatchedTids(uid, tids, next); + Topics.filterWatchedTids(tids, uid, next); } else { next(null, tids); } @@ -149,17 +149,6 @@ module.exports = function (Topics) { ], callback); }; - function filterWatchedTids(uid, tids, callback) { - db.sortedSetScores('uid:' + uid + ':followed_tids', tids, function (err, scores) { - if (err) { - return callback(err); - } - tids = tids.filter(function (tid, index) { - return tid && !!scores[index]; - }); - callback(null, tids); - }); - } function filterTopics(uid, tids, cid, ignoredCids, filter, callback) { if (!Array.isArray(ignoredCids) || !tids.length) { @@ -374,4 +363,16 @@ module.exports = function (Topics) { ], callback); }; + Topics.filterNewTids = function (tids, uid, callback) { + db.sortedSetScores('uid:' + uid + ':tids_read', tids, function (err, scores) { + if (err) { + return callback(err); + } + tids = tids.filter(function (tid, index) { + return tid && !scores[index]; + }); + callback(null, tids); + }); + }; + };