diff --git a/src/github/graphql.ts b/src/github/graphql.ts index ea05342343..46cc5869a4 100644 --- a/src/github/graphql.ts +++ b/src/github/graphql.ts @@ -768,6 +768,15 @@ export interface PullRequest extends Issue { suggestedReviewers: SuggestedReviewerResponse[]; additions?: number; deletions?: number; + closingIssuesReferences?: { + nodes: { + id: number, + title: string, + number: number, + state: 'CLOSED' | 'OPEN', + url: string, + }[]; + }; } export enum DefaultCommitTitle { diff --git a/src/github/interface.ts b/src/github/interface.ts index 4bf23999c5..eb28ac8a86 100644 --- a/src/github/interface.ts +++ b/src/github/interface.ts @@ -223,6 +223,14 @@ export interface Issue { reactions: Reaction[]; } +export interface IssueReference { + id: number; + number: number; + title: string; + state: GithubItemStateEnum; + url: string; +} + export interface PullRequest extends Issue { isDraft?: boolean; isRemoteHeadDeleted?: boolean; @@ -242,6 +250,7 @@ export interface PullRequest extends Issue { mergeCommitMeta?: { title: string, description: string }; squashCommitMeta?: { title: string, description: string }; suggestedReviewers?: ISuggestedReviewer[]; + closingIssues?: IssueReference[] hasComments?: boolean; additions?: number; deletions?: number; diff --git a/src/github/pullRequestModel.ts b/src/github/pullRequestModel.ts index 74b7be8c69..9c0a0a5b08 100644 --- a/src/github/pullRequestModel.ts +++ b/src/github/pullRequestModel.ts @@ -54,6 +54,7 @@ import { IGitTreeItem, IRawFileChange, IRawFileContent, + IssueReference, ISuggestedReviewer, ITeam, MergeMethod, @@ -137,6 +138,7 @@ export class PullRequestModel extends IssueModel implements IPullRe public conflicts?: string[]; public suggestedReviewers?: ISuggestedReviewer[]; public hasChangesSinceLastReview?: boolean; + public closingIssues: IssueReference[] = []; private _showChangesSinceReview: boolean; private _hasPendingReview: boolean = false; private _onDidChangePendingReviewState: vscode.EventEmitter = this._register(new vscode.EventEmitter()); @@ -265,7 +267,7 @@ export class PullRequestModel extends IssueModel implements IPullRe } this.suggestedReviewers = item.suggestedReviewers; - + this.closingIssues = item.closingIssues ?? []; if (item.isRemoteHeadDeleted != null) { this.isRemoteHeadDeleted = item.isRemoteHeadDeleted; } diff --git a/src/github/pullRequestOverview.ts b/src/github/pullRequestOverview.ts index 3abb26ada6..c8da744507 100644 --- a/src/github/pullRequestOverview.ts +++ b/src/github/pullRequestOverview.ts @@ -458,7 +458,8 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel | undefined +): Array<{ id: number, number: number, title: string, state: GithubItemStateEnum, url: string }> { + if (!closingIssuesReferences) { + return []; + } + + return closingIssuesReferences.map(issue => ({ + id: issue.id, + number: issue.number, + title: issue.title, + state: issue.state === 'OPEN' ? GithubItemStateEnum.Open : GithubItemStateEnum.Closed, + url: issue.url, + })); +} + /** * Used for case insensitive sort by login */ diff --git a/src/github/views.ts b/src/github/views.ts index 3517c6f6a9..54d765a12c 100644 --- a/src/github/views.ts +++ b/src/github/views.ts @@ -28,6 +28,13 @@ export enum ReviewType { RequestChanges = 'requestChanges', } +export interface IssueReference { + number: number; + title: string; + state: GithubItemStateEnum; + url: string; +} + export interface DisplayLabel extends ILabel { displayName: string; } @@ -112,6 +119,7 @@ export interface PullRequest extends Issue { busy?: boolean; loadingCommit?: string; generateDescriptionTitle?: string; + closingIssues: IssueReference[]; } export interface ProjectItemsReply { diff --git a/webviews/common/common.css b/webviews/common/common.css index 76535d22a4..3518bdba88 100644 --- a/webviews/common/common.css +++ b/webviews/common/common.css @@ -286,6 +286,14 @@ body img.avatar { fill: var(--vscode-issues-open); } +.section-icon.issue-open svg path { + fill: var(--vscode-issues-open); +} + +.section-icon.issue-closed svg path { + fill: var(--vscode-issues-closed); +} + .reviewer-icons { display: flex; gap: 4px; diff --git a/webviews/components/icon.tsx b/webviews/components/icon.tsx index 403a9a6d72..0c7f8f8d72 100644 --- a/webviews/components/icon.tsx +++ b/webviews/components/icon.tsx @@ -53,6 +53,8 @@ export const outputIcon = ; // Other icons +export const issueIcon = ; +export const issueClosedIcon = ; export const copilotErrorIcon = ; export const copilotInProgressIcon = ; export const copilotSuccessIcon = ; \ No newline at end of file diff --git a/webviews/components/sidebar.tsx b/webviews/components/sidebar.tsx index e8b3459226..b6844e2bb0 100644 --- a/webviews/components/sidebar.tsx +++ b/webviews/components/sidebar.tsx @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import React, { useContext, useEffect, useRef, useState } from 'react'; -import { closeIcon, copilotIcon, settingsIcon } from './icon'; +import { closeIcon, copilotIcon, issueClosedIcon, issueIcon, settingsIcon } from './icon'; import { Reviewer } from './reviewer'; import { COPILOT_LOGINS } from '../../src/common/copilot'; import { gitHubLabelColor } from '../../src/common/utils'; -import { IAccount, IMilestone, IProjectItem, isITeam, reviewerId, reviewerLabel, ReviewState } from '../../src/github/interface'; -import { ChangeReviewersReply, PullRequest } from '../../src/github/views'; +import { GithubItemStateEnum, IAccount, IMilestone, IProjectItem, isITeam, reviewerId, reviewerLabel, ReviewState } from '../../src/github/interface'; +import { ChangeReviewersReply, IssueReference, PullRequest } from '../../src/github/views'; import PullRequestContext from '../common/context'; import { Label } from '../common/label'; import { AuthorLink, Avatar } from '../components/user'; @@ -53,7 +53,7 @@ function Section({ ); } -export default function Sidebar({ reviewers, labels, hasWritePermission, isIssue, projectItems: projects, milestone, assignees, canAssignCopilot, canRequestCopilotReview }: PullRequest) { +export default function Sidebar({ reviewers, labels, closingIssues, hasWritePermission, isIssue, projectItems: projects, milestone, assignees, canAssignCopilot, canRequestCopilotReview }: PullRequest) { const { addReviewers, addReviewerCopilot, @@ -268,6 +268,22 @@ export default function Sidebar({ reviewers, labels, hasWritePermission, isIssue
No milestone
)} + +
+ {closingIssues.length ? ( + closingIssues.map(issue => ( +
+ +
+ )) + ) : ( +
None yet
+ )} +
); } @@ -577,3 +593,16 @@ function ConvertToDraft() { ); } + +function IssueItem({ issue }: { issue: IssueReference }) { + const isOpen = issue.state === GithubItemStateEnum.Open; + return ( +
+ + {isOpen ? issueIcon : issueClosedIcon} + + #{issue.number} {issue.title} +
+ ); +} + diff --git a/webviews/editorWebview/test/builder/pullRequest.ts b/webviews/editorWebview/test/builder/pullRequest.ts index 5355ec2efb..21076a8adb 100644 --- a/webviews/editorWebview/test/builder/pullRequest.ts +++ b/webviews/editorWebview/test/builder/pullRequest.ts @@ -61,6 +61,7 @@ export const PullRequestBuilder = createBuilderClass()({ hasReviewDraft: { default: false }, busy: { default: undefined }, lastReviewType: { default: undefined }, + closingIssues: { default: [] }, canAssignCopilot: { default: false }, canRequestCopilotReview: { default: false }, isCopilotOnMyBehalf: { default: false },