| /** | |
| * @license | |
| * 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, changeComments, revisionInfo)', | |
| }, | |
| _patchDropdownContent: { | |
| type: Object, | |
| computed: '_computePatchDropdownContent(availablePatches,' + | |
| 'basePatchNum, _sortedRevisions, changeComments)', | |
| }, | |
| changeNum: String, | |
| changeComments: Object, | |
| /** @type {{ meta_a: !Array, meta_b: !Array}} */ | |
| filesWeblinks: Object, | |
| patchNum: String, | |
| basePatchNum: String, | |
| revisions: Object, | |
| revisionInfo: Object, | |
| _sortedRevisions: Array, | |
| }, | |
| observers: [ | |
| '_updateSortedRevisions(revisions.*)', | |
| ], | |
| behaviors: [Gerrit.PatchSetBehavior], | |
| _computeBaseDropdownContent(availablePatches, patchNum, _sortedRevisions, | |
| changeComments, revisionInfo) { | |
| const parentCounts = revisionInfo.getParentCountMap(); | |
| const currentParentCount = parentCounts.hasOwnProperty(patchNum) ? | |
| parentCounts[patchNum] : 1; | |
| const maxParents = revisionInfo.getMaxParents(); | |
| const isMerge = currentParentCount > 1; | |
| const dropdownContent = []; | |
| for (const basePatch of availablePatches) { | |
| const basePatchNum = basePatch.num; | |
| const entry = this._createDropdownEntry(basePatchNum, 'Patchset ', | |
| _sortedRevisions, changeComments); | |
| dropdownContent.push(Object.assign({}, entry, { | |
| disabled: this._computeLeftDisabled( | |
| basePatch.num, patchNum, _sortedRevisions), | |
| })); | |
| } | |
| dropdownContent.push({ | |
| text: isMerge ? 'Auto Merge' : 'Base', | |
| value: 'PARENT', | |
| }); | |
| for (let idx = 0; isMerge && idx < maxParents; idx++) { | |
| dropdownContent.push({ | |
| disabled: idx >= currentParentCount, | |
| triggerText: `Parent ${idx + 1}`, | |
| text: `Parent ${idx + 1}`, | |
| mobileText: `Parent ${idx + 1}`, | |
| value: -(idx + 1), | |
| }); | |
| } | |
| return dropdownContent; | |
| }, | |
| _computeMobileText(patchNum, changeComments, revisions) { | |
| return `${patchNum}` + | |
| `${this._computePatchSetCommentsString(changeComments, patchNum)}` + | |
| `${this._computePatchSetDescription(revisions, patchNum, true)}`; | |
| }, | |
| _computePatchDropdownContent(availablePatches, basePatchNum, | |
| _sortedRevisions, changeComments) { | |
| const dropdownContent = []; | |
| for (const patch of availablePatches) { | |
| const patchNum = patch.num; | |
| const entry = this._createDropdownEntry( | |
| patchNum, patchNum === 'edit' ? '' : 'Patchset ', _sortedRevisions, | |
| changeComments); | |
| dropdownContent.push(Object.assign({}, entry, { | |
| disabled: this._computeRightDisabled(basePatchNum, patchNum, | |
| _sortedRevisions), | |
| })); | |
| } | |
| return dropdownContent; | |
| }, | |
| _createDropdownEntry(patchNum, prefix, sortedRevisions, changeComments) { | |
| const entry = { | |
| triggerText: `${prefix}${patchNum}`, | |
| text: `${prefix}${patchNum}` + | |
| `${this._computePatchSetCommentsString( | |
| changeComments, patchNum)}`, | |
| mobileText: this._computeMobileText(patchNum, changeComments, | |
| sortedRevisions), | |
| bottomText: `${this._computePatchSetDescription( | |
| sortedRevisions, patchNum)}`, | |
| value: patchNum, | |
| }; | |
| const date = this._computePatchSetDate(sortedRevisions, patchNum); | |
| if (date) { | |
| entry['date'] = date; | |
| } | |
| return entry; | |
| }, | |
| _updateSortedRevisions(revisionsRecord) { | |
| const revisions = revisionsRecord.base; | |
| this._sortedRevisions = this.sortRevisions(Object.values(revisions)); | |
| }, | |
| /** | |
| * The basePatchNum should always be <= patchNum -- because sortedRevisions | |
| * is sorted in reverse order (higher patchset nums first), invalid base | |
| * patch nums have an index greater than the index of patchNum. | |
| * @param {number|string} basePatchNum The possible base patch num. | |
| * @param {number|string} patchNum The current selected patch num. | |
| * @param {!Array} sortedRevisions | |
| */ | |
| _computeLeftDisabled(basePatchNum, patchNum, sortedRevisions) { | |
| return this.findSortedIndex(basePatchNum, sortedRevisions) <= | |
| this.findSortedIndex(patchNum, sortedRevisions); | |
| }, | |
| /** | |
| * The basePatchNum should always be <= patchNum -- because sortedRevisions | |
| * is sorted in reverse order (higher patchset nums first), invalid patch | |
| * nums have an index greater than the index of basePatchNum. | |
| * | |
| * In addition, if the current basePatchNum is 'PARENT', all patchNums are | |
| * valid. | |
| * | |
| * If the curent basePatchNum is a parent index, then only patches that have | |
| * at least that many parents are valid. | |
| * | |
| * @param {number|string} basePatchNum The current selected base patch num. | |
| * @param {number|string} patchNum The possible patch num. | |
| * @param {!Array} sortedRevisions | |
| * @return {boolean} | |
| */ | |
| _computeRightDisabled(basePatchNum, patchNum, sortedRevisions) { | |
| if (this.patchNumEquals(basePatchNum, 'PARENT')) { return false; } | |
| if (this.isMergeParent(basePatchNum)) { | |
| // Note: parent indices use 1-offset. | |
| return this.revisionInfo.getParentCount(patchNum) < | |
| this.getParentIndex(basePatchNum); | |
| } | |
| return this.findSortedIndex(basePatchNum, sortedRevisions) <= | |
| this.findSortedIndex(patchNum, sortedRevisions); | |
| }, | |
| _computePatchSetCommentsString(changeComments, patchNum) { | |
| if (!changeComments) { return; } | |
| const commentCount = changeComments.computeCommentCount(patchNum); | |
| const commentString = GrCountStringFormatter.computePluralString( | |
| commentCount, 'comment'); | |
| const unresolvedCount = changeComments.computeUnresolvedNum(patchNum); | |
| const unresolvedString = GrCountStringFormatter.computeString( | |
| unresolvedCount, 'unresolved'); | |
| if (!commentString.length && !unresolvedString.length) { | |
| return ''; | |
| } | |
| return ` (${commentString}` + | |
| // Add a comma + space if both comments and unresolved | |
| (commentString && unresolvedString ? ', ' : '') + | |
| `${unresolvedString})`; | |
| }, | |
| /** | |
| * @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) : ''; | |
| }, | |
| /** | |
| * @param {!Array} revisions | |
| * @param {number|string} patchNum | |
| */ | |
| _computePatchSetDate(revisions, patchNum) { | |
| const rev = this.getRevisionByPatchNum(revisions, patchNum); | |
| return rev ? rev.created : undefined; | |
| }, | |
| /** | |
| * 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})); | |
| }, | |
| }); | |
| })(); |