Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds issue icons to branches in graph, and setting to toggle #3889

Merged
merged 1 commit into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions docs/telemetry-events.md
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
3 changes: 3 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
102 changes: 101 additions & 1 deletion src/plus/webviews/graph/graphWebview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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';
Expand Down Expand Up @@ -150,6 +153,8 @@ import type {
GraphHostingServiceType,
GraphIncludeOnlyRef,
GraphIncludeOnlyRefs,
GraphIssueContextValue,
GraphIssueTrackerType,
GraphItemContext,
GraphItemGroupContext,
GraphItemRefContext,
Expand Down Expand Up @@ -1019,6 +1024,8 @@ export class GraphWebviewProvider implements WebviewProvider<State, State, Graph
}
} else if (e.metadata.type === 'pullRequest' && isGraphItemTypedContext(item, 'pullrequest')) {
return void this.openPullRequestOnRemote(item);
} else if (e.metadata.type === 'issue' && isGraphItemTypedContext(item, 'issue')) {
return void this.openIssueOnRemote(item);
}

return;
Expand Down Expand Up @@ -1361,6 +1368,70 @@ export class GraphWebviewProvider implements WebviewProvider<State, State, Graph
metadata.upstream = upstreamMetadata;

this._refsMetadata.set(id, metadata);
continue;
}

// TODO: Issue metadata needs to update for a branch whenever we add an associated issue for it, so that we don't
// have to completely refresh the component to see the new issue
if (type === 'issue') {
let issues: IssueShape[] | undefined = await getAssociatedIssuesForBranch(
this.container,
branch,
).then(issues => 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<IssueShape>(
(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<GraphItemContext>({
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);
}
}
}
Expand Down Expand Up @@ -2327,6 +2398,10 @@ export class GraphWebviewProvider implements WebviewProvider<State, State, Graph
private getEnabledRefMetadataTypes(): GraphRefMetadataType[] {
const types: GraphRefMetadataType[] = [];

if (configuration.get('graph.issues.enabled')) {
types.push('issue');
}

if (configuration.get('graph.pullRequests.enabled')) {
types.push('pullRequest');
}
Expand Down Expand Up @@ -3559,6 +3634,17 @@ export class GraphWebviewProvider implements WebviewProvider<State, State, Graph
return Promise.resolve();
}

@log()
private openIssueOnRemote(item?: GraphItemContext) {
if (isGraphItemTypedContext(item, 'issue')) {
const { url } = item.webviewItemValue;
// TODO: Add a command for this. See openPullRequestOnRemote above.
void openUrl(url);
}

return Promise.resolve();
}

@log()
private async compareAncestryWithWorking(item?: GraphItemContext) {
const ref = this.getGraphItemRef(item);
Expand Down Expand Up @@ -4015,6 +4101,7 @@ function isGraphItemTypedContext(
item: unknown,
type: 'upstreamStatus',
): item is GraphItemTypedContext<GraphUpstreamStatusContextValue>;
function isGraphItemTypedContext(item: unknown, type: 'issue'): item is GraphItemTypedContext<GraphIssueContextValue>;
function isGraphItemTypedContext(
item: unknown,
type: GraphItemTypedContextValue['type'],
Expand Down Expand Up @@ -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;
}
}
13 changes: 12 additions & 1 deletion src/plus/webviews/graph/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {
Head,
HostingServiceType,
IncludeOnlyRefsById,
IssueTrackerType,
PullRequestMetadata,
RefMetadata,
RefMetadataItem,
Expand Down Expand Up @@ -546,7 +547,8 @@ export type GraphItemTypedContext<T = GraphItemTypedContextValue> = WebviewItemC
export type GraphItemTypedContextValue =
| GraphContributorContextValue
| GraphPullRequestContextValue
| GraphUpstreamStatusContextValue;
| GraphUpstreamStatusContextValue
| GraphIssueContextValue;

export type GraphColumnsContextValue = string;

Expand All @@ -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;
Expand All @@ -593,3 +602,5 @@ export interface GraphUpstreamStatusContextValue {
ahead: number;
behind: number;
}

export type GraphIssueTrackerType = IssueTrackerType;
3 changes: 3 additions & 0 deletions src/webviews/apps/plus/graph/GraphWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ const createIconElements = (): Record<string, ReactElement> => {
'changes',
'files',
'worktree',
'issue-github',
'issue-gitlab',
'issue-jiraCloud',
];

const miniIconList = ['upstream-ahead', 'upstream-behind'];
Expand Down
21 changes: 21 additions & 0 deletions src/webviews/apps/plus/graph/graph.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
11 changes: 11 additions & 0 deletions src/webviews/apps/settings/partials/commit-graph.html
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,17 @@ <h2>
</div>
</div>

<div class="setting">
<div class="setting__input">
<input id="graph.issues.enabled" name="graph.issues.enabled" type="checkbox" data-setting />
<label for="graph.issues.enabled">Show associated issues on branches</label>
</div>
<p class="setting__hint hidden" data-visibility="graph.issues.enabled">
<i class="icon icon__info"></i>Requires a connection to a supported issue service (e.g.
GitHub)
</p>
</div>

<div class="setting">
<div class="setting__input">
<input
Expand Down
Loading