/** * @license * Copyright (C) 2019 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 '../../../scripts/bundled-polymer.js'; import '@polymer/iron-icon/iron-icon.js'; import '../../../behaviors/fire-behavior/fire-behavior.js'; import '../../../styles/shared-styles.js'; import '../../shared/gr-dialog/gr-dialog.js'; import '../../shared/gr-overlay/gr-overlay.js'; import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js'; import '../gr-diff/gr-diff.js'; import {mixinBehaviors} from '@polymer/polymer/lib/legacy/class.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-apply-fix-dialog_html.js'; /** * @appliesMixin Gerrit.FireMixin * @extends Polymer.Element */ class GrApplyFixDialog extends mixinBehaviors( [ Gerrit.FireBehavior, ], GestureEventListeners( LegacyElementMixin( PolymerElement))) { static get template() { return htmlTemplate; } static get is() { return 'gr-apply-fix-dialog'; } static get properties() { return { // Diff rendering preference API response. prefs: Array, // ChangeInfo API response object. change: Object, changeNum: String, _patchNum: Number, // robot ID associated with a robot comment. _robotId: String, // Selected FixSuggestionInfo entity from robot comment API response. _currentFix: Object, // Flattened /preview API response DiffInfo map object. _currentPreviews: {type: Array, value: () => []}, // FixSuggestionInfo entities from robot comment API response. _fixSuggestions: Array, _isApplyFixLoading: { type: Boolean, value: false, }, // Index of currently showing suggested fix. _selectedFixIdx: Number, }; } /** * Given robot comment CustomEvent objevt, fetch diffs associated * with first robot comment suggested fix and open dialog. * * @param {*} e CustomEvent to be passed from gr-comment with * robot comment detail. * @return {Promise} Promise that resolves either when all * preview diffs are fetched or no fix suggestions in custom event detail. */ open(e) { this._patchNum = e.detail.patchNum; this._fixSuggestions = e.detail.comment.fix_suggestions; this._robotId = e.detail.comment.robot_id; if (this._fixSuggestions == null || this._fixSuggestions.length == 0) { return Promise.resolve(); } this._selectedFixIdx = 0; const promises = []; promises.push( this._showSelectedFixSuggestion(this._fixSuggestions[0]), this.$.applyFixOverlay.open() ); return Promise.all(promises) .then(() => { // ensures gr-overlay repositions overlay in center this.$.applyFixOverlay.fire('iron-resize'); }); } attached() { super.attached(); this.refitOverlay = () => { // re-center the dialog as content changed this.$.applyFixOverlay.fire('iron-resize'); }; this.addEventListener('diff-context-expanded', this.refitOverlay); } detached() { super.detached(); this.removeEventListener('diff-context-expanded', this.refitOverlay); } _showSelectedFixSuggestion(fixSuggestion) { this._currentFix = fixSuggestion; return this._fetchFixPreview(fixSuggestion.fix_id); } _fetchFixPreview(fixId) { return this.$.restAPI .getRobotCommentFixPreview(this.changeNum, this._patchNum, fixId) .then(res => { if (res != null) { const previews = Object.keys(res).map(key => { return {filepath: key, preview: res[key]}; }); this._currentPreviews = previews; } }) .catch(err => { this._close(); throw err; }); } hasSingleFix(_fixSuggestions) { return (_fixSuggestions || {}).length === 1; } overridePartialPrefs(prefs) { // generate a smaller gr-diff than fullscreen for dialog return Object.assign({}, prefs, {line_length: 50}); } onCancel(e) { if (e) { e.stopPropagation(); } this._close(); } addOneTo(_selectedFixIdx) { return _selectedFixIdx + 1; } _onPrevFixClick(e) { if (e) e.stopPropagation(); if (this._selectedFixIdx >= 1 && this._fixSuggestions != null) { this._selectedFixIdx -= 1; return this._showSelectedFixSuggestion( this._fixSuggestions[this._selectedFixIdx]); } } _onNextFixClick(e) { if (e) e.stopPropagation(); if (this._fixSuggestions && this._selectedFixIdx < this._fixSuggestions.length) { this._selectedFixIdx += 1; return this._showSelectedFixSuggestion( this._fixSuggestions[this._selectedFixIdx]); } } _noPrevFix(_selectedFixIdx) { return _selectedFixIdx === 0; } _noNextFix(_selectedFixIdx, fixSuggestions) { if (fixSuggestions == null) return true; return _selectedFixIdx === fixSuggestions.length - 1; } _close() { this._currentFix = {}; this._currentPreviews = []; this._isApplyFixLoading = false; this.dispatchEvent(new CustomEvent('close-fix-preview', { bubbles: true, composed: true, })); this.$.applyFixOverlay.close(); } _getApplyFixButtonLabel(isLoading) { return isLoading ? 'Saving...' : 'Apply Fix'; } _handleApplyFix(e) { if (e) { e.stopPropagation(); } if (this._currentFix == null || this._currentFix.fix_id == null) { return; } this._isApplyFixLoading = true; return this.$.restAPI .applyFixSuggestion( this.changeNum, this._patchNum, this._currentFix.fix_id ) .then(res => { if (res && res.ok) { Gerrit.Nav.navigateToChange(this.change, 'edit', this._patchNum); this._close(); } this._isApplyFixLoading = false; }); } getFixDescription(currentFix) { return currentFix != null && currentFix.description ? currentFix.description : ''; } } customElements.define(GrApplyFixDialog.is, GrApplyFixDialog);