commit 413c75a9c0449a9d8fc49e737298c684ccae8794 Author: Dmitrii Filippov Date: Mon Oct 12 21:34:30 2020 +0200 Rename files to preserve history Test\Eslint fail - this is expected. Change-Id: I7a3d92646bcd4cecdab97438950ac6d97cb83e2f diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js deleted file mode 100644 index b038bf6..0000000 --- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js +++ /dev/null @@ -1,541 +0,0 @@ -/** - * @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. - */ -import '../../../styles/shared-styles.js'; -import '../../../styles/gr-change-metadata-shared-styles.js'; -import '../../../styles/gr-change-view-integration-shared-styles.js'; -import '../../../styles/gr-voting-styles.js'; -import '../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.js'; -import '../../plugins/gr-endpoint-param/gr-endpoint-param.js'; -import '../../plugins/gr-external-style/gr-external-style.js'; -import '../../shared/gr-account-chip/gr-account-chip.js'; -import '../../shared/gr-date-formatter/gr-date-formatter.js'; -import '../../shared/gr-editable-label/gr-editable-label.js'; -import '../../shared/gr-icons/gr-icons.js'; -import '../../shared/gr-limited-text/gr-limited-text.js'; -import '../../shared/gr-linked-chip/gr-linked-chip.js'; -import '../../shared/gr-tooltip-content/gr-tooltip-content.js'; -import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js'; -import '../gr-change-requirements/gr-change-requirements.js'; -import '../gr-commit-info/gr-commit-info.js'; -import '../gr-reviewer-list/gr-reviewer-list.js'; -import '../../shared/gr-account-list/gr-account-list.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-change-metadata_html.js'; -import {GrReviewerSuggestionsProvider, SUGGESTIONS_PROVIDERS_USERS_TYPES} from '../../../scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider.js'; -import {GerritNav} from '../../core/gr-navigation/gr-navigation.js'; -import {ChangeStatus} from '../../../constants/constants.js'; -import {changeIsOpen} from '../../../utils/change-util.js'; - -const HASHTAG_ADD_MESSAGE = 'Add Hashtag'; - -const SubmitTypeLabel = { - FAST_FORWARD_ONLY: 'Fast Forward Only', - MERGE_IF_NECESSARY: 'Merge if Necessary', - REBASE_IF_NECESSARY: 'Rebase if Necessary', - MERGE_ALWAYS: 'Always Merge', - REBASE_ALWAYS: 'Rebase Always', - CHERRY_PICK: 'Cherry Pick', -}; - -const NOT_CURRENT_MESSAGE = 'Not current - rebase possible'; - -/** - * @enum {string} - */ -const CertificateStatus = { - /** - * This certificate status is bad. - */ - BAD: 'BAD', - /** - * This certificate status is OK. - */ - OK: 'OK', - /** - * This certificate status is TRUSTED. - */ - TRUSTED: 'TRUSTED', -}; - -/** - * @extends PolymerElement - */ -class GrChangeMetadata extends GestureEventListeners( - LegacyElementMixin(PolymerElement)) { - static get template() { return htmlTemplate; } - - static get is() { return 'gr-change-metadata'; } - /** - * Fired when the change topic is changed. - * - * @event topic-changed - */ - - static get properties() { - return { - /** @type {?} */ - change: Object, - labels: { - type: Object, - notify: true, - }, - account: Object, - /** @type {?} */ - revision: Object, - commitInfo: Object, - _mutable: { - type: Boolean, - computed: '_computeIsMutable(account)', - }, - /** @type {?} */ - serverConfig: Object, - parentIsCurrent: Boolean, - _notCurrentMessage: { - type: String, - value: NOT_CURRENT_MESSAGE, - readOnly: true, - }, - _topicReadOnly: { - type: Boolean, - computed: '_computeTopicReadOnly(_mutable, change)', - }, - _hashtagReadOnly: { - type: Boolean, - computed: '_computeHashtagReadOnly(_mutable, change)', - }, - /** - * @type {Gerrit.PushCertificateValidation} - */ - _pushCertificateValidation: { - type: Object, - computed: '_computePushCertificateValidation(serverConfig, change)', - }, - _showRequirements: { - type: Boolean, - computed: '_computeShowRequirements(change)', - }, - - _assignee: Array, - _isWip: { - type: Boolean, - computed: '_computeIsWip(change)', - }, - _newHashtag: String, - - _settingTopic: { - type: Boolean, - value: false, - }, - - _currentParents: { - type: Array, - computed: '_computeParents(change, revision)', - }, - - /** @type {?} */ - _CHANGE_ROLE: { - type: Object, - readOnly: true, - value: { - OWNER: 'owner', - UPLOADER: 'uploader', - AUTHOR: 'author', - COMMITTER: 'committer', - }, - }, - }; - } - - static get observers() { - return [ - '_changeChanged(change)', - '_labelsChanged(change.labels)', - '_assigneeChanged(_assignee.*)', - ]; - } - - _labelsChanged(labels) { - this.labels = ({...labels}) || null; - } - - _changeChanged(change) { - this._assignee = change.assignee ? [change.assignee] : []; - this._settingTopic = false; - } - - _assigneeChanged(assigneeRecord) { - if (!this.change || !this._isAssigneeEnabled(this.serverConfig)) { - return; - } - const assignee = assigneeRecord.base; - if (assignee.length) { - const acct = assignee[0]; - if (this.change.assignee && - acct._account_id === this.change.assignee._account_id) { return; } - this.set(['change', 'assignee'], acct); - this.$.restAPI.setAssignee(this.change._number, acct._account_id); - } else { - if (!this.change.assignee) { return; } - this.set(['change', 'assignee'], undefined); - this.$.restAPI.deleteAssignee(this.change._number); - } - } - - _computeHideStrategy(change) { - return !changeIsOpen(change); - } - - /** - * @param {Object} commitInfo - * @return {?Array} If array is empty, returns null instead so - * an existential check can be used to hide or show the webLinks - * section. - */ - _computeWebLinks(commitInfo, serverConfig) { - if (!commitInfo) { return null; } - const weblinks = GerritNav.getChangeWeblinks( - this.change ? this.change.repo : '', - commitInfo.commit, - { - weblinks: commitInfo.web_links, - config: serverConfig, - }); - return weblinks.length ? weblinks : null; - } - - _isAssigneeEnabled(serverConfig) { - return serverConfig && serverConfig.change - && !!serverConfig.change.enable_assignee; - } - - _computeStrategy(change) { - return SubmitTypeLabel[change.submit_type]; - } - - _computeLabelNames(labels) { - return Object.keys(labels).sort(); - } - - _handleTopicChanged(e, topic) { - const lastTopic = this.change.topic; - if (!topic.length) { topic = null; } - this._settingTopic = true; - const topicChangedForChangeNumber = this.change._number; - this.$.restAPI.setChangeTopic(topicChangedForChangeNumber, topic) - .then(newTopic => { - if (this.change._number !== topicChangedForChangeNumber) { - return; - } - this._settingTopic = false; - this.set(['change', 'topic'], newTopic); - if (newTopic !== lastTopic) { - this.dispatchEvent(new CustomEvent( - 'topic-changed', {bubbles: true, composed: true})); - } - }); - } - - _showAddTopic(changeRecord, settingTopic) { - const hasTopic = !!changeRecord && - !!changeRecord.base && !!changeRecord.base.topic; - return !hasTopic && !settingTopic; - } - - _showTopicChip(changeRecord, settingTopic) { - const hasTopic = !!changeRecord && - !!changeRecord.base && !!changeRecord.base.topic; - return hasTopic && !settingTopic; - } - - _showCherryPickOf(changeRecord) { - const hasCherryPickOf = !!changeRecord && - !!changeRecord.base && !!changeRecord.base.cherry_pick_of_change && - !!changeRecord.base.cherry_pick_of_patch_set; - return hasCherryPickOf; - } - - _handleHashtagChanged(e) { - const lastHashtag = this.change.hashtag; - if (!this._newHashtag.length) { return; } - const newHashtag = this._newHashtag; - this._newHashtag = ''; - this.$.restAPI.setChangeHashtag( - this.change._number, {add: [newHashtag]}).then(newHashtag => { - this.set(['change', 'hashtags'], newHashtag); - if (newHashtag !== lastHashtag) { - this.dispatchEvent( - new CustomEvent('hashtag-changed', { - bubbles: true, composed: true})); - } - }); - } - - _computeTopicReadOnly(mutable, change) { - return !mutable || - !change || - !change.actions || - !change.actions.topic || - !change.actions.topic.enabled; - } - - _computeHashtagReadOnly(mutable, change) { - return !mutable || - !change || - !change.actions || - !change.actions.hashtags || - !change.actions.hashtags.enabled; - } - - _computeAssigneeReadOnly(mutable, change) { - return !mutable || - !change || - !change.actions || - !change.actions.assignee || - !change.actions.assignee.enabled; - } - - _computeTopicPlaceholder(_topicReadOnly) { - // Action items in Material Design are uppercase -- placeholder label text - // is sentence case. - return _topicReadOnly ? 'No topic' : 'ADD TOPIC'; - } - - _computeHashtagPlaceholder(_hashtagReadOnly) { - return _hashtagReadOnly ? '' : HASHTAG_ADD_MESSAGE; - } - - _computeShowRequirements(change) { - if (change.status !== ChangeStatus.NEW) { - // TODO(maximeg) change this to display the stored - // requirements, once it is implemented server-side. - return false; - } - const hasRequirements = !!change.requirements && - Object.keys(change.requirements).length > 0; - const hasLabels = !!change.labels && - Object.keys(change.labels).length > 0; - return hasRequirements || hasLabels || !!change.work_in_progress; - } - - /** - * @return {?Gerrit.PushCertificateValidation} object representing data for - * the push validation. - */ - _computePushCertificateValidation(serverConfig, change) { - if (!change || !serverConfig || !serverConfig.receive || - !serverConfig.receive.enable_signed_push) { - return null; - } - const rev = change.revisions[change.current_revision]; - if (!rev.push_certificate || !rev.push_certificate.key) { - return { - class: 'help', - icon: 'gr-icons:help', - message: 'This patch set was created without a push certificate', - }; - } - - const key = rev.push_certificate.key; - switch (key.status) { - case CertificateStatus.BAD: - return { - class: 'invalid', - icon: 'gr-icons:close', - message: this._problems('Push certificate is invalid', key), - }; - case CertificateStatus.OK: - return { - class: 'notTrusted', - icon: 'gr-icons:info', - message: this._problems( - 'Push certificate is valid, but key is not trusted', key), - }; - case CertificateStatus.TRUSTED: - return { - class: 'trusted', - icon: 'gr-icons:check', - message: this._problems( - 'Push certificate is valid and key is trusted', key), - }; - default: - throw new Error(`unknown certificate status: ${key.status}`); - } - } - - _problems(msg, key) { - if (!key || !key.problems || key.problems.length === 0) { - return msg; - } - - return [msg + ':'].concat(key.problems).join('\n'); - } - - _computeShowRepoBranchTogether(repo, branch) { - return !!repo && !!branch && repo.length + branch.length < 40; - } - - _computeProjectUrl(project) { - return GerritNav.getUrlForProjectChanges(project); - } - - _computeBranchUrl(project, branch) { - if (!this.change || !this.change.status) return ''; - return GerritNav.getUrlForBranch(branch, project, - this.change.status == ChangeStatus.NEW ? 'open' : - this.change.status.toLowerCase()); - } - - _computeCherryPickOfUrl(change, patchset, project) { - return GerritNav.getUrlForChangeById(change, project, patchset); - } - - _computeTopicUrl(topic) { - return GerritNav.getUrlForTopic(topic); - } - - _computeHashtagUrl(hashtag) { - return GerritNav.getUrlForHashtag(hashtag); - } - - _handleTopicRemoved(e) { - const target = dom(e).rootTarget; - target.disabled = true; - this.$.restAPI.setChangeTopic(this.change._number, null) - .then(() => { - target.disabled = false; - this.set(['change', 'topic'], ''); - this.dispatchEvent( - new CustomEvent('topic-changed', - {bubbles: true, composed: true})); - }) - .catch(err => { - target.disabled = false; - return; - }); - } - - _handleHashtagRemoved(e) { - e.preventDefault(); - const target = dom(e).rootTarget; - target.disabled = true; - this.$.restAPI.setChangeHashtag(this.change._number, - {remove: [target.text]}) - .then(newHashtag => { - target.disabled = false; - this.set(['change', 'hashtags'], newHashtag); - }) - .catch(err => { - target.disabled = false; - return; - }); - } - - _computeIsWip(change) { - return !!change.work_in_progress; - } - - _computeShowRoleClass(change, role) { - return this._getNonOwnerRole(change, role) ? '' : 'hideDisplay'; - } - - /** - * Get the user with the specified role on the change. Returns null if the - * user with that role is the same as the owner. - * - * @param {!Object} change - * @param {string} role One of the values from _CHANGE_ROLE - * @return {Object|null} either an account or null. - */ - _getNonOwnerRole(change, role) { - if (!change || !change.current_revision || - !change.revisions[change.current_revision]) { - return null; - } - - const rev = change.revisions[change.current_revision]; - if (!rev) { return null; } - - if (role === this._CHANGE_ROLE.UPLOADER && - rev.uploader && - change.owner._account_id !== rev.uploader._account_id) { - return rev.uploader; - } - - if (role === this._CHANGE_ROLE.AUTHOR && - rev.commit && rev.commit.author && - change.owner.email !== rev.commit.author.email) { - return rev.commit.author; - } - - if (role === this._CHANGE_ROLE.COMMITTER && - rev.commit && rev.commit.committer && - change.owner.email !== rev.commit.committer.email) { - return rev.commit.committer; - } - - return null; - } - - _computeParents(change, revision) { - if (!revision || !revision.commit) { - if (!change || !change.current_revision) { return []; } - revision = change.revisions[change.current_revision]; - if (!revision || !revision.commit) { return []; } - } - return revision.commit.parents; - } - - _computeParentsLabel(parents) { - return parents && parents.length > 1 ? 'Parents' : 'Parent'; - } - - _computeParentListClass(parents, parentIsCurrent) { - // Undefined check for polymer 2 - if (parents === undefined || parentIsCurrent === undefined) { - return ''; - } - - return [ - 'parentList', - parents && parents.length > 1 ? 'merge' : 'nonMerge', - parentIsCurrent ? 'current' : 'notCurrent', - ].join(' '); - } - - _computeIsMutable(account) { - return !!Object.keys(account).length; - } - - editTopic() { - if (this._topicReadOnly || this.change.topic) { return; } - // Cannot use `this.$.ID` syntax because the element exists inside of a - // dom-if. - this.shadowRoot.querySelector('.topicEditableLabel').open(); - } - - _getReviewerSuggestionsProvider(change) { - const provider = GrReviewerSuggestionsProvider.create(this.$.restAPI, - change._number, SUGGESTIONS_PROVIDERS_USERS_TYPES.ANY); - provider.init(); - return provider; - } -} - -customElements.define(GrChangeMetadata.is, GrChangeMetadata); diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.ts b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.ts new file mode 100644 index 0000000..b038bf6 --- /dev/null +++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.ts @@ -0,0 +1,541 @@ +/** + * @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. + */ +import '../../../styles/shared-styles.js'; +import '../../../styles/gr-change-metadata-shared-styles.js'; +import '../../../styles/gr-change-view-integration-shared-styles.js'; +import '../../../styles/gr-voting-styles.js'; +import '../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.js'; +import '../../plugins/gr-endpoint-param/gr-endpoint-param.js'; +import '../../plugins/gr-external-style/gr-external-style.js'; +import '../../shared/gr-account-chip/gr-account-chip.js'; +import '../../shared/gr-date-formatter/gr-date-formatter.js'; +import '../../shared/gr-editable-label/gr-editable-label.js'; +import '../../shared/gr-icons/gr-icons.js'; +import '../../shared/gr-limited-text/gr-limited-text.js'; +import '../../shared/gr-linked-chip/gr-linked-chip.js'; +import '../../shared/gr-tooltip-content/gr-tooltip-content.js'; +import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js'; +import '../gr-change-requirements/gr-change-requirements.js'; +import '../gr-commit-info/gr-commit-info.js'; +import '../gr-reviewer-list/gr-reviewer-list.js'; +import '../../shared/gr-account-list/gr-account-list.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-change-metadata_html.js'; +import {GrReviewerSuggestionsProvider, SUGGESTIONS_PROVIDERS_USERS_TYPES} from '../../../scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider.js'; +import {GerritNav} from '../../core/gr-navigation/gr-navigation.js'; +import {ChangeStatus} from '../../../constants/constants.js'; +import {changeIsOpen} from '../../../utils/change-util.js'; + +const HASHTAG_ADD_MESSAGE = 'Add Hashtag'; + +const SubmitTypeLabel = { + FAST_FORWARD_ONLY: 'Fast Forward Only', + MERGE_IF_NECESSARY: 'Merge if Necessary', + REBASE_IF_NECESSARY: 'Rebase if Necessary', + MERGE_ALWAYS: 'Always Merge', + REBASE_ALWAYS: 'Rebase Always', + CHERRY_PICK: 'Cherry Pick', +}; + +const NOT_CURRENT_MESSAGE = 'Not current - rebase possible'; + +/** + * @enum {string} + */ +const CertificateStatus = { + /** + * This certificate status is bad. + */ + BAD: 'BAD', + /** + * This certificate status is OK. + */ + OK: 'OK', + /** + * This certificate status is TRUSTED. + */ + TRUSTED: 'TRUSTED', +}; + +/** + * @extends PolymerElement + */ +class GrChangeMetadata extends GestureEventListeners( + LegacyElementMixin(PolymerElement)) { + static get template() { return htmlTemplate; } + + static get is() { return 'gr-change-metadata'; } + /** + * Fired when the change topic is changed. + * + * @event topic-changed + */ + + static get properties() { + return { + /** @type {?} */ + change: Object, + labels: { + type: Object, + notify: true, + }, + account: Object, + /** @type {?} */ + revision: Object, + commitInfo: Object, + _mutable: { + type: Boolean, + computed: '_computeIsMutable(account)', + }, + /** @type {?} */ + serverConfig: Object, + parentIsCurrent: Boolean, + _notCurrentMessage: { + type: String, + value: NOT_CURRENT_MESSAGE, + readOnly: true, + }, + _topicReadOnly: { + type: Boolean, + computed: '_computeTopicReadOnly(_mutable, change)', + }, + _hashtagReadOnly: { + type: Boolean, + computed: '_computeHashtagReadOnly(_mutable, change)', + }, + /** + * @type {Gerrit.PushCertificateValidation} + */ + _pushCertificateValidation: { + type: Object, + computed: '_computePushCertificateValidation(serverConfig, change)', + }, + _showRequirements: { + type: Boolean, + computed: '_computeShowRequirements(change)', + }, + + _assignee: Array, + _isWip: { + type: Boolean, + computed: '_computeIsWip(change)', + }, + _newHashtag: String, + + _settingTopic: { + type: Boolean, + value: false, + }, + + _currentParents: { + type: Array, + computed: '_computeParents(change, revision)', + }, + + /** @type {?} */ + _CHANGE_ROLE: { + type: Object, + readOnly: true, + value: { + OWNER: 'owner', + UPLOADER: 'uploader', + AUTHOR: 'author', + COMMITTER: 'committer', + }, + }, + }; + } + + static get observers() { + return [ + '_changeChanged(change)', + '_labelsChanged(change.labels)', + '_assigneeChanged(_assignee.*)', + ]; + } + + _labelsChanged(labels) { + this.labels = ({...labels}) || null; + } + + _changeChanged(change) { + this._assignee = change.assignee ? [change.assignee] : []; + this._settingTopic = false; + } + + _assigneeChanged(assigneeRecord) { + if (!this.change || !this._isAssigneeEnabled(this.serverConfig)) { + return; + } + const assignee = assigneeRecord.base; + if (assignee.length) { + const acct = assignee[0]; + if (this.change.assignee && + acct._account_id === this.change.assignee._account_id) { return; } + this.set(['change', 'assignee'], acct); + this.$.restAPI.setAssignee(this.change._number, acct._account_id); + } else { + if (!this.change.assignee) { return; } + this.set(['change', 'assignee'], undefined); + this.$.restAPI.deleteAssignee(this.change._number); + } + } + + _computeHideStrategy(change) { + return !changeIsOpen(change); + } + + /** + * @param {Object} commitInfo + * @return {?Array} If array is empty, returns null instead so + * an existential check can be used to hide or show the webLinks + * section. + */ + _computeWebLinks(commitInfo, serverConfig) { + if (!commitInfo) { return null; } + const weblinks = GerritNav.getChangeWeblinks( + this.change ? this.change.repo : '', + commitInfo.commit, + { + weblinks: commitInfo.web_links, + config: serverConfig, + }); + return weblinks.length ? weblinks : null; + } + + _isAssigneeEnabled(serverConfig) { + return serverConfig && serverConfig.change + && !!serverConfig.change.enable_assignee; + } + + _computeStrategy(change) { + return SubmitTypeLabel[change.submit_type]; + } + + _computeLabelNames(labels) { + return Object.keys(labels).sort(); + } + + _handleTopicChanged(e, topic) { + const lastTopic = this.change.topic; + if (!topic.length) { topic = null; } + this._settingTopic = true; + const topicChangedForChangeNumber = this.change._number; + this.$.restAPI.setChangeTopic(topicChangedForChangeNumber, topic) + .then(newTopic => { + if (this.change._number !== topicChangedForChangeNumber) { + return; + } + this._settingTopic = false; + this.set(['change', 'topic'], newTopic); + if (newTopic !== lastTopic) { + this.dispatchEvent(new CustomEvent( + 'topic-changed', {bubbles: true, composed: true})); + } + }); + } + + _showAddTopic(changeRecord, settingTopic) { + const hasTopic = !!changeRecord && + !!changeRecord.base && !!changeRecord.base.topic; + return !hasTopic && !settingTopic; + } + + _showTopicChip(changeRecord, settingTopic) { + const hasTopic = !!changeRecord && + !!changeRecord.base && !!changeRecord.base.topic; + return hasTopic && !settingTopic; + } + + _showCherryPickOf(changeRecord) { + const hasCherryPickOf = !!changeRecord && + !!changeRecord.base && !!changeRecord.base.cherry_pick_of_change && + !!changeRecord.base.cherry_pick_of_patch_set; + return hasCherryPickOf; + } + + _handleHashtagChanged(e) { + const lastHashtag = this.change.hashtag; + if (!this._newHashtag.length) { return; } + const newHashtag = this._newHashtag; + this._newHashtag = ''; + this.$.restAPI.setChangeHashtag( + this.change._number, {add: [newHashtag]}).then(newHashtag => { + this.set(['change', 'hashtags'], newHashtag); + if (newHashtag !== lastHashtag) { + this.dispatchEvent( + new CustomEvent('hashtag-changed', { + bubbles: true, composed: true})); + } + }); + } + + _computeTopicReadOnly(mutable, change) { + return !mutable || + !change || + !change.actions || + !change.actions.topic || + !change.actions.topic.enabled; + } + + _computeHashtagReadOnly(mutable, change) { + return !mutable || + !change || + !change.actions || + !change.actions.hashtags || + !change.actions.hashtags.enabled; + } + + _computeAssigneeReadOnly(mutable, change) { + return !mutable || + !change || + !change.actions || + !change.actions.assignee || + !change.actions.assignee.enabled; + } + + _computeTopicPlaceholder(_topicReadOnly) { + // Action items in Material Design are uppercase -- placeholder label text + // is sentence case. + return _topicReadOnly ? 'No topic' : 'ADD TOPIC'; + } + + _computeHashtagPlaceholder(_hashtagReadOnly) { + return _hashtagReadOnly ? '' : HASHTAG_ADD_MESSAGE; + } + + _computeShowRequirements(change) { + if (change.status !== ChangeStatus.NEW) { + // TODO(maximeg) change this to display the stored + // requirements, once it is implemented server-side. + return false; + } + const hasRequirements = !!change.requirements && + Object.keys(change.requirements).length > 0; + const hasLabels = !!change.labels && + Object.keys(change.labels).length > 0; + return hasRequirements || hasLabels || !!change.work_in_progress; + } + + /** + * @return {?Gerrit.PushCertificateValidation} object representing data for + * the push validation. + */ + _computePushCertificateValidation(serverConfig, change) { + if (!change || !serverConfig || !serverConfig.receive || + !serverConfig.receive.enable_signed_push) { + return null; + } + const rev = change.revisions[change.current_revision]; + if (!rev.push_certificate || !rev.push_certificate.key) { + return { + class: 'help', + icon: 'gr-icons:help', + message: 'This patch set was created without a push certificate', + }; + } + + const key = rev.push_certificate.key; + switch (key.status) { + case CertificateStatus.BAD: + return { + class: 'invalid', + icon: 'gr-icons:close', + message: this._problems('Push certificate is invalid', key), + }; + case CertificateStatus.OK: + return { + class: 'notTrusted', + icon: 'gr-icons:info', + message: this._problems( + 'Push certificate is valid, but key is not trusted', key), + }; + case CertificateStatus.TRUSTED: + return { + class: 'trusted', + icon: 'gr-icons:check', + message: this._problems( + 'Push certificate is valid and key is trusted', key), + }; + default: + throw new Error(`unknown certificate status: ${key.status}`); + } + } + + _problems(msg, key) { + if (!key || !key.problems || key.problems.length === 0) { + return msg; + } + + return [msg + ':'].concat(key.problems).join('\n'); + } + + _computeShowRepoBranchTogether(repo, branch) { + return !!repo && !!branch && repo.length + branch.length < 40; + } + + _computeProjectUrl(project) { + return GerritNav.getUrlForProjectChanges(project); + } + + _computeBranchUrl(project, branch) { + if (!this.change || !this.change.status) return ''; + return GerritNav.getUrlForBranch(branch, project, + this.change.status == ChangeStatus.NEW ? 'open' : + this.change.status.toLowerCase()); + } + + _computeCherryPickOfUrl(change, patchset, project) { + return GerritNav.getUrlForChangeById(change, project, patchset); + } + + _computeTopicUrl(topic) { + return GerritNav.getUrlForTopic(topic); + } + + _computeHashtagUrl(hashtag) { + return GerritNav.getUrlForHashtag(hashtag); + } + + _handleTopicRemoved(e) { + const target = dom(e).rootTarget; + target.disabled = true; + this.$.restAPI.setChangeTopic(this.change._number, null) + .then(() => { + target.disabled = false; + this.set(['change', 'topic'], ''); + this.dispatchEvent( + new CustomEvent('topic-changed', + {bubbles: true, composed: true})); + }) + .catch(err => { + target.disabled = false; + return; + }); + } + + _handleHashtagRemoved(e) { + e.preventDefault(); + const target = dom(e).rootTarget; + target.disabled = true; + this.$.restAPI.setChangeHashtag(this.change._number, + {remove: [target.text]}) + .then(newHashtag => { + target.disabled = false; + this.set(['change', 'hashtags'], newHashtag); + }) + .catch(err => { + target.disabled = false; + return; + }); + } + + _computeIsWip(change) { + return !!change.work_in_progress; + } + + _computeShowRoleClass(change, role) { + return this._getNonOwnerRole(change, role) ? '' : 'hideDisplay'; + } + + /** + * Get the user with the specified role on the change. Returns null if the + * user with that role is the same as the owner. + * + * @param {!Object} change + * @param {string} role One of the values from _CHANGE_ROLE + * @return {Object|null} either an account or null. + */ + _getNonOwnerRole(change, role) { + if (!change || !change.current_revision || + !change.revisions[change.current_revision]) { + return null; + } + + const rev = change.revisions[change.current_revision]; + if (!rev) { return null; } + + if (role === this._CHANGE_ROLE.UPLOADER && + rev.uploader && + change.owner._account_id !== rev.uploader._account_id) { + return rev.uploader; + } + + if (role === this._CHANGE_ROLE.AUTHOR && + rev.commit && rev.commit.author && + change.owner.email !== rev.commit.author.email) { + return rev.commit.author; + } + + if (role === this._CHANGE_ROLE.COMMITTER && + rev.commit && rev.commit.committer && + change.owner.email !== rev.commit.committer.email) { + return rev.commit.committer; + } + + return null; + } + + _computeParents(change, revision) { + if (!revision || !revision.commit) { + if (!change || !change.current_revision) { return []; } + revision = change.revisions[change.current_revision]; + if (!revision || !revision.commit) { return []; } + } + return revision.commit.parents; + } + + _computeParentsLabel(parents) { + return parents && parents.length > 1 ? 'Parents' : 'Parent'; + } + + _computeParentListClass(parents, parentIsCurrent) { + // Undefined check for polymer 2 + if (parents === undefined || parentIsCurrent === undefined) { + return ''; + } + + return [ + 'parentList', + parents && parents.length > 1 ? 'merge' : 'nonMerge', + parentIsCurrent ? 'current' : 'notCurrent', + ].join(' '); + } + + _computeIsMutable(account) { + return !!Object.keys(account).length; + } + + editTopic() { + if (this._topicReadOnly || this.change.topic) { return; } + // Cannot use `this.$.ID` syntax because the element exists inside of a + // dom-if. + this.shadowRoot.querySelector('.topicEditableLabel').open(); + } + + _getReviewerSuggestionsProvider(change) { + const provider = GrReviewerSuggestionsProvider.create(this.$.restAPI, + change._number, SUGGESTIONS_PROVIDERS_USERS_TYPES.ANY); + provider.init(); + return provider; + } +} + +customElements.define(GrChangeMetadata.is, GrChangeMetadata);