blob: e10b12b7d1300658588bae1c89145b638128bac7 [file] [log] [blame]
/**
* @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 '../../shared/gr-dialog/gr-dialog';
import '../../../styles/shared-styles';
import '../../plugins/gr-endpoint-decorator/gr-endpoint-decorator';
import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners';
import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin';
import {PolymerElement} from '@polymer/polymer/polymer-element';
import {htmlTemplate} from './gr-confirm-revert-dialog_html';
import {customElement, property} from '@polymer/decorators';
import {ChangeInfo, CommitId} from '../../../types/common';
import {fireAlert} from '../../../utils/event-util';
import {appContext} from '../../../services/app-context';
const ERR_COMMIT_NOT_FOUND = 'Unable to find the commit hash of this change.';
const CHANGE_SUBJECT_LIMIT = 50;
// TODO(dhruvsri): clean up repeated definitions after moving to js modules
export enum RevertType {
REVERT_SINGLE_CHANGE = 1,
REVERT_SUBMISSION = 2,
}
export interface ConfirmRevertEventDetail {
revertType: RevertType;
message?: string;
}
@customElement('gr-confirm-revert-dialog')
export class GrConfirmRevertDialog extends GestureEventListeners(
LegacyElementMixin(PolymerElement)
) {
static get template() {
return htmlTemplate;
}
/**
* Fired when the confirm button is pressed.
*
* @event confirm
*/
/**
* Fired when the cancel button is pressed.
*
* @event cancel
*/
/* The revert message updated by the user
The default value is set by the dialog */
@property({type: String})
_message?: string;
@property({type: Number})
_revertType = RevertType.REVERT_SINGLE_CHANGE;
@property({type: Boolean})
_showRevertSubmission = false;
@property({type: Number})
_changesCount?: number;
@property({type: Boolean})
_showErrorMessage = false;
/* store the default revert messages per revert type so that we can
check if user has edited the revert message or not
Set when populate() is called */
@property({type: Array})
_originalRevertMessages: string[] = [];
// Store the actual messages that the user has edited
@property({type: Array})
_revertMessages: string[] = [];
private readonly jsAPI = appContext.jsApiService;
_computeIfSingleRevert(revertType: number) {
return revertType === RevertType.REVERT_SINGLE_CHANGE;
}
_computeIfRevertSubmission(revertType: number) {
return revertType === RevertType.REVERT_SUBMISSION;
}
_modifyRevertMsg(change: ChangeInfo, commitMessage: string, message: string) {
return this.jsAPI.modifyRevertMsg(change, message, commitMessage);
}
populate(change: ChangeInfo, commitMessage: string, changes: ChangeInfo[]) {
this._changesCount = changes.length;
// The option to revert a single change is always available
this._populateRevertSingleChangeMessage(
change,
commitMessage,
change.current_revision
);
this._populateRevertSubmissionMessage(change, changes, commitMessage);
}
_populateRevertSingleChangeMessage(
change: ChangeInfo,
commitMessage: string,
commitHash?: CommitId
) {
// Figure out what the revert title should be.
const originalTitle = (commitMessage || '').split('\n')[0];
const revertTitle = `Revert "${originalTitle}"`;
if (!commitHash) {
fireAlert(this, ERR_COMMIT_NOT_FOUND);
return;
}
const revertCommitText = `This reverts commit ${commitHash}.`;
const message =
`${revertTitle}\n\n${revertCommitText}\n\n` +
'Reason for revert: <INSERT REASONING HERE>\n';
// This is to give plugins a chance to update message
this._message = this._modifyRevertMsg(change, commitMessage, message);
this._revertType = RevertType.REVERT_SINGLE_CHANGE;
this._showRevertSubmission = false;
this._revertMessages[this._revertType] = this._message;
this._originalRevertMessages[this._revertType] = this._message;
}
_getTrimmedChangeSubject(subject: string) {
if (!subject) return '';
if (subject.length < CHANGE_SUBJECT_LIMIT) return subject;
return subject.substring(0, CHANGE_SUBJECT_LIMIT) + '...';
}
_modifyRevertSubmissionMsg(
change: ChangeInfo,
msg: string,
commitMessage: string
) {
return this.jsAPI.modifyRevertSubmissionMsg(change, msg, commitMessage);
}
_populateRevertSubmissionMessage(
change: ChangeInfo,
changes: ChangeInfo[],
commitMessage: string
) {
// Follow the same convention of the revert
const commitHash = change.current_revision;
if (!commitHash) {
fireAlert(this, ERR_COMMIT_NOT_FOUND);
return;
}
if (!changes || changes.length <= 1) return;
const revertTitle = `Revert submission ${change.submission_id}`;
let message =
revertTitle +
'\n\n' +
'Reason for revert: <INSERT ' +
'REASONING HERE>\n';
message += 'Reverted Changes:\n';
changes.forEach(change => {
message +=
`${change.change_id.substring(0, 10)}:` +
`${this._getTrimmedChangeSubject(change.subject)}\n`;
});
this._message = this._modifyRevertSubmissionMsg(
change,
message,
commitMessage
);
this._revertType = RevertType.REVERT_SUBMISSION;
this._revertMessages[this._revertType] = this._message;
this._originalRevertMessages[this._revertType] = this._message;
this._showRevertSubmission = true;
}
_handleRevertSingleChangeClicked() {
this._showErrorMessage = false;
if (this._message)
this._revertMessages[RevertType.REVERT_SUBMISSION] = this._message;
this._message = this._revertMessages[RevertType.REVERT_SINGLE_CHANGE];
this._revertType = RevertType.REVERT_SINGLE_CHANGE;
}
_handleRevertSubmissionClicked() {
this._showErrorMessage = false;
this._revertType = RevertType.REVERT_SUBMISSION;
if (this._message)
this._revertMessages[RevertType.REVERT_SINGLE_CHANGE] = this._message;
this._message = this._revertMessages[RevertType.REVERT_SUBMISSION];
}
_handleConfirmTap(e: MouseEvent) {
e.preventDefault();
e.stopPropagation();
if (this._message === this._originalRevertMessages[this._revertType]) {
this._showErrorMessage = true;
return;
}
const detail: ConfirmRevertEventDetail = {
revertType: this._revertType,
message: this._message,
};
this.dispatchEvent(
new CustomEvent('confirm', {
detail,
composed: true,
bubbles: false,
})
);
}
_handleCancelTap(e: MouseEvent) {
e.preventDefault();
e.stopPropagation();
this.dispatchEvent(
new CustomEvent('cancel', {
detail: {revertType: this._revertType},
composed: true,
bubbles: false,
})
);
}
}
declare global {
interface HTMLElementTagNameMap {
'gr-confirm-revert-dialog': GrConfirmRevertDialog;
}
}