/changes/: Display account dashboards

Replace the old JSON-RPC call showing a dashboard to instead be
three queries using the newer /changes/ REST API. This moves all
of the logic into the common ListChanges and QueryProcessor, and
simplifies the server code.

Like the All > Open view, the columns in the table now update
dynamically to reflect the labels actually required to submit
the changes displayed.

Changes in the outgoing and incoming sections are sorted by project,
branch, topic, and then the legacy change number.  This gives a
more predictable sorting when looking at a very busy dashboard,
as users can scan the Project and Branch columns and work down
the page until they locate the change(s) they are looking for. In
particular it helps me when I want to look at changes for git-repo,
or stable changes for gerrit near release time.

Change-Id: Ia9e9fc103561b43fe0188958bfa226fdc21b6992
This commit is contained in:
Shawn O. Pearce
2012-04-16 16:27:47 -07:00
parent a7cc5042cc
commit 2a912fba44
8 changed files with 143 additions and 53 deletions

View File

@@ -14,39 +14,46 @@
package com.google.gerrit.client.changes; package com.google.gerrit.client.changes;
import com.google.gerrit.client.FormatUtil;
import com.google.gerrit.client.Gerrit; import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.changes.ChangeTable.ApprovalViewType; import com.google.gerrit.client.NotFoundScreen;
import com.google.gerrit.client.rpc.NativeList;
import com.google.gerrit.client.rpc.ScreenLoadCallback; import com.google.gerrit.client.rpc.ScreenLoadCallback;
import com.google.gerrit.client.ui.Screen; import com.google.gerrit.client.ui.Screen;
import com.google.gerrit.common.PageLinks; import com.google.gerrit.common.PageLinks;
import com.google.gerrit.common.data.AccountDashboardInfo;
import com.google.gerrit.common.data.AccountInfo;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
import java.util.Collections;
import java.util.Comparator;
public class AccountDashboardScreen extends Screen implements ChangeListScreen { public class AccountDashboardScreen extends Screen implements ChangeListScreen {
private final Account.Id ownerId; private final Account.Id ownerId;
private ChangeTable table; private final boolean mine;
private ChangeTable.Section byOwner; private ChangeTable2 table;
private ChangeTable.Section forReview; private ChangeTable2.Section outgoing;
private ChangeTable.Section closed; private ChangeTable2.Section incoming;
private ChangeTable2.Section closed;
public AccountDashboardScreen(final Account.Id id) { public AccountDashboardScreen(final Account.Id id) {
ownerId = id; ownerId = id;
mine = Gerrit.isSignedIn() && ownerId.equals(Gerrit.getUserAccount().getId());
} }
@Override @Override
protected void onInitUI() { protected void onInitUI() {
super.onInitUI(); super.onInitUI();
table = new ChangeTable(true); table = new ChangeTable2();
table.addStyleName(Gerrit.RESOURCES.css().accountDashboard()); table.addStyleName(Gerrit.RESOURCES.css().accountDashboard());
byOwner = new ChangeTable.Section("", ApprovalViewType.STRONGEST, null);
forReview = new ChangeTable.Section("", ApprovalViewType.USER, ownerId);
closed = new ChangeTable.Section("", ApprovalViewType.STRONGEST, null);
table.addSection(byOwner); outgoing = new ChangeTable2.Section();
table.addSection(forReview); incoming = new ChangeTable2.Section();
closed = new ChangeTable2.Section();
outgoing.setTitleText(Util.C.outgoingReviews());
incoming.setTitleText(Util.C.incomingReviews());
closed.setTitleText(Util.C.recentlyClosed());
table.addSection(outgoing);
table.addSection(incoming);
table.addSection(closed); table.addSection(closed);
add(table); add(table);
table.setSavePointerId(PageLinks.toAccountDashboard(ownerId)); table.setSavePointerId(PageLinks.toAccountDashboard(ownerId));
@@ -55,13 +62,17 @@ public class AccountDashboardScreen extends Screen implements ChangeListScreen {
@Override @Override
protected void onLoad() { protected void onLoad() {
super.onLoad(); super.onLoad();
Util.LIST_SVC.forAccount(ownerId, String who = mine ? "self" : ownerId.toString();
new ScreenLoadCallback<AccountDashboardInfo>(this) { ChangeList.query(
new ScreenLoadCallback<NativeList<ChangeList>>(this) {
@Override @Override
protected void preDisplay(final AccountDashboardInfo r) { protected void preDisplay(NativeList<ChangeList> result) {
display(r); display(result);
} }
}); },
"is:open owner:" + who,
"is:open reviewer:" + who + " -owner:" + who,
"is:closed owner:" + who + " -age:1w limit:10");
} }
@Override @Override
@@ -70,20 +81,80 @@ public class AccountDashboardScreen extends Screen implements ChangeListScreen {
table.setRegisterKeys(true); table.setRegisterKeys(true);
} }
private void display(final AccountDashboardInfo r) { private void display(NativeList<ChangeList> result) {
table.setAccountInfoCache(r.getAccounts()); if (!mine && !hasChanges(result)) {
// When no results are returned and the data is not for the
// current user, the target user is presumed to not exist.
Gerrit.display(getToken(), new NotFoundScreen());
return;
}
final AccountInfo o = r.getAccounts().get(r.getOwner()); ChangeList out = result.get(0);
final String name = FormatUtil.name(o); ChangeList in = result.get(1);
setWindowTitle(name); ChangeList done = result.get(2);
setPageTitle(Util.M.accountDashboardTitle(name));
byOwner.setTitleText(Util.M.changesStartedBy(name));
forReview.setTitleText(Util.M.changesReviewableBy(name));
closed.setTitleText(Util.C.changesRecentlyClosed());
byOwner.display(r.getByOwner()); if (mine) {
forReview.display(r.getForReview()); setWindowTitle(Util.C.myDashboardTitle());
closed.display(r.getClosed()); setPageTitle(Util.C.myDashboardTitle());
} else {
// The server doesn't tell us who the dashboard is for. Try to guess
// by looking at a change started by the owner and extract the name.
String name = guessName(out);
if (name == null) {
name = guessName(done);
}
if (name != null) {
setWindowTitle(name);
setPageTitle(Util.M.accountDashboardTitle(name));
} else {
setWindowTitle(Util.C.unknownDashboardTitle());
setWindowTitle(Util.C.unknownDashboardTitle());
}
}
Collections.sort(out.asList(), compare());
Collections.sort(in.asList(), compare());
table.updateColumnsForLabels(out, in, done);
outgoing.display(out);
incoming.display(in);
closed.display(done);
table.finishDisplay(); table.finishDisplay();
} }
private Comparator<ChangeInfo> compare() {
return new Comparator<ChangeInfo>() {
@Override
public int compare(ChangeInfo a, ChangeInfo b) {
int cmp = a.project().compareTo(b.project());
if (cmp != 0) return cmp;
cmp = a.branch().compareTo(b.branch());
if (cmp != 0) return cmp;
String at = a.topic() != null ? a.topic() : "";
String bt = b.topic() != null ? b.topic() : "";
cmp = at.compareTo(bt);
if (cmp != 0) return cmp;
return a._number() - b._number();
}
};
}
private boolean hasChanges(NativeList<ChangeList> result) {
for (ChangeList list : result.asList()) {
if (!list.isEmpty()) {
return true;
}
}
return false;
}
private static String guessName(ChangeList list) {
for (ChangeInfo change : list.asList()) {
if (change.owner() != null && change.owner().name() != null) {
return change.owner().name();
}
}
return null;
}
} }

View File

@@ -23,7 +23,11 @@ public interface ChangeConstants extends Constants {
String statusLongAbandoned(); String statusLongAbandoned();
String statusLongDraft(); String statusLongDraft();
String changesRecentlyClosed(); String myDashboardTitle();
String unknownDashboardTitle();
String incomingReviews();
String outgoingReviews();
String recentlyClosed();
String starredHeading(); String starredHeading();
String watchedHeading(); String watchedHeading();

View File

@@ -7,7 +7,11 @@ statusLongDraft = Draft
starredHeading = Starred Changes starredHeading = Starred Changes
watchedHeading = Open Changes of Watched Projects watchedHeading = Open Changes of Watched Projects
draftsHeading = Changes with unpublished drafts draftsHeading = Changes with unpublished drafts
changesRecentlyClosed = Recently closed myDashboardTitle = My Reviews
unknownDashboardTitle = Code Review Dashboard
incomingReviews = Incoming reviews
outgoingReviews = Outgoing reviews
recentlyClosed = Recently closed
allOpenChanges = All open changes allOpenChanges = All open changes
allAbandonedChanges = All abandoned changes allAbandonedChanges = All abandoned changes
allMergedChanges = All merged changes allMergedChanges = All merged changes

View File

@@ -23,6 +23,17 @@ import com.google.gwtorm.client.KeyUtil;
public class ChangeList extends NativeList<ChangeInfo> { public class ChangeList extends NativeList<ChangeInfo> {
private static final String URI = "/changes/"; private static final String URI = "/changes/";
/** Run 2 or more queries in a single remote invocation. */
public static void query(
AsyncCallback<NativeList<ChangeList>> callback, String... queries) {
assert queries.length >= 2; // At least 2 is required for correct result.
RestApi call = new RestApi(URI);
for (String q : queries) {
call.addParameterRaw("q", KeyUtil.encode(q));
}
call.send(callback);
}
public static void prev(String query, public static void prev(String query,
int limit, String sortkey, int limit, String sortkey,
AsyncCallback<ChangeList> callback) { AsyncCallback<ChangeList> callback) {

View File

@@ -18,8 +18,6 @@ import com.google.gwt.i18n.client.Messages;
public interface ChangeMessages extends Messages { public interface ChangeMessages extends Messages {
String accountDashboardTitle(String fullName); String accountDashboardTitle(String fullName);
String changesStartedBy(String fullName);
String changesReviewableBy(String fullName);
String changesOpenInProject(String string); String changesOpenInProject(String string);
String changesMergedInProject(String string); String changesMergedInProject(String string);
String changesAbandonedInProject(String string); String changesAbandonedInProject(String string);

View File

@@ -1,6 +1,4 @@
accountDashboardTitle = Code Review Dashboard for {0} accountDashboardTitle = Code Review Dashboard for {0}
changesStartedBy = Started by {0}
changesReviewableBy = Review Requests for {0}
changesOpenInProject = Open Changes In {0} changesOpenInProject = Open Changes In {0}
changesMergedInProject = Merged Changes In {0} changesMergedInProject = Merged Changes In {0}
changesAbandonedInProject = Abandoned Changes In {0} changesAbandonedInProject = Abandoned Changes In {0}

View File

@@ -145,12 +145,14 @@ public class ChangeTable2 extends NavigationTable<ChangeInfo> {
} }
} }
private void updateTableLayoutForLabels(ChangeList list) { public void updateColumnsForLabels(ChangeList... lists) {
labelNames = new ArrayList<String>(); labelNames = new ArrayList<String>();
for (int i = 0; i < list.size(); i++) { for (ChangeList list : lists) {
for (String name : list.get(i).labels()) { for (int i = 0; i < list.size(); i++) {
if (!labelNames.contains(name)) { for (String name : list.get(i).labels()) {
labelNames.add(name); if (!labelNames.contains(name)) {
labelNames.add(name);
}
} }
} }
} }
@@ -177,6 +179,12 @@ public class ChangeTable2 extends NavigationTable<ChangeInfo> {
table.getCellFormatter().getElement(0, col).setTitle(name); table.getCellFormatter().getElement(0, col).setTitle(name);
fmt.addStyleName(0, col, Gerrit.RESOURCES.css().dataHeader()); fmt.addStyleName(0, col, Gerrit.RESOURCES.css().dataHeader());
} }
for (Section s : sections) {
if (s.titleRow >= 0) {
fmt.setColSpan(s.titleRow, 0, columns);
}
}
} }
private void populateChangeRow(final int row, final ChangeInfo c) { private void populateChangeRow(final int row, final ChangeInfo c) {
@@ -374,20 +382,15 @@ public class ChangeTable2 extends NavigationTable<ChangeInfo> {
parent.removeRow(dataBegin); parent.removeRow(dataBegin);
rows--; rows--;
} }
} } else {
if (sz == 0) {
if (hadData) {
parent.insertNoneRow(dataBegin);
}
return;
}
if (!hadData) {
parent.removeRow(dataBegin); parent.removeRow(dataBegin);
} }
parent.updateTableLayoutForLabels(changeList); if (sz == 0) {
parent.insertNoneRow(dataBegin);
return;
}
while (rows < sz) { while (rows < sz) {
parent.insertChangeRow(dataBegin + rows); parent.insertChangeRow(dataBegin + rows);
rows++; rows++;

View File

@@ -131,6 +131,7 @@ public abstract class PagedSingleListScreen extends Screen {
next.setVisible(l._more_changes()); next.setVisible(l._more_changes());
} }
} }
table.updateColumnsForLabels(result);
section.display(result); section.display(result);
table.finishDisplay(); table.finishDisplay();
} }