blob: 87320b85e763a53308fb9b45807679b53c10fe3f [file] [log] [blame]
// Copyright (C) 2013 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.client.change;
import com.google.gerrit.client.ConfirmationCallback;
import com.google.gerrit.client.ConfirmationDialog;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.account.AccountInfo;
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.Util;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.rpc.NativeMap;
import com.google.gerrit.client.rpc.NativeString;
import com.google.gerrit.client.rpc.Natives;
import com.google.gerrit.client.ui.HintTextBox;
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.core.client.JsArray;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
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.rpc.StatusCodeException;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HTMLPanel;
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. */
public class Reviewers extends Composite {
interface Binder extends UiBinder<HTMLPanel, Reviewers> {}
private static final Binder uiBinder = GWT.create(Binder.class);
@UiField Element reviewersText;
@UiField Button openForm;
@UiField Button addMe;
@UiField Element form;
@UiField Element error;
@UiField(provided = true)
SuggestBox suggestBox;
private ChangeScreen2.Style style;
private Element ccText;
private RestReviewerSuggestOracle reviewerSuggestOracle;
private HintTextBox nameTxtBox;
private Change.Id changeId;
private boolean submitOnSelection;
Reviewers() {
reviewerSuggestOracle = new RestReviewerSuggestOracle();
nameTxtBox = new HintTextBox();
suggestBox = new SuggestBox(reviewerSuggestOracle, nameTxtBox);
initWidget(uiBinder.createAndBindUi(this));
nameTxtBox.setVisibleLength(55);
nameTxtBox.setHintText(Util.C.approvalTableAddReviewerHint());
nameTxtBox.addKeyDownHandler(new KeyDownHandler() {
@Override
public void onKeyDown(KeyDownEvent e) {
submitOnSelection = false;
if (e.getNativeEvent().getKeyCode() == KeyCodes.KEY_ESCAPE) {
onCancel(null);
} else if (e.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) {
if (((DefaultSuggestionDisplay) suggestBox.getSuggestionDisplay())
.isSuggestionListShowing()) {
submitOnSelection = true;
} else {
onAdd(null);
}
}
}
});
suggestBox.addSelectionHandler(new SelectionHandler<Suggestion>() {
@Override
public void onSelection(SelectionEvent<Suggestion> event) {
nameTxtBox.setFocus(true);
if (submitOnSelection) {
onAdd(null);
}
}
});
}
void init(ChangeScreen2.Style style, Element ccText) {
this.style = style;
this.ccText = ccText;
}
void set(ChangeInfo info) {
this.changeId = info.legacy_id();
display(info);
reviewerSuggestOracle.setChange(changeId);
openForm.setVisible(Gerrit.isSignedIn());
}
@UiHandler("openForm")
void onOpenForm(ClickEvent e) {
onOpenForm();
}
void onOpenForm() {
UIObject.setVisible(form, true);
UIObject.setVisible(error, false);
openForm.setVisible(false);
suggestBox.setFocus(true);
}
@UiHandler("add")
void onAdd(ClickEvent e) {
String reviewer = suggestBox.getText();
if (!reviewer.isEmpty()) {
addReviewer(reviewer, false);
}
}
@UiHandler("addMe")
void onAddMe(ClickEvent e) {
String accountId = String.valueOf(Gerrit.getUserAccountInfo()._account_id());
addReviewer(accountId, false);
}
@UiHandler("cancel")
void onCancel(ClickEvent e) {
openForm.setVisible(true);
UIObject.setVisible(form, false);
suggestBox.setFocus(false);
}
private void addReviewer(final String reviewer, boolean confirmed) {
ChangeApi.reviewers(changeId.get()).post(
PostInput.create(reviewer, confirmed),
new GerritCallback<PostResult>() {
public void onSuccess(PostResult result) {
nameTxtBox.setEnabled(true);
if (result.confirm()) {
askForConfirmation(result.error());
} else if (result.error() != null) {
UIObject.setVisible(error, true);
error.setInnerText(result.error());
} else {
UIObject.setVisible(error, false);
error.setInnerText("");
nameTxtBox.setText("");
if (result.reviewers() != null
&& result.reviewers().length() > 0) {
updateReviewerList();
}
}
}
private void askForConfirmation(String text) {
new ConfirmationDialog(
Util.C.approvalTableAddManyReviewersConfirmationDialogTitle(),
new SafeHtmlBuilder().append(text),
new ConfirmationCallback() {
@Override
public void onOk() {
addReviewer(reviewer, true);
}
}).center();
}
@Override
public void onFailure(Throwable err) {
UIObject.setVisible(error, true);
error.setInnerText(err instanceof StatusCodeException
? ((StatusCodeException) err).getEncodedResponse()
: err.getMessage());
nameTxtBox.setEnabled(true);
}
});
}
private void updateReviewerList() {
ChangeApi.detail(changeId.get(),
new GerritCallback<ChangeInfo>() {
@Override
public void onSuccess(ChangeInfo result) {
display(result);
}
});
}
private void display(ChangeInfo info) {
Map<Integer, AccountInfo> r = new HashMap<>();
Map<Integer, AccountInfo> cc = new HashMap<>();
for (LabelInfo label : Natives.asList(info.all_labels().values())) {
if (label.all() != null) {
for (ApprovalInfo ai : Natives.asList(label.all())) {
(ai.value() != 0 ? r : cc).put(ai._account_id(), ai);
}
}
}
for (Integer i : r.keySet()) {
cc.remove(i);
}
r.remove(info.owner()._account_id());
cc.remove(info.owner()._account_id());
Set<Integer> removable = new HashSet<>();
if (info.removable_reviewers() != null) {
for (AccountInfo a : Natives.asList(info.removable_reviewers())) {
removable.add(a._account_id());
}
}
Map<Integer, VotableInfo> votable = votable(info);
SafeHtml rHtml = Labels.formatUserList(style,
r.values(), removable, votable);
SafeHtml ccHtml = Labels.formatUserList(style,
cc.values(), removable, votable);
reviewersText.setInnerSafeHtml(rHtml);
ccText.setInnerSafeHtml(ccHtml);
if (Gerrit.isSignedIn()) {
int currentUser = Gerrit.getUserAccountInfo()._account_id();
boolean showAddMeButton = info.owner()._account_id() != currentUser
&& !cc.containsKey(currentUser)
&& !r.containsKey(currentUser);
addMe.setVisible(showAddMeButton);
}
}
private static Map<Integer, VotableInfo> votable(ChangeInfo change) {
Map<Integer, VotableInfo> d = new HashMap<>();
for (String name : change.labels()) {
LabelInfo label = change.label(name);
if (label.all() != null) {
for (ApprovalInfo ai : Natives.asList(label.all())) {
int id = ai._account_id();
VotableInfo ad = d.get(id);
if (ad == null) {
ad = new VotableInfo();
d.put(id, ad);
}
if (ai.has_value()) {
ad.votable(name);
}
}
}
}
return d;
}
public static class PostInput extends JavaScriptObject {
public static PostInput create(String reviewer, boolean confirmed) {
PostInput input = createObject().cast();
input.init(reviewer, confirmed);
return input;
}
private native void init(String reviewer, boolean confirmed) /*-{
this.reviewer = reviewer;
if (confirmed) {
this.confirmed = true;
}
}-*/;
protected PostInput() {
}
}
public static class ReviewerInfo extends AccountInfo {
final Set<String> approvals() {
return Natives.keys(_approvals());
}
final native String approval(String l) /*-{ return this.approvals[l]; }-*/;
private final native NativeMap<NativeString> _approvals() /*-{ return this.approvals; }-*/;
protected ReviewerInfo() {
}
}
public static class PostResult extends JavaScriptObject {
public final native JsArray<ReviewerInfo> reviewers() /*-{ return this.reviewers; }-*/;
public final native boolean confirm() /*-{ return this.confirm || false; }-*/;
public final native String error() /*-{ return this.error; }-*/;
protected PostResult() {
}
}
}