diff --git a/public/src/forum/topic.js b/public/src/forum/topic.js index 39c6df7bca..3e65275d77 100644 --- a/public/src/forum/topic.js +++ b/public/src/forum/topic.js @@ -8,7 +8,8 @@ deleted: templates.get('deleted'), pinned: templates.get('pinned') }, - topic_name = templates.get('topic_name'); + topic_name = templates.get('topic_name'), + infiniteLoaderActive = false; function addCommasToNumbers() { $('.formatted-number').each(function(index, element) { @@ -204,6 +205,20 @@ followEl[0].addEventListener('click', function() { socket.emit('api:topic.follow', tid); }, false); + + // Infinite scrolling of posts + window.addEventListener('scroll', function() { + var windowHeight = document.body.offsetHeight - $(window).height(), + half = windowHeight / 2; + + if (document.body.scrollTop > half && !infiniteLoaderActive) { + infiniteLoaderActive = true; + socket.emit('api:topic.loadMore', { + tid: tid, + after: document.querySelectorAll('#post-container li[data-pid]').length + }); + } + }); }); @@ -304,18 +319,19 @@ socket.on('event:new_post', function(data) { data.posts[0].display_moderator_tools = 'none'; var html = templates.prepare(templates['topic'].blocks['posts']).parse(data), - uniqueid = new Date().getTime(); - - jQuery('<div id="' + uniqueid + '"></div>') - .appendTo("#post-container") - .hide() - .append(html) - .fadeIn('slow'); - - socket.once('api:post.privileges', function(privileges) { - if (privileges.editable) toggle_mod_tools(data.posts[0].pid, true); - }); - socket.emit('api:post.privileges', data.posts[0].pid); + uniqueid = new Date().getTime(), + tempContainer = jQuery('<div id="' + uniqueid + '"></div>') + .appendTo("#post-container") + .hide() + .append(html) + .fadeIn('slow'); + + for(var x=0,numPosts=data.posts.length;x<numPosts;x++) { + socket.emit('api:post.privileges', data.posts[x].pid); + } + + tempContainer.replaceWith(tempContainer.contents()); + infiniteLoaderActive = false; set_up_posts(uniqueid); @@ -397,6 +413,10 @@ if (data.pid) toggle_post_delete_state(data.pid, true); }); + socket.on('api:post.privileges', function(privileges) { + if (privileges.editable) toggle_mod_tools(privileges.pid, true); + }); + function adjust_rep(value, pid, uid) { var post_rep = jQuery('.post_rep_' + pid), user_rep = jQuery('.user_rep_' + uid); diff --git a/src/postTools.js b/src/postTools.js index f64569b558..243e1e7cc4 100644 --- a/src/postTools.js +++ b/src/postTools.js @@ -4,7 +4,8 @@ var RDB = require('./redis.js'), threadTools = require('./threadTools.js'), user = require('./user.js'), async = require('async'), - marked = require('marked'); + marked = require('marked'), + utils = require('../public/src/utils'); marked.setOptions({ breaks: true @@ -115,4 +116,65 @@ marked.setOptions({ }); } + PostTools.constructPostObject = function(rawPosts, tid, current_user, privileges, callback) { + var postObj = []; + + async.waterfall([ + function(next) { + if (!privileges) { + threadTools.privileges(tid, current_user, function(privs) { + privileges = privs; + next(); + }); + } else { + next(); + } + }, + function(next) { + var postData = rawPosts.postData, + userData = rawPosts.userData, + voteData = rawPosts.voteData; + + if (!postData) { + return next(null, []); + } + + for (var i=0, ii= postData.pid.length; i<ii; i++) { + var uid = postData.uid[i], + pid = postData.pid[i]; + + // ############ to be moved into posts.getPostsByTid ############ + if (postData.deleted[i] === null || (postData.deleted[i] === '1' && privileges.view_deleted) || current_user === uid) { + var post_obj = { + 'pid' : pid, + 'uid' : uid, + 'content' : marked(postData.content[i] || ''), + 'post_rep' : postData.reputation[i] || 0, + 'timestamp' : postData.timestamp[i], + 'relativeTime': utils.relativeTime(postData.timestamp[i]), + 'username' : userData[uid].username || 'anonymous', + 'userslug' : userData[uid].userslug || '', + 'user_rep' : userData[uid].reputation || 0, + 'gravatar' : userData[uid].picture || 'http://www.gravatar.com/avatar/d41d8cd98f00b204e9800998ecf8427e', + 'signature' : marked(userData[uid].signature || ''), + 'fav_star_class' : voteData[pid] ? 'icon-star' : 'icon-star-empty', + 'display_moderator_tools': (uid == current_user || privileges.editable) ? 'show' : 'none', + 'edited-class': postData.editor[i] !== null ? '' : 'none', + 'editor': postData.editor[i] !== null ? userData[postData.editor[i]].username : '', + 'relativeEditTime': postData.editTime !== null ? utils.relativeTime(postData.editTime[i]) : '', + 'deleted': postData.deleted[i] || '0' + }; + + postObj.push(post_obj); + } + // ########## end to be moved into posts.getPostsByTid ############ + } + + next(null); + } + ], function(err) { + callback(postObj); + }); + } + }(exports)); \ No newline at end of file diff --git a/src/posts.js b/src/posts.js index d9207833dc..a0afe4892d 100644 --- a/src/posts.js +++ b/src/posts.js @@ -17,19 +17,17 @@ marked.setOptions({ Posts.getPostsByTid = function(tid, current_user, start, end, callback) { RDB.lrange('tid:' + tid + ':posts', start, end, function(err, pids) { RDB.handle(err); - - if (pids.length === 0 ) { - throw new Error('Topic should never have 0 posts. tid: ' + tid); - } - topics.markAsRead(tid, current_user); - Posts.getPostsByPids(pids, current_user, function(posts) { - callback(posts); - }) - - - + if (pids.length) { + Posts.getPostsByPids(pids, current_user, function(posts) { + callback(posts); + }); + } else { + callback({ + error: 'no-posts' + }); + } }); } diff --git a/src/topics.js b/src/topics.js index 8a3b42ee31..5c87fa95d4 100644 --- a/src/topics.js +++ b/src/topics.js @@ -7,6 +7,7 @@ var RDB = require('./redis.js') posts = require('./posts.js'), marked = require('marked'), threadTools = require('./threadTools.js'), + postTools = require('./postTools'), async = require('async'), feed = require('./feed.js'); @@ -39,7 +40,7 @@ marked.setOptions({ } function getTopicPosts(next) { - posts.getPostsByTid(tid, current_user, 0, 10, function(postData) { + posts.getPostsByTid(tid, current_user, 0, 9, function(postData) { next(null, postData); }); } @@ -51,69 +52,30 @@ marked.setOptions({ } async.parallel([getTopicData, getTopicPosts, getPrivileges], function(err, results) { - var retrieved_posts = [], - main_posts = []; - var topicData = results[0], - postData = results[1].postData, - userData = results[1].userData, - voteData = results[1].voteData, privileges = results[2]; - if (!postData) { - callback(false); - return; - } - - for (var i=0, ii= postData.pid.length; i<ii; i++) { - var uid = postData.uid[i], - pid = postData.pid[i]; - - // ############ to be moved into posts.getPostsByTid ############ - if (postData.deleted[i] === null || (postData.deleted[i] === '1' && privileges.view_deleted) || current_user === uid) { - var post_obj = { - 'pid' : pid, - 'uid' : uid, - 'content' : marked(postData.content[i] || ''), - 'post_rep' : postData.reputation[i] || 0, - 'timestamp' : postData.timestamp[i], - 'relativeTime': utils.relativeTime(postData.timestamp[i]), - 'username' : userData[uid].username || 'anonymous', - 'userslug' : userData[uid].userslug || '', - 'user_rep' : userData[uid].reputation || 0, - 'gravatar' : userData[uid].picture || 'http://www.gravatar.com/avatar/d41d8cd98f00b204e9800998ecf8427e', - 'signature' : marked(userData[uid].signature || ''), - 'fav_star_class' : voteData[pid] ? 'icon-star' : 'icon-star-empty', - 'display_moderator_tools': (uid == current_user || privileges.editable) ? 'show' : 'none', - 'edited-class': postData.editor[i] !== null ? '' : 'none', - 'editor': postData.editor[i] !== null ? userData[postData.editor[i]].username : '', - 'relativeEditTime': postData.editTime !== null ? utils.relativeTime(postData.editTime[i]) : '', - 'deleted': postData.deleted[i] || '0' - }; - - if (i == 0) { - main_posts.push(post_obj); - } else { - retrieved_posts.push(post_obj); - } + postTools.constructPostObject(results[1], tid, current_user, privileges, function(postObj) { + if (postObj.length) { + var main_posts = postObj.splice(0, 1); + + callback({ + 'topic_name':topicData.topic_name, + 'category_name':topicData.category_name, + 'category_slug':topicData.category_slug, + 'locked': parseInt(topicData.locked) || 0, + 'deleted': parseInt(topicData.deleted) || 0, + 'pinned': parseInt(topicData.pinned) || 0, + 'slug': topicData.slug, + 'topic_id': tid, + 'expose_tools': privileges.editable ? 1 : 0, + 'posts': postObj, + 'main_posts': main_posts + }); + } else { + return callback(false); } - // ########## end to be moved into posts.getPostsByTid ############ - } - - callback({ - 'topic_name':topicData.topic_name, - 'category_name':topicData.category_name, - 'category_slug':topicData.category_slug, - 'locked': parseInt(topicData.locked) || 0, - 'deleted': parseInt(topicData.deleted) || 0, - 'pinned': parseInt(topicData.pinned) || 0, - 'slug': topicData.slug, - 'topic_id': tid, - 'expose_tools': privileges.editable ? 1 : 0, - 'posts': retrieved_posts, - 'main_posts': main_posts }); - }); } diff --git a/src/websockets.js b/src/websockets.js index ea4bb44adf..eebee7a90d 100644 --- a/src/websockets.js +++ b/src/websockets.js @@ -381,6 +381,7 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }), socket.on('api:post.privileges', function(pid) { postTools.privileges(pid, uid, function(privileges) { + privileges.pid = parseInt(pid); socket.emit('api:post.privileges', privileges); }); }); @@ -404,6 +405,21 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }), } }); + socket.on('api:topic.loadMore', function(data) { + var start = data.after, + end = start + 10; + + posts.getPostsByTid(data.tid, uid, start, end, function(posts){ + if (!posts.error) { + postTools.constructPostObject(posts, data.tid, uid, null, function(postObj) { + io.sockets.in('topic_' + data.tid).emit('event:new_post', { + posts: postObj + }); + }); + } + }); + }); + socket.on('api:admin.topics.getMore', function(data) { topics.getAllTopics(data.limit, data.after, function(topics) { socket.emit('api:admin.topics.getMore', JSON.stringify(topics));