| /** |
| * @license |
| * 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. |
| */ |
| import '../../../styles/shared-styles.js'; |
| import '../../diff/gr-diff-mode-selector/gr-diff-mode-selector.js'; |
| import '../../diff/gr-patch-range-select/gr-patch-range-select.js'; |
| import '../../edit/gr-edit-controls/gr-edit-controls.js'; |
| import '../../shared/gr-editable-label/gr-editable-label.js'; |
| import '../../shared/gr-linked-chip/gr-linked-chip.js'; |
| import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js'; |
| import '../../shared/gr-select/gr-select.js'; |
| import '../../shared/gr-button/gr-button.js'; |
| import '../../shared/gr-icons/gr-icons.js'; |
| import '../gr-commit-info/gr-commit-info.js'; |
| import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js'; |
| import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js'; |
| import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js'; |
| import {PolymerElement} from '@polymer/polymer/polymer-element.js'; |
| import {htmlTemplate} from './gr-file-list-header_html.js'; |
| import {KeyboardShortcutMixin} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.js'; |
| import {GrFileListConstants} from '../gr-file-list-constants.js'; |
| import {GerritNav} from '../../core/gr-navigation/gr-navigation.js'; |
| import { |
| computeLatestPatchNum, |
| getRevisionByPatchNum, |
| patchNumEquals, |
| } from '../../../utils/patch-set-util.js'; |
| |
| // Maximum length for patch set descriptions. |
| const PATCH_DESC_MAX_LENGTH = 500; |
| const MERGED_STATUS = 'MERGED'; |
| |
| /** |
| * @extends PolymerElement |
| */ |
| class GrFileListHeader extends KeyboardShortcutMixin( |
| GestureEventListeners( |
| LegacyElementMixin( |
| PolymerElement))) { |
| static get template() { return htmlTemplate; } |
| |
| static get is() { return 'gr-file-list-header'; } |
| /** |
| * @event expand-diffs |
| */ |
| |
| /** |
| * @event collapse-diffs |
| */ |
| |
| /** |
| * @event open-diff-prefs |
| */ |
| |
| /** |
| * @event open-included-in-dialog |
| */ |
| |
| /** |
| * @event open-download-dialog |
| */ |
| |
| /** |
| * @event open-upload-help-dialog |
| */ |
| |
| static get properties() { |
| return { |
| account: Object, |
| allPatchSets: Array, |
| /** @type {?} */ |
| change: Object, |
| changeNum: String, |
| changeUrl: String, |
| changeComments: Object, |
| commitInfo: Object, |
| editMode: Boolean, |
| loggedIn: Boolean, |
| serverConfig: Object, |
| shownFileCount: Number, |
| diffPrefs: Object, |
| diffPrefsDisabled: Boolean, |
| diffViewMode: { |
| type: String, |
| notify: true, |
| }, |
| patchNum: String, |
| basePatchNum: String, |
| filesExpanded: String, |
| // 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, |
| }, |
| _patchsetDescription: { |
| type: String, |
| value: '', |
| }, |
| _descriptionReadOnly: { |
| type: Boolean, |
| computed: '_computeDescriptionReadOnly(loggedIn, change, account)', |
| }, |
| revisionInfo: Object, |
| }; |
| } |
| |
| static get observers() { |
| return [ |
| '_computePatchSetDescription(change, patchNum)', |
| ]; |
| } |
| |
| setDiffViewMode(mode) { |
| this.$.modeSelect.setMode(mode); |
| } |
| |
| _expandAllDiffs() { |
| this._expanded = true; |
| this.dispatchEvent(new CustomEvent('expand-diffs', { |
| composed: true, bubbles: true, |
| })); |
| } |
| |
| _collapseAllDiffs() { |
| this._expanded = false; |
| this.dispatchEvent(new CustomEvent('collapse-diffs', { |
| composed: true, bubbles: true, |
| })); |
| } |
| |
| _computeExpandedClass(filesExpanded) { |
| const classes = []; |
| if (filesExpanded === GrFileListConstants.FilesExpandedState.ALL) { |
| classes.push('expanded'); |
| } |
| if (filesExpanded === GrFileListConstants.FilesExpandedState.SOME || |
| filesExpanded === GrFileListConstants.FilesExpandedState.ALL) { |
| classes.push('openFile'); |
| } |
| return classes.join(' '); |
| } |
| |
| _computeDescriptionPlaceholder(readOnly) { |
| return (readOnly ? 'No' : 'Add') + ' patchset description'; |
| } |
| |
| _computeDescriptionReadOnly(loggedIn, change, account) { |
| // Polymer 2: check for undefined |
| if ([loggedIn, change, account].includes(undefined)) { |
| return undefined; |
| } |
| |
| return !(loggedIn && (account._account_id === change.owner._account_id)); |
| } |
| |
| _computePatchSetDescription(change, patchNum) { |
| // Polymer 2: check for undefined |
| if ([change, patchNum].includes(undefined)) { |
| return; |
| } |
| |
| const rev = getRevisionByPatchNum(change.revisions, patchNum); |
| this._patchsetDescription = (rev && rev.description) ? |
| rev.description.substring(0, PATCH_DESC_MAX_LENGTH) : ''; |
| } |
| |
| _handleDescriptionRemoved(e) { |
| return this._updateDescription('', e); |
| } |
| |
| /** |
| * @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(); |
| this._updateDescription(desc, e); |
| } |
| |
| /** |
| * Update the patchset description with the rest API. |
| * |
| * @param {string} desc |
| * @param {?(Event|Node)} e |
| * @return {!Promise} |
| */ |
| _updateDescription(desc, e) { |
| const target = dom(e).rootTarget; |
| if (target) { target.disabled = true; } |
| const rev = getRevisionByPatchNum(this.change.revisions, |
| this.patchNum); |
| const sha = this._getPatchsetHash(this.change.revisions, rev); |
| return this.$.restAPI.setDescription(this.changeNum, this.patchNum, desc) |
| .then(res => { |
| if (res.ok) { |
| if (target) { target.disabled = false; } |
| this.set(['change', 'revisions', sha, 'description'], desc); |
| this._patchsetDescription = desc; |
| } |
| }) |
| .catch(err => { |
| if (target) { target.disabled = false; } |
| return; |
| }); |
| } |
| |
| _computePrefsButtonHidden(prefs, diffPrefsDisabled) { |
| return diffPrefsDisabled || !prefs; |
| } |
| |
| _fileListActionsVisible(shownFileCount, maxFilesForBulkActions) { |
| return shownFileCount <= maxFilesForBulkActions; |
| } |
| |
| _handlePatchChange(e) { |
| const {basePatchNum, patchNum} = e.detail; |
| if (patchNumEquals(basePatchNum, this.basePatchNum) && |
| patchNumEquals(patchNum, this.patchNum)) { return; } |
| GerritNav.navigateToChange(this.change, patchNum, basePatchNum); |
| } |
| |
| _handlePrefsTap(e) { |
| e.preventDefault(); |
| this.dispatchEvent(new CustomEvent('open-diff-prefs', { |
| composed: true, bubbles: true, |
| })); |
| } |
| |
| _handleIncludedInTap(e) { |
| e.preventDefault(); |
| this.dispatchEvent(new CustomEvent('open-included-in-dialog', { |
| composed: true, bubbles: true, |
| })); |
| } |
| |
| _handleDownloadTap(e) { |
| e.preventDefault(); |
| e.stopPropagation(); |
| this.dispatchEvent( |
| new CustomEvent('open-download-dialog', {bubbles: false})); |
| } |
| |
| _computeEditModeClass(editMode) { |
| return editMode ? 'editMode' : ''; |
| } |
| |
| _computePatchInfoClass(patchNum, allPatchSets) { |
| const latestNum = computeLatestPatchNum(allPatchSets); |
| if (patchNumEquals(patchNum, latestNum)) { |
| return ''; |
| } |
| return 'patchInfoOldPatchSet'; |
| } |
| |
| _hideIncludedIn(change) { |
| return change && change.status === MERGED_STATUS ? '' : 'hide'; |
| } |
| |
| _handleUploadTap(e) { |
| e.preventDefault(); |
| e.stopPropagation(); |
| this.dispatchEvent( |
| new CustomEvent('open-upload-help-dialog', {bubbles: false})); |
| } |
| |
| _computeUploadHelpContainerClass(change, account) { |
| const changeIsMerged = change && change.status === MERGED_STATUS; |
| const ownerId = change && change.owner && change.owner._account_id ? |
| change.owner._account_id : null; |
| const userId = account && account._account_id; |
| const userIsOwner = ownerId && userId && ownerId === userId; |
| const hideContainer = !userIsOwner || changeIsMerged; |
| return 'uploadContainer desktop' + (hideContainer ? ' hide' : ''); |
| } |
| } |
| |
| customElements.define(GrFileListHeader.is, GrFileListHeader); |