/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:
@@ -14,39 +14,46 @@
|
||||
|
||||
package com.google.gerrit.client.changes;
|
||||
|
||||
import com.google.gerrit.client.FormatUtil;
|
||||
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.ui.Screen;
|
||||
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 java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
|
||||
public class AccountDashboardScreen extends Screen implements ChangeListScreen {
|
||||
private final Account.Id ownerId;
|
||||
private ChangeTable table;
|
||||
private ChangeTable.Section byOwner;
|
||||
private ChangeTable.Section forReview;
|
||||
private ChangeTable.Section closed;
|
||||
private final boolean mine;
|
||||
private ChangeTable2 table;
|
||||
private ChangeTable2.Section outgoing;
|
||||
private ChangeTable2.Section incoming;
|
||||
private ChangeTable2.Section closed;
|
||||
|
||||
public AccountDashboardScreen(final Account.Id id) {
|
||||
ownerId = id;
|
||||
mine = Gerrit.isSignedIn() && ownerId.equals(Gerrit.getUserAccount().getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInitUI() {
|
||||
super.onInitUI();
|
||||
table = new ChangeTable(true);
|
||||
table = new ChangeTable2();
|
||||
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);
|
||||
table.addSection(forReview);
|
||||
outgoing = new ChangeTable2.Section();
|
||||
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);
|
||||
add(table);
|
||||
table.setSavePointerId(PageLinks.toAccountDashboard(ownerId));
|
||||
@@ -55,13 +62,17 @@ public class AccountDashboardScreen extends Screen implements ChangeListScreen {
|
||||
@Override
|
||||
protected void onLoad() {
|
||||
super.onLoad();
|
||||
Util.LIST_SVC.forAccount(ownerId,
|
||||
new ScreenLoadCallback<AccountDashboardInfo>(this) {
|
||||
String who = mine ? "self" : ownerId.toString();
|
||||
ChangeList.query(
|
||||
new ScreenLoadCallback<NativeList<ChangeList>>(this) {
|
||||
@Override
|
||||
protected void preDisplay(final AccountDashboardInfo r) {
|
||||
display(r);
|
||||
protected void preDisplay(NativeList<ChangeList> result) {
|
||||
display(result);
|
||||
}
|
||||
});
|
||||
},
|
||||
"is:open owner:" + who,
|
||||
"is:open reviewer:" + who + " -owner:" + who,
|
||||
"is:closed owner:" + who + " -age:1w limit:10");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -70,20 +81,80 @@ public class AccountDashboardScreen extends Screen implements ChangeListScreen {
|
||||
table.setRegisterKeys(true);
|
||||
}
|
||||
|
||||
private void display(final AccountDashboardInfo r) {
|
||||
table.setAccountInfoCache(r.getAccounts());
|
||||
private void display(NativeList<ChangeList> result) {
|
||||
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());
|
||||
final String name = FormatUtil.name(o);
|
||||
ChangeList out = result.get(0);
|
||||
ChangeList in = result.get(1);
|
||||
ChangeList done = result.get(2);
|
||||
|
||||
if (mine) {
|
||||
setWindowTitle(Util.C.myDashboardTitle());
|
||||
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));
|
||||
byOwner.setTitleText(Util.M.changesStartedBy(name));
|
||||
forReview.setTitleText(Util.M.changesReviewableBy(name));
|
||||
closed.setTitleText(Util.C.changesRecentlyClosed());
|
||||
} else {
|
||||
setWindowTitle(Util.C.unknownDashboardTitle());
|
||||
setWindowTitle(Util.C.unknownDashboardTitle());
|
||||
}
|
||||
}
|
||||
|
||||
byOwner.display(r.getByOwner());
|
||||
forReview.display(r.getForReview());
|
||||
closed.display(r.getClosed());
|
||||
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();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,11 @@ public interface ChangeConstants extends Constants {
|
||||
String statusLongAbandoned();
|
||||
String statusLongDraft();
|
||||
|
||||
String changesRecentlyClosed();
|
||||
String myDashboardTitle();
|
||||
String unknownDashboardTitle();
|
||||
String incomingReviews();
|
||||
String outgoingReviews();
|
||||
String recentlyClosed();
|
||||
|
||||
String starredHeading();
|
||||
String watchedHeading();
|
||||
|
||||
@@ -7,7 +7,11 @@ statusLongDraft = Draft
|
||||
starredHeading = Starred Changes
|
||||
watchedHeading = Open Changes of Watched Projects
|
||||
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
|
||||
allAbandonedChanges = All abandoned changes
|
||||
allMergedChanges = All merged changes
|
||||
|
||||
@@ -23,6 +23,17 @@ import com.google.gwtorm.client.KeyUtil;
|
||||
public class ChangeList extends NativeList<ChangeInfo> {
|
||||
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,
|
||||
int limit, String sortkey,
|
||||
AsyncCallback<ChangeList> callback) {
|
||||
|
||||
@@ -18,8 +18,6 @@ import com.google.gwt.i18n.client.Messages;
|
||||
|
||||
public interface ChangeMessages extends Messages {
|
||||
String accountDashboardTitle(String fullName);
|
||||
String changesStartedBy(String fullName);
|
||||
String changesReviewableBy(String fullName);
|
||||
String changesOpenInProject(String string);
|
||||
String changesMergedInProject(String string);
|
||||
String changesAbandonedInProject(String string);
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
accountDashboardTitle = Code Review Dashboard for {0}
|
||||
changesStartedBy = Started by {0}
|
||||
changesReviewableBy = Review Requests for {0}
|
||||
changesOpenInProject = Open Changes In {0}
|
||||
changesMergedInProject = Merged Changes In {0}
|
||||
changesAbandonedInProject = Abandoned Changes In {0}
|
||||
|
||||
@@ -145,8 +145,9 @@ public class ChangeTable2 extends NavigationTable<ChangeInfo> {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTableLayoutForLabels(ChangeList list) {
|
||||
public void updateColumnsForLabels(ChangeList... lists) {
|
||||
labelNames = new ArrayList<String>();
|
||||
for (ChangeList list : lists) {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
for (String name : list.get(i).labels()) {
|
||||
if (!labelNames.contains(name)) {
|
||||
@@ -154,6 +155,7 @@ public class ChangeTable2 extends NavigationTable<ChangeInfo> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Collections.sort(labelNames);
|
||||
|
||||
if (BASE_COLUMNS + labelNames.size() < columns) {
|
||||
@@ -177,6 +179,12 @@ public class ChangeTable2 extends NavigationTable<ChangeInfo> {
|
||||
table.getCellFormatter().getElement(0, col).setTitle(name);
|
||||
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) {
|
||||
@@ -374,20 +382,15 @@ public class ChangeTable2 extends NavigationTable<ChangeInfo> {
|
||||
parent.removeRow(dataBegin);
|
||||
rows--;
|
||||
}
|
||||
}
|
||||
|
||||
if (sz == 0) {
|
||||
if (hadData) {
|
||||
parent.insertNoneRow(dataBegin);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hadData) {
|
||||
} else {
|
||||
parent.removeRow(dataBegin);
|
||||
}
|
||||
|
||||
parent.updateTableLayoutForLabels(changeList);
|
||||
if (sz == 0) {
|
||||
parent.insertNoneRow(dataBegin);
|
||||
return;
|
||||
}
|
||||
|
||||
while (rows < sz) {
|
||||
parent.insertChangeRow(dataBegin + rows);
|
||||
rows++;
|
||||
|
||||
@@ -131,6 +131,7 @@ public abstract class PagedSingleListScreen extends Screen {
|
||||
next.setVisible(l._more_changes());
|
||||
}
|
||||
}
|
||||
table.updateColumnsForLabels(result);
|
||||
section.display(result);
|
||||
table.finishDisplay();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user