From c732764eb5159b893cdb97fc780937a883f48b58 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Mon, 3 Feb 2014 18:00:39 +0100 Subject: [PATCH 1/2] 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) --- core/css/multiselect.css | 8 ++++- core/css/styles.css | 12 +++++++ settings/js/users.js | 77 +++++++++++++++++++++++++++------------- 3 files changed, 72 insertions(+), 25 deletions(-) diff --git a/core/css/multiselect.css b/core/css/multiselect.css index 60f2f47e69..8d949e7cdb 100644 --- a/core/css/multiselect.css +++ b/core/css/multiselect.css @@ -48,7 +48,7 @@ ul.multiselectoptions > li input[type='checkbox']:checked+label { font-weight: bold; } -div.multiselect { +div.multiselect, select.multiselect { display: inline-block; max-width: 400px; min-width: 150px; @@ -58,6 +58,12 @@ div.multiselect { 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 { background-color: #fff; position: relative; diff --git a/core/css/styles.css b/core/css/styles.css index bee44785f1..cbfab618ec 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -933,3 +933,15 @@ div.crumb:active { 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; +} + diff --git a/settings/js/users.js b/settings/js/users.js index 6886db668b..a6edc02e16 100644 --- a/settings/js/users.js +++ b/settings/js/users.js @@ -85,19 +85,24 @@ var UserList = { add: function (username, displayname, groups, subadmin, quota, sort) { 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); } tr.attr('data-uid', username); tr.attr('data-displayName', displayname); tr.find('td.name').text(username); tr.find('td.displayName > span').text(displayname); - var groupsSelect = $('') + + // make them look like the multiselect buttons + // until they get time to really get initialized + groupsSelect = $('') .attr('data-username', username) .data('user-groups', groups); - tr.find('td.groups').empty(); if (tr.find('td.subadmins').length > 0) { - var subadminSelect = $('') .attr('data-username', username) .data('user-groups', groups) .data('subadmin', subadmin); @@ -109,11 +114,10 @@ var UserList = { subadminSelect.append($('')); } }); - tr.find('td.groups').append(groupsSelect); - UserList.applyMultiplySelect(groupsSelect); - if (tr.find('td.subadmins').length > 0) { - tr.find('td.subadmins').append(subadminSelect); - UserList.applyMultiplySelect(subadminSelect); + tr.find('td.groups').empty().append(groupsSelect); + subadminsEl = tr.find('td.subadmins'); + if (subadminsEl.length > 0) { + subadminsEl.append(subadminSelect); } if (tr.find('td.remove img').length === 0 && OC.currentUser !== username) { var rm_img = $('').attr({ @@ -139,11 +143,11 @@ var UserList = { } } $(tr).appendTo('tbody'); + if (sort) { UserList.doSort(); } - quotaSelect.singleSelect(); quotaSelect.on('change', function () { var uid = $(this).parent().parent().attr('data-uid'); 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 alphanum: function(a, b) { @@ -211,26 +225,34 @@ var UserList = { } UserList.updating = true; $.get(OC.Router.generate('settings_ajax_userlist', { offset: UserList.offset, limit: UserList.usersToLoad }), function (result) { + var loadedUsers = 0; + var trs = []; if (result.status === 'success') { //The offset does not mirror the amount of users available, //because it is backend-dependent. For correct retrieval, //always the limit(requested amount of users) needs to be added. - UserList.offset += UserList.usersToLoad; $.each(result.data, function (index, user) { if($('tr[data-uid="' + user.name + '"]').length > 0) { return true; } var tr = UserList.add(user.name, user.displayname, user.groups, user.subadmin, user.quota, false); - if (index === 9) { - $(tr).bind('inview', function (event, isInView, visiblePartX, visiblePartY) { - $(this).unbind(event); - UserList.update(); - }); - } + tr.addClass('appear transparent'); + trs.push(tr); + loadedUsers++; }); if (result.data.length > 0) { 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; }); @@ -239,7 +261,7 @@ var UserList = { applyMultiplySelect: function (element) { var checked = []; var user = element.attr('data-username'); - if ($(element).attr('class') === 'groupsselect') { + if ($(element).hasClass('groupsselect')) { if (element.data('userGroups')) { checked = element.data('userGroups'); } @@ -295,7 +317,7 @@ var UserList = { minWidth: 100 }); } - if ($(element).attr('class') === 'subadminsselect') { + if ($(element).hasClass('subadminsselect')) { if (element.data('subadmin')) { checked = element.data('subadmin'); } @@ -330,17 +352,24 @@ var UserList = { minWidth: 100 }); } - } + }, + + _onScroll: function(e) { + if (!!UserList.noMoreEntries) { + return; + } + if ($(window).scrollTop() + $(window).height() > $(document).height() - 500) { + UserList.update(true); + } + }, }; $(document).ready(function () { UserList.doSort(); UserList.availableGroups = $('#content table').data('groups'); - $('tbody tr:last').bind('inview', function (event, isInView, visiblePartX, visiblePartY) { - OC.Router.registerLoadedCallback(function () { - UserList.update(); - }); + OC.Router.registerLoadedCallback(function() { + $(window).scroll(function(e) {UserList._onScroll(e);}); }); $('select[multiple]').each(function (index, element) { From 58b1dc5e76bb18ebae7e980566153b04cd1c76b2 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Tue, 18 Feb 2014 10:52:05 +0100 Subject: [PATCH 2/2] Added loading spinner to users list on scroll --- settings/js/users.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/settings/js/users.js b/settings/js/users.js index a6edc02e16..1028a003bc 100644 --- a/settings/js/users.js +++ b/settings/js/users.js @@ -223,6 +223,7 @@ var UserList = { if (UserList.updating) { return; } + $('table+.loading').css('visibility', 'visible'); UserList.updating = true; $.get(OC.Router.generate('settings_ajax_userlist', { offset: UserList.offset, limit: UserList.usersToLoad }), function (result) { var loadedUsers = 0; @@ -242,9 +243,11 @@ var UserList = { }); if (result.data.length > 0) { UserList.doSort(); + $('table+.loading').css('visibility', 'hidden'); } else { UserList.noMoreEntries = true; + $('table+.loading').remove(); } UserList.offset += loadedUsers; // animate @@ -371,6 +374,7 @@ $(document).ready(function () { OC.Router.registerLoadedCallback(function() { $(window).scroll(function(e) {UserList._onScroll(e);}); }); + $('table').after($('')); $('select[multiple]').each(function (index, element) { UserList.applyMultiplySelect($(element));