mirror of
				https://github.com/NodeBB/NodeBB.git
				synced 2025-10-31 11:05:54 +01:00 
			
		
		
		
	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
This commit is contained in:
		| @@ -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); | ||||||
|  |  | ||||||
| @@ -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,8 +201,7 @@ | |||||||
| 		}, 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; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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) { | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										46
									
								
								src/posts.js
									
									
									
									
									
								
							
							
						
						
									
										46
									
								
								src/posts.js
									
									
									
									
									
								
							| @@ -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)); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user