blob: a8314e7618e7989269147a5d1da6bb9801c24e09 [file] [log] [blame]
// Copyright (C) 2016 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.
(function() {
'use strict';
// Maximum length for patch set descriptions.
const PATCH_DESC_MAX_LENGTH = 500;
/**
* Fired when the patch range changes
*
* @event patch-range-change
*
* @property {string} patchNum
* @property {string} basePatchNum
*/
Polymer({
is: 'gr-patch-range-select',
properties: {
availablePatches: Array,
_baseDropdownContent: {
type: Object,
computed: '_computeBaseDropdownContent(availablePatches, patchNum,' +
'_sortedRevisions, revisions, comments)',
},
_patchDropdownContent: {
type: Object,
computed: '_computePatchDropdownContent(availablePatches,' +
'basePatchNum, _sortedRevisions, revisions, comments)',
},
changeNum: String,
// In the case of a patch range select (like diff view) comments should
// be an empty array, so that the patch and base content computed values
// get triggered.
comments: {
type: Object,
value: () => { return {}; },
},
/** @type {{ meta_a: !Array, meta_b: !Array}} */
filesWeblinks: Object,
patchNum: String,
basePatchNum: String,
revisions: Object,
_sortedRevisions: Array,
},
observers: [
'_updateSortedRevisions(revisions.*)',
],
behaviors: [Gerrit.PatchSetBehavior],
_computeBaseDropdownContent(availablePatches, patchNum, _sortedRevisions,
revisions, comments) {
const dropdownContent = [];
dropdownContent.push({
text: 'Base',
value: 'PARENT',
});
for (const basePatch of availablePatches) {
const basePatchNum = basePatch.num;
dropdownContent.push({
disabled: this._computeLeftDisabled(
basePatch.num, patchNum, _sortedRevisions),
triggerText: `Patchset ${basePatchNum}`,
text: `Patchset ${basePatchNum}` +
this._computePatchSetCommentsString(this.comments, basePatchNum),
mobileText: this._computeMobileText(basePatchNum, comments,
revisions),
bottomText: `${this._computePatchSetDescription(
revisions, basePatchNum)}`,
value: basePatch.num,
});
}
return dropdownContent;
},
_computeMobileText(patchNum, comments, revisions) {
return `${patchNum}` +
`${this._computePatchSetCommentsString(this.comments, patchNum)}` +
`${this._computePatchSetDescription(revisions, patchNum, true)}`;
},
_computePatchDropdownContent(availablePatches, basePatchNum,
_sortedRevisions, revisions, comments) {
const dropdownContent = [];
for (const patch of availablePatches) {
const patchNum = patch.num;
dropdownContent.push({
disabled: this._computeRightDisabled(patchNum, basePatchNum,
_sortedRevisions),
triggerText: `${patchNum === 'edit' ? '': 'Patchset '}` +
patchNum,
text: `${patchNum === 'edit' ? '': 'Patchset '}${patchNum}` +
`${this._computePatchSetCommentsString(
this.comments, patchNum)}`,
mobileText: this._computeMobileText(patchNum, comments, revisions),
bottomText: `${this._computePatchSetDescription(
revisions, patchNum)}`,
value: patchNum,
});
}
return dropdownContent;
},
_updateSortedRevisions(revisionsRecord) {
const revisions = revisionsRecord.base;
this._sortedRevisions = this.sortRevisions(Object.values(revisions));
},
_computeLeftDisabled(basePatchNum, patchNum, sortedRevisions) {
return this.findSortedIndex(basePatchNum, sortedRevisions) >=
this.findSortedIndex(patchNum, sortedRevisions);
},
_computeRightDisabled(patchNum, basePatchNum, sortedRevisions) {
if (basePatchNum == 'PARENT') { return false; }
return this.findSortedIndex(patchNum, sortedRevisions) <=
this.findSortedIndex(basePatchNum, sortedRevisions);
},
// Copied from gr-file-list
// @todo(beckysiegel) clean up.
_getCommentsForPath(comments, patchNum, path) {
return (comments[path] || []).filter(c => {
return this.patchNumEquals(c.patch_set, patchNum);
});
},
// Copied from gr-file-list
// @todo(beckysiegel) clean up.
_computeUnresolvedNum(comments, drafts, patchNum, path) {
comments = this._getCommentsForPath(comments, patchNum, path);
drafts = this._getCommentsForPath(drafts, patchNum, path);
comments = comments.concat(drafts);
// Create an object where every comment ID is the key of an unresolved
// comment.
const idMap = comments.reduce((acc, comment) => {
if (comment.unresolved) {
acc[comment.id] = true;
}
return acc;
}, {});
// Set false for the comments that are marked as parents.
for (const comment of comments) {
idMap[comment.in_reply_to] = false;
}
// The unresolved comments are the comments that still have true.
const unresolvedLeaves = Object.keys(idMap).filter(key => {
return idMap[key];
});
return unresolvedLeaves.length;
},
_computePatchSetCommentsString(allComments, patchNum) {
// todo (beckysiegel) get comment strings for diff view also.
if (!allComments) { return ''; }
let numComments = 0;
let numUnresolved = 0;
for (const file in allComments) {
if (allComments.hasOwnProperty(file)) {
numComments += this._getCommentsForPath(
allComments, patchNum, file).length;
numUnresolved += this._computeUnresolvedNum(
allComments, {}, patchNum, file);
}
}
let commentsStr = '';
if (numComments > 0) {
commentsStr = ' (' + numComments + ' comments';
if (numUnresolved > 0) {
commentsStr += ', ' + numUnresolved + ' unresolved';
}
commentsStr += ')';
}
return commentsStr;
},
/**
* @param {!Array} revisions
* @param {number|string} patchNum
* @param {boolean=} opt_addFrontSpace
*/
_computePatchSetDescription(revisions, patchNum, opt_addFrontSpace) {
const rev = this.getRevisionByPatchNum(revisions, patchNum);
return (rev && rev.description) ?
(opt_addFrontSpace ? ' ' : '') +
rev.description.substring(0, PATCH_DESC_MAX_LENGTH) : '';
},
/**
* Catches value-change events from the patchset dropdowns and determines
* whether or not a patch change event should be fired.
*/
_handlePatchChange(e) {
const detail = {patchNum: this.patchNum, basePatchNum: this.basePatchNum};
const target = Polymer.dom(e).localTarget;
if (target === this.$.patchNumDropdown) {
detail.patchNum = e.detail.value;
} else {
detail.basePatchNum = e.detail.value;
}
this.dispatchEvent(
new CustomEvent('patch-range-change', {detail, bubbles: false}));
},
});
})();