Skip to content
Permalink
Browse files

closes #450

backup database before upgrade!
upgrade script will take the first post of each topic and set the
`mainPid` property on the topic. then it will remove that pid from the
sorted sets for that topic, this was done to make alternative sorting
work.

added a new sorted set called `tid:<id>:posts:votes` that is used to
sort topic posts by vote count, the original sorted set `tid:<id>:posts`
is used to sort by oldest first or newest first.

the main post is added to the returned posts array on topic load and is
always at the top.
theme changes are minimal just a few new data properties on the posts
and the sorting dropdown.
hopefully didn't miss anything too critical.
  • Loading branch information...
barisusakli committed Jun 7, 2014
1 parent c5b8a7b commit 7610c11cd1b9de53ce185227ab80f86d55d3bd69
@@ -111,5 +111,10 @@

"more_users_and_guests": "%1 more user(s) and %2 guest(s)",
"more_users": "%1 more user(s)",
"more_guests": "%1 more guest(s)"
"more_guests": "%1 more guest(s)",

"sort_by": "Sort by",
"oldest_to_newest": "Oldest to Newest",
"newest_to_oldest": "Newest to Oldest",
"most_votes": "Most votes"
}
@@ -8,14 +8,16 @@ define('forum/infinitescroll', function() {
var callback;
var previousScrollTop = 0;
var loadingMore = false;
var topOffset = 0;

scroll.init = function(cb) {
scroll.init = function(cb, _topOffest) {
callback = cb;
topOffset = _topOffest || 0;
$(window).off('scroll', onScroll).on('scroll', onScroll);
};

function onScroll() {
var top = $(window).height() * 0.1;
var top = $(window).height() * 0.1 + topOffset;
var bottom = ($(document).height() - $(window).height()) * 0.9;
var currentScrollTop = $(window).scrollTop();

@@ -40,6 +40,8 @@ define('forum/topic', ['forum/pagination', 'forum/infinitescroll', 'forum/topic/
threadTools.init(tid, thread_state);
events.init();

handleSorting();

hidePostToolsForDeletedPosts();

enableInfiniteLoadingOrPagination();
@@ -77,6 +79,21 @@ define('forum/topic', ['forum/pagination', 'forum/infinitescroll', 'forum/topic/
socket.emit('topics.increaseViewCount', tid);
};

function handleSorting() {
var threadSort = $('.thread-sort');
threadSort.find('i').removeClass('fa-check');
var currentSetting = threadSort.find('a[data-sort="' + config.topicPostSort + '"]');
currentSetting.find('i').addClass('fa-check');

$('.thread-sort').on('click', 'a', function() {
var newSetting = $(this).attr('data-sort');
socket.emit('user.setTopicSort', newSetting, function(err) {
config.topicPostSort = newSetting;
ajaxify.go('topic/' + ajaxify.variables.get('topic_slug'));
});
});
}

function getPostIndex() {
var parts = window.location.pathname.split('/');
return parts[4] ? (parseInt(parts[4], 10) - 1) : '';
@@ -122,7 +139,7 @@ define('forum/topic', ['forum/pagination', 'forum/infinitescroll', 'forum/topic/

function enableInfiniteLoadingOrPagination() {
if(!config.usePagination) {
infinitescroll.init(loadMorePosts);
infinitescroll.init(loadMorePosts, $('#post-container .post-row[data-index="0"]').height());
} else {
navigator.hide();

@@ -283,25 +300,36 @@ define('forum/topic', ['forum/pagination', 'forum/infinitescroll', 'forum/topic/
before = null;

function findInsertionPoint() {
var firstPid = parseInt(data.posts[0].pid, 10);

$('#post-container li[data-pid]').each(function() {
var $this = $(this);

if(firstPid > parseInt($this.attr('data-pid'), 10)) {
after = $this;
if(after.next().length && after.next().hasClass('post-bar')) {
after = after.next();
}
} else {
return false;
var firstPostTimestamp = parseInt(data.posts[0].timestamp, 10);
var firstPostVotes = parseInt(data.posts[0].votes, 10);
var firstPostPid = data.posts[0].pid;

var firstReply = $('#post-container li.post-row[data-index!="0"]').first();
var lastReply = $('#post-container li.post-row[data-index!="0"]').last();

if (config.topicPostSort === 'oldest_to_newest') {
if (firstPostTimestamp < parseInt(firstReply.attr('data-timestamp'), 10)) {
before = firstReply;
} else if(firstPostTimestamp >= parseInt(lastReply.attr('data-timestamp'), 10)) {
after = lastReply;
}
});

if (!after) {
var firstPost = $('#post-container .post-row').first();
if(firstPid < parseInt(firstPost.attr('data-pid'), 10)) {
before = firstPost;
} else if(config.topicPostSort === 'newest_to_oldest') {
if (firstPostTimestamp > parseInt(firstReply.attr('data-timestamp'), 10)) {
before = firstReply;
} else if(firstPostTimestamp <= parseInt(lastReply.attr('data-timestamp'), 10)) {
after = lastReply;
}
} else if(config.topicPostSort === 'most_votes') {
if (firstPostVotes > parseInt(firstReply.attr('data-votes'), 10)) {
before = firstReply;
} else if(firstPostVotes < parseInt(firstReply.attr('data-votes'), 10)) {
after = lastReply;
} else {
if (firstPostPid > firstReply.attr('data-pid')) {
before = firstReply;
} else if(firstPostPid <= firstReply.attr('data-pid')) {
after = lastReply;
}
}
}
}
@@ -373,7 +401,7 @@ define('forum/topic', ['forum/pagination', 'forum/infinitescroll', 'forum/topic/
return;
}

infinitescroll.calculateAfter(direction, '#post-container .post-row', config.postsPerPage, function(after, offset, el) {
infinitescroll.calculateAfter(direction, '#post-container .post-row[data-index!="0"]', config.postsPerPage, function(after, offset, el) {
loadPostsAfter(after, function() {
if (direction < 0 && el) {
Topic.scrollToPost(el.attr('data-index'), false, 0, offset);
@@ -384,7 +412,7 @@ define('forum/topic', ['forum/pagination', 'forum/infinitescroll', 'forum/topic/

function loadPostsAfter(after, callback) {
var tid = ajaxify.variables.get('topic_id');
if (!utils.isNumber(tid) || !utils.isNumber(after) || (after === 0 && $('#post-container li.post-row[data-index="0"]').length)) {
if (!utils.isNumber(tid) || !utils.isNumber(after) || (after === 0 && $('#post-container li.post-row[data-index="1"]').length)) {
return;
}

@@ -42,6 +42,7 @@ apiController.getConfig = function(req, res, next) {
config.isLoggedIn = !!req.user;
config['cache-buster'] = meta.config['cache-buster'] || '';
config.requireEmailConfirmation = parseInt(meta.config.requireEmailConfirmation, 10) === 1;
config.topicPostSort = meta.config.topicPostSort || 'oldest_to_newest';
config.version = pkg.version;

if (!req.user) {
@@ -64,6 +65,7 @@ apiController.getConfig = function(req, res, next) {
config.notificationSounds = settings.notificationSounds;
config.defaultLang = settings.language || config.defaultLang;
config.openOutgoingLinksInNewTab = settings.openOutgoingLinksInNewTab;
config.topicPostSort = settings.topicPostSort || config.topicPostSort;

if (res.locals.isAPI) {
res.json(200, config);
@@ -44,7 +44,17 @@ topicsController.get = function(req, res, next) {
var start = (page - 1) * settings.postsPerPage + postIndex,
end = start + settings.postsPerPage - 1;

topics.getTopicWithPosts(tid, uid, start, end, function (err, topicData) {
var set = 'tid:' + tid + ':posts',
reverse = false;

if (settings.topicPostSort === 'newest_to_oldest') {
reverse = true;
} else if (settings.topicPostSort === 'most_votes') {
reverse = true;
set = 'tid:' + tid + ':posts:votes';
}

topics.getTopicWithPosts(tid, set, uid, start, end, reverse, function (err, topicData) {
if (topicData) {
if (parseInt(topicData.deleted, 10) === 1 && !userPrivileges.view_deleted) {
return next(new Error('[[error:no-topic]]'));
@@ -88,7 +88,8 @@ var async = require('async'),
return callback(err);
}
var voteCount = parseInt(results.upvotes, 10) - parseInt(results.downvotes, 10);
posts.setPostField(pid, 'votes', voteCount, function(err) {

posts.updatePostVoteCount(pid, voteCount, function(err) {
callback(err, voteCount);
});
});
@@ -85,9 +85,10 @@ middleware.checkPostIndex = function(req, res, next) {
return next(err);
}
var postIndex = parseInt(req.params.post_index, 10);
postCount = parseInt(postCount, 10) + 1;
if (postIndex > postCount) {
return res.locals.isAPI ? res.json(302, '/topic/' + req.params.topic_id + '/' + req.params.slug + '/' + postCount) : res.redirect('/topic/' + req.params.topic_id + '/' + req.params.slug + '/' + postCount);
} else if (postIndex < 1) {
} else if (postIndex <= 1) {
return res.locals.isAPI ? res.json(302, '/topic/' + req.params.topic_id + '/' + req.params.slug) : res.redirect('/topic/' + req.params.topic_id + '/' + req.params.slug);
}
next();
@@ -90,8 +90,8 @@ var db = require('./database'),
], callback);
};

Posts.getPostsByTid = function(tid, start, end, reverse, callback) {
db[reverse ? 'getSortedSetRevRange' : 'getSortedSetRange']('tid:' + tid + ':posts', start, end, function(err, pids) {
Posts.getPostsByTid = function(tid, set, start, end, reverse, callback) {
db[reverse ? 'getSortedSetRevRange' : 'getSortedSetRange'](set, start, end, function(err, pids) {
if(err) {
return callback(err);
}
@@ -157,8 +157,6 @@ var db = require('./database'),
});
};



Posts.getRecentPosts = function(uid, start, stop, term, callback) {
var terms = {
day: 86400000,
@@ -469,7 +467,9 @@ var db = require('./database'),
return callback(err);
}

db.sortedSetRank('tid:' + tid + ':posts', pid, callback);
db.sortedSetRank('tid:' + tid + ':posts', pid, function(err, index) {
callback(err, parseInt(index, 10) + 1);
});
});
};

@@ -482,5 +482,28 @@ var db = require('./database'),
});
};

Posts.updatePostVoteCount = function(pid, voteCount, callback) {
async.parallel([
function(next) {
Posts.getPostField(pid, 'tid', function(err, tid) {
if (err) {
return next(err);
}
topics.getTopicField(tid, 'mainPid', function(err, mainPid) {
if (err) {
return next(err);
}
if (parseInt(mainPid, 10) === parseInt(pid, 10)) {
return next();
}
db.sortedSetAdd('tid:' + tid + ':posts:votes', voteCount, pid, next);
});
});
},
function(next) {
Posts.setPostField(pid, 'votes', voteCount, next);
}
], callback);
};

}(exports));
@@ -40,7 +40,7 @@ function hasPrivileges(method, id, req, res, next) {
function generateForTopic(req, res, next) {
var tid = req.params.topic_id;
var uid = req.user ? req.user.uid : 0;
topics.getTopicWithPosts(tid, uid, 0, 25, function (err, topicData) {
topics.getTopicWithPosts(tid, 'tid:' + tid + ':posts', uid, 0, 25, false, function (err, topicData) {
if (err) {
return next(err);
}
@@ -314,12 +314,22 @@ SocketTopics.loadMore = function(socket, data, callback) {
return callback(err);
}

var start = parseInt(data.after, 10),
var start = Math.max(parseInt(data.after, 10) - 1, 0),
end = start + settings.postsPerPage - 1;

var set = 'tid:' + data.tid + ':posts',
reverse = false;

if (settings.topicPostSort === 'newest_to_oldest') {
reverse = true;
} else if (settings.topicPostSort === 'most_votes') {
reverse = true;
set = 'tid:' + data.tid + ':posts:votes';
}

async.parallel({
posts: function(next) {
topics.getTopicPosts(data.tid, start, end, socket.uid, false, next);
topics.getTopicPosts(data.tid, set, start, end, socket.uid, reverse, next);
},
privileges: function(next) {
privileges.topics.get(data.tid, socket.uid, next);
@@ -178,6 +178,12 @@ SocketUser.saveSettings = function(socket, data, callback) {
}
};

SocketUser.setTopicSort = function(socket, sort, callback) {
if(socket.uid) {
user.setSetting(socket.uid, 'topicPostSort', sort, callback);
}
};

SocketUser.getOnlineUsers = function(socket, data, callback) {
var returnData = {};
if(!data) {
@@ -262,15 +262,15 @@ var async = require('async'),
});
};

Topics.getTopicWithPosts = function(tid, uid, start, end, callback) {
Topics.getTopicWithPosts = function(tid, set, uid, start, end, reverse, callback) {
Topics.getTopicData(tid, function(err, topicData) {
if (err || !topicData) {
return callback(err || new Error('[[error:no-topic]]'));
}

async.parallel({
posts: function(next) {
Topics.getTopicPosts(tid, start, end, uid, false, next);
Topics.getTopicPosts(tid, set, start, end, uid, reverse, next);
},
category: function(next) {
Topics.getCategoryData(tid, next);
@@ -283,14 +283,34 @@ var async = require('async'),
},
tags: function(next) {
Topics.getTopicTagsObjects(tid, next);
},
mainPost: function(next) {
Topics.getTopicField(tid, 'mainPid', function(err, mainPid) {
if (err) {
return next(err);
}
if (!parseInt(mainPid, 10)) {
return next(null, []);
}
posts.getPostsByPids([mainPid], function(err, postData) {
if (err) {
return next(err);
}
if (!Array.isArray(postData) || !postData.length) {
return next(null, []);
}
postData[0].index = 0;
Topics.addPostData(postData, uid, next);
});
});
}
}, function(err, results) {
if (err) {
return callback(err);
}

topicData.category = results.category;
topicData.posts = results.posts;
topicData.posts = results.mainPost.concat(results.posts);
topicData.tags = results.tags;
topicData.thread_tools = results.threadTools;
topicData.pageCount = results.pageCount;
@@ -37,6 +37,7 @@ module.exports = function(Topics) {
'tid': tid,
'uid': uid,
'cid': cid,
'mainPid': 0,
'title': title,
'slug': slug,
'timestamp': timestamp,
@@ -71,7 +71,7 @@ module.exports = function(Topics) {
return callback(err || new Error('[[error:no-topic]]'));
}

posts.getPostFields(pid, ['deleted', 'tid', 'timestamp'], function(err, postData) {
posts.getPostFields(pid, ['deleted', 'tid', 'timestamp', 'votes'], function(err, postData) {
if(err) {
return callback(err);
}
@@ -91,7 +91,7 @@ module.exports = function(Topics) {
}

posts.setPostField(pid, 'tid', tid);
Topics.addPostToTopic(tid, pid, postData.timestamp, callback);
Topics.addPostToTopic(tid, pid, postData.timestamp, postData.votes, callback);
});
});
});

5 comments on commit 7610c11

@a5mith

This comment has been minimized.

Copy link
Contributor

replied Jun 7, 2014

https://www.youtube.com/watch?v=wQnGLHOYnck

No err, you know. Good job!!

@barisusakli

This comment has been minimized.

Copy link
Member Author

replied Jun 7, 2014

Haha did you already update?

@a5mith

This comment has been minimized.

Copy link
Contributor

replied Jun 7, 2014

Yeah I've got a separate development vps with hardly any posts on for trying latest & greatest. Can't see any obvious issues. Will test it more thoroughly in the morning with a few more posts etc.

@barisusakli

This comment has been minimized.

Copy link
Member Author

replied Jun 7, 2014

Thanks 👼

@cnvo

This comment has been minimized.

Copy link
Contributor

replied Jun 7, 2014

👍 Nice work Baris.

Please sign in to comment.
You can’t perform that action at this time.