infinite scrolling for topics in category view, changed how topics are stored in categories, using sorted sets instead of sets now, if you update to this commit run node app --upgrade to upgrade the redis schema, #141

v1.18.x
Baris Soner Usakli 12 years ago
parent 04e0d075ec
commit 1cc8214d16

@ -6,7 +6,8 @@
googleEl = document.getElementById('google-share'), googleEl = document.getElementById('google-share'),
twitter_url = templates.get('twitter-intent-url'), twitter_url = templates.get('twitter-intent-url'),
facebook_url = templates.get('facebook-share-url'), facebook_url = templates.get('facebook-share-url'),
google_url = templates.get('google-share-url'); google_url = templates.get('google-share-url'),
loadingMoreTopics = false;
app.enter_room(room); app.enter_room(room);
@ -26,7 +27,7 @@
var new_post = document.getElementById('new_post'); var new_post = document.getElementById('new_post');
new_post.onclick = function() { new_post.onclick = function() {
require(['composer'], function(cmp) { require(['composer'], function(cmp) {
cmp.push(0, cid); cmp.push(0, cid);
}); });
} }
@ -34,7 +35,8 @@
'event:new_topic' 'event:new_topic'
]); ]);
socket.on('event:new_topic', function(data) { function onNewTopic(data) {
var html = templates.prepare(templates['category'].blocks['topics']).parse({ topics: [data] }), var html = templates.prepare(templates['category'].blocks['topics']).parse({ topics: [data] }),
topic = document.createElement('div'), topic = document.createElement('div'),
container = document.getElementById('topics-container'), container = document.getElementById('topics-container'),
@ -59,8 +61,9 @@
container.insertBefore(topic, null); container.insertBefore(topic, null);
$(topic).hide().fadeIn('slow'); $(topic).hide().fadeIn('slow');
} }
}); }
socket.on('event:new_topic', onNewTopic);
socket.emit('api:categories.getRecentReplies', cid); socket.emit('api:categories.getRecentReplies', cid);
socket.on('api:categories.getRecentReplies', function(posts) { socket.on('api:categories.getRecentReplies', function(posts) {
@ -92,4 +95,40 @@
} }
}); });
function onTopicsLoaded(topics) {
var html = templates.prepare(templates['category'].blocks['topics']).parse({ topics: topics }),
container = $('#topics-container');
jQuery('#topics-container, .category-sidebar').removeClass('hidden');
jQuery('#category-no-topics').remove();
container.append(html);
}
function loadMoreTopics(cid) {
loadingMoreTopics = true;
socket.emit('api:category.loadMore', {
cid: cid,
after: $('#topics-container').children().length
}, function(data) {
if(data.topics.length) {
onTopicsLoaded(data.topics);
loadingMoreTopics = false;
}
});
}
$(window).off('scroll').on('scroll', function(ev) {
var windowHeight = document.body.offsetHeight - $(window).height(),
half = windowHeight / 2;
if (document.body.scrollTop > half && !loadingMoreTopics) {
loadMoreTopics(cid);
}
});
})(); })();

