ChangeScreen2: Remove reviewers using [X] in "chip"

If the current user can remove a reviewer, add a "[X]" button to the
right of the reviewer's name in their chip in Reviewers or CC. After
removing an entry the screen is reloaded to rebuild the correct data
for Reviewers, CC and the labels.

Bug: issue 2067
Change-Id: I9c69f46036dd2eeb39eb3d85c9e9d0d6fe6f64f1
This commit is contained in:
Shawn Pearce 2013-09-29 21:23:43 -07:00
parent 0264894220
commit c0e89a7216
7 changed files with 118 additions and 5 deletions

View File

@ -105,6 +105,14 @@ public class SafeHtmlBuilder extends SafeHtml {
return this;
}
/** Append already safe HTML as-is, avoiding double escaping. */
public SafeHtmlBuilder append(com.google.gwt.safehtml.shared.SafeHtml in) {
if (in != null) {
cb.append(in.asString());
}
return this;
}
/** Append already safe HTML as-is, avoiding double escaping. */
public SafeHtmlBuilder append(final SafeHtml in) {
if (in != null) {

View File

@ -53,6 +53,7 @@ import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.client.JsArrayString;
import com.google.gwt.dom.client.AnchorElement;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.logical.shared.CloseEvent;
@ -63,6 +64,8 @@ import com.google.gwt.resources.client.CssResource;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.EventListener;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlowPanel;
@ -86,6 +89,7 @@ public class ChangeScreen2 extends Screen {
interface Style extends CssResource {
String labelName();
String avatar();
String label_user();
String label_ok();
String label_reject();
@ -95,6 +99,17 @@ public class ChangeScreen2 extends Screen {
String selected();
}
static ChangeScreen2 get(NativeEvent in) {
com.google.gwt.user.client.Element e = in.getEventTarget().cast();
for (e = DOM.getParent(e); e != null; e = DOM.getParent(e)) {
EventListener l = DOM.getEventListener(e);
if (l instanceof ChangeScreen2) {
return (ChangeScreen2) l;
}
}
return null;
}
private final Change.Id changeId;
private String revision;
private ChangeInfo changeInfo;
@ -154,6 +169,10 @@ public class ChangeScreen2 extends Screen {
add(uiBinder.createAndBindUi(this));
}
Change.Id getChangeId() {
return changeId;
}
@Override
protected void onLoad() {
super.onLoad();

View File

@ -180,12 +180,20 @@ limitations under the License.
border: 1px solid trimColor;
white-space: nowrap;
}
.label_user img {
.label_user img.avatar {
margin: 0 2px 0 0;
width: 16px;
height: 16px;
vertical-align: bottom;
}
.label_user button {
cursor: pointer;
padding: 0;
margin: 0 0 0 5px;
border: 0;
background-color: transparent;
white-space: nowrap;
}
.label_ok {color: #060;}
.label_reject {color: #d14836;}

View File

@ -14,16 +14,27 @@
package com.google.gerrit.client.change;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.account.AccountInfo;
import com.google.gerrit.client.account.AccountInfo.AvatarInfo;
import com.google.gerrit.client.changes.ChangeApi;
import com.google.gerrit.client.changes.ChangeInfo;
import com.google.gerrit.client.changes.ChangeInfo.ApprovalInfo;
import com.google.gerrit.client.changes.ChangeInfo.LabelInfo;
import com.google.gerrit.client.changes.ChangeResources;
import com.google.gerrit.client.changes.Util;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.rpc.Natives;
import com.google.gerrit.common.PageLinks;
import com.google.gerrit.common.data.LabelValue;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.ImageResourceRenderer;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwtexpui.safehtml.client.SafeHtml;
import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
@ -40,6 +51,48 @@ import java.util.Set;
/** Displays a table of label and reviewer scores. */
class Labels extends Grid {
private static final String DATA_ID = "data-id";
private static final String REMOVE;
static {
REMOVE = DOM.createUniqueId().replace('-', '_');
init(REMOVE);
}
private static final native void init(String r) /*-{
$wnd[r] = $entry(function(e) {
@com.google.gerrit.client.change.Labels::onRemove(Lcom/google/gwt/dom/client/NativeEvent;)(e)
});
}-*/;
private static void onRemove(NativeEvent event) {
Integer user = getDataId(event);
if (user != null) {
final ChangeScreen2 screen = ChangeScreen2.get(event);
ChangeApi.reviewer(screen.getChangeId().get(), user).delete(
new GerritCallback<JavaScriptObject>() {
@Override
public void onSuccess(JavaScriptObject result) {
if (screen.isCurrentView()) {
Gerrit.display(PageLinks.toChange(screen.getChangeId()));
}
}
});
}
}
private static Integer getDataId(NativeEvent event) {
Element e = event.getEventTarget().cast();
while (e != null) {
String v = e.getAttribute(DATA_ID);
if (!v.isEmpty()) {
return Integer.parseInt(v);
}
e = e.getParentElement();
}
return null;
}
private ChangeScreen2.Style style;
private Element statusText;
@ -124,7 +177,8 @@ class Labels extends Grid {
html.setStyleName(style.label_reject());
}
html.append(val).append(" ");
html.append(formatUserList(style, m.get(v)));
html.append(formatUserList(style, m.get(v),
Collections.<Integer> emptySet()));
html.closeSpan();
}
return html.toBlockWidget();
@ -169,7 +223,8 @@ class Labels extends Grid {
}
static SafeHtml formatUserList(ChangeScreen2.Style style,
Collection<? extends AccountInfo> in) {
Collection<? extends AccountInfo> in,
Set<Integer> removable) {
List<AccountInfo> users = new ArrayList<AccountInfo>(in);
Collections.sort(users, new Comparator<AccountInfo>() {
@Override
@ -210,9 +265,11 @@ class Labels extends Grid {
html.openSpan();
html.setAttribute("role", "listitem");
html.setAttribute(DATA_ID, ai._account_id());
html.setStyleName(style.label_user());
if (img != null) {
html.openElement("img");
html.setStyleName(style.avatar());
html.setAttribute("src", img.url());
if (img.width() > 0) {
html.setAttribute("width", img.width());
@ -223,6 +280,12 @@ class Labels extends Grid {
html.closeSelf();
}
html.append(name);
if (removable.contains(ai._account_id())) {
html.openElement("button");
html.setAttribute("title", Util.M.removeReviewer(name));
html.setAttribute("onclick", REMOVE + "(event)");
html.append(new ImageResourceRenderer().render(Resources.I.remove_reviewer()));
}
html.closeSpan();
}
return html;

View File

@ -27,6 +27,7 @@ public interface Resources extends ClientBundle {
@Source("star_filled.png") ImageResource star_filled();
@Source("reload_black.png") ImageResource reload_black();
@Source("reload_white.png") ImageResource reload_white();
@Source("remove_reviewer.png") ImageResource remove_reviewer();
@Source("common.css") Style style();
public interface Style extends CssResource {

View File

@ -48,10 +48,13 @@ import com.google.gwt.user.client.ui.SuggestBox;
import com.google.gwt.user.client.ui.SuggestBox.DefaultSuggestionDisplay;
import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwtexpui.safehtml.client.SafeHtml;
import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/** Add reviewers. */
class Reviewers extends Composite {
@ -220,7 +223,18 @@ class Reviewers extends Composite {
}
r.remove(info.owner()._account_id());
cc.remove(info.owner()._account_id());
reviewersText.setInnerSafeHtml(Labels.formatUserList(style, r.values()));
ccText.setInnerSafeHtml(Labels.formatUserList(style, cc.values()));
Set<Integer> removable = new HashSet<Integer>();
if (info.removable_reviewers() != null) {
for (AccountInfo a : Natives.asList(info.removable_reviewers())) {
removable.add(a._account_id());
}
}
SafeHtml rHtml = Labels.formatUserList(style, r.values(), removable);
SafeHtml ccHtml = Labels.formatUserList(style, cc.values(), removable);
reviewersText.setInnerSafeHtml(rHtml);
ccText.setInnerSafeHtml(ccHtml);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 417 B