refactor user search

use pagination on results
removed infinite scroll
changed the term and section to use the query param as well
pagination urls respect search
v1.18.x
barisusakli 9 years ago
parent 768c1b37d4
commit 84f88a6f15

@ -1,43 +1,13 @@
'use strict';
/* globals define, socket, utils */
/* globals define */
define('forum/account/followers', ['forum/account/header', 'forum/infinitescroll'], function(header, infinitescroll) {
define('forum/account/followers', ['forum/account/header'], function(header) {
var Followers = {};
Followers.init = function() {
header.init();
infinitescroll.init(function(direction) {
Followers.loadMore(direction, 'account/followers', 'followers:' + ajaxify.data.uid);
});
};
Followers.loadMore = function(direction, tpl, set) {
if (direction < 0) {
return;
}
infinitescroll.loadMore('user.loadMore', {
set: set,
after: $('#users-container').attr('data-nextstart')
}, function(data, done) {
if (data.users && data.users.length) {
onUsersLoaded(tpl, data.users, done);
$('#users-container').attr('data-nextstart', data.nextStart);
} else {
done();
}
});
};
function onUsersLoaded(tpl, users, callback) {
app.parseAndTranslate(tpl, 'users', {users: users}, function(html) {
$('#users-container').append(html);
utils.addCommasToNumbers(html.find('.formatted-number'));
callback();
});
}
return Followers;
});

@ -2,15 +2,11 @@
/* globals define */
define('forum/account/following', ['forum/account/header', 'forum/infinitescroll', 'forum/account/followers'], function(header, infinitescroll, followers) {
define('forum/account/following', ['forum/account/header'], function(header) {
var Following = {};
Following.init = function() {
header.init();
infinitescroll.init(function(direction) {
followers.loadMore(direction, 'account/following', 'following:' + ajaxify.data.uid);
});
};
return Following;

@ -22,15 +22,10 @@ define('forum/search', ['search', 'autocomplete'], function(searchModule, autoco
$('#advanced-search').off('submit').on('submit', function(e) {
e.preventDefault();
var input = $('#search-input');
var searchData = getSearchData();
searchData.term = input.val();
searchModule.query(searchData, function() {
input.val('');
searchModule.query(getSearchData(), function() {
$('#search-input').val('');
});
return false;
});
handleSavePreferences();
@ -43,7 +38,7 @@ define('forum/search', ['search', 'autocomplete'], function(searchModule, autoco
var searchData = {
in: $('#search-in').val()
};
searchData.term = $('#search-input').val();
if (searchData.in === 'posts' || searchData.in === 'titlesposts' || searchData.in === 'titles') {
searchData.by = form.find('#posted-by-user').val();
searchData.categories = form.find('#posted-in-categories').val();
@ -71,6 +66,10 @@ define('forum/search', ['search', 'autocomplete'], function(searchModule, autoco
params = utils.merge(searchData, params);
if (params) {
if (params.term) {
$('#search-input').val(params.term);
}
if (params.in) {
$('#search-in').val(params.in);
updateFormItemVisiblity(params.in);

@ -1,16 +1,24 @@
'use strict';
/* globals define, socket, app, templates, bootbox, ajaxify */
/* globals define, socket, app, templates, bootbox, utils */
define('forum/users', ['translator'], function(translator) {
var Users = {};
var loadingMoreUsers = false;
var searchTimeoutID = 0;
$(window).on('action:ajaxify.start', function() {
if (searchTimeoutID) {
clearTimeout(searchTimeoutID);
searchTimeoutID = 0;
}
});
Users.init = function() {
app.enterRoom('user_list');
$('.nav-pills li').removeClass('active').find('a[href="' + window.location.pathname + '"]').parent().addClass('active');
var section = utils.params().section ? ('?section=' + utils.params().section) : '';
$('.nav-pills li').removeClass('active').find('a[href="' + window.location.pathname + section + '"]').parent().addClass('active');
handleSearch();
@ -18,110 +26,55 @@ define('forum/users', ['translator'], function(translator) {
socket.removeListener('event:user_status_change', onUserStatusChange);
socket.on('event:user_status_change', onUserStatusChange);
$('#load-more-users-btn').on('click', loadMoreUsers);
$(window).off('scroll').on('scroll', function() {
var bottom = ($(document).height() - $(window).height()) * 0.9;
if ($(window).scrollTop() > bottom && !loadingMoreUsers) {
loadMoreUsers();
}
});
};
function loadMoreUsers() {
if ($('#search-user').val()) {
return;
}
if (ajaxify.data.setName) {
startLoading(ajaxify.data.setName, $('#users-container').children('.registered-user').length);
}
}
function startLoading(set, after) {
loadingMoreUsers = true;
socket.emit('user.loadMore', {
set: set,
after: after
}, function(err, data) {
if (err) {
return app.alertError(err.message);
}
if (data && data.users.length) {
onUsersLoaded(data);
$('#load-more-users-btn').removeClass('disabled');
} else {
$('#load-more-users-btn').addClass('disabled');
}
loadingMoreUsers = false;
});
}
function onUsersLoaded(data) {
data.users = data.users.filter(function(user) {
return !$('.users-box[data-uid="' + user.uid + '"]').length;
});
templates.parse('users', 'users', data, function(html) {
translator.translate(html, function(translated) {
translated = $(translated);
$('#users-container').append(translated);
translated.find('span.timeago').timeago();
utils.addCommasToNumbers(translated.find('.formatted-number'));
$('#users-container .anon-user').appendTo($('#users-container'));
});
});
}
function handleSearch() {
var timeoutId = 0;
searchTimeoutID = 0;
$('#search-user').on('keyup', function() {
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = 0;
if (searchTimeoutID) {
clearTimeout(searchTimeoutID);
searchTimeoutID = 0;
}
timeoutId = setTimeout(doSearch, 250);
searchTimeoutID = setTimeout(doSearch, 150);
});
$('.search select, .search input[type="checkbox"]').on('change', function() {
doSearch();
});
$('.users').on('click', '.pagination a', function() {
doSearch($(this).attr('data-page'));
return false;
});
}
function doSearch(page) {
$('[component="user/search/icon"]').removeClass('fa-search').addClass('fa-spinner fa-spin');
var username = $('#search-user').val();
page = page || 1;
var activeSection = getActiveSection();
var query = {
section: activeSection,
page: page
};
if (!username) {
return loadPage(page);
return loadPage(query);
}
var activeSection = getActiveSection();
socket.emit('user.search', {
query: username,
page: page,
searchBy: 'username',
sortBy: $('.search select').val() || getSortBy(),
onlineOnly: $('.search .online-only').is(':checked') || (activeSection === 'online'),
bannedOnly: activeSection === 'banned',
flaggedOnly: activeSection === 'flagged'
}, function(err, data) {
if (err) {
return app.alertError(err.message);
}
renderSearchResults(data);
});
query.term = username;
query.sortBy = getSortBy();
if ($('.search .online-only').is(':checked') || (activeSection === 'online')) {
query.onlineOnly = true;
}
if (activeSection === 'banned') {
query.bannedOnly = true;
}
if (activeSection === 'flagged') {
query.flaggedOnly = true;
}
loadPage(query);
}
function getSortBy() {
@ -137,16 +90,17 @@ define('forum/users', ['translator'], function(translator) {
return sortBy;
}
function loadPage(page) {
var section = getActiveSection();
section = section !== 'users' ? section : '';
$.get('/api/users/' + section + '?page=' + page, function(data) {
renderSearchResults(data);
function loadPage(query) {
var qs = decodeURIComponent($.param(query));
$.get('/api/users?' + qs, renderSearchResults).fail(function(xhrErr) {
if (xhrErr && xhrErr.responseJSON && xhrErr.responseJSON.error) {
app.alertError(xhrErr.responseJSON.error);
}
});
}
function renderSearchResults(data) {
$('#load-more-users-btn').addClass('hide');
templates.parse('partials/paginator', {pagination: data.pagination}, function(html) {
$('.pagination-container').replaceWith(html);
});
@ -156,6 +110,7 @@ define('forum/users', ['translator'], function(translator) {
translated = $(translated);
$('#users-container').html(translated);
translated.find('span.timeago').timeago();
$('[component="user/search/icon"]').addClass('fa-search').removeClass('fa-spinner fa-spin');
});
});
}
@ -173,9 +128,7 @@ define('forum/users', ['translator'], function(translator) {
}
function getActiveSection() {
var url = window.location.href;
var parts = url.split('/');
return parts[parts.length - 1];
return utils.params().section || '';
}
function handleInvite() {

@ -4,8 +4,8 @@
define('search', ['navigator', 'translator'], function(nav, translator) {
var Search = {
current: {}
};
current: {}
};
Search.query = function(data, callback) {
var term = data.term;
@ -22,11 +22,11 @@ define('search', ['navigator', 'translator'], function(nav, translator) {
return app.alertError('[[error:invalid-search-term]]');
}
ajaxify.go('search/' + term + '?' + createQueryString(data));
ajaxify.go('search?' + createQueryString(data));
callback();
} else {
var cleanedTerm = term.replace(topicSearch[0], ''),
tid = topicSearch[1];
var cleanedTerm = term.replace(topicSearch[0], '');
var tid = topicSearch[1];
if (cleanedTerm.length > 0) {
Search.queryTopic(tid, cleanedTerm, callback);
@ -38,8 +38,9 @@ define('search', ['navigator', 'translator'], function(nav, translator) {
var searchIn = data['in'] || 'titlesposts';
var postedBy = data.by || '';
var query = {
'in': searchIn
};
term: data.term,
'in': searchIn
};
if (postedBy && (searchIn === 'posts' || searchIn === 'titles' || searchIn === 'titlesposts')) {
query.by = postedBy;

@ -1,10 +1,11 @@
'use strict';
var async = require('async'),
var async = require('async');
user = require('../../user'),
helpers = require('../helpers'),
accountHelpers = require('./helpers');
var user = require('../../user');
var helpers = require('../helpers');
var accountHelpers = require('./helpers');
var pagination = require('../../pagination');
var followController = {};
@ -19,6 +20,11 @@ followController.getFollowers = function(req, res, next) {
function getFollow(tpl, name, req, res, callback) {
var userData;
var page = parseInt(req.query.page, 10) || 1;
var resultsPerPage = 50;
var start = Math.max(0, page - 1) * resultsPerPage;
var stop = start + resultsPerPage - 1;
async.waterfall([
function(next) {
accountHelpers.getBaseUser(req.params.userslug, req.uid, next);
@ -29,7 +35,7 @@ function getFollow(tpl, name, req, res, callback) {
return callback();
}
var method = name === 'following' ? 'getFollowing' : 'getFollowers';
user[method](userData.uid, 0, 49, next);
user[method](userData.uid, start, stop, next);
}
], function(err, users) {
if (err) {
@ -37,8 +43,10 @@ function getFollow(tpl, name, req, res, callback) {
}
userData.users = users;
userData.nextStart = 50;
userData.title = '[[pages:' + tpl + ', ' + userData.username + ']]';
var count = name === 'following' ? userData.followingCount : userData.followerCount;
var pageCount = Math.ceil(count / resultsPerPage);
userData.pagination = pagination.create(page, pageCount);
userData.breadcrumbs = helpers.buildBreadcrumbs([{text: userData.username, url: '/user/' + userData.userslug}, {text: '[[user:' + name + ']]'}]);
res.render(tpl, userData);

@ -133,7 +133,19 @@ helpers.getBaseUser = function(userslug, callerUID, callback) {
async.parallel({
user: function(next) {
user.getUserFields(uid, ['uid', 'username', 'userslug', 'picture', 'cover:url', 'cover:position', 'status', 'lastonline', 'groupTitle'], next);
user.getUserFields(uid, [
'uid',
'username',
'userslug',
'picture',
'cover:url',
'cover:position',
'status',
'lastonline',
'groupTitle',
'followingCount',
'followerCount'
], next);
},
isAdmin: function(next) {
user.isAdministrator(callerUID, next);

@ -28,7 +28,7 @@ searchController.search = function(req, res, next) {
}
var data = {
query: req.params.term,
query: req.query.term,
searchIn: req.query.in || 'posts',
postedBy: req.query.by,
categories: req.query.categories,
@ -59,7 +59,7 @@ searchController.search = function(req, res, next) {
searchData.showAsTopics = req.query.showAs === 'topics';
searchData.title = '[[global:header.search]]';
searchData.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[global:search]]'}]);
searchData.expandSearch = !req.params.term;
searchData.expandSearch = !req.query.term;
res.render('search', searchData);
});

@ -11,10 +11,61 @@ var helpers = require('./helpers');
var usersController = {};
usersController.index = function(req, res, next) {
var section = req.query.section || 'joindate';
var sectionToController = {
joindate: usersController.getUsersSortedByJoinDate,
online: usersController.getOnlineUsers,
'sort-posts': usersController.getUsersSortedByPosts,
'sort-reputation': usersController.getUsersSortedByReputation,
banned: usersController.getBannedUsers,
flagged: usersController.getFlaggedUsers
};
if (req.query.term) {
usersController.search(req, res, next);
} else if (sectionToController[section]) {
sectionToController[section](req, res, next);
} else {
usersController.getUsersSortedByJoinDate(req, res, next);
}
};
usersController.search = function(req, res, next) {
async.parallel({
search: function(next) {
user.search({
query: req.query.term,
searchBy: req.query.searchBy || 'username',
page: req.query.page || 1,
sortBy: req.query.sortBy,
onlineOnly: req.query.onlineOnly === 'true',
bannedOnly: req.query.bannedOnly === 'true',
flaggedOnly: req.query.flaggedOnly === 'true'
}, next);
},
isAdminOrGlobalMod: function(next) {
user.isAdminOrGlobalMod(req.uid, next);
}
}, function(err, results) {
if (err) {
return next(err);
}
var section = req.query.section || 'joindate';
results.search.isAdminOrGlobalMod = results.isAdminOrGlobalMod;
results.search.pagination = pagination.create(req.query.page, results.search.pageCount, req.query);
results.search['section_' + section] = true;
render(req, res, results.search, next);
});
};
usersController.getOnlineUsers = function(req, res, next) {
async.parallel({
users: function(next) {
usersController.getUsers('users:online', req.uid, req.query.page, next);
usersController.getUsers('users:online', req.uid, req.query, next);
},
guests: function(next) {
require('../socket.io/admin/rooms').getTotalGuestCount(next);
@ -56,7 +107,7 @@ usersController.getUsersSortedByJoinDate = function(req, res, next) {
};
usersController.getBannedUsers = function(req, res, next) {
usersController.getUsers('users:banned', req.uid, req.query.page, function(err, userData) {
usersController.getUsers('users:banned', req.uid, req.query, function(err, userData) {
if (err) {
return next(err);
}
@ -70,7 +121,7 @@ usersController.getBannedUsers = function(req, res, next) {
};
usersController.getFlaggedUsers = function(req, res, next) {
usersController.getUsers('users:flags', req.uid, req.query.page, function(err, userData) {
usersController.getUsers('users:flags', req.uid, req.query, function(err, userData) {
if (err) {
return next(err);
}
@ -84,15 +135,16 @@ usersController.getFlaggedUsers = function(req, res, next) {
};
usersController.renderUsersPage = function(set, req, res, next) {
usersController.getUsers(set, req.uid, req.query.page, function(err, userData) {
usersController.getUsers(set, req.uid, req.query, function(err, userData) {
if (err) {
return next(err);
}
render(req, res, userData, next);
});
};
usersController.getUsers = function(set, uid, page, callback) {
usersController.getUsers = function(set, uid, query, callback) {
var setToData = {
'users:postcount': {title: '[[pages:users/sort-posts]]', crumb: '[[users:top_posters]]'},
'users:reputation': {title: '[[pages:users/sort-reputation]]', crumb: '[[users:most_reputation]]'},
@ -112,17 +164,14 @@ usersController.getUsers = function(set, uid, page, callback) {
breadcrumbs.unshift({text: '[[global:users]]', url: '/users'});
}
page = parseInt(page, 10) || 1;
var page = parseInt(query.page, 10) || 1;
var resultsPerPage = parseInt(meta.config.userSearchResultsPerPage, 10) || 50;
var start = Math.max(0, page - 1) * resultsPerPage;
var stop = start + resultsPerPage - 1;
async.parallel({
isAdministrator: function(next) {
user.isAdministrator(uid, next);
},
isGlobalMod: function(next) {
user.isGlobalModerator(uid, next);
isAdminOrGlobalMod: function(next) {
user.isAdminOrGlobalMod(uid, next);
},
usersData: function(next) {
usersController.getUsersAndCount(set, uid, start, stop, next);
@ -134,16 +183,14 @@ usersController.getUsers = function(set, uid, page, callback) {
var pageCount = Math.ceil(results.usersData.count / resultsPerPage);
var userData = {
loadmore_display: results.usersData.count > (stop - start + 1) ? 'block' : 'hide',
users: results.usersData.users,
pagination: pagination.create(page, pageCount),
pagination: pagination.create(page, pageCount, query),
userCount: results.usersData.count,
title: setToData[set].title || '[[pages:users/latest]]',
breadcrumbs: helpers.buildBreadcrumbs(breadcrumbs),
setName: set,
isAdminOrGlobalMod: results.isAdministrator || results.isGlobalMod
isAdminOrGlobalMod: results.isAdminOrGlobalMod
};
userData['route_' + set] = true;
userData['section_' + (query.section || 'joindate')] = true;
callback(null, userData);
});
};

@ -33,7 +33,7 @@ function mainRoutes(app, middleware, controllers) {
setupPageRoute(app, '/compose', middleware, [], controllers.compose);
setupPageRoute(app, '/confirm/:code', middleware, [], controllers.confirmEmail);
setupPageRoute(app, '/outgoing', middleware, [], controllers.outgoing);
setupPageRoute(app, '/search/:term?', middleware, [], controllers.search.search);
setupPageRoute(app, '/search', middleware, [], controllers.search.search);
setupPageRoute(app, '/reset/:code?', middleware, [], controllers.reset);
setupPageRoute(app, '/tos', middleware, [], controllers.termsOfUse);
@ -73,12 +73,7 @@ function categoryRoutes(app, middleware, controllers) {
function userRoutes(app, middleware, controllers) {
var middlewares = [middleware.checkGlobalPrivacySettings];
setupPageRoute(app, '/users', middleware, middlewares, controllers.users.getUsersSortedByJoinDate);
setupPageRoute(app, '/users/online', middleware, middlewares, controllers.users.getOnlineUsers);
setupPageRoute(app, '/users/sort-posts', middleware, middlewares, controllers.users.getUsersSortedByPosts);
setupPageRoute(app, '/users/sort-reputation', middleware, middlewares, controllers.users.getUsersSortedByReputation);
setupPageRoute(app, '/users/banned', middleware, middlewares, controllers.users.getBannedUsers);
setupPageRoute(app, '/users/flagged', middleware, middlewares, controllers.users.getFlaggedUsers);
setupPageRoute(app, '/users', middleware, middlewares, controllers.users.index);
}
function groupRoutes(app, middleware, controllers) {
@ -91,15 +86,15 @@ function groupRoutes(app, middleware, controllers) {
module.exports = function(app, middleware, hotswapIds) {
var routers = [
express.Router(), // plugin router
express.Router(), // main app router
express.Router() // auth router
],
router = routers[1],
pluginRouter = routers[0],
authRouter = routers[2],
relativePath = nconf.get('relative_path'),
ensureLoggedIn = require('connect-ensure-login');
express.Router(), // plugin router
express.Router(), // main app router
express.Router() // auth router
];
var router = routers[1];
var pluginRouter = routers[0];
var authRouter = routers[2];
var relativePath = nconf.get('relative_path');
var ensureLoggedIn = require('connect-ensure-login');
if (Array.isArray(hotswapIds) && hotswapIds.length) {
for(var idx,x=0;x<hotswapIds.length;x++) {

@ -265,51 +265,6 @@ SocketUser.getUnreadCounts = function(socket, data, callback) {
}, callback);
};
SocketUser.loadMore = function(socket, data, callback) {
if (!data || !data.set || parseInt(data.after, 10) < 0) {
return callback(new Error('[[error:invalid-data]]'));
}
if (!socket.uid && !!parseInt(meta.config.privateUserInfo, 10)) {
return callback(new Error('[[error:no-privileges]]'));
}
var start = parseInt(data.after, 10);
var stop = start + 19;
async.parallel({
isAdmin: function(next) {
user.isAdministrator(socket.uid, next);
},
isGlobalMod: function(next) {
user.isGlobalModerator(socket.uid, next);
},
users: function(next) {
user.getUsersFromSet(data.set, socket.uid, start, stop, next);
}
}, function(err, results) {
if (err) {
return callback(err);
}
if (data.set === 'users:banned' && !results.isAdmin && !results.isGlobalMod) {
return callback(new Error('[[error:no-privileges]]'));
}
if (!results.isAdmin && data.set === 'users:online') {
results.users = results.users.filter(function(user) {
return user.status !== 'offline';
});
}
var result = {
users: results.users,
nextStart: stop + 1,
};
result['route_' + data.set] = true;
callback(null, result);
});
};
SocketUser.invite = function(socket, email, callback) {
if (!email || !socket.uid) {
return callback(new Error('[[error:invalid-data]]'));

@ -84,7 +84,16 @@ module.exports = function(User) {
function filterAndSortUids(uids, data, callback) {
var sortBy = data.sortBy || 'joindate';
var fields = ['uid', 'status', 'lastonline', 'banned', 'flags', sortBy];
var fields = ['uid', sortBy];
if (data.onlineOnly) {
fields = fields.concat(['status', 'lastonline']);
}
if (data.bannedOnly) {
fields.push('banned');
}
if (data.flaggedOnly) {
fields.push('flags');
}
User.getUsersFields(uids, fields, function(err, userData) {
if (err) {

Loading…
Cancel
Save