Improve users list scrolling performance

- fixed JS error when avatar mode is disabled
- added spinner at the bottom of the table
- scroll detection now happens earlier
- single/multiselect init is deferred so that the new rows are first appended
  into the list (more responsive) and initialized afterwards
- disabled users sorting after add (assuming they are always sorted on
  the server side)
This commit is contained in:
Vincent Petry 2014-02-03 18:00:39 +01:00
parent 14d1abf63f
commit c732764eb5
3 changed files with 72 additions and 25 deletions

View File

@ -48,7 +48,7 @@ ul.multiselectoptions > li input[type='checkbox']:checked+label {
font-weight: bold; font-weight: bold;
} }
div.multiselect { div.multiselect, select.multiselect {
display: inline-block; display: inline-block;
max-width: 400px; max-width: 400px;
min-width: 150px; min-width: 150px;
@ -58,6 +58,12 @@ div.multiselect {
vertical-align: bottom; vertical-align: bottom;
} }
/* To make a select look like a multiselect until it's initialized */
select.multiselect {
height: 30px;
min-width: 113px;
}
div.multiselect.active { div.multiselect.active {
background-color: #fff; background-color: #fff;
position: relative; position: relative;

View File

@ -933,3 +933,15 @@ div.crumb:active {
opacity:.7; opacity:.7;
} }
.appear {
opacity: 1;
transition: opacity 500ms ease 0s;
-moz-transition: opacity 500ms ease 0s;
-ms-transition: opacity 500ms ease 0s;
-o-transition: opacity 500ms ease 0s;
-webkit-transition: opacity 500ms ease 0s;
}
.appear.transparent {
opacity: 0;
}

View File

@ -85,19 +85,24 @@ var UserList = {
add: function (username, displayname, groups, subadmin, quota, sort) { add: function (username, displayname, groups, subadmin, quota, sort) {
var tr = $('tbody tr').first().clone(); var tr = $('tbody tr').first().clone();
if (tr.find('div.avatardiv')){ var subadminsEl;
var subadminSelect;
var groupsSelect;
if (tr.find('div.avatardiv').length){
$('div.avatardiv', tr).avatar(username, 32); $('div.avatardiv', tr).avatar(username, 32);
} }
tr.attr('data-uid', username); tr.attr('data-uid', username);
tr.attr('data-displayName', displayname); tr.attr('data-displayName', displayname);
tr.find('td.name').text(username); tr.find('td.name').text(username);
tr.find('td.displayName > span').text(displayname); tr.find('td.displayName > span').text(displayname);
var groupsSelect = $('<select multiple="multiple" class="groupsselect" data-placehoder="Groups" title="' + t('settings', 'Groups') + '"></select>')
// make them look like the multiselect buttons
// until they get time to really get initialized
groupsSelect = $('<select multiple="multiple" class="groupsselect multiselect button" data-placehoder="Groups" title="' + t('settings', 'Groups') + '"></select>')
.attr('data-username', username) .attr('data-username', username)
.data('user-groups', groups); .data('user-groups', groups);
tr.find('td.groups').empty();
if (tr.find('td.subadmins').length > 0) { if (tr.find('td.subadmins').length > 0) {
var subadminSelect = $('<select multiple="multiple" class="subadminsselect" data-placehoder="subadmins" title="' + t('settings', 'Group Admin') + '">') subadminSelect = $('<select multiple="multiple" class="subadminsselect multiselect button" data-placehoder="subadmins" title="' + t('settings', 'Group Admin') + '">')
.attr('data-username', username) .attr('data-username', username)
.data('user-groups', groups) .data('user-groups', groups)
.data('subadmin', subadmin); .data('subadmin', subadmin);
@ -109,11 +114,10 @@ var UserList = {
subadminSelect.append($('<option value="' + escapeHTML(group) + '">' + escapeHTML(group) + '</option>')); subadminSelect.append($('<option value="' + escapeHTML(group) + '">' + escapeHTML(group) + '</option>'));
} }
}); });
tr.find('td.groups').append(groupsSelect); tr.find('td.groups').empty().append(groupsSelect);
UserList.applyMultiplySelect(groupsSelect); subadminsEl = tr.find('td.subadmins');
if (tr.find('td.subadmins').length > 0) { if (subadminsEl.length > 0) {
tr.find('td.subadmins').append(subadminSelect); subadminsEl.append(subadminSelect);
UserList.applyMultiplySelect(subadminSelect);
} }
if (tr.find('td.remove img').length === 0 && OC.currentUser !== username) { if (tr.find('td.remove img').length === 0 && OC.currentUser !== username) {
var rm_img = $('<img class="svg action">').attr({ var rm_img = $('<img class="svg action">').attr({
@ -139,11 +143,11 @@ var UserList = {
} }
} }
$(tr).appendTo('tbody'); $(tr).appendTo('tbody');
if (sort) { if (sort) {
UserList.doSort(); UserList.doSort();
} }
quotaSelect.singleSelect();
quotaSelect.on('change', function () { quotaSelect.on('change', function () {
var uid = $(this).parent().parent().attr('data-uid'); var uid = $(this).parent().parent().attr('data-uid');
var quota = $(this).val(); var quota = $(this).val();
@ -153,6 +157,16 @@ var UserList = {
} }
}); });
}); });
// defer init so the user first sees the list appear more quickly
window.setTimeout(function(){
quotaSelect.singleSelect();
UserList.applyMultiplySelect(groupsSelect);
if (subadminSelect) {
UserList.applyMultiplySelect(subadminSelect);
}
}, 0);
return tr;
}, },
// From http://my.opera.com/GreyWyvern/blog/show.dml/1671288 // From http://my.opera.com/GreyWyvern/blog/show.dml/1671288
alphanum: function(a, b) { alphanum: function(a, b) {
@ -211,26 +225,34 @@ var UserList = {
} }
UserList.updating = true; UserList.updating = true;
$.get(OC.Router.generate('settings_ajax_userlist', { offset: UserList.offset, limit: UserList.usersToLoad }), function (result) { $.get(OC.Router.generate('settings_ajax_userlist', { offset: UserList.offset, limit: UserList.usersToLoad }), function (result) {
var loadedUsers = 0;
var trs = [];
if (result.status === 'success') { if (result.status === 'success') {
//The offset does not mirror the amount of users available, //The offset does not mirror the amount of users available,
//because it is backend-dependent. For correct retrieval, //because it is backend-dependent. For correct retrieval,
//always the limit(requested amount of users) needs to be added. //always the limit(requested amount of users) needs to be added.
UserList.offset += UserList.usersToLoad;
$.each(result.data, function (index, user) { $.each(result.data, function (index, user) {
if($('tr[data-uid="' + user.name + '"]').length > 0) { if($('tr[data-uid="' + user.name + '"]').length > 0) {
return true; return true;
} }
var tr = UserList.add(user.name, user.displayname, user.groups, user.subadmin, user.quota, false); var tr = UserList.add(user.name, user.displayname, user.groups, user.subadmin, user.quota, false);
if (index === 9) { tr.addClass('appear transparent');
$(tr).bind('inview', function (event, isInView, visiblePartX, visiblePartY) { trs.push(tr);
$(this).unbind(event); loadedUsers++;
UserList.update();
});
}
}); });
if (result.data.length > 0) { if (result.data.length > 0) {
UserList.doSort(); UserList.doSort();
} }
else {
UserList.noMoreEntries = true;
}
UserList.offset += loadedUsers;
// animate
setTimeout(function() {
for (var i = 0; i < trs.length; i++) {
trs[i].removeClass('transparent');
}
}, 0);
} }
UserList.updating = false; UserList.updating = false;
}); });
@ -239,7 +261,7 @@ var UserList = {
applyMultiplySelect: function (element) { applyMultiplySelect: function (element) {
var checked = []; var checked = [];
var user = element.attr('data-username'); var user = element.attr('data-username');
if ($(element).attr('class') === 'groupsselect') { if ($(element).hasClass('groupsselect')) {
if (element.data('userGroups')) { if (element.data('userGroups')) {
checked = element.data('userGroups'); checked = element.data('userGroups');
} }
@ -295,7 +317,7 @@ var UserList = {
minWidth: 100 minWidth: 100
}); });
} }
if ($(element).attr('class') === 'subadminsselect') { if ($(element).hasClass('subadminsselect')) {
if (element.data('subadmin')) { if (element.data('subadmin')) {
checked = element.data('subadmin'); checked = element.data('subadmin');
} }
@ -330,17 +352,24 @@ var UserList = {
minWidth: 100 minWidth: 100
}); });
} }
} },
_onScroll: function(e) {
if (!!UserList.noMoreEntries) {
return;
}
if ($(window).scrollTop() + $(window).height() > $(document).height() - 500) {
UserList.update(true);
}
},
}; };
$(document).ready(function () { $(document).ready(function () {
UserList.doSort(); UserList.doSort();
UserList.availableGroups = $('#content table').data('groups'); UserList.availableGroups = $('#content table').data('groups');
$('tbody tr:last').bind('inview', function (event, isInView, visiblePartX, visiblePartY) { OC.Router.registerLoadedCallback(function() {
OC.Router.registerLoadedCallback(function () { $(window).scroll(function(e) {UserList._onScroll(e);});
UserList.update();
});
}); });
$('select[multiple]').each(function (index, element) { $('select[multiple]').each(function (index, element) {