Skip to content

Commit 323dc45

Browse files
committed
Finish implementing context dropdowns.
1 parent f535424 commit 323dc45

7 files changed

Lines changed: 164 additions & 65 deletions

File tree

package.json

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,16 @@
986986
"title": "%command.pr.readyForReviewAndMerge.title%",
987987
"category": "%command.pull.request.category%"
988988
},
989+
{
990+
"command": "pr.readyForReviewDescription",
991+
"title": "%command.pr.readyForReview.title%",
992+
"category": "%command.pull.request.category%"
993+
},
994+
{
995+
"command": "pr.readyForReviewAndMergeDescription",
996+
"title": "%command.pr.readyForReviewAndMerge.title%",
997+
"category": "%command.pull.request.category%"
998+
},
989999
{
9901000
"command": "pr.openPullRequestOnGitHub",
9911001
"title": "%command.pr.openPullRequestOnGitHub.title%",
@@ -2062,7 +2072,15 @@
20622072
},
20632073
{
20642074
"command": "pr.readyForReview",
2065-
"when": "gitHubOpenRepositoryCount != 0 && github:inReviewMode"
2075+
"when": "false"
2076+
},
2077+
{
2078+
"command": "pr.readyForReviewDescription",
2079+
"when": "false"
2080+
},
2081+
{
2082+
"command": "pr.readyForReviewAndMergeDescription",
2083+
"when": "false"
20662084
},
20672085
{
20682086
"command": "pr.readyForReviewAndMerge",
@@ -3536,13 +3554,21 @@
35363554
"command": "pr.copyVscodeDevPrLink",
35373555
"when": "webviewId == PullRequestOverview && github:copyMenu"
35383556
},
3557+
{
3558+
"command": "pr.readyForReviewDescription",
3559+
"when": "(webviewId == PullRequestOverview) && github:readyForReviewMenu"
3560+
},
3561+
{
3562+
"command": "pr.readyForReviewAndMergeDescription",
3563+
"when": "(webviewId == PullRequestOverview) && github:readyForReviewMenu && github:readyForReviewMenuWithMerge"
3564+
},
35393565
{
35403566
"command": "pr.readyForReview",
3541-
"when": "(webviewId == PullRequestOverview || webviewId == 'github:activePullRequest') && github:readyForReviewMenu"
3567+
"when": "(webviewId == 'github:activePullRequest') && github:readyForReviewMenu"
35423568
},
35433569
{
35443570
"command": "pr.readyForReviewAndMerge",
3545-
"when": "(webviewId == PullRequestOverview || webviewId == 'github:activePullRequest') && github:readyForReviewMenu && github:readyForReviewMenuWithMerge"
3571+
"when": "(webviewId == 'github:activePullRequest') && github:readyForReviewMenu && github:readyForReviewMenuWithMerge"
35463572
},
35473573
{
35483574
"command": "pr.openChanges",

src/commands.ts

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -813,37 +813,6 @@ export function registerCommands(
813813
}),
814814
);
815815

