// Copyright (C) 2017 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; Polymer({ is: 'gr-file-list-header', properties: { account: Object, allPatchSets: Array, change: Object, changeNum: String, changeUrl: String, comments: Object, commitInfo: Object, editLoaded: Boolean, loggedIn: Boolean, serverConfig: Object, shownFileCount: Number, diffPrefs: Object, diffViewMode: String, /** @type {?} */ patchRange: { type: Object, observer: 'updateSelected', }, revisions: Array, // Caps the number of files that can be shown and have the 'show diffs' / // 'hide diffs' buttons still be functional. _maxFilesForBulkActions: { type: Number, readOnly: true, value: 225, }, _descriptionReadOnly: { type: Boolean, computed: '_computeDescriptionReadOnly(loggedIn, change, account)', }, _selectedPatchSet: String, _diffAgainst: String, }, behaviors: [ Gerrit.PatchSetBehavior, ], _expandAllDiffs() { this.fire('expand-diffs'); }, _collapseAllDiffs() { this.fire('collapse-diffs'); }, updateSelected() { this._selectedPatchSet = this.patchRange.patchNum; this._diffAgainst = this.patchRange.basePatchNum; }, _computeDescriptionPlaceholder(readOnly) { return (readOnly ? 'No' : 'Add a') + ' patch set description'; }, _computeDescriptionReadOnly(loggedIn, change, account) { return !(loggedIn && (account._account_id === change.owner._account_id)); }, _computePatchSetDescription(change, patchNum) { const rev = this.getRevisionByPatchNum(change.revisions, patchNum); return (rev && rev.description) ? rev.description.substring(0, PATCH_DESC_MAX_LENGTH) : ''; }, /** * @param {!Object} revisions The revisions object keyed by revision hashes * @param {?Object} patchSet A revision already fetched from {revisions} * @return {string|undefined} the SHA hash corresponding to the revision. */ _getPatchsetHash(revisions, patchSet) { for (const rev in revisions) { if (revisions.hasOwnProperty(rev) && revisions[rev] === patchSet) { return rev; } } }, _handleDescriptionChanged(e) { const desc = e.detail.trim(); const rev = this.getRevisionByPatchNum(this.change.revisions, this._selectedPatchSet); const sha = this._getPatchsetHash(this.change.revisions, rev); this.$.restAPI.setDescription(this.changeNum, this._selectedPatchSet, desc) .then(res => { if (res.ok) { this.set(['_change', 'revisions', sha, 'description'], desc); } }); }, _computeBasePatchDisabled(patchNum, currentPatchNum) { return this.findSortedIndex(patchNum, this.revisions) >= this.findSortedIndex(currentPatchNum, this.revisions); }, _computePrefsButtonHidden(prefs, loggedIn) { return !loggedIn || !prefs; }, // Copied from gr-file-list _getCommentsForPath(comments, patchNum, path) { return (comments[path] || []).filter(c => { return this.patchNumEquals(c.patch_set, patchNum); }); }, // Copied from gr-file-list _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) { 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; }, _fileListActionsVisible(shownFileCount, maxFilesForBulkActions) { return shownFileCount <= maxFilesForBulkActions; }, /** * Determines if a patch number should be disabled based on value of the * basePatchNum from gr-file-list. * @param {number} patchNum Patch number available in dropdown * @param {number|string} basePatchNum Base patch number from file list * @return {boolean} */ _computePatchSetDisabled(patchNum, basePatchNum) { if (basePatchNum === 'PARENT') { return false; } return this.findSortedIndex(patchNum, this.revisions) <= this.findSortedIndex(basePatchNum, this.revisions); }, /** * Change active patch to the provided patch num. * @param {number|string} basePatchNum the base patch to be viewed. * @param {number|string} patchNum the patch number to be viewed. * @param {boolean} opt_forceParams When set to true, the resulting URL will * always include the patch range, even if the requested patchNum is * known to be the latest. */ _changePatchNum(patchNum, basePatchNum, opt_forceParams) { if (!opt_forceParams) { let currentPatchNum; if (this.change.current_revision) { currentPatchNum = this.change.revisions[this.change.current_revision]._number; } else { currentPatchNum = this.computeLatestPatchNum(this.allPatchSets); } if (this.patchNumEquals(patchNum, currentPatchNum) && basePatchNum === 'PARENT') { Gerrit.Nav.navigateToChange(this.change); return; } } Gerrit.Nav.navigateToChange(this.change, patchNum, basePatchNum); }, _handleBasePatchChange(e) { this._changePatchNum(this._selectedPatchSet, e.target.value, true); }, _handlePatchChange(e) { this._changePatchNum(e.target.value, this._diffAgainst, true); }, _handlePrefsTap(e) { e.preventDefault(); this.fire('open-diff-prefs'); }, _handleDownloadTap(e) { e.preventDefault(); this.fire('open-download-dialog'); }, _computeEditLoadedClass(editLoaded) { return editLoaded ? 'editLoaded' : ''; }, _computePatchInfoClass(patchNum, allPatchSets) { if (this.patchNumEquals(patchNum, this.EDIT_NAME)) { return 'patchInfoEdit'; } const latestNum = this.computeLatestPatchNum(allPatchSets); if (this.patchNumEquals(patchNum, latestNum)) { return ''; } return 'patchInfoOldPatchSet'; }, }); })();