Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions common/views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ export interface CreatePullRequestNew {
milestone?: IMilestone;
}

export interface CancelCreatePullRequestNew extends CreatePullRequestNew {
hasUnsavedChanges: boolean;
}

// #region new create view

export interface CreateParamsNew {
Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@
"deprecationMessage": "Use the setting 'githubPullRequests.defaultCreateOption' instead.",
"description": "%githubPullRequests.createDraft%"
},
"githubPullRequests.showPullRequestCancelConfirmation": {
"type": "boolean",
"default": true,
"description": "%githubPullRequests.showPullRequestCancelConfirmation%"
},
"githubPullRequests.logLevel": {
"type": "string",
"enum": [
Expand Down
1 change: 1 addition & 0 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"githubPullRequests.defaultCreateOption.createDraft": "The pull request will be created as a draft.",
"githubPullRequests.defaultCreateOption.createAutoMerge": "The pull request will be created with auto-merge enabled. The merge method selected will be the default for the repo or the value of `githubPullRequests.defaultMergeMethod` if set.",
"githubPullRequests.createDraft": "Whether the \"Draft\" checkbox will be checked by default when creating a pull request.",
"githubPullRequests.showPullRequestCancelConfirmation": "Show a confirmation dialog when canceling pull request creation if there are unsaved changes (a manually edited title or description, or any selected labels, assignees, reviewers, projects, or milestone).",
"githubPullRequests.logLevel.description": "Logging for GitHub Pull Request extension. The log is emitted to the output channel named GitHub Pull Request.",
"githubPullRequests.logLevel.markdownDeprecationMessage": {
"message": "Log level is now controlled by the [Developer: Set Log Level...](command:workbench.action.setLogLevel) command. You can set the log level for the current session and also the default log level from there.",
Expand Down
1 change: 1 addition & 0 deletions src/common/settingKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const QUERIES = 'queries';
export const PULL_REQUEST_LABELS = 'labelCreated';
export const FOCUSED_MODE = 'focusedMode';
export const CREATE_DRAFT = 'createDraft';
export const SHOW_PULL_REQUEST_CANCEL_CONFIRMATION = 'showPullRequestCancelConfirmation';
export const QUICK_DIFF = 'quickDiff';
export const SET_AUTO_MERGE = 'setAutoMerge';
export const SHOW_PULL_REQUEST_NUMBER_IN_TREE = 'showPullRequestNumberInTree';
Expand Down
26 changes: 22 additions & 4 deletions src/github/createPRViewProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { branchPicks, cachedBranchPicks, getAssigneesQuickPickItems, getLabelOpt
import { ISSUE_EXPRESSION, parseIssueExpressionOutput, variableSubstitution } from './utils';
import { ChangeTemplateReply, DisplayLabel, PreReviewState } from './views';
import { RemoteInfo } from '../../common/types';
import { ChooseBaseRemoteAndBranchResult, ChooseCompareRemoteAndBranchResult, ChooseRemoteAndBranchArgs, CreateParamsNew, CreatePullRequestNew, TitleAndDescriptionArgs } from '../../common/views';
import { CancelCreatePullRequestNew, ChooseBaseRemoteAndBranchResult, ChooseCompareRemoteAndBranchResult, ChooseRemoteAndBranchArgs, CreateParamsNew, CreatePullRequestNew, TitleAndDescriptionArgs } from '../../common/views';
import type { Branch } from '../api/api';
import { debounce } from '../common/async';
import { GitHubServerType } from '../common/authentication';
Expand All @@ -35,7 +35,8 @@ import {
PR_SETTINGS_NAMESPACE,
PULL_REQUEST_DESCRIPTION,
PULL_REQUEST_LABELS,
PUSH_BRANCH
PUSH_BRANCH,
SHOW_PULL_REQUEST_CANCEL_CONFIRMATION
} from '../common/settingKeys';
import { ITelemetry } from '../common/telemetry';
import { asPromise, compareIgnoreCase, formatError, promiseWithTimeout } from '../common/utils';
Expand Down Expand Up @@ -560,11 +561,28 @@ export abstract class BaseCreatePullRequestViewProvider<T extends BasePullReques
this.setProjects(createdPR, message.args.projects)]);
}

private async cancel(message: IRequestMessage<CreatePullRequestNew>) {
private async cancel(message: IRequestMessage<CancelCreatePullRequestNew>) {
if (message.args.hasUnsavedChanges
&& vscode.workspace.getConfiguration(PR_SETTINGS_NAMESPACE).get<boolean>(SHOW_PULL_REQUEST_CANCEL_CONFIRMATION, true)) {
const discard = vscode.l10n.t('Discard');
const dontAskAgain = vscode.l10n.t('Don\'t Ask Again');
const result = await vscode.window.showWarningMessage(
vscode.l10n.t('Are you sure you want to cancel creating this pull request?'),
{ modal: true, detail: vscode.l10n.t('Your unsaved changes to this pull request will be lost.') },
discard,
dontAskAgain
);
if (result !== discard && result !== dontAskAgain) {
return this._replyMessage(message, { cancelled: false });
}
if (result === dontAskAgain) {
await vscode.workspace.getConfiguration(PR_SETTINGS_NAMESPACE).update(SHOW_PULL_REQUEST_CANCEL_CONFIRMATION, false, vscode.ConfigurationTarget.Global);
}
}
this._onDone.fire(undefined);
// Re-fetch the automerge info so that it's updated for next time.
await this.getMergeConfiguration(message.args.owner, message.args.repo, true);
return this._replyMessage(message, undefined);
return this._replyMessage(message, { cancelled: true });
}

private async openDescriptionSettings(): Promise<void> {
Expand Down
31 changes: 26 additions & 5 deletions webviews/common/createContextNew.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { createContext } from 'react';
import { getMessageHandler, MessageHandler, vscode } from './message';
import { RemoteInfo } from '../../common/types';
import { ChooseBaseRemoteAndBranchResult, ChooseCompareRemoteAndBranchResult, ChooseRemoteAndBranchArgs, CreateParamsNew, CreatePullRequestNew, ScrollPosition, TitleAndDescriptionArgs, TitleAndDescriptionResult } from '../../common/views';
import { CancelCreatePullRequestNew, ChooseBaseRemoteAndBranchResult, ChooseCompareRemoteAndBranchResult, ChooseRemoteAndBranchArgs, CreateParamsNew, CreatePullRequestNew, ScrollPosition, TitleAndDescriptionArgs, TitleAndDescriptionResult } from '../../common/views';
import { compareIgnoreCase } from '../../src/common/utils';
import { PreReviewState } from '../../src/github/views';

Expand Down Expand Up @@ -92,12 +92,33 @@ export class CreatePRContextNew {
}
};

public cancelCreate = (): Promise<void> => {
const args = this.copyParams();
vscode.setState(defaultCreateParams);
return this.postMessage({ command: 'pr.cancelCreate', args });
public cancelCreate = async (): Promise<void> => {
const args: CancelCreatePullRequestNew = {
...this.copyParams(),
hasUnsavedChanges: this.hasUnsavedChanges()
};
const result = await this.postMessage({ command: 'pr.cancelCreate', args }) as { cancelled?: boolean } | undefined;
// Only clear persisted state if the extension explicitly confirmed the
// cancellation. Otherwise (e.g. the user declined the confirmation
// dialog, or the message did not get a response) preserve the user's
// in-progress title/description.
if (result?.cancelled === true) {
vscode.setState(defaultCreateParams);
}
};

private hasUnsavedChanges(): boolean {
const params = this.createParams;
const titleEdited = !!params.pendingTitle && params.pendingTitle !== params.defaultTitle;
const descriptionEdited = !!params.pendingDescription && params.pendingDescription !== params.defaultDescription;
const hasLabels = (params.labels?.length ?? 0) > 0;
const hasAssignees = (params.assignees?.length ?? 0) > 0;
const hasReviewers = (params.reviewers?.length ?? 0) > 0;
const hasProjects = (params.projects?.length ?? 0) > 0;
const hasMilestone = !!params.milestone;
return titleEdited || descriptionEdited || hasLabels || hasAssignees || hasReviewers || hasProjects || hasMilestone;
}

public updateState = (params: Partial<CreateParamsNew>, reset: boolean = false): void => {
this.createParams = reset ? { ...defaultCreateParams, ...params } : { ...this.createParams, ...params };
vscode.setState(this.createParams);
Expand Down