@ -201,9 +201,8 @@
}, false); }, false);
// Infinite scrolling of posts // Infinite scrolling of posts
$(window).off('scroll'); $(window).off('scroll').on('scroll', function() {
$(window).on('scroll', function() { var windowHeight = document.body.offsetHeight - $(window).height(),
var windowHeight = document.body.offsetHeight - $(window).height(),
half = windowHeight / 2; half = windowHeight / 2;
if (document.body.scrollTop > half && !app.infiniteLoaderActive && $('#post-container').children().length) { if (document.body.scrollTop > half && !app.infiniteLoaderActive && $('#post-container').children().length) {

@ -17,8 +17,9 @@ var RDB = require('./redis.js'),
category_description = categoryData.description; category_description = categoryData.description;
function getTopicIds(next) { function getTopicIds(next) {
Categories.getTopicIds(category_id, next); Categories.getTopicIds(category_id, 0, 19, next);
} }
function getActiveUsers(next) { function getActiveUsers(next) {
Categories.getActiveUsers(category_id, next); Categories.getActiveUsers(category_id, next);
} }
@ -44,15 +45,7 @@ var RDB = require('./redis.js'),
function getTopics(next) { function getTopics(next) {
topics.getTopicsByTids(tids, current_user, function(topicsData) { topics.getTopicsByTids(tids, current_user, function(topicsData) {
// Float pinned topics to the top
topicsData = topicsData.sort(function(a, b) {
if (a.pinned !== b.pinned) return b.pinned - a.pinned;
else {
return b.lastposttime - a.lastposttime;
}
});
next(null, topicsData); next(null, topicsData);
}, category_id); }, category_id);
} }
@ -89,8 +82,16 @@ var RDB = require('./redis.js'),
}); });
} }
Categories.getTopicIds = function(cid, callback) { Categories.getCategoryTopics = function(cid, start, stop, uid, callback) {
RDB.smembers('categories:' + cid + ':tid', callback); Categories.getTopicIds(cid, start, stop, function(err, tids) {
topics.getTopicsByTids(tids, uid, function(topicsData) {
callback(topicsData);
}, cid);
});
}
Categories.getTopicIds = function(cid, start, stop, callback) {
RDB.zrevrange('categories:' + cid + ':tid', start, stop, callback);
} }
Categories.getActiveUsers = function(cid, callback) { Categories.getActiveUsers = function(cid, callback) {
@ -144,7 +145,7 @@ var RDB = require('./redis.js'),
} }
Categories.isTopicsRead = function(cid, uid, callback) { Categories.isTopicsRead = function(cid, uid, callback) {
RDB.smembers('categories:' + cid + ':tid', function(err, tids) { RDB.zrange('categories:' + cid + ':tid', 0, -1, function(err, tids) {
topics.hasReadTopics(tids, uid, function(hasRead) { topics.hasReadTopics(tids, uid, function(hasRead) {

@ -108,6 +108,20 @@ var RDB = require('./redis.js'),
}); });
} }
Posts.getPostField = function(pid, field, callback) {
RDB.hget('post:' + pid, field, function(err, data) {
if(err === null)
callback(data);
else
console.log(err);
});
}
Posts.setPostField = function(pid, field, value) {
RDB.hset('post:' + pid, field, value);
}
Posts.getPostsByPids = function(pids, callback) { Posts.getPostsByPids = function(pids, callback) {
var posts = []; var posts = [];
@ -141,35 +155,6 @@ var RDB = require('./redis.js'),
}); });
} }
Posts.getPostField = function(pid, field, callback) {
RDB.hget('post:' + pid, field, function(err, data) {
if(err === null)
callback(data);
else
console.log(err);
});
}
Posts.setPostField = function(pid, field, value) {
RDB.hset('post:' + pid, field, value);
}
Posts.getPostFields = function(pid, fields, callback) {
RDB.hmget('post:' + pid, fields, function(err, data) {
if(err === null) {
var returnData = {};
for(var i=0, ii=fields.length; i<ii; ++i) {
returnData[fields[i]] = data[i];
}
callback(returnData);
}
else
console.log(err);
});
}
Posts.get_cid_by_pid = function(pid, callback) { Posts.get_cid_by_pid = function(pid, callback) {
Posts.getPostField(pid, 'tid', function(tid) { Posts.getPostField(pid, 'tid', function(tid) {
if (tid) { if (tid) {
@ -317,7 +302,8 @@ var RDB = require('./redis.js'),
feed.updateTopic(tid, cid); feed.updateTopic(tid, cid);
RDB.zadd('categories:recent_posts:cid:' + cid, Date.now(), pid); RDB.zadd('categories:recent_posts:cid:' + cid, timestamp, pid);
RDB.zadd('categories:' + cid + ':tid', timestamp, tid);
// this is a bit of a naive implementation, defn something to look at post-MVP // this is a bit of a naive implementation, defn something to look at post-MVP
RDB.scard('cid:' + cid + ':active_users', function(amount) { RDB.scard('cid:' + cid + ':active_users', function(amount) {

@ -1,11 +1,12 @@
(function(RedisDB) { (function(RedisDB) {
var redis = require('redis'), var redis = require('redis'),
nconf = require('nconf'),
utils = require('./../public/src/utils.js'); utils = require('./../public/src/utils.js');
RedisDB.exports = redis.createClient(global.nconf.get('redis:port'), global.nconf.get('redis:host')); RedisDB.exports = redis.createClient(nconf.get('redis:port'), nconf.get('redis:host'));
if( global.nconf.get('redis:password') ) { if(nconf.get('redis:password')) {
RedisDB.exports.auth(global.nconf.get('redis:password')); RedisDB.exports.auth(nconf.get('redis:password'));
} }
RedisDB.exports.handle = function(error) { RedisDB.exports.handle = function(error) {

@ -133,6 +133,9 @@ var RDB = require('./redis.js'),
if (privileges.editable) { if (privileges.editable) {
topics.setTopicField(tid, 'pinned', 1); topics.setTopicField(tid, 'pinned', 1);
topics.getTopicField(tid, 'cid', function(cid) {
RDB.zadd('categories:' + cid + ':tid', Math.pow(2,53), tid);
});
if (socket) { if (socket) {
io.sockets.in('topic_' + tid).emit('event:topic_pinned', { io.sockets.in('topic_' + tid).emit('event:topic_pinned', {
@ -154,7 +157,9 @@ var RDB = require('./redis.js'),
if (privileges.editable) { if (privileges.editable) {
topics.setTopicField(tid, 'pinned', 0); topics.setTopicField(tid, 'pinned', 0);
topics.getTopicFields(tid, ['cid', 'lastposttime'], function(topicData) {
RDB.zadd('categories:' + topicData.cid + ':tid', topicData.lastposttime, tid);
});
if (socket) { if (socket) {
io.sockets.in('topic_' + tid).emit('event:topic_unpinned', { io.sockets.in('topic_' + tid).emit('event:topic_unpinned', {
tid: tid, tid: tid,
@ -172,9 +177,15 @@ var RDB = require('./redis.js'),
ThreadTools.move = function(tid, cid, socket) { ThreadTools.move = function(tid, cid, socket) {
topics.getTopicField(tid, 'cid', function(oldCid) { topics.getTopicFields(tid, ['cid', 'lastposttime'], function(topicData) {
var oldCid = topicData.cid;
var multi = RDB.multi();
multi.zrem('categories:' + oldCid + ':tid', tid);
multi.zadd('categories:' + cid + ':tid', topicData.lastposttime, tid);
multi.exec(function(err, result) {
RDB.smove('categories:' + oldCid + ':tid', 'categories:' + cid + ':tid', tid, function(err, result) {
if (!err && result === 1) { if (!err && result === 1) {
topics.setTopicField(tid, 'cid', cid); topics.setTopicField(tid, 'cid', cid);

@ -628,7 +628,7 @@ marked.setOptions({
// in future it may be possible to add topics to several categories, so leaving the door open here. // in future it may be possible to add topics to several categories, so leaving the door open here.
RDB.sadd('categories:' + category_id + ':tid', tid); RDB.zadd('categories:' + category_id + ':tid', timestamp, tid);
RDB.hincrby('category:' + category_id, 'topic_count', 1); RDB.hincrby('category:' + category_id, 'topic_count', 1);
RDB.incr('totaltopiccount'); RDB.incr('totaltopiccount');
@ -653,6 +653,17 @@ marked.setOptions({
}); });
} }
Topics.getTopicFields = function(tid, fields, callback) {
RDB.hmgetObject('topic:' + tid, fields, function(err, data) {
if(err === null) {
callback(data);
}
else {
console.log(err);
}
});
}
Topics.setTopicField = function(tid, field, value) { Topics.setTopicField = function(tid, field, value) {
RDB.hset('topic:' + tid, field, value); RDB.hset('topic:' + tid, field, value);
} }

@ -1,6 +1,74 @@
var RDB = require('./redis.js'),
async = require('async');
function upgradeCategory(cid, callback) {
RDB.type('categories:'+ cid +':tid', function(err, type) {
if (type === 'set') {
RDB.smembers('categories:' + cid + ':tid', function(err, tids) {
function moveTopic(tid, callback) {
RDB.hget('topic:' + tid, 'timestamp', function(err, timestamp) {
if(err)
return callback(err);
RDB.zadd('temp_categories:'+ cid + ':tid', timestamp, tid);
callback(null);
});
}
async.each(tids, moveTopic, function(err) {
if(!err) {
console.log('renaming ' + cid);
RDB.rename('temp_categories:' + cid + ':tid', 'categories:' + cid + ':tid');
callback(null);
}
else
callback(err);
});
});
} else {
console.log('category already upgraded '+ cid);
callback(null);
}
});
}
exports.upgrade = function() { exports.upgrade = function() {
console.log('upgrading nodebb now'); console.log('upgrading nodebb now');
var schema = [
function upgradeCategories(next) {
console.log('upgrading categories');
RDB.lrange('categories:cid', 0, -1, function(err, cids) {
async.each(cids, upgradeCategory, function(err) {
if(!err)
next(null, 'upgraded categories');
else
next(err, null);
});
});
},
function upgradeUsers(next) {
console.log('upgrading users');
next(null, 'upgraded users');
}
];
async.series(schema, function(err, results) {
if(!err)
console.log('upgrade complete');
else
console.log(err);
process.exit();
});
} }

@ -554,13 +554,22 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
socket.on('api:topic.loadMore', function(data, callback) { socket.on('api:topic.loadMore', function(data, callback) {
var start = data.after, var start = data.after,
end = start + 10; end = start + 9;
topics.getTopicPosts(data.tid, start, end, uid, function(posts) { topics.getTopicPosts(data.tid, start, end, uid, function(posts) {
callback({posts:posts}); callback({posts:posts});
}); });
}); });
socket.on('api:category.loadMore', function(data, callback) {
var start = data.after,
end = start + 9;
categories.getCategoryTopics(data.cid, start, end, uid, function(topics) {
callback({topics:topics});
});
});
socket.on('api:admin.topics.getMore', function(data) { socket.on('api:admin.topics.getMore', function(data) {
topics.getAllTopics(data.limit, data.after, function(topics) { topics.getAllTopics(data.limit, data.after, function(topics) {
socket.emit('api:admin.topics.getMore', JSON.stringify(topics)); socket.emit('api:admin.topics.getMore', JSON.stringify(topics));

Loading…
Cancel
Save