diff --git a/package.json b/package.json index 7f77cddc69..121f52703a 100644 --- a/package.json +++ b/package.json @@ -752,6 +752,36 @@ "default": false, "description": "%githubPullRequests.showPullRequestNumberInTree.description%" }, + "githubPullRequests.treeViewIconMode": { + "type": "string", + "enum": [ + "author", + "state", + "generic" + ], + "enumDescriptions": [ + "%githubPullRequests.treeViewIconMode.author%", + "%githubPullRequests.treeViewIconMode.state%", + "%githubPullRequests.treeViewIconMode.generic%" + ], + "default": "author", + "description": "%githubPullRequests.treeViewIconMode.description%" + }, + "githubIssues.treeViewIconMode": { + "type": "string", + "enum": [ + "author", + "state", + "generic" + ], + "enumDescriptions": [ + "%githubIssues.treeViewIconMode.author%", + "%githubIssues.treeViewIconMode.state%", + "%githubIssues.treeViewIconMode.generic%" + ], + "default": "author", + "description": "%githubIssues.treeViewIconMode.description%" + }, "githubIssues.alwaysPromptForNewIssueRepo": { "type": "boolean", "default": false, diff --git a/package.nls.json b/package.nls.json index d73f75cccc..ba5fbae346 100644 --- a/package.nls.json +++ b/package.nls.json @@ -153,6 +153,14 @@ "githubPullRequests.showPullRequestNumberInTree.description": "Shows the pull request number in the tree view.", "githubPullRequests.labelCreated.description": "Group of labels that you want to add to the pull request automatically. Labels that don't exist in the repository won't be added.", "githubPullRequests.labelCreated.label.description": "Each string element is value of label that you want to add", + "githubPullRequests.treeViewIconMode.description": "Which icon to use in the pull request tree view", + "githubPullRequests.treeViewIconMode.author": "Show the pull request author avatar", + "githubPullRequests.treeViewIconMode.state": "Show the pull request type (draft or not) and state (open/closed/merged) as a colored icon", + "githubPullRequests.treeViewIconMode.generic": "Show a GitHub icon", + "githubIssues.treeViewIconMode.description": "Which icon to use in the issues tree view", + "githubIssues.treeViewIconMode.author": "Show the issue author avatar", + "githubIssues.treeViewIconMode.state": "Show the issue state (open/closed) as a colored icon", + "githubIssues.treeViewIconMode.generic": "Show an issues icon regardless of state", "githubIssues.alwaysPromptForNewIssueRepo.description": "Enabling will always prompt which repository to create an issue in instead of basing off the current open file.", "view.github.pull.requests.name": "GitHub", "view.github.pull.request.name": "GitHub Pull Request", diff --git a/src/common/settingKeys.ts b/src/common/settingKeys.ts index fc273813bc..58635daca6 100644 --- a/src/common/settingKeys.ts +++ b/src/common/settingKeys.ts @@ -58,6 +58,9 @@ export const EXPERIMENTAL_NOTIFICATIONS_PAGE_SIZE = 'experimental.notificationsV export const EXPERIMENTAL_NOTIFICATIONS_SCORE = 'experimental.notificationsScore'; export const WEBVIEW_REFRESH_INTERVAL = 'webviewRefreshInterval'; +export const TREE_VIEW_ICON_MODE = 'treeViewIconMode'; +export type TreeViewIconMode = 'author' | 'state' | 'generic'; + // git export const GIT = 'git'; export const PULL_BEFORE_CHECKOUT = 'pullBeforeCheckout'; diff --git a/src/issues/issuesView.ts b/src/issues/issuesView.ts index 41dec7c4cf..dccca8edf0 100644 --- a/src/issues/issuesView.ts +++ b/src/issues/issuesView.ts @@ -6,6 +6,7 @@ import * as path from 'path'; import * as vscode from 'vscode'; import { commands, contexts } from '../common/executeCommands'; +import { ISSUES_SETTINGS_NAMESPACE, TREE_VIEW_ICON_MODE, TreeViewIconMode } from '../common/settingKeys'; import { DataUri } from '../common/uri'; import { groupBy } from '../common/utils'; import { FolderRepositoryManager, ReposManagerState } from '../github/folderRepositoryManager'; @@ -59,6 +60,14 @@ export class IssuesTreeData this._onDidChangeTreeData.fire(); }), ); + + context.subscriptions.push( + vscode.workspace.onDidChangeConfiguration((e) => { + if (e.affectsConfiguration(`${ISSUES_SETTINGS_NAMESPACE}.${TREE_VIEW_ICON_MODE}`)) { + this._onDidChangeTreeData.fire(); + } + }), + ); } private getFolderRepoItem(element: FolderRepositoryManager): vscode.TreeItem { @@ -77,10 +86,22 @@ export class IssuesTreeData private async getIssueTreeItem(element: IssueItem): Promise { const treeItem = new vscode.TreeItem(element.title, vscode.TreeItemCollapsibleState.None); - treeItem.iconPath = (await DataUri.avatarCirclesAsImageDataUris(this.context, [element.author], 16, 16))[0] ?? - (element.isOpen - ? new vscode.ThemeIcon('issues', new vscode.ThemeColor('issues.open')) - : new vscode.ThemeIcon('issue-closed', new vscode.ThemeColor('issues.closed'))); + + const iconMode = vscode.workspace.getConfiguration(ISSUES_SETTINGS_NAMESPACE).get(TREE_VIEW_ICON_MODE, 'author'); + let iconPath: vscode.Uri | vscode.ThemeIcon = element.isOpen + ? new vscode.ThemeIcon('issues', new vscode.ThemeColor('issues.open')) + : new vscode.ThemeIcon('issue-closed', new vscode.ThemeColor('issues.closed')); + + if (iconMode === 'author') { + const authorAvatar = (await DataUri.avatarCirclesAsImageDataUris(this.context, [element.author], 16, 16))[0]; + if (authorAvatar) { + iconPath = authorAvatar; + } + } else if (iconMode === 'generic') { + iconPath = new vscode.ThemeIcon('issues'); + } + + treeItem.iconPath = iconPath; treeItem.command = { command: 'issue.openDescription', diff --git a/src/view/treeNodes/pullRequestNode.ts b/src/view/treeNodes/pullRequestNode.ts index 0aaf93ae74..e940649bbd 100644 --- a/src/view/treeNodes/pullRequestNode.ts +++ b/src/view/treeNodes/pullRequestNode.ts @@ -9,10 +9,11 @@ import { COPILOT_ACCOUNTS } from '../../common/comment'; import { getCommentingRanges } from '../../common/commentingRanges'; import { InMemFileChange, SlimFileChange } from '../../common/file'; import Logger from '../../common/logger'; -import { FILE_LIST_LAYOUT, PR_SETTINGS_NAMESPACE, SHOW_PULL_REQUEST_NUMBER_IN_TREE } from '../../common/settingKeys'; +import { FILE_LIST_LAYOUT, PR_SETTINGS_NAMESPACE, SHOW_PULL_REQUEST_NUMBER_IN_TREE, TREE_VIEW_ICON_MODE, TreeViewIconMode } from '../../common/settingKeys'; import { createPRNodeUri, DataUri, fromPRUri, Schemes } from '../../common/uri'; import { FolderRepositoryManager } from '../../github/folderRepositoryManager'; import { CopilotWorkingStatus } from '../../github/githubRepository'; +import { GithubItemStateEnum } from '../../github/interface'; import { NotificationProvider } from '../../github/notifications'; import { IResolvedPullRequestModel, PullRequestModel } from '../../github/pullRequestModel'; import { InMemFileChangeModel, RemoteFileChangeModel } from '../fileChangeModel'; @@ -140,7 +141,7 @@ export class PRNode extends TreeNode implements vscode.CommentingRangeProvider2 protected registerConfigurationChange() { this._register(vscode.workspace.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(`${PR_SETTINGS_NAMESPACE}.${SHOW_PULL_REQUEST_NUMBER_IN_TREE}`)) { + if (e.affectsConfiguration(`${PR_SETTINGS_NAMESPACE}.${SHOW_PULL_REQUEST_NUMBER_IN_TREE}`) || e.affectsConfiguration(`${PR_SETTINGS_NAMESPACE}.${TREE_VIEW_ICON_MODE}`)) { this.refresh(); } })); @@ -271,11 +272,33 @@ export class PRNode extends TreeNode implements vscode.CommentingRangeProvider2 } private async _getIcon(): Promise { + const { author, state, isDraft } = this.pullRequestModel; + const iconMode = vscode.workspace.getConfiguration(PR_SETTINGS_NAMESPACE).get(TREE_VIEW_ICON_MODE, 'author'); + let iconPath: vscode.Uri | vscode.ThemeIcon = new vscode.ThemeIcon('github'); + if (iconMode === 'author') { + const authorAvatar = (await DataUri.avatarCirclesAsImageDataUris(this._folderReposManager.context, [author], 16, 16))[0]; + if (authorAvatar) { + iconPath = authorAvatar; + } + } else if (iconMode === 'state') { + iconPath = new vscode.ThemeIcon( + state === GithubItemStateEnum.Closed ? 'git-pull-request-closed' + : state === GithubItemStateEnum.Merged ? 'git-merge' + : isDraft ? 'git-pull-request-draft' + : 'git-pull-request', + new vscode.ThemeColor( + state === GithubItemStateEnum.Closed ? 'pullRequests.closed' + : state === GithubItemStateEnum.Merged ? 'pullRequests.merged' + : isDraft ? 'pullRequests.draft' + : 'pullRequests.open' + ) + ); + } + const copilotWorkingStatus = await this.pullRequestModel.copilotWorkingStatus(this.pullRequestModel); const theme = this._folderReposManager.themeWatcher.themeData; if (!theme || copilotWorkingStatus === CopilotWorkingStatus.NotCopilotIssue) { - return (await DataUri.avatarCirclesAsImageDataUris(this._folderReposManager.context, [this.pullRequestModel.author], 16, 16))[0] - ?? new vscode.ThemeIcon('github'); + return iconPath; } switch (copilotWorkingStatus) { case CopilotWorkingStatus.InProgress: @@ -294,8 +317,7 @@ export class PRNode extends TreeNode implements vscode.CommentingRangeProvider2 dark: DataUri.copilotErrorAsImageDataURI(getIconForeground(theme, 'dark'), getListErrorForeground(theme, 'dark')) }; default: - return (await DataUri.avatarCirclesAsImageDataUris(this._folderReposManager.context, [this.pullRequestModel.author], 16, 16))[0] - ?? new vscode.ThemeIcon('github'); + return iconPath; } } @@ -303,8 +325,8 @@ export class PRNode extends TreeNode implements vscode.CommentingRangeProvider2 const currentBranchIsForThisPR = this.pullRequestModel.equals(this._folderReposManager.activePullRequest); const { title, number, author, isDraft, html_url } = this.pullRequestModel; - let labelTitle = this.pullRequestModel.title.length > 50 ? `${this.pullRequestModel.title.substring(0, 50)}...` : this.pullRequestModel.title; - if (COPILOT_ACCOUNTS[this.pullRequestModel.author.login]) { + let labelTitle = title.length > 50 ? `${title.substring(0, 50)}...` : title; + if (COPILOT_ACCOUNTS[author.login]) { labelTitle = labelTitle.replace('[WIP]', ''); } const login = author.specialDisplayName ?? author.login; @@ -322,7 +344,9 @@ export class PRNode extends TreeNode implements vscode.CommentingRangeProvider2 labelPrefix += `#${formattedPRNumber}: `; } - const label = `${labelPrefix}${isDraft ? '[DRAFT] ' : ''}${labelTitle}`; + const iconMode = vscode.workspace.getConfiguration(PR_SETTINGS_NAMESPACE).get(TREE_VIEW_ICON_MODE, 'author'); + + const label = `${labelPrefix}${isDraft && iconMode !== 'state' ? '[DRAFT] ' : ''}${labelTitle}`; const description = `by @${login}`; const command = { title: vscode.l10n.t('View Pull Request Description'),