From 5c37ec9644ca1091e7c7636a118d959e745aae15 Mon Sep 17 00:00:00 2001 From: Ramin Tadayon Date: Wed, 18 Dec 2024 14:11:24 -0700 Subject: [PATCH] Adds issue icons to branches in graph, and setting to toggle --- docs/telemetry-events.md | 1 + package.json | 9 +- src/config.ts | 3 + src/plus/webviews/graph/graphWebview.ts | 102 +++++++++++++++++- src/plus/webviews/graph/protocol.ts | 13 ++- src/webviews/apps/plus/graph/GraphWrapper.tsx | 3 + src/webviews/apps/plus/graph/graph.scss | 21 ++++ .../apps/settings/partials/commit-graph.html | 11 ++ 8 files changed, 160 insertions(+), 3 deletions(-) diff --git a/docs/telemetry-events.md b/docs/telemetry-events.md index d4ae876d3aa42..065e3dccc9a87 100644 --- a/docs/telemetry-events.md +++ b/docs/telemetry-events.md @@ -849,6 +849,7 @@ or 'context.config.defaultItemLimit': number, 'context.config.dimMergeCommits': boolean, 'context.config.highlightRowsOnRefHover': boolean, + 'context.config.issues.enabled': boolean, 'context.config.layout': 'editor' | 'panel', 'context.config.minimap.additionalTypes': string, 'context.config.minimap.dataType': 'commits' | 'lines', diff --git a/package.json b/package.json index 21de123199916..013134fd9db2c 100644 --- a/package.json +++ b/package.json @@ -1157,12 +1157,19 @@ "scope": "window", "order": 702 }, + "gitlens.graph.issues.enabled": { + "type": "boolean", + "default": true, + "markdownDescription": "Specifies whether to show associated issues on branches in the _Commit Graph_. Requires a connection to a supported issue service (e.g. GitHub)", + "scope": "window", + "order": 703 + }, "gitlens.graph.pullRequests.enabled": { "type": "boolean", "default": true, "markdownDescription": "Specifies whether to show associated pull requests on remote branches in the _Commit Graph_. Requires a connection to a supported remote service (e.g. GitHub)", "scope": "window", - "order": 703 + "order": 704 }, "gitlens.graph.avatars": { "type": "boolean", diff --git a/src/config.ts b/src/config.ts index f128ac2c8eb1a..6064c344f69f1 100644 --- a/src/config.ts +++ b/src/config.ts @@ -347,6 +347,9 @@ export interface GraphConfig { readonly defaultItemLimit: number; readonly dimMergeCommits: boolean; readonly highlightRowsOnRefHover: boolean; + readonly issues: { + readonly enabled: boolean; + }; readonly layout: 'editor' | 'panel'; readonly minimap: { readonly enabled: boolean; diff --git a/src/plus/webviews/graph/graphWebview.ts b/src/plus/webviews/graph/graphWebview.ts index 0f8176322a76d..5edcd01576f08 100644 --- a/src/plus/webviews/graph/graphWebview.ts +++ b/src/plus/webviews/graph/graphWebview.ts @@ -19,6 +19,7 @@ import type { } from '../../../config'; import { GlyphChars } from '../../../constants'; import { GlCommand } from '../../../constants.commands'; +import { HostingIntegrationId, IssueIntegrationId } from '../../../constants.integrations'; import type { StoredGraphFilters, StoredGraphRefType } from '../../../constants.storage'; import type { GraphShownTelemetryContext, GraphTelemetryContext, TelemetryEvents } from '../../../constants.telemetry'; import type { Container } from '../../../container'; @@ -50,6 +51,7 @@ import { GitSearchError } from '../../../git/errors'; import { CommitFormatter } from '../../../git/formatters/commitFormatter'; import type { GitBranch } from '../../../git/models/branch'; import { + getAssociatedIssuesForBranch, getBranchId, getBranchNameWithoutRemote, getDefaultBranchName, @@ -62,6 +64,7 @@ import { isStash } from '../../../git/models/commit'; import { splitCommitMessage } from '../../../git/models/commit.utils'; import { GitContributor } from '../../../git/models/contributor'; import type { GitGraph, GitGraphRowType } from '../../../git/models/graph'; +import type { IssueShape } from '../../../git/models/issue'; import type { PullRequest } from '../../../git/models/pullRequest'; import { getComparisonRefsForPullRequest, @@ -113,7 +116,7 @@ import { import { configuration } from '../../../system/vscode/configuration'; import { getContext, onDidChangeContext } from '../../../system/vscode/context'; import type { OpenWorkspaceLocation } from '../../../system/vscode/utils'; -import { isDarkTheme, isLightTheme, openWorkspace } from '../../../system/vscode/utils'; +import { isDarkTheme, isLightTheme, openUrl, openWorkspace } from '../../../system/vscode/utils'; import { isWebviewItemContext, isWebviewItemGroupContext, serializeWebviewItemContext } from '../../../system/webview'; import { DeepLinkActionType } from '../../../uris/deepLinks/deepLink'; import { RepositoryFolderNode } from '../../../views/nodes/abstract/repositoryFolderNode'; @@ -150,6 +153,8 @@ import type { GraphHostingServiceType, GraphIncludeOnlyRef, GraphIncludeOnlyRefs, + GraphIssueContextValue, + GraphIssueTrackerType, GraphItemContext, GraphItemGroupContext, GraphItemRefContext, @@ -1019,6 +1024,8 @@ export class GraphWebviewProvider implements WebviewProvider issues.value); + if (issues == null || issues.length === 0) { + issues = await branch.getEnrichedAutolinks().then(async enrichedAutolinks => { + if (enrichedAutolinks == null) return undefined; + return ( + await Promise.all( + [...enrichedAutolinks.values()].map(async ([issueOrPullRequestPromise]) => + // eslint-disable-next-line no-return-await + issueOrPullRequestPromise != null ? await issueOrPullRequestPromise : undefined, + ), + ) + ).filter( + (a?: unknown): a is IssueShape => + a != null && a instanceof Object && 'type' in a && a.type === 'issue', + ); + }); + + if (issues == null || issues.length === 0) { + metadata.issue = null; + this._refsMetadata.set(id, metadata); + continue; + } + } + + const issuesMetadata = []; + for (const issue of issues) { + const issueTracker = toGraphIssueTrackerType(issue.provider.id); + if (issueTracker == null) continue; + issuesMetadata.push({ + displayId: issue.id, + id: issue.nodeId ?? issue.id, + // TODO: This is a hack/workaround because the graph component doesn't support this in the tooltip. + // Update this once that is fixed. + title: `${issue.title}\nDouble-click to open issue on ${issue.provider.name}`, + issueTrackerType: issueTracker, + url: issue.url, + context: serializeWebviewItemContext({ + webviewItem: `gitlens:issue`, + webviewItemValue: { + type: 'issue', + id: issue.id, + url: issue.url, + provider: { + id: issue.provider.id, + name: issue.provider.name, + domain: issue.provider.domain, + icon: issue.provider.icon, + }, + }, + }), + }); + } + + metadata.issue = issuesMetadata; + this._refsMetadata.set(id, metadata); } } } @@ -2327,6 +2398,10 @@ export class GraphWebviewProvider implements WebviewProvider; +function isGraphItemTypedContext(item: unknown, type: 'issue'): item is GraphItemTypedContext; function isGraphItemTypedContext( item: unknown, type: GraphItemTypedContextValue['type'], @@ -4059,3 +4146,16 @@ export function hasGitReference(o: unknown): o is { ref: GitReference } { return isGitReference(o.ref); } + +function toGraphIssueTrackerType(id: string): GraphIssueTrackerType | undefined { + switch (id) { + case HostingIntegrationId.GitHub: + return 'github'; + case HostingIntegrationId.GitLab: + return 'gitlab'; + case IssueIntegrationId.Jira: + return 'jiraCloud'; + default: + return undefined; + } +} diff --git a/src/plus/webviews/graph/protocol.ts b/src/plus/webviews/graph/protocol.ts index bbf4539905959..baeb7707adcfa 100644 --- a/src/plus/webviews/graph/protocol.ts +++ b/src/plus/webviews/graph/protocol.ts @@ -11,6 +11,7 @@ import type { Head, HostingServiceType, IncludeOnlyRefsById, + IssueTrackerType, PullRequestMetadata, RefMetadata, RefMetadataItem, @@ -546,7 +547,8 @@ export type GraphItemTypedContext = WebviewItemC export type GraphItemTypedContextValue = | GraphContributorContextValue | GraphPullRequestContextValue - | GraphUpstreamStatusContextValue; + | GraphUpstreamStatusContextValue + | GraphIssueContextValue; export type GraphColumnsContextValue = string; @@ -567,6 +569,13 @@ export interface GraphPullRequestContextValue { provider: ProviderReference; } +export interface GraphIssueContextValue { + type: 'issue'; + id: string; + url: string; + provider: ProviderReference; +} + export interface GraphBranchContextValue { type: 'branch'; ref: GitBranchReference; @@ -593,3 +602,5 @@ export interface GraphUpstreamStatusContextValue { ahead: number; behind: number; } + +export type GraphIssueTrackerType = IssueTrackerType; diff --git a/src/webviews/apps/plus/graph/GraphWrapper.tsx b/src/webviews/apps/plus/graph/GraphWrapper.tsx index 0b71890f54f50..1431f6d7acb95 100644 --- a/src/webviews/apps/plus/graph/GraphWrapper.tsx +++ b/src/webviews/apps/plus/graph/GraphWrapper.tsx @@ -166,6 +166,9 @@ const createIconElements = (): Record => { 'changes', 'files', 'worktree', + 'issue-github', + 'issue-gitlab', + 'issue-jiraCloud', ]; const miniIconList = ['upstream-ahead', 'upstream-behind']; diff --git a/src/webviews/apps/plus/graph/graph.scss b/src/webviews/apps/plus/graph/graph.scss index 2c47c9bb00ea3..944f329e85365 100644 --- a/src/webviews/apps/plus/graph/graph.scss +++ b/src/webviews/apps/plus/graph/graph.scss @@ -823,6 +823,27 @@ button:not([disabled]), @include iconUtils.glicon('worktrees-view'); } } + + &--issue-github { + &::before { + font-family: codicon; + @include iconUtils.codicon('github-inverted'); + } + } + + &--issue-gitlab { + &::before { + font-family: 'glicons'; + @include iconUtils.glicon('provider-gitlab'); + } + } + + &--issue-jiraCloud { + &::before { + font-family: 'glicons'; + @include iconUtils.glicon('provider-jira'); + } + } } .titlebar { diff --git a/src/webviews/apps/settings/partials/commit-graph.html b/src/webviews/apps/settings/partials/commit-graph.html index b33cd41b438c0..e384ee399b815 100644 --- a/src/webviews/apps/settings/partials/commit-graph.html +++ b/src/webviews/apps/settings/partials/commit-graph.html @@ -194,6 +194,17 @@

+
+
+ + +
+ +
+