| /** |
| * @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'; |
| |
| Polymer({ |
| is: 'gr-related-changes-list', |
| |
| /** |
| * Fired when a new section is loaded so that the change view can determine |
| * a show more button is needed, sometimes before all the sections finish |
| * loading. |
| * |
| * @event new-section-loaded |
| */ |
| |
| properties: { |
| change: Object, |
| hasParent: { |
| type: Boolean, |
| notify: true, |
| value: false, |
| }, |
| patchNum: String, |
| parentChange: Object, |
| hidden: { |
| type: Boolean, |
| value: false, |
| reflectToAttribute: true, |
| }, |
| loading: { |
| type: Boolean, |
| notify: true, |
| }, |
| mergeable: Boolean, |
| _connectedRevisions: { |
| type: Array, |
| computed: '_computeConnectedRevisions(change, patchNum, ' + |
| '_relatedResponse.changes)', |
| }, |
| /** @type {?} */ |
| _relatedResponse: { |
| type: Object, |
| value() { return {changes: []}; }, |
| }, |
| /** @type {?} */ |
| _submittedTogether: { |
| type: Object, |
| value() { return {changes: []}; }, |
| }, |
| _conflicts: { |
| type: Array, |
| value() { return []; }, |
| }, |
| _cherryPicks: { |
| type: Array, |
| value() { return []; }, |
| }, |
| _sameTopic: { |
| type: Array, |
| value() { return []; }, |
| }, |
| }, |
| |
| behaviors: [ |
| Gerrit.PatchSetBehavior, |
| Gerrit.RESTClientBehavior, |
| ], |
| |
| observers: [ |
| '_resultsChanged(_relatedResponse, _submittedTogether, ' + |
| '_conflicts, _cherryPicks, _sameTopic)', |
| ], |
| |
| clear() { |
| this.loading = true; |
| this.hidden = true; |
| |
| this._relatedResponse = {changes: []}; |
| this._submittedTogether = {changes: []}; |
| this._conflicts = []; |
| this._cherryPicks = []; |
| this._sameTopic = []; |
| }, |
| |
| reload() { |
| if (!this.change || !this.patchNum) { |
| return Promise.resolve(); |
| } |
| this.loading = true; |
| const promises = [ |
| this._getRelatedChanges().then(response => { |
| this._relatedResponse = response; |
| this._fireReloadEvent(); |
| this.hasParent = this._calculateHasParent(this.change.change_id, |
| response.changes); |
| }), |
| this._getSubmittedTogether().then(response => { |
| this._submittedTogether = response; |
| this._fireReloadEvent(); |
| }), |
| this._getCherryPicks().then(response => { |
| this._cherryPicks = response; |
| this._fireReloadEvent(); |
| }), |
| ]; |
| |
| // Get conflicts if change is open and is mergeable. |
| if (this.changeIsOpen(this.change.status) && this.mergeable) { |
| promises.push(this._getConflicts().then(response => { |
| // Because the server doesn't always return a response and the |
| // template expects an array, always return an array. |
| this._conflicts = response ? response : []; |
| this._fireReloadEvent(); |
| })); |
| } |
| |
| promises.push(this._getServerConfig().then(config => { |
| if (this.change.topic && !config.change.submit_whole_topic) { |
| return this._getChangesWithSameTopic().then(response => { |
| this._sameTopic = response; |
| }); |
| } else { |
| this._sameTopic = []; |
| } |
| return this._sameTopic; |
| })); |
| |
| return Promise.all(promises).then(() => { |
| this.loading = false; |
| }); |
| }, |
| |
| _fireReloadEvent() { |
| // The listener on the change computes height of the related changes |
| // section, so they have to be rendered first, and inside a dom-repeat, |
| // that requires a flush. |
| Polymer.dom.flush(); |
| this.dispatchEvent(new CustomEvent('new-section-loaded')); |
| }, |
| |
| /** |
| * Determines whether or not the given change has a parent change. If there |
| * is a relation chain, and the change id is not the last item of the |
| * relation chain, there is a parent. |
| * @param {number} currentChangeId |
| * @param {!Array} relatedChanges |
| * @return {boolean} |
| */ |
| _calculateHasParent(currentChangeId, relatedChanges) { |
| return relatedChanges.length > 0 && |
| relatedChanges[relatedChanges.length - 1].change_id !== |
| currentChangeId; |
| }, |
| |
| _getRelatedChanges() { |
| return this.$.restAPI.getRelatedChanges(this.change._number, |
| this.patchNum); |
| }, |
| |
| _getSubmittedTogether() { |
| return this.$.restAPI.getChangesSubmittedTogether(this.change._number); |
| }, |
| |
| _getServerConfig() { |
| return this.$.restAPI.getConfig(); |
| }, |
| |
| _getConflicts() { |
| return this.$.restAPI.getChangeConflicts(this.change._number); |
| }, |
| |
| _getCherryPicks() { |
| return this.$.restAPI.getChangeCherryPicks(this.change.project, |
| this.change.change_id, this.change._number); |
| }, |
| |
| _getChangesWithSameTopic() { |
| return this.$.restAPI.getChangesWithSameTopic(this.change.topic); |
| }, |
| |
| /** |
| * @param {number} changeNum |
| * @param {string} project |
| * @param {number=} opt_patchNum |
| * @return {string} |
| */ |
| _computeChangeURL(changeNum, project, opt_patchNum) { |
| return Gerrit.Nav.getUrlForChangeById(changeNum, project, opt_patchNum); |
| }, |
| |
| _computeChangeContainerClass(currentChange, relatedChange) { |
| const classes = ['changeContainer']; |
| if (this._changesEqual(relatedChange, currentChange)) { |
| classes.push('thisChange'); |
| } |
| return classes.join(' '); |
| }, |
| |
| /** |
| * Do the given objects describe the same change? Compares the changes by |
| * their numbers. |
| * @see /Documentation/rest-api-changes.html#change-info |
| * @see /Documentation/rest-api-changes.html#related-change-and-commit-info |
| * @param {!Object} a Either ChangeInfo or RelatedChangeAndCommitInfo |
| * @param {!Object} b Either ChangeInfo or RelatedChangeAndCommitInfo |
| * @return {boolean} |
| */ |
| _changesEqual(a, b) { |
| const aNum = this._getChangeNumber(a); |
| const bNum = this._getChangeNumber(b); |
| return aNum === bNum; |
| }, |
| |
| /** |
| * Get the change number from either a ChangeInfo (such as those included in |
| * SubmittedTogetherInfo responses) or get the change number from a |
| * RelatedChangeAndCommitInfo (such as those included in a |
| * RelatedChangesInfo response). |
| * @see /Documentation/rest-api-changes.html#change-info |
| * @see /Documentation/rest-api-changes.html#related-change-and-commit-info |
| * |
| * @param {!Object} change Either a ChangeInfo or a |
| * RelatedChangeAndCommitInfo object. |
| * @return {number} |
| */ |
| _getChangeNumber(change) { |
| if (change.hasOwnProperty('_change_number')) { |
| return change._change_number; |
| } |
| return change._number; |
| }, |
| |
| _computeLinkClass(change) { |
| const statuses = []; |
| if (change.status == this.ChangeStatus.ABANDONED) { |
| statuses.push('strikethrough'); |
| } |
| if (change.submittable) { |
| statuses.push('submittable'); |
| } |
| return statuses.join(' '); |
| }, |
| |
| _computeChangeStatusClass(change) { |
| const classes = ['status']; |
| if (change._revision_number != change._current_revision_number) { |
| classes.push('notCurrent'); |
| } else if (this._isIndirectAncestor(change)) { |
| classes.push('indirectAncestor'); |
| } else if (change.submittable) { |
| classes.push('submittable'); |
| } else if (change.status == this.ChangeStatus.NEW) { |
| classes.push('hidden'); |
| } |
| return classes.join(' '); |
| }, |
| |
| _computeChangeStatus(change) { |
| switch (change.status) { |
| case this.ChangeStatus.MERGED: |
| return 'Merged'; |
| case this.ChangeStatus.ABANDONED: |
| return 'Abandoned'; |
| } |
| if (change._revision_number != change._current_revision_number) { |
| return 'Not current'; |
| } else if (this._isIndirectAncestor(change)) { |
| return 'Indirect ancestor'; |
| } else if (change.submittable) { |
| return 'Submittable'; |
| } |
| return ''; |
| }, |
| |
| _resultsChanged(related, submittedTogether, conflicts, |
| cherryPicks, sameTopic) { |
| const results = [ |
| related && related.changes, |
| submittedTogether && submittedTogether.changes, |
| conflicts, |
| cherryPicks, |
| sameTopic, |
| ]; |
| for (let i = 0; i < results.length; i++) { |
| if (results[i] && results[i].length > 0) { |
| this.hidden = false; |
| this.fire('update', null, {bubbles: false}); |
| return; |
| } |
| } |
| this.hidden = true; |
| }, |
| |
| _isIndirectAncestor(change) { |
| return !this._connectedRevisions.includes(change.commit.commit); |
| }, |
| |
| _computeConnectedRevisions(change, patchNum, relatedChanges) { |
| const connected = []; |
| let changeRevision; |
| for (const rev in change.revisions) { |
| if (this.patchNumEquals(change.revisions[rev]._number, patchNum)) { |
| changeRevision = rev; |
| } |
| } |
| const commits = relatedChanges.map(c => { return c.commit; }); |
| let pos = commits.length - 1; |
| |
| while (pos >= 0) { |
| const commit = commits[pos].commit; |
| connected.push(commit); |
| if (commit == changeRevision) { |
| break; |
| } |
| pos--; |
| } |
| while (pos >= 0) { |
| for (let i = 0; i < commits[pos].parents.length; i++) { |
| if (connected.includes(commits[pos].parents[i].commit)) { |
| connected.push(commits[pos].commit); |
| break; |
| } |
| } |
| --pos; |
| } |
| return connected; |
| }, |
| |
| _computeSubmittedTogetherClass(submittedTogether) { |
| if (!submittedTogether || ( |
| submittedTogether.changes.length === 0 && |
| !submittedTogether.non_visible_changes)) { |
| return 'hidden'; |
| } |
| return ''; |
| }, |
| |
| _computeNonVisibleChangesNote(n) { |
| const noun = n === 1 ? 'change' : 'changes'; |
| return `(+ ${n} non-visible ${noun})`; |
| }, |
| }); |
| })(); |