also fix reverse infinite scroll when sorting is changed
v1.18.x
Baris Usakli 8 years ago
parent 1d26fc0d89
commit 037a0e5239

@ -264,7 +264,7 @@ define('forum/category', [
var topics = $('[component="category/topic"]'); var topics = $('[component="category/topic"]');
var afterEl = direction > 0 ? topics.last() : topics.first(); var afterEl = direction > 0 ? topics.last() : topics.first();
var after = (parseInt(afterEl.attr('data-index'), 10) || 0) + 1; var after = (parseInt(afterEl.attr('data-index'), 10) || 0) + (direction > 0 ? 1 : 0);
loadTopicsAfter(after, direction); loadTopicsAfter(after, direction);
}; };
@ -281,8 +281,7 @@ define('forum/category', [
cid: ajaxify.data.cid, cid: ajaxify.data.cid,
after: after, after: after,
direction: direction, direction: direction,
author: params.author, query: params,
tag: params.tag,
categoryTopicSort: config.categoryTopicSort, categoryTopicSort: config.categoryTopicSort,
}, function (data, done) { }, function (data, done) {
if (data.topics && data.topics.length) { if (data.topics && data.topics.length) {

@ -35,17 +35,13 @@ Categories.getCategoryById = function (data, callback) {
return next(new Error('[[error:invalid-cid]]')); return next(new Error('[[error:invalid-cid]]'));
} }
category = categories[0]; category = categories[0];
data.category = category;
async.parallel({ async.parallel({
topics: function (next) { topics: function (next) {
Categories.getCategoryTopics(data, next); Categories.getCategoryTopics(data, next);
}, },
topicCount: function (next) { topicCount: function (next) {
if (Array.isArray(data.set)) { Categories.getTopicCount(data, next);
db.sortedSetIntersectCard(data.set, next);
} else {
next(null, category.topic_count);
}
}, },
isIgnored: function (next) { isIgnored: function (next) {
Categories.isIgnored([data.cid], data.uid, next); Categories.isIgnored([data.cid], data.uid, next);

@ -5,6 +5,7 @@ var async = require('async');
var db = require('../database'); var db = require('../database');
var topics = require('../topics'); var topics = require('../topics');
var plugins = require('../plugins'); var plugins = require('../plugins');
var meta = require('../meta');
module.exports = function (Categories) { module.exports = function (Categories) {
Categories.getCategoryTopics = function (data, callback) { Categories.getCategoryTopics = function (data, callback) {
@ -37,40 +38,52 @@ module.exports = function (Categories) {
Categories.getTopicIds = function (data, callback) { Categories.getTopicIds = function (data, callback) {
var pinnedTids; var pinnedTids;
var pinnedCount;
var totalPinnedCount;
var start = data.start;
var stop = data.stop;
var set = data.set;
async.waterfall([ async.waterfall([
function (next) { function (next) {
Categories.getPinnedTids(data.cid, 0, -1, next); Categories.getPinnedTids(data.cid, 0, -1, next);
}, },
function (_pinnedTids, next) { function (_pinnedTids, next) {
totalPinnedCount = _pinnedTids.length; var totalPinnedCount = _pinnedTids.length;
pinnedTids = _pinnedTids.slice(start, stop === -1 ? undefined : stop + 1); pinnedTids = _pinnedTids.slice(data.start, data.stop === -1 ? undefined : data.stop + 1);
pinnedCount = pinnedTids.length; var pinnedCount = pinnedTids.length;
var topicsPerPage = stop - start + 1; var topicsPerPage = data.stop - data.start + 1;
var normalTidsToGet = Math.max(0, topicsPerPage - pinnedCount); var normalTidsToGet = Math.max(0, topicsPerPage - pinnedCount);
if (!normalTidsToGet && stop !== -1) { if (!normalTidsToGet && data.stop !== -1) {
return next(null, []); return next(null, []);
} }
if (plugins.hasListeners('filter:categories.getTopicIds')) {
return plugins.fireHook('filter:categories.getTopicIds', {
tids: [],
data: data,
pinnedTids: pinnedTids,
allPinnedTids: _pinnedTids,
totalPinnedCount: totalPinnedCount,
normalTidsToGet: normalTidsToGet,
}, function (err, data) {
callback(err, data && data.tids);
});
}
var set = Categories.buildTopicsSortedSet(data);
var reverse = Categories.getSortedSetRangeDirection(data.sort);
var start = data.start;
if (start > 0 && totalPinnedCount) { if (start > 0 && totalPinnedCount) {
start -= totalPinnedCount - pinnedCount; start -= totalPinnedCount - pinnedCount;
} }
stop = stop === -1 ? stop : start + normalTidsToGet - 1;
var stop = data.stop === -1 ? data.stop : start + normalTidsToGet - 1;
if (Array.isArray(set)) { if (Array.isArray(set)) {
db[data.reverse ? 'getSortedSetRevIntersect' : 'getSortedSetIntersect']({ sets: set, start: start, stop: stop }, next); db[reverse ? 'getSortedSetRevIntersect' : 'getSortedSetIntersect']({ sets: set, start: start, stop: stop }, next);
} else { } else {
db[data.reverse ? 'getSortedSetRevRange' : 'getSortedSetRange'](set, start, stop, next); db[reverse ? 'getSortedSetRevRange' : 'getSortedSetRange'](set, start, stop, next);
} }
}, },
function (normalTids, next) { function (normalTids, next) {
@ -83,6 +96,54 @@ module.exports = function (Categories) {
], callback); ], callback);
}; };
Categories.getTopicCount = function (data, callback) {
if (plugins.hasListeners('filter:categories.getTopicCount')) {
return plugins.fireHook('filter:categories.getTopicCount', {
topicCount: data.category.topic_count,
data: data,
}, function (err, data) {
callback(err, data && data.topicCount);
});
}
var set = Categories.buildTopicsSortedSet(data);
if (Array.isArray(set)) {
db.sortedSetIntersectCard(set, callback);
} else {
callback(null, data.category.topic_count);
}
};
Categories.buildTopicsSortedSet = function (data) {
var cid = data.cid;
var set = 'cid:' + cid + ':tids';
var sort = data.sort || (data.settings && data.settings.categoryTopicSort) || meta.config.categoryTopicSort || 'newest_to_oldest';
if (sort === 'most_posts') {
set = 'cid:' + cid + ':tids:posts';
}
if (data.targetUid) {
set = 'cid:' + cid + ':uid:' + data.targetUid + ':tids';
}
if (data.tag) {
if (Array.isArray(data.tag)) {
set = [set].concat(data.tag.map(function (tag) {
return 'tag:' + tag + ':topics';
}));
} else {
set = [set, 'tag:' + data.tag + ':topics'];
}
}
return set;
};
Categories.getSortedSetRangeDirection = function (sort) {
sort = sort || 'newest_to_oldest';
var reverse = sort === 'newest_to_oldest' || sort === 'most_posts';
return reverse;
};
Categories.getAllTopicIds = function (cid, start, stop, callback) { Categories.getAllTopicIds = function (cid, start, stop, callback) {
db.getSortedSetRange(['cid:' + cid + ':tids:pinned', 'cid:' + cid + ':tids'], start, stop, callback); db.getSortedSetRange(['cid:' + cid + ':tids:pinned', 'cid:' + cid + ':tids'], start, stop, callback);
}; };

@ -77,49 +77,27 @@ categoryController.get = function (req, res, callback) {
topicIndex = 0; topicIndex = 0;
} }
var set = 'cid:' + cid + ':tids';
var reverse = false;
// `sort` qs has priority over user setting
var sort = req.query.sort || settings.categoryTopicSort; var sort = req.query.sort || settings.categoryTopicSort;
if (sort === 'newest_to_oldest') {
reverse = true;
} else if (sort === 'most_posts') {
reverse = true;
set = 'cid:' + cid + ':tids:posts';
}
var start = ((currentPage - 1) * settings.topicsPerPage) + topicIndex; var start = ((currentPage - 1) * settings.topicsPerPage) + topicIndex;
var stop = start + settings.topicsPerPage - 1; var stop = start + settings.topicsPerPage - 1;
async.waterfall([
function (next) {
user.getUidByUserslug(req.query.author, next);
},
function (targetUid, next) {
var payload = { var payload = {
uid: req.uid,
cid: cid, cid: cid,
set: set,
reverse: reverse,
start: start, start: start,
stop: stop, stop: stop,
uid: req.uid, sort: sort,
settings: settings, settings: settings,
query: req.query,
tag: req.query.tag,
targetUid: targetUid,
}; };
async.waterfall([
function (next) {
user.getUidByUserslug(req.query.author, next);
},
function (uid, next) {
payload.targetUid = uid;
if (uid) {
payload.set = 'cid:' + cid + ':uid:' + uid + ':tids';
}
if (req.query.tag) {
if (Array.isArray(req.query.tag)) {
payload.set = [payload.set].concat(req.query.tag.map(function (tag) {
return 'tag:' + tag + ':topics';
}));
} else {
payload.set = [payload.set, 'tag:' + req.query.tag + ':topics'];
}
}
categories.getCategoryById(payload, next); categories.getCategoryById(payload, next);
}, },
], next); ], next);

@ -59,6 +59,7 @@ SocketCategories.loadMore = function (socket, data, callback) {
if (!data) { if (!data) {
return callback(new Error('[[error:invalid-data]]')); return callback(new Error('[[error:invalid-data]]'));
} }
data.query = data.query || {};
var userPrivileges; var userPrivileges;
async.waterfall([ async.waterfall([
function (next) { function (next) {
@ -70,8 +71,8 @@ SocketCategories.loadMore = function (socket, data, callback) {
user.getSettings(socket.uid, next); user.getSettings(socket.uid, next);
}, },
targetUid: function (next) { targetUid: function (next) {
if (data.author) { if (data.query.author) {
user.getUidByUserslug(data.author, next); user.getUidByUserslug(data.query.author, next);
} else { } else {
next(); next();
} }
@ -84,44 +85,28 @@ SocketCategories.loadMore = function (socket, data, callback) {
return callback(new Error('[[error:no-privileges]]')); return callback(new Error('[[error:no-privileges]]'));
} }
var infScrollTopicsPerPage = 20; var infScrollTopicsPerPage = 20;
var set = 'cid:' + data.cid + ':tids'; var sort = data.sort || data.categoryTopicSort;
var reverse = false;
if (data.categoryTopicSort === 'newest_to_oldest') {
reverse = true;
} else if (data.categoryTopicSort === 'most_posts') {
reverse = true;
set = 'cid:' + data.cid + ':tids:posts';
}
var start = Math.max(0, parseInt(data.after, 10)); var start = Math.max(0, parseInt(data.after, 10));
if (data.direction === -1) { if (data.direction === -1) {
start -= reverse ? infScrollTopicsPerPage : -infScrollTopicsPerPage; start -= infScrollTopicsPerPage;
} }
var stop = start + infScrollTopicsPerPage - 1; var stop = start + infScrollTopicsPerPage - 1;
start = Math.max(0, start); start = Math.max(0, start);
stop = Math.max(0, stop); stop = Math.max(0, stop);
if (results.targetUid) {
set = 'cid:' + data.cid + ':uid:' + results.targetUid + ':tids';
}
if (data.tag) {
set = [set, 'tag:' + data.tag + ':topics'];
}
categories.getCategoryTopics({ categories.getCategoryTopics({
uid: socket.uid,
cid: data.cid, cid: data.cid,
set: set,
reverse: reverse,
start: start, start: start,
stop: stop, stop: stop,
uid: socket.uid, sort: sort,
targetUid: results.targetUid,
settings: results.settings, settings: results.settings,
query: data.query,
tag: data.query.tag,
targetUid: results.targetUid,
}, next); }, next);
}, },
function (data, next) { function (data, next) {

@ -27,7 +27,12 @@ module.exports = function (Topics) {
async.waterfall([ async.waterfall([
function (next) { function (next) {
if (cid) { if (cid) {
categories.getTopicIds(cid, 'cid:' + cid + ':tids', true, 0, 199, next); categories.getTopicIds({
cid: cid,
start: 0,
stop: 199,
sort: 'newest_to_oldest',
}, next);
} else { } else {
db.getSortedSetRevRange('topics:recent', 0, 199, next); db.getSortedSetRevRange('topics:recent', 0, 199, next);
} }

@ -73,7 +73,11 @@ module.exports = function (Topics) {
Topics.getTopicField(tid, 'cid', next); Topics.getTopicField(tid, 'cid', next);
}, },
function (cid, next) { function (cid, next) {
categories.getTopicIds(cid, 'cid:' + cid + ':tids', true, 0, 9, next); categories.getTopicIds({
cid: cid,
start: 0,
stop: 9,
}, next);
}, },
], callback); ], callback);
} }

@ -54,8 +54,6 @@ describe('Categories', function () {
it('should retrieve a newly created category by its ID', function (done) { it('should retrieve a newly created category by its ID', function (done) {
Categories.getCategoryById({ Categories.getCategoryById({
cid: categoryObj.cid, cid: categoryObj.cid,
set: 'cid:' + categoryObj.cid + ':tids',
reverse: true,
start: 0, start: 0,
stop: -1, stop: -1,
uid: 0, uid: 0,
@ -103,11 +101,10 @@ describe('Categories', function () {
it('should return a list of topics', function (done) { it('should return a list of topics', function (done) {
Categories.getCategoryTopics({ Categories.getCategoryTopics({
cid: categoryObj.cid, cid: categoryObj.cid,
set: 'cid:' + categoryObj.cid + ':tids',
reverse: true,
start: 0, start: 0,
stop: 10, stop: 10,
uid: 0, uid: 0,
sort: 'oldest-to-newest',
}, function (err, result) { }, function (err, result) {
assert.equal(err, null); assert.equal(err, null);
@ -123,12 +120,11 @@ describe('Categories', function () {
it('should return a list of topics by a specific user', function (done) { it('should return a list of topics by a specific user', function (done) {
Categories.getCategoryTopics({ Categories.getCategoryTopics({
cid: categoryObj.cid, cid: categoryObj.cid,
set: 'cid:' + categoryObj.cid + ':uid:' + 1 + ':tids',
reverse: true,
start: 0, start: 0,
stop: 10, stop: 10,
uid: 0, uid: 0,
targetUid: 1, targetUid: 1,
sort: 'oldest-to-newest',
}, function (err, result) { }, function (err, result) {
assert.equal(err, null); assert.equal(err, null);
assert(Array.isArray(result.topics)); assert(Array.isArray(result.topics));
@ -226,7 +222,14 @@ describe('Categories', function () {
}); });
it('should load more topics', function (done) { it('should load more topics', function (done) {
socketCategories.loadMore({ uid: posterUid }, { cid: categoryObj.cid, after: 0, author: 'poster', tag: 'nodebb' }, function (err, data) { socketCategories.loadMore({ uid: posterUid }, {
cid: categoryObj.cid,
after: 0,
query: {
author: 'poster',
tag: 'nodebb',
},
}, function (err, data) {
assert.ifError(err); assert.ifError(err);
assert(Array.isArray(data.topics)); assert(Array.isArray(data.topics));
assert.equal(data.topics[0].user.username, 'poster'); assert.equal(data.topics[0].user.username, 'poster');
@ -244,7 +247,7 @@ describe('Categories', function () {
}); });
}); });
it('should load page count', function (done) { it('should load topic count', function (done) {
socketCategories.getTopicCount({ uid: posterUid }, categoryObj.cid, function (err, topicCount) { socketCategories.getTopicCount({ uid: posterUid }, categoryObj.cid, function (err, topicCount) {
assert.ifError(err); assert.ifError(err);
assert.equal(topicCount, 2); assert.equal(topicCount, 2);
@ -680,4 +683,31 @@ describe('Categories', function () {
}); });
}); });
}); });
describe('getTopicIds', function () {
var plugins = require('../src/plugins');
it('should get topic ids with filter', function (done) {
function method(data, callback) {
data.tids = [1, 2, 3];
callback(null, data);
}
plugins.registerHook('my-test-plugin', {
hook: 'filter:categories.getTopicIds',
method: method,
});
Categories.getTopicIds({
cid: categoryObj.cid,
start: 0,
stop: 19,
}, function (err, tids) {
assert.ifError(err);
assert.deepEqual(tids, [1, 2, 3]);
plugins.unregisterHook('my-test-plugin', 'filter:categories.getTopicIds', method);
done();
});
});
});
}); });

Loading…
Cancel
Save