Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
8 changes: 8 additions & 0 deletions src/github/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,14 @@ export interface PullRequest extends Issue {
viewerCanDisableAutoMerge: boolean;
isDraft?: boolean;
suggestedReviewers: SuggestedReviewerResponse[];
closingIssuesReferences?: {

This comment was marked as spam.

nodes: {
id: number,
title: string,
number: number,
state: 'CLOSED' | 'OPEN'
}[];
};
}

export enum DefaultCommitTitle {
Expand Down
1 change: 1 addition & 0 deletions src/github/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ export interface PullRequest extends Issue {
mergeCommitMeta?: { title: string, description: string };
squashCommitMeta?: { title: string, description: string };
suggestedReviewers?: ISuggestedReviewer[];
closingIssues?: Pick<Issue, 'id' | 'title' | 'number' | 'state'>[]
hasComments?: boolean;
}

Expand Down
6 changes: 5 additions & 1 deletion src/github/pullRequestModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ export class PullRequestModel extends IssueModel<PullRequest> implements IPullRe
public conflicts?: string[];
public suggestedReviewers?: ISuggestedReviewer[];
public hasChangesSinceLastReview?: boolean;
public closingIssues: Pick<IssueModel, 'id' | 'title' | 'number' | 'state'>[];
private _showChangesSinceReview: boolean;
private _hasPendingReview: boolean = false;
private _onDidChangePendingReviewState: vscode.EventEmitter<boolean> = new vscode.EventEmitter<boolean>();
Expand Down Expand Up @@ -248,7 +249,10 @@ export class PullRequestModel extends IssueModel<PullRequest> implements IPullRe
super.update(item);
this.isDraft = item.isDraft;
this.suggestedReviewers = item.suggestedReviewers;

this.closingIssues = (item.closingIssues ?? []).map(issue => ({
...issue,
state: issue.state as GithubItemStateEnum
}));
if (item.isRemoteHeadDeleted != null) {
this.isRemoteHeadDeleted = item.isRemoteHeadDeleted;
}
Expand Down
3 changes: 2 additions & 1 deletion src/github/pullRequestOverview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,8 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel<PullRequestMode
emailForCommit,
isAuthor: currentUser.login === pullRequest.author.login,
currentUserReviewState: reviewState,
revertable: pullRequest.state === GithubItemStateEnum.Merged
revertable: pullRequest.state === GithubItemStateEnum.Merged,
closingIssues: pullRequest.closingIssues
};
this._postMessage({
command: 'pr.initialize',
Expand Down
8 changes: 8 additions & 0 deletions src/github/queries.gql
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,14 @@ fragment PullRequestFragment on PullRequest {
mergeCommitMessage
mergeCommitTitle
}
closingIssuesReferences(first: 50) {
nodes {
id
number
title
state
}
}
merged
mergeable
mergeQueueEntry {
Expand Down
17 changes: 17 additions & 0 deletions src/github/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { GitHubRepository, ViewerPermission } from './githubRepository';
import * as GraphQL from './graphql';
import {
AccountType,
GithubItemStateEnum,
IAccount,
IActor,
IGitHubRef,
Expand Down Expand Up @@ -780,6 +781,7 @@ export function parseGraphQLPullRequest(
commits: parseCommits(graphQLPullRequest.commits.nodes),
reactionCount: graphQLPullRequest.reactions.totalCount,
commentCount: graphQLPullRequest.comments.totalCount,
closingIssues: parseClosingIssuesReferences(graphQLPullRequest.closingIssuesReferences?.nodes)
};
pr.mergeCommitMeta = parseCommitMeta(graphQLPullRequest.baseRepository.mergeCommitTitle, graphQLPullRequest.baseRepository.mergeCommitMessage, pr);
pr.squashCommitMeta = parseCommitMeta(graphQLPullRequest.baseRepository.squashMergeCommitTitle, graphQLPullRequest.baseRepository.squashMergeCommitMessage, pr);
Expand Down Expand Up @@ -922,6 +924,21 @@ function parseSuggestedReviewers(
return ret.sort(loginComparator);
}

function parseClosingIssuesReferences(
closingIssuesReferences: Array<{ id: number, number: number, title: string, state: 'CLOSED' | 'OPEN' }> | undefined
): Array<{ id: number, number: number, title: string, state: GithubItemStateEnum }> {
if (!closingIssuesReferences) {
return [];
}

return closingIssuesReferences.map(issue => ({
id: issue.id,
number: issue.number,
title: issue.title,
state: issue.state as GithubItemStateEnum
}));
}

/**
* Used for case insensitive sort by login
*/
Expand Down
1 change: 1 addition & 0 deletions src/github/views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export interface PullRequest extends Issue {
lastReviewType?: ReviewType;
revertable?: boolean;
busy?: boolean;
closingIssues: Pick<Issue, 'title' | 'number' | 'state'>[];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pick<Issue, 'title' | 'number' | 'state'> is used in more than one place, can you refactor it into an IssueReference type in this file?

}

export interface ProjectItemsReply {
Expand Down
52 changes: 48 additions & 4 deletions webviews/components/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
import React, { useContext } from 'react';
import { COPILOT_LOGINS } from '../../src/common/copilot';
import { gitHubLabelColor } from '../../src/common/utils';
import { IMilestone, IProjectItem, reviewerId } from '../../src/github/interface';
import { IMilestone, IProjectItem, Issue, reviewerId, } from '../../src/github/interface';
import { PullRequest } from '../../src/github/views';
import PullRequestContext from '../common/context';
import { Label } from '../common/label';
import { AuthorLink, Avatar } from '../components/user';
import { closeIcon, copilotIcon, settingsIcon } from './icon';
import { Reviewer } from './reviewer';

export default function Sidebar({ reviewers, labels, hasWritePermission, isIssue, projectItems: projects, milestone, assignees, canAssignCopilot }: PullRequest) {
export default function Sidebar({ reviewers, labels, closingIssues, hasWritePermission, isIssue, projectItems: projects, milestone, assignees, canAssignCopilot }: PullRequest) {
const {
addReviewers,
addAssignees,
Expand Down Expand Up @@ -54,7 +54,7 @@ export default function Sidebar({ reviewers, labels, hasWritePermission, isIssue
</div>
{reviewers && reviewers.length ? (
reviewers.map(state => (
<Reviewer key={reviewerId(state.reviewer)} {...{reviewState: state}} />
<Reviewer key={reviewerId(state.reviewer)} {...{ reviewState: state }} />
))
) : (
<div className="section-placeholder">None yet</div>
Expand Down Expand Up @@ -199,7 +199,27 @@ export default function Sidebar({ reviewers, labels, hasWritePermission, isIssue
{milestone ? (
<Milestone key={milestone.title} {...milestone} canDelete={hasWritePermission} />
) : (
<div className="section-placeholder">No milestone</div>
<>
<div className="section-placeholder">No milestone</div>
</>
)}
</div>
<div id="closingIssues" className="section">
<div className="section-header">
<div className="section-title">Linked Issues</div>
</div>
{closingIssues.length > 0 ? (
<div className="p-2">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use a class name that is consistent with the class naming scheme in the file.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for gap-2 below.

{closingIssues.map(issue => (
<div className="section-item reviewer">
<div className="avatar-with-author gap-2">
<IssueItem key={issue.title} issue={issue} />
</div>
</div>
))}
</div>
) : (
<div className="p-4 text-sm text-gray-500 text-center">None yet</div>
)}
</div>
</div>
Expand Down Expand Up @@ -273,3 +293,27 @@ function Project(project: IProjectItem & { canDelete: boolean }) {
</div>
);
}

function IssueItem({ issue }: { issue: Pick<Issue, 'title' | 'number' | 'state'> }) {
return (
<>
<IssueStateIcon state={issue.state} />
<span className="h2">{issue.title}</span>
</>

);
}

function IssueStateIcon({ state }: { state: string }) {
const normalizedState = state.toLowerCase().trim();

switch (normalizedState) {
case 'open':
return settingsIcon;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not issueIcon?

case 'closed':
return closeIcon;
default:
return closeIcon;
}
}

1 change: 1 addition & 0 deletions webviews/editorWebview/test/builder/pullRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,6 @@ export const PullRequestBuilder = createBuilderClass<PullRequest>()({
hasReviewDraft: { default: false },
busy: { default: undefined },
lastReviewType: { default: undefined },
closingIssues: { default: [] },
canAssignCopilot: { default: false },
});