From 20e375ddcd56f8e27eca06d6ed4e4287fbcb7fe7 Mon Sep 17 00:00:00 2001 From: Anthony Chin Date: Thu, 20 Mar 2014 14:06:25 -0400 Subject: [PATCH] Implement pagination in project list screen The project list screen was taking a long time to render over a large amount of projects (1,000+) and with even larger number of projects (3,000+), it could make the browser unresponsive. Project list screen now uses pagination to resolve this issue. The number of projects displayed is determined by the 'Maximum Page Size' user preference. Bug: issue 2215 Change-Id: Icd0a7d54fd5c5b3c2301c31026e7c6717a648a24 --- .../gerrit/client/admin/AdminConstants.java | 3 + .../client/admin/AdminConstants.properties | 2 + .../client/admin/ProjectListScreen.java | 87 +++++++++++++++++-- .../gerrit/client/projects/ProjectMap.java | 26 ++++-- .../gerrit/client/ui/ProjectsTable.java | 6 +- 5 files changed, 106 insertions(+), 18 deletions(-) diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java index 7affd1ce45..530c0a77bc 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java @@ -130,4 +130,7 @@ public interface AdminConstants extends Constants { String sectionTypeReference(); String sectionTypeSection(); Map sectionNames(); + + String pagedProjectListPrev(); + String pagedProjectListNext(); } diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties index fa1dc87367..c54adb51fd 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties @@ -95,6 +95,8 @@ noGroupSelected = (No group selected) errorNoMatchingGroups = No Matching Groups errorNoGitRepository = No Git Repository +pagedProjectListPrev = ⇦Prev +pagedProjectListNext = Next⇨ addPermission = Add Permission ... diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java index 2a8c497e84..68ac3a6b8a 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java @@ -24,11 +24,13 @@ import com.google.gerrit.client.projects.ProjectMap; import com.google.gerrit.client.rpc.GerritCallback; import com.google.gerrit.client.ui.FilteredUserInterface; import com.google.gerrit.client.ui.HighlightingInlineHyperlink; +import com.google.gerrit.client.ui.Hyperlink; import com.google.gerrit.client.ui.IgnoreOutdatedFilterResultsCallbackWrapper; import com.google.gerrit.client.ui.ProjectSearchLink; import com.google.gerrit.client.ui.ProjectsTable; import com.google.gerrit.client.ui.Screen; import com.google.gerrit.common.PageLinks; +import com.google.gerrit.reviewdb.client.AccountGeneralPreferences; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyUpEvent; import com.google.gwt.event.dom.client.KeyUpHandler; @@ -41,11 +43,16 @@ import com.google.gwt.user.client.ui.Label; import com.google.gwtexpui.globalkey.client.NpTextBox; public class ProjectListScreen extends Screen implements FilteredUserInterface { + private Hyperlink prev; + private Hyperlink next; private ProjectsTable projects; private NpTextBox filterTxt; private String subname = ""; + private int startPosition; + private int pageSize; public ProjectListScreen() { + configurePageSize(); } public ProjectListScreen(String params) { @@ -58,6 +65,22 @@ public class ProjectListScreen extends Screen implements FilteredUserInterface { if ("filter".equals(kv[0])) { subname = URL.decodeQueryString(kv[1]); } + + if ("skip".equals(kv[0]) && URL.decodeQueryString(kv[1]).matches("^[\\d]+")) { + startPosition = Integer.parseInt(URL.decodeQueryString(kv[1])); + } + } + configurePageSize(); + } + + private void configurePageSize() { + if (Gerrit.isSignedIn()) { + final AccountGeneralPreferences p = + Gerrit.getUserAccount().getGeneralPreferences(); + final short m = p.getMaximumPageSize(); + pageSize = 0 < m ? m : AccountGeneralPreferences.DEFAULT_PAGESIZE; + } else { + pageSize = AccountGeneralPreferences.DEFAULT_PAGESIZE; } } @@ -65,13 +88,17 @@ public class ProjectListScreen extends Screen implements FilteredUserInterface { protected void onLoad() { super.onLoad(); display(); - refresh(false); + refresh(false, false); } - private void refresh(final boolean open) { - setToken(subname == null || "".equals(subname) ? ADMIN_PROJECTS - : ADMIN_PROJECTS + "?filter=" + URL.encodeQueryString(subname)); - ProjectMap.match(subname, + private void refresh(final boolean open, final boolean filterModified) { + if (filterModified){ + startPosition = 0; + } + setToken(getTokenForScreen(subname, startPosition)); + // Retrieve one more project than page size to determine if there are more + // projects to display + ProjectMap.match(subname, pageSize + 1, startPosition, new IgnoreOutdatedFilterResultsCallbackWrapper(this, new GerritCallback() { @Override @@ -80,12 +107,44 @@ public class ProjectListScreen extends Screen implements FilteredUserInterface { Gerrit.display(PageLinks.toProject( result.values().get(0).name_key())); } else { - projects.display(result); + if (result.size() <= pageSize) { + projects.display(result); + next.setVisible(false); + } else { + projects.displaySubset(result, 0, result.size() - 1); + setupNavigationLink(next, subname, startPosition + pageSize); + } + if (startPosition > 0) { + setupNavigationLink(prev, subname, startPosition - pageSize); + } else { + prev.setVisible(false); + } } } })); } + private void setupNavigationLink(Hyperlink link, String filter, int skip) { + link.setTargetHistoryToken(getTokenForScreen(filter, skip)); + link.setVisible(true); + } + + private String getTokenForScreen(String filter, int skip) { + String token = ADMIN_PROJECTS; + if (filter != null && !filter.isEmpty()) { + token += "?filter=" + URL.encodeQueryString(filter); + } + if (skip > 0) { + if (token.contains("?filter=")) { + token += ","; + } else { + token += "?"; + } + token += "skip=" + skip; + } + return token; + } + @Override public String getCurrentFilter() { return subname; @@ -97,6 +156,12 @@ public class ProjectListScreen extends Screen implements FilteredUserInterface { setPageTitle(Util.C.projectListTitle()); initPageHeader(); + prev = new Hyperlink(Util.C.pagedProjectListPrev(), true, ""); + prev.setVisible(false); + + next = new Hyperlink(Util.C.pagedProjectListNext(), true, ""); + next.setVisible(false); + projects = new ProjectsTable() { @Override protected void initColumnHeaders() { @@ -145,6 +210,11 @@ public class ProjectListScreen extends Screen implements FilteredUserInterface { projects.setSavePointerId(PageLinks.ADMIN_PROJECTS); add(projects); + final HorizontalPanel buttons = new HorizontalPanel(); + buttons.setStyleName(Gerrit.RESOURCES.css().changeTablePrevNextLinks()); + buttons.add(prev); + buttons.add(next); + add(buttons); } private void initPageHeader() { @@ -160,9 +230,10 @@ public class ProjectListScreen extends Screen implements FilteredUserInterface { public void onKeyUp(KeyUpEvent event) { boolean enterPressed = event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER; - if (enterPressed || !filterTxt.getValue().equals(subname)) { + boolean filterModified = !filterTxt.getValue().equals(subname); + if (enterPressed || filterModified) { subname = filterTxt.getValue(); - refresh(enterPressed); + refresh(enterPressed, filterModified); } } }); diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectMap.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectMap.java index bbeabd3033..f262660450 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectMap.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectMap.java @@ -53,16 +53,24 @@ public class ProjectMap extends NativeMap { .get(NativeMap.copyKeysIntoChildren(cb)); } - public static void match(String match, AsyncCallback cb) { - if (match == null || "".equals(match)) { - all(cb); - } else { - new RestApi("/projects/") - .addParameter("m", match) - .addParameterRaw("type", "ALL") - .addParameterTrue("d") // description - .get(NativeMap.copyKeysIntoChildren(cb)); + public static void match(String match, int limit, int start, AsyncCallback cb) { + RestApi call = new RestApi("/projects/"); + if (match != null) { + call.addParameter("m", match); } + if (limit > 0) { + call.addParameter("n", limit); + } + if (start > 0) { + call.addParameter("S", start); + } + call.addParameterRaw("type", "ALL"); + call.addParameterTrue("d"); // description + call.get(NativeMap.copyKeysIntoChildren(cb)); + } + + public static void match(String match, AsyncCallback cb) { + match(match, 0, 0, cb); } protected ProjectMap() { diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectsTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectsTable.java index 052878b4fd..e17ec3f4ba 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectsTable.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectsTable.java @@ -53,6 +53,10 @@ public class ProjectsTable extends NavigationTable { } public void display(ProjectMap projects) { + displaySubset(projects, 0, projects.size()); + } + + public void displaySubset(ProjectMap projects, int fromIndex, int toIndex) { while (1 < table.getRowCount()) table.removeRow(table.getRowCount() - 1); @@ -63,7 +67,7 @@ public class ProjectsTable extends NavigationTable { return a.name().compareTo(b.name()); } }); - for(ProjectInfo p : list) + for(ProjectInfo p : list.subList(fromIndex, toIndex)) insert(table.getRowCount(), p); finishDisplay();