Skip to content

Commit 135b242

Browse files
Copilotalexr00
andcommitted
Add convert to draft functionality - fix function replacement error
Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com>
1 parent 7432eb2 commit 135b242

File tree

9 files changed

+145
-1
lines changed

9 files changed

+145
-1
lines changed

src/github/graphql.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,16 @@ export interface MarkPullRequestReadyForReviewResponse {
491491
};
492492
}
493493

494+
export interface ConvertPullRequestToDraftResponse {
495+
convertPullRequestToDraft: {
496+
pullRequest: {
497+
isDraft: boolean;
498+
mergeable: 'MERGEABLE' | 'CONFLICTING' | 'UNKNOWN';
499+
mergeStateStatus: 'BEHIND' | 'BLOCKED' | 'CLEAN' | 'DIRTY' | 'HAS_HOOKS' | 'UNKNOWN' | 'UNSTABLE';
500+
};
501+
};
502+
}
503+
494504
export interface MergeQueueForBranchResponse {
495505
repository: {
496506
mergeQueue?: {

src/github/interface.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ export interface ReadyForReview {
5050
allowAutoMerge: boolean;
5151
}
5252

53+
export interface ConvertToDraft {
54+
isDraft: boolean;
55+
mergeable: PullRequestMergeability;
56+
}
57+
5358
export interface IActor {
5459
login: string;
5560
avatarUrl?: string;

src/github/pullRequestModel.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
AddCommentResponse,
1919
AddReactionResponse,
2020
AddReviewThreadResponse,
21+
ConvertPullRequestToDraftResponse,
2122
DeleteReactionResponse,
2223
DeleteReviewResponse,
2324
DequeuePullRequestResponse,
@@ -44,6 +45,7 @@ import {
4445
} from './graphql';
4546
import {
4647
AccountType,
48+
ConvertToDraft,
4749
GithubItemStateEnum,
4850
IAccount,
4951
IGitTreeItem,
@@ -1837,6 +1839,44 @@ export class PullRequestModel extends IssueModel<PullRequest> implements IPullRe
18371839
}
18381840
}
18391841

1842+
/**
1843+
* Convert a pull request to draft.
1844+
*/
1845+
async convertToDraft(): Promise<ConvertToDraft> {
1846+
try {
1847+
const { mutate, schema } = await this.githubRepository.ensure();
1848+
1849+
const { data } = await mutate<ConvertPullRequestToDraftResponse>({
1850+
mutation: schema.ConvertToDraft,
1851+
variables: {
1852+
input: {
1853+
pullRequestId: this.graphNodeId,
1854+
},
1855+
},
1856+
});
1857+
1858+
/* __GDPR__
1859+
"pr.convertToDraft.success" : {}
1860+
*/
1861+
this._telemetry.sendTelemetryEvent('pr.convertToDraft.success');
1862+
1863+
const result: ConvertToDraft = {
1864+
isDraft: data!.convertPullRequestToDraft.pullRequest.isDraft,
1865+
mergeable: parseMergeability(data!.convertPullRequestToDraft.pullRequest.mergeable, data!.convertPullRequestToDraft.pullRequest.mergeStateStatus),
1866+
};
1867+
this.item.isDraft = result.isDraft;
1868+
this.item.mergeable = result.mergeable;
1869+
this._onDidChange.fire({ draft: true });
1870+
return result;
1871+
} catch (e) {
1872+
/* __GDPR__
1873+
"pr.convertToDraft.failure" : {}
1874+
*/
1875+
this._telemetry.sendTelemetryErrorEvent('pr.convertToDraft.failure');
1876+
throw e;
1877+
}
1878+
}
1879+
18401880
private updateCommentReactions(graphNodeId: string, reactionGroups: ReactionGroup[]) {
18411881
const reviewThread = this._reviewThreadsCache?.find(thread =>
18421882
thread.comments.some(c => c.graphNodeId === graphNodeId),

src/github/pullRequestOverview.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,8 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel<PullRequestMode
385385
return this.setReadyForReview(message);
386386
case 'pr.readyForReviewAndMerge':
387387
return this.setReadyForReviewAndMerge(message);
388+
case 'pr.convertToDraft':
389+
return this.setConvertToDraft(message);
388390
case 'pr.approve':
389391
return this.approvePullRequestMessage(message);
390392
case 'pr.request-changes':
@@ -666,6 +668,10 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel<PullRequestMode
666668
return PullRequestReviewCommon.setReadyForReviewAndMerge(this.getReviewContext(), message);
667669
}
668670

671+
private async setConvertToDraft(message: IRequestMessage<{}>): Promise<void> {
672+
return PullRequestReviewCommon.setConvertToDraft(this.getReviewContext(), message);
673+
}
674+
669675
private async readyForReviewCommand(): Promise<void> {
670676
return PullRequestReviewCommon.readyForReviewCommand(this.getReviewContext());
671677
}
@@ -674,6 +680,10 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel<PullRequestMode
674680
return PullRequestReviewCommon.readyForReviewAndMergeCommand(this.getReviewContext(), context);
675681
}
676682

683+
private async convertToDraftCommand(): Promise<void> {
684+
return PullRequestReviewCommon.convertToDraftCommand(this.getReviewContext());
685+
}
686+
677687
private async checkoutDefaultBranch(message: IRequestMessage<string>): Promise<void> {
678688
return PullRequestReviewCommon.checkoutDefaultBranch(this.getReviewContext(), message);
679689
}

src/github/pullRequestReviewCommon.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,16 @@ export namespace PullRequestReviewCommon {
229229
}
230230
}
231231

232+
export async function setConvertToDraft(ctx: ReviewContext, _message: IRequestMessage<{}>): Promise<void> {
233+
try {
234+
const result = await ctx.item.convertToDraft();
235+
ctx.replyMessage(_message, result);
236+
} catch (e) {
237+
vscode.window.showErrorMessage(vscode.l10n.t('Unable to convert pull request to draft. {0}', formatError(e)));
238+
ctx.throwError(_message, '');
239+
}
240+
}
241+
232242
export async function readyForReviewCommand(ctx: ReviewContext): Promise<void> {
233243
ctx.postMessage({
234244
command: 'pr.readying-for-review'
@@ -274,6 +284,26 @@ export namespace PullRequestReviewCommon {
274284
}
275285
}
276286

287+
export async function convertToDraftCommand(ctx: ReviewContext): Promise<void> {
288+
ctx.postMessage({
289+
command: 'pr.converting-to-draft'
290+
});
291+
try {
292+
const result = await ctx.item.convertToDraft();
293+
294+
const convertedResult = {
295+
isDraft: result.isDraft
296+
};
297+
await ctx.postMessage({
298+
command: 'pr.converted-to-draft',
299+
result: convertedResult
300+
});
301+
} catch (e) {
302+
vscode.window.showErrorMessage(`Unable to convert pull request to draft. ${formatError(e)}`);
303+
ctx.throwError(undefined, e.message);
304+
}
305+
}
306+
277307
export async function deleteBranch(folderRepositoryManager: FolderRepositoryManager, item: PullRequestModel): Promise<{ isReply: boolean, message: any }> {
278308
const branchInfo = await folderRepositoryManager.getBranchNameForPullRequest(item);
279309
const actions: (vscode.QuickPickItem & { type: 'remoteHead' | 'local' | 'remote' | 'suspend' })[] = [];

src/github/queriesShared.gql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,16 @@ mutation ReadyForReview($input: MarkPullRequestReadyForReviewInput!) {
738738
}
739739
}
740740

741+
mutation ConvertToDraft($input: ConvertPullRequestToDraftInput!) {
742+
convertPullRequestToDraft(input: $input) {
743+
pullRequest {
744+
isDraft
745+
mergeable
746+
mergeStateStatus
747+
}
748+
}
749+
}
750+
741751
mutation StartReview($input: AddPullRequestReviewInput!) {
742752
addPullRequestReview(input: $input) {
743753
pullRequestReview {

src/github/views.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ export interface ReadyForReviewReply {
133133
autoMerge?: boolean;
134134
}
135135

136+
export interface ConvertToDraftReply {
137+
isDraft: boolean;
138+
}
139+
136140
export interface MergeArguments {
137141
title: string | undefined;
138142
description: string | undefined;

webviews/common/context.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { getMessageHandler, MessageHandler } from './message';
99
import { CloseResult, OpenCommitChangesArgs } from '../../common/views';
1010
import { IComment } from '../../src/common/comment';
1111
import { EventType, ReviewEvent, SessionLinkInfo, TimelineEvent } from '../../src/common/timelineEvent';
12-
import { IProjectItem, MergeMethod, ReadyForReview } from '../../src/github/interface';
12+
import { ConvertToDraft, IProjectItem, MergeMethod, ReadyForReview } from '../../src/github/interface';
1313
import { CancelCodingAgentReply, ChangeAssigneesReply, DeleteReviewResult, MergeArguments, MergeResult, ProjectItemsReply, PullRequest, ReadyForReviewReply, SubmitReviewReply } from '../../src/github/views';
1414

1515
export class PRContext {
@@ -91,6 +91,8 @@ export class PRContext {
9191

9292
public readyForReviewAndMerge = (args: { mergeMethod: MergeMethod }): Promise<ReadyForReview> => this.postMessage({ command: 'pr.readyForReviewAndMerge', args });
9393

94+
public convertToDraft = (): Promise<ConvertToDraft> => this.postMessage({ command: 'pr.convertToDraft' });
95+
9496
public addReviewers = () => this.postMessage({ command: 'pr.change-reviewers' });
9597
public changeProjects = (): Promise<ProjectItemsReply> => this.postMessage({ command: 'pr.change-projects' });
9698
public removeProject = (project: IProjectItem) => this.postMessage({ command: 'pr.remove-project', args: project });

webviews/components/sidebar.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ export default function Sidebar({ reviewers, labels, hasWritePermission, isIssue
9595
) : (
9696
<div className="section-placeholder">None yet</div>
9797
)}
98+
{!pr!.isDraft && (hasWritePermission || pr!.isAuthor) && (
99+
<ConvertToDraft />
100+
)}
98101
</Section>
99102
)}
100103

@@ -507,3 +510,33 @@ function Project(project: IProjectItem & { canDelete: boolean }) {
507510
</div>
508511
);
509512
}
513+
514+
function ConvertToDraft() {
515+
const { convertToDraft, updatePR, pr } = useContext(PullRequestContext);
516+
const [isBusy, setBusy] = useState(false);
517+
518+
const handleConvertToDraft = async () => {
519+
try {
520+
setBusy(true);
521+
const result = await convertToDraft();
522+
updatePR({ isDraft: result.isDraft });
523+
} catch (e) {
524+
// Error handling is done in the backend
525+
} finally {
526+
setBusy(false);
527+
}
528+
};
529+
530+
return (
531+
<div className="section-item">
532+
<button
533+
className="secondary"
534+
onClick={handleConvertToDraft}
535+
disabled={isBusy || pr?.busy}
536+
style={{ width: '100%', marginTop: '8px' }}
537+
>
538+
Convert to draft
539+
</button>
540+
</div>
541+
);
542+
}

0 commit comments

Comments
 (0)