Allow removing already-added reviewers in the reply dialog
Enforces two way data binding between the reviewer list in the
reply-dialog and the reviewer list in the change, and fires the
corresponding API call to remove reviewers.
Feature: Issue 4988
Change-Id: Ib03a98947a3e27abe8851279a92c19951f9b2c04
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
index aa4041b..26b8b73 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
@@ -23,6 +23,11 @@
REVIEWERS: 'reviewers',
};
+ var ReviewerTypes = {
+ REVIEWER: 'REVIEWER',
+ CC: 'CC',
+ };
+
Polymer({
is: 'gr-reply-dialog',
@@ -95,6 +100,13 @@
value: false,
observer: '_handleHeightChanged',
},
+ _reviewersPendingRemove: {
+ type: Object,
+ value: {
+ CC: [],
+ REVIEWER: [],
+ },
+ },
},
FocusTarget: FocusTarget,
@@ -105,6 +117,8 @@
observers: [
'_changeUpdated(change.reviewers.*, change.owner, serverConfig)',
+ '_ccsChanged(_ccs.splices)',
+ '_reviewersChanged(_reviewers.splices)',
],
attached: function() {
@@ -144,6 +158,76 @@
selectorEl.selectIndex(selectorEl.indexOf(item));
},
+ _ccsChanged: function(splices) {
+ if (splices && splices.indexSplices) {
+ this._processReviewerChange(splices.indexSplices, ReviewerTypes.CC);
+ }
+ },
+
+ _reviewersChanged: function(splices) {
+ if (splices && splices.indexSplices) {
+ this._processReviewerChange(splices.indexSplices,
+ ReviewerTypes.REVIEWER);
+ }
+ },
+
+ _processReviewerChange: function(indexSplices, type) {
+ indexSplices.forEach(function(splice) {
+ splice.removed.forEach(function(account) {
+ if (!this._reviewersPendingRemove[type]) {
+ console.err('Invalid type ' + type + ' for reviewer.');
+ return;
+ }
+ this._reviewersPendingRemove[type].push(account);
+ }.bind(this));
+ }.bind(this));
+ },
+
+ /**
+ * Resets the state of the _reviewersPendingRemove object, and removes
+ * accounts if necessary.
+ *
+ * @param {Boolean} isCancel true if the action is a cancel.
+ */
+ _purgeReviewersPendingRemove: function(isCancel) {
+ var reviewerArr;
+ for (var type in this._reviewersPendingRemove) {
+ if (this._reviewersPendingRemove.hasOwnProperty(type)) {
+ if (!isCancel) {
+ reviewerArr = this._reviewersPendingRemove[type];
+ for (var i = 0; i < reviewerArr.length; i++) {
+ this._removeAccount(reviewerArr[i], type);
+ }
+ }
+ this._reviewersPendingRemove[type] = [];
+ }
+ }
+ },
+
+ /**
+ * Removes an account from the change, both on the backend and the client.
+ * Does nothing if the account is a pending addition.
+ *
+ * @param {Object} account
+ * @param {ReviewerTypes} type
+ */
+ _removeAccount: function(account, type) {
+ if (account._pendingAdd) { return; }
+
+ return this.$.restAPI.removeChangeReviewer(this.change._number,
+ account._account_id).then(function(response) {
+ if (!response.ok) { return response; }
+
+ var reviewers = this.change.reviewers[type] || [];
+ for (var i = 0; i < reviewers.length; i++) {
+ if (reviewers[i]._account_id == account._account_id) {
+ this.splice(['change', 'reviewers', type], i, 1);
+ break;
+ }
+ }
+ }.bind(this));
+ },
+
_mapReviewer: function(reviewer) {
var reviewerId;
var confirmed;
@@ -161,6 +245,7 @@
drafts: 'PUBLISH_ALL_REVISIONS',
labels: {},
};
+
for (var label in this.permittedLabels) {
if (!this.permittedLabels.hasOwnProperty(label)) { continue; }
@@ -341,17 +426,21 @@
},
_changeUpdated: function(changeRecord, owner, serverConfig) {
+ this._rebuildReviewerArrays(changeRecord.base, owner, serverConfig);
+ },
+
+ _rebuildReviewerArrays: function(change, owner, serverConfig) {
this._owner = owner;
var reviewers = [];
var ccs = [];
- for (var key in changeRecord.base) {
+ for (var key in change) {
if (key !== 'REVIEWER' && key !== 'CC') {
console.warn('unexpected reviewer state:', key);
continue;
}
- changeRecord.base[key].forEach(function(entry) {
+ change[key].forEach(function(entry) {
if (entry._account_id === owner._account_id) {
return;
}
@@ -409,11 +498,16 @@
_cancelTapHandler: function(e) {
e.preventDefault();
this.fire('cancel', null, {bubbles: false});
+ this._purgeReviewersPendingRemove(true);
+ this._rebuildReviewerArrays(this.change.reviewers, this._owner,
+ this.serverConfig);
},
_sendTapHandler: function(e) {
e.preventDefault();
- this.send();
+ this.send().then(function() {
+ this._purgeReviewersPendingRemove();
+ }.bind(this));
},
_saveReview: function(review, opt_errFn) {