diff --git a/package.json b/package.json index b84000c6b4..8997f2903d 100644 --- a/package.json +++ b/package.json @@ -659,6 +659,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 74385798dd..1ab86dec72 100644 --- a/package.nls.json +++ b/package.nls.json @@ -142,6 +142,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 712312812d..8bf6f8592c 100644 --- a/src/common/settingKeys.ts +++ b/src/common/settingKeys.ts @@ -58,6 +58,9 @@ export const EXPERIMENTAL_NOTIFICATIONS = 'experimental.notificationsView'; export const EXPERIMENTAL_NOTIFICATIONS_PAGE_SIZE = 'experimental.notificationsViewPageSize'; export const EXPERIMENTAL_NOTIFICATIONS_SCORE = 'experimental.notificationsScore'; +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 ff5d50fbad..9d27d2d9be 100644 --- a/src/view/treeNodes/pullRequestNode.ts +++ b/src/view/treeNodes/pullRequestNode.ts @@ -8,9 +8,10 @@ import { Repository } from '../../api/api'; 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 { GithubItemStateEnum } from '../../github/interface'; import { NotificationProvider } from '../../github/notifications'; import { IResolvedPullRequestModel, PullRequestModel } from '../../github/pullRequestModel'; import { InMemFileChangeModel, RemoteFileChangeModel } from '../fileChangeModel'; @@ -129,7 +130,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(); } })); @@ -262,7 +263,7 @@ export class PRNode extends TreeNode implements vscode.CommentingRangeProvider2 async getTreeItem(): Promise { const currentBranchIsForThisPR = this.pullRequestModel.equals(this._folderReposManager.activePullRequest); - const { title, number, author, isDraft, html_url } = this.pullRequestModel; + const { title, number, author, isDraft, html_url, state } = this.pullRequestModel; const labelTitle = this.pullRequestModel.title.length > 50 ? `${this.pullRequestModel.title.substring(0, 50)}...` : this.pullRequestModel.title; const { login } = author; @@ -279,7 +280,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'), @@ -287,6 +290,26 @@ export class PRNode extends TreeNode implements vscode.CommentingRangeProvider2 arguments: [this], }; + let iconPath: vscode.Uri | vscode.ThemeIcon = new vscode.ThemeIcon('github'); + if (iconMode === 'author') { + const authorAvatar = (await DataUri.avatarCirclesAsImageDataUris(this._folderReposManager.context, [this.pullRequestModel.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' + )); + } + return { label, id: `${this.parent instanceof TreeNode ? (this.parent.id ?? this.parent.label) : ''}${html_url}${this._isLocal ? this.pullRequestModel.localBranchName : ''}`, // unique id stable across checkout status @@ -298,8 +321,7 @@ export class PRNode extends TreeNode implements vscode.CommentingRangeProvider2 (currentBranchIsForThisPR ? ':active' : ':nonactive') + (hasNotification ? ':notification' : '') + (((this.pullRequestModel.item.isRemoteHeadDeleted && !this._isLocal) || !this._folderReposManager.isPullRequestAssociatedWithOpenRepository(this.pullRequestModel)) ? '' : ':hasHeadRef'), - iconPath: (await DataUri.avatarCirclesAsImageDataUris(this._folderReposManager.context, [this.pullRequestModel.author], 16, 16))[0] - ?? new vscode.ThemeIcon('github'), + iconPath, accessibilityInformation: { label: `${isDraft ? 'Draft ' : ''}Pull request number ${formattedPRNumber}: ${title} by ${login}` },