From 8427601b04088efb4491a85172ee1525a8bf3937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 29 Jan 2018 11:56:06 -0500 Subject: [PATCH] closes #6287 --- install/package.json | 4 +-- public/src/client/popular.js | 25 ++++++++++++++- src/controllers/popular.js | 30 ++++++++++++------ src/routes/feeds.js | 6 ++-- src/socket.io/topics/infinitescroll.js | 38 +++++++++++------------ src/topics/popular.js | 43 ++++++++++++++++---------- src/user/digest.js | 8 ++--- 7 files changed, 99 insertions(+), 55 deletions(-) diff --git a/install/package.json b/install/package.json index 0d05a4a220..dcf98231f3 100644 --- a/install/package.json +++ b/install/package.json @@ -69,9 +69,9 @@ "nodebb-plugin-spam-be-gone": "0.5.1", "nodebb-rewards-essentials": "0.0.11", "nodebb-theme-lavender": "5.0.1", - "nodebb-theme-persona": "7.2.19", + "nodebb-theme-persona": "7.2.20", "nodebb-theme-slick": "1.1.4", - "nodebb-theme-vanilla": "8.1.7", + "nodebb-theme-vanilla": "8.1.9", "nodebb-widget-essentials": "4.0.1", "nodemailer": "4.4.1", "passport": "^0.4.0", diff --git a/public/src/client/popular.js b/public/src/client/popular.js index 02a1348156..b63f776bff 100644 --- a/public/src/client/popular.js +++ b/public/src/client/popular.js @@ -1,7 +1,7 @@ 'use strict'; -define('forum/popular', ['components'], function (components) { +define('forum/popular', ['forum/recent', 'components', 'forum/infinitescroll'], function (recent, components, infinitescroll) { var Popular = {}; Popular.init = function () { @@ -11,7 +11,30 @@ define('forum/popular', ['components'], function (components) { .removeClass('active') .find('a[href="' + window.location.pathname + '"]') .parent().addClass('active'); + + if (!config.usePagination) { + infinitescroll.init(loadMoreTopics); + } }; + function loadMoreTopics(direction) { + if (direction < 0 || !$('[component="category"]').length) { + return; + } + + infinitescroll.loadMore('topics.loadMorePopularTopics', { + after: $('[component="category"]').attr('data-nextstart'), + count: config.topicsPerPage, + term: ajaxify.data.term, + }, function (data, done) { + if (data.topics && data.topics.length) { + recent.onTopicsLoaded('popular', data.topics, false, done); + $('[component="category"]').attr('data-nextstart', data.nextStart); + } else { + done(); + } + }); + } + return Popular; }); diff --git a/src/controllers/popular.js b/src/controllers/popular.js index b6a189383c..6c7c2e6011 100644 --- a/src/controllers/popular.js +++ b/src/controllers/popular.js @@ -3,9 +3,12 @@ var async = require('async'); var nconf = require('nconf'); + var topics = require('../topics'); var meta = require('../meta'); +var user = require('../user'); var helpers = require('./helpers'); +var pagination = require('../pagination'); var popularController = module.exports; @@ -19,6 +22,7 @@ var terms = { }; popularController.get = function (req, res, next) { + var page = parseInt(req.query.page, 10) || 1; var term = terms[req.params.term]; if (!term && req.params.term) { @@ -38,19 +42,25 @@ popularController.get = function (req, res, next) { return res.render('popular', anonCache[term]); } } - + var settings; async.waterfall([ function (next) { - topics.getPopular(term, req.uid, meta.config.topicsPerList, next); + user.getSettings(req.uid, next); }, - function (topics) { - var data = { - title: meta.config.homePageTitle || '[[pages:home]]', - topics: topics, - 'feeds:disableRSS': parseInt(meta.config['feeds:disableRSS'], 10) === 1, - rssFeedUrl: nconf.get('relative_path') + '/popular/' + (req.params.term || 'daily') + '.rss', - term: term, - }; + function (_settings, next) { + settings = _settings; + var start = Math.max(0, (page - 1) * settings.topicsPerPage); + var stop = start + settings.topicsPerPage - 1; + topics.getPopularTopics(term, req.uid, start, stop, next); + }, + function (data) { + var pageCount = Math.max(1, Math.ceil(data.topicCount / settings.topicsPerPage)); + + data.title = meta.config.homePageTitle || '[[pages:home]]'; + data['feeds:disableRSS'] = parseInt(meta.config['feeds:disableRSS'], 10) === 1; + data.rssFeedUrl = nconf.get('relative_path') + '/popular/' + (req.params.term || 'alltime') + '.rss'; + data.term = term; + data.pagination = pagination.create(page, pageCount, req.query); if (req.originalUrl.startsWith(nconf.get('relative_path') + '/api/popular') || req.originalUrl.startsWith(nconf.get('relative_path') + '/popular')) { data.title = '[[pages:popular-' + term + ']]'; diff --git a/src/routes/feeds.js b/src/routes/feeds.js index eba2fee4ee..0ed4cac217 100644 --- a/src/routes/feeds.js +++ b/src/routes/feeds.js @@ -252,16 +252,16 @@ function generateForPopular(req, res, next) { async.waterfall([ function (next) { - topics.getPopular(term, req.uid, 19, next); + topics.getPopularTopics(term, req.uid, 0, 19, next); }, - function (topics, next) { + function (result, next) { generateTopicsFeed({ uid: req.uid, title: 'Popular Topics', description: 'A list of topics that are sorted by post count', feed_url: '/popular/' + (req.params.term || 'daily') + '.rss', site_url: '/popular/' + (req.params.term || 'daily'), - }, topics, next); + }, result.topics, next); }, function (feed) { sendFeed(feed, res); diff --git a/src/socket.io/topics/infinitescroll.js b/src/socket.io/topics/infinitescroll.js index 18be5f6341..64fa087430 100644 --- a/src/socket.io/topics/infinitescroll.js +++ b/src/socket.io/topics/infinitescroll.js @@ -88,37 +88,37 @@ module.exports = function (SocketTopics) { }; SocketTopics.loadMoreUnreadTopics = function (socket, data, callback) { - if (!data || !utils.isNumber(data.after) || parseInt(data.after, 10) < 0) { - return callback(new Error('[[error:invalid-data]]')); - } - - var start = parseInt(data.after, 10); - var stop = start + Math.max(0, Math.min(meta.config.topicsPerPage || 20, parseInt(data.count, 10) || meta.config.topicsPerPage || 20) - 1); - - topics.getUnreadTopics({ cid: data.cid, uid: socket.uid, start: start, stop: stop, filter: data.filter }, callback); + loadData(data, callback, function (start, stop) { + topics.getUnreadTopics({ cid: data.cid, uid: socket.uid, start: start, stop: stop, filter: data.filter }, callback); + }); }; SocketTopics.loadMoreRecentTopics = function (socket, data, callback) { - if (!data || !utils.isNumber(data.after) || parseInt(data.after, 10) < 0) { - return callback(new Error('[[error:invalid-data]]')); - } - - var start = parseInt(data.after, 10); - var stop = start + Math.max(0, Math.min(meta.config.topicsPerPage || 20, parseInt(data.count, 10) || meta.config.topicsPerPage || 20) - 1); + loadData(data, callback, function (start, stop) { + topics.getRecentTopics(data.cid, socket.uid, start, stop, data.filter, callback); + }); + }; - topics.getRecentTopics(data.cid, socket.uid, start, stop, data.filter, callback); + SocketTopics.loadMorePopularTopics = function (socket, data, callback) { + loadData(data, callback, function (start, stop) { + topics.getPopularTopics(data.term, socket.uid, start, stop, callback); + }); }; SocketTopics.loadMoreTopTopics = function (socket, data, callback) { + loadData(data, callback, function (start, stop) { + topics.getTopTopics(data.cid, socket.uid, start, stop, data.filter, callback); + }); + }; + + function loadData(data, callback, loadFn) { if (!data || !utils.isNumber(data.after) || parseInt(data.after, 10) < 0) { return callback(new Error('[[error:invalid-data]]')); } - var start = parseInt(data.after, 10); var stop = start + Math.max(0, Math.min(meta.config.topicsPerPage || 20, parseInt(data.count, 10) || meta.config.topicsPerPage || 20) - 1); - - topics.getTopTopics(data.cid, socket.uid, start, stop, data.filter, callback); - }; + loadFn(start, stop); + } SocketTopics.loadMoreFromSet = function (socket, data, callback) { if (!data || !utils.isNumber(data.after) || parseInt(data.after, 10) < 0 || !data.set) { diff --git a/src/topics/popular.js b/src/topics/popular.js index 85d25abeaf..99855b17d3 100644 --- a/src/topics/popular.js +++ b/src/topics/popular.js @@ -2,39 +2,50 @@ 'use strict'; var async = require('async'); + +var db = require('../database'); var privileges = require('../privileges'); module.exports = function (Topics) { Topics.getPopular = function (term, uid, count, callback) { count = parseInt(count, 10) || 20; - - if (term === 'alltime') { - return getAllTimePopular(uid, count, callback); - } - async.waterfall([ function (next) { - Topics.getLatestTidsFromSet('topics:tid', 0, -1, term, next); + Topics.getPopularTopics(term, uid, 0, count - 1, next); }, - function (tids, next) { - getTopics(tids, uid, count, next); + function (data, next) { + next(null, data.topics); }, ], callback); }; - function getAllTimePopular(uid, count, callback) { + Topics.getPopularTopics = function (term, uid, start, stop, callback) { + var popularTopics = { + nextStart: 0, + topicCount: 0, + topics: [], + }; async.waterfall([ function (next) { - Topics.getTopicsFromSet('topics:posts', uid, 0, count - 1, next); + if (term === 'alltime') { + db.getSortedSetRevRange('topics:posts', 0, 199, next); + } else { + Topics.getLatestTidsFromSet('topics:tid', 0, -1, term, next); + } }, - function (data, next) { - data.topics.sort(sortPopular); - next(null, data.topics); + function (tids, next) { + popularTopics.topicCount = tids.length; + getTopics(tids, uid, start, stop, next); + }, + function (topics, next) { + popularTopics.topics = topics; + popularTopics.nextStart = stop + 1; + next(null, popularTopics); }, ], callback); - } + }; - function getTopics(tids, uid, count, callback) { + function getTopics(tids, uid, start, stop, callback) { async.waterfall([ function (next) { Topics.getTopicsFields(tids, ['tid', 'postcount', 'deleted'], next); @@ -42,7 +53,7 @@ module.exports = function (Topics) { function (topics, next) { tids = topics.filter(function (topic) { return topic && parseInt(topic.deleted, 10) !== 1; - }).sort(sortPopular).slice(0, count).map(function (topic) { + }).sort(sortPopular).slice(start, stop !== -1 ? stop - 1 : undefined).map(function (topic) { return topic.tid; }); privileges.topics.filterTids('read', tids, uid, next); diff --git a/src/user/digest.js b/src/user/digest.js index c08a0afe93..b1562aab3f 100644 --- a/src/user/digest.js +++ b/src/user/digest.js @@ -106,14 +106,14 @@ Digest.send = function (data, callback) { function (next) { async.parallel({ notifications: async.apply(user.notifications.getDailyUnread, userObj.uid), - topics: async.apply(topics.getPopular, data.interval, userObj.uid, 10), + popular: async.apply(topics.getPopularTopics, data.interval, userObj.uid, 0, 9), }, next); }, function (data, next) { var notifications = data.notifications.filter(Boolean); // If there are no notifications and no new topics, don't bother sending a digest - if (!notifications.length && !data.topics.length) { + if (!notifications.length && !data.popular.topics.length) { return next(); } @@ -124,7 +124,7 @@ Digest.send = function (data, callback) { }); // Fix relative paths in topic data - data.topics = data.topics.map(function (topicObj) { + data.popular.topics = data.popular.topics.map(function (topicObj) { var user = topicObj.hasOwnProperty('teaser') && topicObj.teaser !== undefined ? topicObj.teaser.user : topicObj.user; if (user && user.picture && utils.isRelativeUrl(user.picture)) { user.picture = nconf.get('base_url') + user.picture; @@ -138,7 +138,7 @@ Digest.send = function (data, callback) { username: userObj.username, userslug: userObj.userslug, notifications: notifications, - recent: data.topics, + recent: data.popular.topics, interval: data.interval, showUnsubscribe: true, }, function (err) {