816-
context.subscriptions.push(
817-
vscode.commands.registerCommand('pr.readyForReview', async (pr?: PRNode) => {
818-
const folderManager = reposManager.getManagerForIssueModel(pr?.pullRequestModel);
819-
if (!folderManager) {
820-
return;
821-
}
822-
const pullRequest = ensurePR(folderManager, pr);
823-
const yes = vscode.l10n.t('Yes');
824-
return vscode.window
825-
.showWarningMessage(
826-
vscode.l10n.t('Are you sure you want to mark this pull request as ready to review on GitHub?'),
827-
{ modal: true },
828-
yes,
829-
)
830-
.then(async value => {
831-
let isDraft;
832-
if (value === yes) {
833-
try {
834-
isDraft = (await pullRequest.setReadyForReview()).isDraft;
835-
return isDraft;
836-
} catch (e) {
837-
vscode.window.showErrorMessage(
838-
`Unable to mark pull request as ready to review. ${formatError(e)}`,
839-
);
840-
return isDraft;
841-
}
842-
}
843-
});
844-
}),
845-
);
846-
847816
context.subscriptions.push(
848817
vscode.commands.registerCommand('pr.dismissNotification', node => {
849818
if (node instanceof PRNode) {

src/github/activityBarViewProvider.ts

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
import * as vscode from 'vscode';
77
import { openPullRequestOnGitHub } from '../commands';
88
import { FolderRepositoryManager } from './folderRepositoryManager';
9-
import { GithubItemStateEnum, IAccount, isITeam, ITeam, PullRequestMergeability, reviewerId, ReviewEventEnum, ReviewState } from './interface';
9+
import { GithubItemStateEnum, IAccount, isITeam, ITeam, MergeMethod, PullRequestMergeability, reviewerId, ReviewEventEnum, ReviewState } from './interface';
1010
import { isCopilotOnMyBehalf, PullRequestModel } from './pullRequestModel';
1111
import { getDefaultMergeMethod } from './pullRequestOverview';
1212
import { PullRequestView } from './pullRequestOverviewCommon';
1313
import { isInCodespaces, parseReviewers } from './utils';
14-
import { MergeArguments, PullRequest, ReviewType, SubmitReviewReply } from './views';
14+
import { MergeArguments, PullRequest, ReadyForReviewReply, ReviewType, SubmitReviewReply } from './views';
1515
import { IComment } from '../common/comment';
1616
import { emojify, ensureEmojis } from '../common/emoji';
1717
import { disposeAll } from '../common/lifecycle';
@@ -34,6 +34,12 @@ export class PullRequestViewProvider extends WebviewViewBase implements vscode.W
3434
) {
3535
super(extensionUri);
3636

37+
this._register(vscode.commands.registerCommand('pr.readyForReview', async () => {
38+
return this.readyForReviewCommand();
39+
}));
40+
this._register(vscode.commands.registerCommand('pr.readyForReviewAndMerge', async (context: { mergeMethod: MergeMethod }) => {
41+
return this.readyForReviewAndMergeCommand(context);
42+
}));
3743
this._register(vscode.commands.registerCommand('review.approve', (e: { body: string }) => this.approvePullRequestCommand(e)));
3844
this._register(vscode.commands.registerCommand('review.comment', (e: { body: string }) => this.submitReviewCommand(e)));
3945
this._register(vscode.commands.registerCommand('review.requestChanges', (e: { body: string }) => this.requestChangesCommand(e)));
@@ -459,6 +465,51 @@ export class PullRequestViewProvider extends WebviewViewBase implements vscode.W
459465
});
460466
}
461467

468+
private async readyForReviewCommand(): Promise<void> {
469+
this._postMessage({
470+
command: 'pr.readying-for-review'
471+
});
472+
try {
473+
const result = await this._item.setReadyForReview();
474+
475+
const readiedResult: ReadyForReviewReply = {
476+
isDraft: result.isDraft
477+
};
478+
await this._postMessage({
479+
command: 'pr.readied-for-review',
480+
result: readiedResult
481+
});
482+
} catch (e) {
483+
vscode.window.showErrorMessage(`Unable to set pull request ready for review. ${formatError(e)}`);
484+
this._throwError(undefined, e.message);
485+
}
486+
}
487+
488+
private async readyForReviewAndMergeCommand(context: { mergeMethod: MergeMethod }): Promise<void> {
489+
this._postMessage({
490+
command: 'pr.readying-for-review'
491+
});
492+
try {
493+
const [readyResult, approveResult] = await Promise.all([this._item.setReadyForReview(), this._item.approve(this._folderRepositoryManager.repository)]);
494+
await this._item.enableAutoMerge(context.mergeMethod);
495+
this.updateReviewers(approveResult);
496+
497+
const readiedResult: ReadyForReviewReply = {
498+
isDraft: readyResult.isDraft,
499+
autoMerge: true,
500+
reviewEvent: approveResult,
501+
reviewers: this._existingReviewers
502+
};
503+
await this._postMessage({
504+
command: 'pr.readied-for-review',
505+
result: readiedResult
506+
});
507+
} catch (e) {
508+
vscode.window.showErrorMessage(`Unable to set pull request ready for review. ${formatError(e)}`);
509+
this._throwError(undefined, e.message);
510+
}
511+
}
512+
462513
private async mergePullRequest(
463514
message: IRequestMessage<MergeArguments>,
464515
): Promise<void> {

src/github/pullRequestOverview.ts

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { isCopilotOnMyBehalf, PullRequestModel } from './pullRequestModel';
2727
import { PullRequestView } from './pullRequestOverviewCommon';
2828
import { pickEmail, reviewersQuickPick } from './quickPicks';
2929
import { parseReviewers } from './utils';
30-
import { CancelCodingAgentReply, DeleteReviewResult, MergeArguments, MergeResult, PullRequest, ReviewType, SubmitReviewReply } from './views';
30+
import { CancelCodingAgentReply, DeleteReviewResult, MergeArguments, MergeResult, PullRequest, ReadyForReviewReply, ReviewType, SubmitReviewReply } from './views';
3131
import { IComment } from '../common/comment';
3232
import { COPILOT_SWE_AGENT, copilotEventToStatus, CopilotPRStatus, mostRecentCopilotEvent } from '../common/copilot';
3333
import { commands, contexts } from '../common/executeCommands';
@@ -142,10 +142,10 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel<PullRequestMode
142142

143143
this.setVisibilityContext();
144144

145-
this._register(vscode.commands.registerCommand('pr.readyForReview', async () => {
145+
this._register(vscode.commands.registerCommand('pr.readyForReviewDescription', async () => {
146146
return this.readyForReviewCommand();
147147
}));
148-
this._register(vscode.commands.registerCommand('pr.readyForReviewAndMerge', async (context?: { mergeMethod: MergeMethod }) => {
148+
this._register(vscode.commands.registerCommand('pr.readyForReviewAndMergeDescription', async (context: { mergeMethod: MergeMethod }) => {
149149
return this.readyForReviewAndMergeCommand(context);
150150
}));
151151
this._register(vscode.commands.registerCommand('review.approveDescription', (e) => this.approvePullRequestCommand(e)));
@@ -685,19 +685,48 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel<PullRequestMode
685685
}
686686

687687
private async readyForReviewCommand(): Promise<void> {
688-
// Trigger the webview action by posting a message
689-
// This will use the existing webview logic which handles busy state
690688
this._postMessage({
691-
command: 'pr.readyForReview-trigger'
689+
command: 'pr.readying-for-review'
692690
});
691+
try {
692+
const result = await this._item.setReadyForReview();
693+
694+
const readiedResult: ReadyForReviewReply = {
695+
isDraft: result.isDraft
696+
};
697+
await this._postMessage({
698+
command: 'pr.readied-for-review',
699+
result: readiedResult
700+
});
701+
} catch (e) {
702+
vscode.window.showErrorMessage(`Unable to set pull request ready for review. ${formatError(e)}`);
703+
this._throwError(undefined, e.message);
704+
}
693705
}
694706

695-
private async readyForReviewAndMergeCommand(_context?: { mergeMethod: MergeMethod }): Promise<void> {
696-
// Trigger the webview action by posting a message
697-
// This will use the existing webview logic which handles busy state
707+
private async readyForReviewAndMergeCommand(context: { mergeMethod: MergeMethod }): Promise<void> {
698708
this._postMessage({
699-
command: 'pr.readyForReviewAndMerge-trigger'
709+
command: 'pr.readying-for-review'
700710
});
711+
try {
712+
const [readyResult, approveResult] = await Promise.all([this._item.setReadyForReview(), this._item.approve(this._folderRepositoryManager.repository)]);
713+
await this._item.enableAutoMerge(context.mergeMethod);
714+
this.updateReviewers(approveResult);
715+
716+
const readiedResult: ReadyForReviewReply = {
717+
isDraft: readyResult.isDraft,
718+
autoMerge: true,
719+
reviewEvent: approveResult,
720+
reviewers: this._existingReviewers
721+
};
722+
await this._postMessage({
723+
command: 'pr.readied-for-review',
724+
result: readiedResult
725+
});
726+
} catch (e) {
727+
vscode.window.showErrorMessage(`Unable to set pull request ready for review. ${formatError(e)}`);
728+
this._throwError(undefined, e.message);
729+
}
701730
}
702731

703732
private async checkoutDefaultBranch(message: IRequestMessage<string>): Promise<void> {

src/github/views.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,13 @@ export interface SubmitReviewReply {
126126
reviewers?: ReviewState[];
127127
}
128128

129+
export interface ReadyForReviewReply {
130+
isDraft: boolean;
131+
reviewEvent?: ReviewEvent;
132+
reviewers?: ReviewState[];
133+
autoMerge?: boolean;
134+
}
135+
129136
export interface MergeArguments {
130137
title: string | undefined;
131138
description: string | undefined;

webviews/common/context.tsx

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { CloseResult, OpenCommitChangesArgs } from '../../common/views';
1010
import { IComment } from '../../src/common/comment';
1111
import { EventType, ReviewEvent, SessionLinkInfo, TimelineEvent } from '../../src/common/timelineEvent';
1212
import { IProjectItem, MergeMethod, ReadyForReview } from '../../src/github/interface';
13-
import { CancelCodingAgentReply, ChangeAssigneesReply, DeleteReviewResult, MergeArguments, MergeResult, ProjectItemsReply, PullRequest, SubmitReviewReply } from '../../src/github/views';
13+
import { CancelCodingAgentReply, ChangeAssigneesReply, DeleteReviewResult, MergeArguments, MergeResult, ProjectItemsReply, PullRequest, ReadyForReviewReply, SubmitReviewReply } from '../../src/github/views';
1414

1515
export class PRContext {
1616
constructor(
@@ -245,6 +245,32 @@ export class PRContext {
245245
this.updatePR(state);
246246
}
247247

248+
private readyForReviewComplete(reply: ReadyForReviewReply) {
249+
const { pr: state } = this;
250+
if (!state) {
251+
throw new Error('Unexpectedly no pull request when trying to ready for review');
252+
}
253+
const { isDraft, reviewEvent, reviewers } = reply;
254+
state.busy = false;
255+
state.isDraft = isDraft;
256+
if (!reviewEvent) {
257+
this.updatePR(state);
258+
return;
259+
}
260+
if (reviewers) {
261+
state.reviewers = reviewers;
262+
}
263+
state.events = [...state.events, reviewEvent];
264+
if (reviewEvent.event === EventType.Reviewed) {
265+
state.currentUserReviewState = reviewEvent.state;
266+
}
267+
if (reply.autoMerge !== undefined) {
268+
state.autoMerge = reply.autoMerge;
269+
state.autoMergeMethod = state.defaultMergeMethod;
270+
}
271+
this.updatePR(state);
272+
}
273+
248274
public reRequestReview = async (reviewerId: string) => {
249275
const { pr: state } = this;
250276
if (!state) {
@@ -388,20 +414,10 @@ export class PRContext {
388414
return this.updatePR({ busy: true, lastReviewType: message.lastReviewType });
389415
case 'pr.append-review':
390416
return this.appendReview(message);
391-
case 'pr.readyForReview-trigger':
392-
// Trigger the ready for review action by simulating a button click
393-
const readyButton = document.querySelector('[value="ready"]') as HTMLButtonElement;
394-
if (readyButton) {
395-
readyButton.click();
396-
}
397-
return;
398-
case 'pr.readyForReviewAndMerge-trigger':
399-
// Trigger the ready and merge action by simulating a button click
400-
const readyMergeButton = document.querySelector('[value="readyAndMerge"]') as HTMLButtonElement;
401-
if (readyMergeButton) {
402-
readyMergeButton.click();
403-
}
404-
return;
417+
case 'pr.readying-for-review':
418+
return this.updatePR({ busy: true });
419+
case 'pr.readied-for-review':
420+
return this.readyForReviewComplete(message);
405421
}
406422
};
407423

webviews/components/merge.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -285,8 +285,8 @@ export const OfferToUpdate = ({ mergeable, isSimple, isCurrentlyCheckedOut, canU
285285
};
286286

287287
export const ReadyForReview = ({ isSimple, isCopilotOnMyBehalf, mergeMethod }: { isSimple: boolean; isCopilotOnMyBehalf?: boolean; mergeMethod: MergeMethod }) => {
288-
const { readyForReview, readyForReviewAndMerge, updatePR } = useContext(PullRequestContext);
289-
const [isBusy, setBusy] = useState(false);
288+
const { readyForReview, readyForReviewAndMerge, updatePR, pr } = useContext(PullRequestContext);
289+
const [isBusy, setBusy] = useState(pr?.busy ?? false);
290290

291291
const markReadyForReview = useCallback(async () => {
292292
try {
@@ -342,14 +342,15 @@ export const ReadyForReview = ({ isSimple, isCopilotOnMyBehalf, mergeMethod }: {
342342
optionsContext={() => JSON.stringify({
343343
'preventDefaultContextMenuItems': true,
344344
'github:readyForReviewMenu': true,
345-
'github:readyForReviewMenuWithMerge': isCopilotOnMyBehalf
345+
'github:readyForReviewMenuWithMerge': isCopilotOnMyBehalf,
346+
'mergeMethod': mergeMethod
346347
})}
347348
defaultAction={markReadyForReview}
348349
defaultOptionLabel={() => 'Ready for Review'}
349350
defaultOptionValue={() => 'ready'}
350351
allOptions={allOptions}
351352
optionsTitle='Ready for Review'
352-
disabled={isBusy}
353+
disabled={isBusy || pr?.busy}
353354
hasSingleAction={!isCopilotOnMyBehalf}
354355
spreadable={false}
355356
/>

0 commit comments

Comments
 (0)