From 606367701fd59f632709e7b10163112e3e3cb488 Mon Sep 17 00:00:00 2001 From: Baris Soner Usakli Date: Fri, 24 Jan 2014 20:00:56 -0500 Subject: [PATCH] pagination on posts --- public/src/app.js | 1 - public/src/forum/topic.js | 142 +++++++++++++++++++++++++++++++++----- src/database/mongo.js | 13 ++++ src/database/redis.js | 4 ++ src/routes/api.js | 7 +- src/routes/debug.js | 8 ++- src/socket.io/topics.js | 38 +++++++++- src/topics.js | 51 +++++++++++--- 8 files changed, 232 insertions(+), 32 deletions(-) diff --git a/public/src/app.js b/public/src/app.js index c6ad9c7305..3f9f33e152 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -531,7 +531,6 @@ var socket, templates.setGlobal('usePagination', config.usePagination); templates.setGlobal('topicsPerPage', config.topicsPerPage); templates.setGlobal('postsPerPage', config.postsPerPage); - console.log(templates.globals); }); showWelcomeMessage = location.href.indexOf('loggedin') !== -1; diff --git a/public/src/forum/topic.js b/public/src/forum/topic.js index 62ded4c4ce..e972ee11ae 100644 --- a/public/src/forum/topic.js +++ b/public/src/forum/topic.js @@ -17,7 +17,9 @@ define(['composer'], function(composer) { deleted: templates.get('deleted'), pinned: templates.get('pinned') }, - topic_name = templates.get('topic_name'); + topic_name = templates.get('topic_name'), + currentPage = parseInt(templates.get('currentPage'), 10), + pageCount = parseInt(templates.get('pageCount'), 10); function fixDeleteStateForPosts() { @@ -323,14 +325,10 @@ define(['composer'], function(composer) { var bookmark = localStorage.getItem('topic:' + tid + ':bookmark'); - if(bookmark) { + if(bookmark && !config.usePagination) { Topic.scrollToPost(parseInt(bookmark, 10)); } - $('#post-container').on('click', '.deleted', function(ev) { - $(this).toggleClass('deleted-expanded'); - }); - // Show the paginator block, now that the DOM has finished loading (function delayedHeaderUpdate() { if (!Topic.postCount) { @@ -366,18 +364,129 @@ define(['composer'], function(composer) { } }); } else { - $('.pagination .previous').on('click', function() { + $('.pagination-block').addClass('hide'); + updatePageLinks(); - return false; + $('.pagination').on('click', '.previous', function() { + loadPage(currentPage - 1); }); - $('.pagination .next').on('click', function() { + $('.pagination').on('click', '.next', function() { + loadPage(currentPage + 1); + }); - return false; + $('.pagination').on('click', '.page', function() { + loadPage($(this).attr('data-page')); }); } } + function updatePageLinks() { + $('.pagination .next').removeClass('disabled'); + $('.pagination .previous').removeClass('disabled'); + + if(currentPage === 1) { + $('.pagination .previous').addClass('disabled'); + } + + if(currentPage === parseInt($('.pagination').attr('data-pageCount'), 10)) { + $('.pagination .next').addClass('disabled'); + } + + $('.pagination .page').removeClass('active'); + $('.pagination .page[data-page="' + currentPage + '"]').addClass('active'); + } + + function loadPage(page) { + page = parseInt(page, 10); + if(page < 1 || page > pageCount) { + return; + } + + socket.emit('topics.loadPage', {tid: tid, page: page}, function(err, data) { + if(err) { + return app.alertError(err.message); + } + currentPage = page; + + if (data && data.posts && data.posts.length) { + createPagePosts(data, function() { + fixDeleteStateForPosts(); + }); + } + + updatePageLinks(); + }); + } + + function onNewPostPagination(data) { + var posts = data.posts; + socket.emit('topics.getPageCount', tid, function(err, newPageCount) { + + pageCount = newPageCount; + recreatePaginationLinks(); + if(currentPage === newPageCount) { + createNewPosts(data); + } else if(data.posts && data.posts.length && parseInt(data.posts[0].uid, 10) === parseInt(app.uid, 10)) { + loadPage(pageCount); + } + }); + + } + + function recreatePaginationLinks() { + var pages = []; + for(var i=1; i<=pageCount; ++i) { + pages.push({pageNumber: i}); + } + var html = templates.prepare(templates['topic'].blocks['pages']).parse({pages:pages}); + html = $(html); + $('.pagination li.page').remove(); + html.insertAfter($('.pagination li.previous')); + updatePageLinks(); + } + + function createPagePosts(data, callback) { + if(!data || (data.posts && !data.posts.length)) { + return; + } + + var html = templates.prepare(templates['topic'].blocks['posts']).parse(data); + var regexp = new RegExp("([\\s\\S]*?)", 'g'); + html = html.replace(regexp, ''); + + translator.translate(html, function(translatedHTML) { + var translated = $(translatedHTML); + + $('#post-container').fadeOut(function() { + + $(this).empty(); + translated.appendTo($(this)); + $(this).fadeIn('slow'); + + for (var x = 0, numPosts = data.posts.length; x < numPosts; x++) { + socket.emit('posts.getPrivileges', data.posts[x].pid, function(err, privileges) { + if(err) { + return app.alertError(err.message); + } + toggle_mod_tools(privileges.pid, privileges.editable); + }); + } + + app.populateOnlineUsers(); + app.createUserTooltips(); + app.addCommasToNumbers(); + $('span.timeago').timeago(); + $('.post-content img').addClass('img-responsive'); + updatePostCount(); + showBottomPostBar(); + + callback(); + }); + }); + } + + $('.topic').on('click', '.post_reply', function() { var selectionText = '', selection = window.getSelection() || document.getSelection(); @@ -653,6 +762,11 @@ define(['composer'], function(composer) { }); socket.on('event:new_post', function(data) { + if(config.usePagination) { + onNewPostPagination(data); + return; + } + var posts = data.posts; for (var p in posts) { if (posts.hasOwnProperty(p)) { @@ -926,11 +1040,8 @@ define(['composer'], function(composer) { } } - if (privileges.view_deleted) { - postEl.toggleClass('deleted'); - } else { - postEl.toggleClass('none'); - } + postEl.toggleClass('deleted'); + updatePostCount(); }); } @@ -1162,7 +1273,6 @@ define(['composer'], function(composer) { return; } - if (indicatorEl.attr('done') === '0') { infiniteLoaderActive = true; indicatorEl.fadeIn(); diff --git a/src/database/mongo.js b/src/database/mongo.js index 3b653a1693..59fbc41b5a 100644 --- a/src/database/mongo.js +++ b/src/database/mongo.js @@ -626,6 +626,19 @@ }); } + module.sortedSetCard = function(key, callback) { + db.collection('objects').count({_key:key}, function(err, count) { + if(err) { + return callback(err); + } + + if(!count) { + return callback(null, 0); + } + callback(null, count); + }); + } + module.sortedSetRank = function(key, value, callback) { if(value !== null && value !== undefined) { value = value.toString(); diff --git a/src/database/redis.js b/src/database/redis.js index 6af259f9ca..de4802456c 100644 --- a/src/database/redis.js +++ b/src/database/redis.js @@ -374,6 +374,10 @@ redisClient.zcount(key, min, max, callback); } + module.sortedSetCard = function(key, callback) { + redisClient.zcard(key, callback); + } + module.sortedSetRank = function(key, value, callback) { redisClient.zrank(key, value, callback); } diff --git a/src/routes/api.js b/src/routes/api.js index 3d1fb270df..ca5c63fd6f 100644 --- a/src/routes/api.js +++ b/src/routes/api.js @@ -166,14 +166,15 @@ var path = require('path'), app.get('/topic/:id/:slug?', function (req, res, next) { console.log(req.query); var uid = (req.user) ? req.user.uid : 0; + ThreadTools.privileges(req.params.id, uid, function(err, privileges) { if (privileges.read) { - var postsPerPage = meta.config.postsPerPage ? (meta.config.postsPerPage - 1) : 20; - topics.getTopicWithPosts(req.params.id, uid, 0, postsPerPage, false, function (err, data) { + var postsPerPage = parseInt(meta.config.postsPerPage ? meta.config.postsPerPage : 20, 10); + topics.getTopicWithPosts(req.params.id, uid, 0, postsPerPage - 1, false, function (err, data) { if(err) { return next(err); } - + data.currentPage = 1; data.privileges = privileges; if (parseInt(data.deleted, 10) === 1 && parseInt(data.expose_tools, 10) === 0) { diff --git a/src/routes/debug.js b/src/routes/debug.js index db55f82258..b2a6e5c335 100644 --- a/src/routes/debug.js +++ b/src/routes/debug.js @@ -74,7 +74,13 @@ var DebugRoute = function(app) { }); app.get('/test', function(req, res) { - res.send(); + + /*topics.getTopicPosts2(2, 0, 10, 5, function(err, data) { + res.json(data); + })*/ + topics.getTopicWithPosts(2, 1, 0, -1, true, function (err, topicData) { + res.json(topicData); + }); }); }); diff --git a/src/socket.io/topics.js b/src/socket.io/topics.js index dc39ca209a..3a45f1293e 100644 --- a/src/socket.io/topics.js +++ b/src/socket.io/topics.js @@ -230,14 +230,46 @@ SocketTopics.loadMore = function(socket, data, callback) { return callback(new Error('invalid data')); } + var postsPerPage = parseInt(meta.config.postsPerPage, 10); + postsPerPage = postsPerPage ? postsPerPage : 20; + var start = data.after, - end = start + 9; + end = start + postsPerPage - 1; topics.getTopicPosts(data.tid, start, end, socket.uid, function(err, posts) { + if(err) { + return callback(err); + } + callback(err, {posts: posts}); }); }; +SocketTopics.loadPage = function(socket, data, callback) { + if(!data || !data.tid || !data.page || parseInt(data.page < 0)) { + return callback(new Error('invalid data')); + } + + var postsPerPage = parseInt((meta.config.postsPerPage ? meta.config.postsPerPage : 20), 10); + + topics.getPageCount(data.tid, function(err, pageCount) { + if(err) { + return callback(err); + } + + if(data.page > pageCount) { + return callback(new Error('page doesn\'t exist')); + } + + var start = (data.page-1) * postsPerPage, + end = start + postsPerPage - 1; + + topics.getTopicPosts(data.tid, start, end, socket.uid, function(err, posts) { + callback(err, {posts: posts}); + }); + }); +} + SocketTopics.loadMoreRecentTopics = function(socket, data, callback) { if(!data || !data.term) { return callback(new Error('invalid data')); @@ -256,4 +288,8 @@ SocketTopics.loadMoreUnreadTopics = function(socket, data, callback) { topics.getUnreadTopics(socket.uid, start, end, callback); }; +SocketTopics.getPageCount = function(socket, tid, callback) { + topics.getPageCount(tid, callback); +} + module.exports = SocketTopics; \ No newline at end of file diff --git a/src/topics.js b/src/topics.js index aeac06f43e..b64e4fee1a 100644 --- a/src/topics.js +++ b/src/topics.js @@ -330,10 +330,6 @@ var async = require('async'), postData[i].index = start + i; } - postData = postData.filter(function(post) { - return parseInt(current_user, 10) !== 0 || parseInt(post.deleted, 10) === 0; - }); - pids = postData.map(function(post) { return post.pid; }); @@ -386,6 +382,9 @@ var async = require('async'), postData[i].favourited = fav_data[pid]; postData[i].display_moderator_tools = (current_user != 0) && privileges[pid].editable; postData[i].display_move_tools = privileges[pid].move ? '' : 'hidden'; + if(parseInt(postData[i].deleted, 10) === 1 && !privileges[pid].view_deleted) { + postData[i].content = 'This post is deleted!'; + } } callback(null, postData); @@ -393,6 +392,19 @@ var async = require('async'), }); } + Topics.getPageCount = function(tid, callback) { + db.sortedSetCard('tid:' + tid + ':posts', function(err, postCount) { + if(err) { + return callback(err); + } + + var postsPerPage = parseInt(meta.config.postsPerPage, 10); + postsPerPage = postsPerPage ? postsPerPage : 20; + + callback(null, Math.ceil(parseInt(postCount, 10) / postsPerPage)); + }); + } + Topics.getCategoryData = function(tid, callback) { Topics.getTopicField(tid, 'cid', function(err, cid) { if(err) { @@ -755,21 +767,34 @@ var async = require('async'), function getTopicData(next) { Topics.getTopicData(tid, next); - }; + } function getTopicPosts(next) { Topics.getTopicPosts(tid, start, end, current_user, next); - }; + } function getPrivileges(next) { threadTools.privileges(tid, current_user, next); - }; + } function getCategoryData(next) { Topics.getCategoryData(tid, next); - }; + } - async.parallel([getTopicData, getTopicPosts, getPrivileges, getCategoryData], function(err, results) { + function getPages(next) { + Topics.getPageCount(tid, function(err, pageCount) { + if(err) { + return next(err); + } + var pages = []; + for(var i=1; i<=pageCount; ++i) { + pages.push({pageNumber: i}); + } + next(null, pages); + }); + } + + async.parallel([getTopicData, getTopicPosts, getPrivileges, getCategoryData, getPages], function(err, results) { if (err) { winston.error('[Topics.getTopicWithPosts] Could not retrieve topic data: ', err.message); return callback(err, null); @@ -778,7 +803,11 @@ var async = require('async'), var topicData = results[0], topicPosts = results[1], privileges = results[2], - categoryData = results[3]; + categoryData = results[3], + pages = results[4]; + + var postsPerPage = parseInt(meta.config.postsPerPage, 10); + postsPerPage = postsPerPage ? postsPerPage : 20; callback(null, { 'topic_name': topicData.title, @@ -791,6 +820,8 @@ var async = require('async'), 'slug': topicData.slug, 'postcount': topicData.postcount, 'viewcount': topicData.viewcount, + 'pages': pages, + 'pageCount': pages.length, 'unreplied': parseInt(topicData.postcount, 10) > 1, 'topic_id': tid, 'expose_tools': privileges.editable ? 1 : 0,