浏览代码

Reworks dynamic metadata retrieval on the Graph

Adds ref PR context menus
main
Eric Amodio 2 年前
父节点
当前提交
74795fb053
共有 9 个文件被更改,包括 223 次插入118 次删除
  1. +30
    -0
      package.json
  2. +10
    -10
      src/env/node/git/localGitProvider.ts
  3. +5
    -1
      src/git/models/branch.ts
  4. +1
    -5
      src/git/models/graph.ts
  5. +4
    -13
      src/plus/github/githubGitProvider.ts
  6. +120
    -54
      src/plus/webviews/graph/graphWebview.ts
  7. +24
    -12
      src/plus/webviews/graph/protocol.ts
  8. +15
    -13
      src/webviews/apps/plus/graph/GraphWrapper.tsx
  9. +14
    -10
      src/webviews/apps/plus/graph/graph.tsx

+ 30
- 0
package.json 查看文件

@ -6431,6 +6431,18 @@
"icon": "$(git-pull-request-create)"
},
{
"command": "gitlens.graph.openPullRequestOnRemote",
"title": "Open Pull Request on Remote",
"category": "GitLens",
"icon": "$(globe)"
},
{
"command": "gitlens.graph.copyRemotePullRequestUrl",
"title": "Copy Pull Request Url",
"category": "GitLens",
"icon": "$(copy)"
},
{
"command": "gitlens.graph.columnAuthorOn",
"title": "Show Author",
"category": "GitLens"
@ -8375,6 +8387,14 @@
"when": "false"
},
{
"command": "gitlens.graph.openPullRequestOnRemote",
"when": "false"
},
{
"command": "gitlens.graph.copyRemotePullRequestUrl",
"when": "false"
},
{
"command": "gitlens.graph.columnAuthorOn",
"when": "false"
},
@ -10988,6 +11008,16 @@
"group": "1_gitlens_actions@3"
},
{
"command": "gitlens.graph.openPullRequestOnRemote",
"when": "webviewItem =~ /gitlens:graph:pullrequest\\b/",
"group": "1_gitlens_actions@1"
},
{
"command": "gitlens.graph.copyRemotePullRequestUrl",
"when": "webviewItem =~ /gitlens:graph:pullrequest\\b/",
"group": "7_gitlens_cutcopypaste@1"
},
{
"command": "gitlens.graph.columnAuthorOn",
"when": "webviewItem =~ /gitlens:graph:columns\\b/ && webviewItemValue =~ /\\bauthor\\b/",
"group": "1_columns@1"

+ 10
- 10
src/env/node/git/localGitProvider.ts 查看文件

@ -14,9 +14,8 @@ import type {
} from '../../../@types/vscode.git';
import { getCachedAvatarUri } from '../../../avatars';
import { configuration } from '../../../configuration';
import { ContextKeys, CoreGitConfiguration, GlyphChars, Schemes } from '../../../constants';
import { CoreGitConfiguration, GlyphChars, Schemes } from '../../../constants';
import type { Container } from '../../../container';
import { getContext } from '../../../context';
import { emojify } from '../../../emojis';
import { Features } from '../../../features';
import { GitErrorHandling } from '../../../git/commandOptions';
@ -48,6 +47,7 @@ import { encodeGitLensRevisionUriAuthority, GitUri } from '../../../git/gitUri';
import type { GitBlame, GitBlameAuthor, GitBlameLine, GitBlameLines } from '../../../git/models/blame';
import type { BranchSortOptions } from '../../../git/models/branch';
import {
getBranchId,
getBranchNameWithoutRemote,
getRemoteNameFromBranchName,
GitBranch,
@ -62,7 +62,6 @@ import type { GitFile, GitFileStatus } from '../../../git/models/file';
import { GitFileChange } from '../../../git/models/file';
import type {
GitGraph,
GitGraphRefMetadata,
GitGraphRow,
GitGraphRowContexts,
GitGraphRowHead,
@ -1662,9 +1661,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
);
}
const hasConnectedRemotes = getContext(ContextKeys.HasConnectedRemotes);
const avatars = new Map<string, string>();
const refMetadata = hasConnectedRemotes ? new Map<string, GitGraphRefMetadata>() : undefined;
const ids = new Set<string>();
const reachableFromHEAD = new Set<string>();
const skippedIds = new Set<string>();
@ -1710,7 +1707,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
if (cursorIndex === -1) {
// If we didn't find any new commits, we must have them all so return that we have everything
if (size === data.length) {
return { repoPath: repoPath, avatars: avatars, ids: ids, refMetadata: refMetadata, rows: [] };
return { repoPath: repoPath, avatars: avatars, ids: ids, rows: [] };
}
size = data.length;
@ -1736,7 +1733,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
}
}
if (!data) return { repoPath: repoPath, avatars: avatars, ids: ids, refMetadata: refMetadata, rows: [] };
if (!data) return { repoPath: repoPath, avatars: avatars, ids: ids, rows: [] };
log = data;
if (limit !== 0) {
@ -1816,7 +1813,6 @@ export class LocalGitProvider implements GitProvider, Disposable {
}
}
branch = branchMap.get(tip);
remoteName = getRemoteNameFromBranchName(tip);
if (remoteName) {
remote = remoteMap.get(remoteName);
@ -1824,8 +1820,9 @@ export class LocalGitProvider implements GitProvider, Disposable {
branchName = getBranchNameWithoutRemote(tip);
if (branchName === 'HEAD') continue;
const remoteBranchId = getBranchId(repoPath, true, tip);
refRemoteHeads.push({
id: branch?.id ?? remote.id,
id: remoteBranchId,
name: branchName,
owner: remote.name,
url: remote.url,
@ -1851,7 +1848,11 @@ export class LocalGitProvider implements GitProvider, Disposable {
}
}
branch = branchMap.get(tip);
const branchId = branch?.id ?? getBranchId(repoPath, false, tip);
refHeads.push({
id: branchId,
name: tip,
isCurrentHead: current,
context: serializeWebviewItemContext<GraphItemRefContext>({
@ -1968,7 +1969,6 @@ export class LocalGitProvider implements GitProvider, Disposable {
avatars: avatars,
ids: ids,
skippedIds: skippedIds,
refMetadata: refMetadata,
rows: rows,
id: sha,

+ 5
- 1
src/git/models/branch.ts 查看文件

@ -38,6 +38,10 @@ export interface BranchSortOptions {
orderBy?: BranchSorting;
}
export function getBranchId(repoPath: string, remote: boolean, name: string): string {
return `${repoPath}|${remote ? 'remotes/' : 'heads/'}${name}`;
}
export class GitBranch implements GitBranchReference {
readonly refType = 'branch';
readonly detached: boolean;
@ -58,7 +62,7 @@ export class GitBranch implements GitBranchReference {
detached: boolean = false,
public readonly rebasing: boolean = false,
) {
this.id = `${repoPath}|${remote ? 'remotes/' : 'heads/'}${name}`;
this.id = getBranchId(repoPath, remote, name);
this.detached = detached || (this.current ? isDetachedHead(name) : false);
if (this.detached) {

+ 1
- 5
src/git/models/graph.ts 查看文件

@ -1,11 +1,9 @@
import type { GraphRow, Head, HostingServiceType, RefMetadata, Remote, RowContexts, Tag } from '@gitkraken/gitkraken-components';
import type { GraphRow, Head, Remote, RowContexts, Tag } from '@gitkraken/gitkraken-components';
export type GitGraphRowHead = Head;
export type GitGraphRowRemoteHead = Remote;
export type GitGraphRowTag = Tag;
export type GitGraphRowContexts = RowContexts;
export type GitGraphRefMetadata = RefMetadata;
export type GitGraphHostingServiceType = HostingServiceType;
export const enum GitGraphRowType {
Commit = 'commit-node',
MergeCommit = 'merge-node',
@ -27,8 +25,6 @@ export interface GitGraph {
readonly repoPath: string;
/** A map of all avatar urls */
readonly avatars: Map<string, string>;
/** A map of all ref metadata */
readonly refMetadata: Map<string, RefMetadata> | undefined;
/** A set of all "seen" commit ids */
readonly ids: Set<string>;
/** A set of all skipped commit ids -- typically for stash index/untracked commits */

+ 4
- 13
src/plus/github/githubGitProvider.ts 查看文件

@ -14,7 +14,7 @@ import { encodeUtf8Hex } from '@env/hex';
import { configuration } from '../../configuration';
import { CharCode, ContextKeys, Schemes } from '../../constants';
import type { Container } from '../../container';
import { getContext, setContext } from '../../context';
import { setContext } from '../../context';
import { emojify } from '../../emojis';
import {
AuthenticationError,
@ -39,7 +39,7 @@ import { GitProviderId, RepositoryVisibility } from '../../git/gitProvider';
import { GitUri } from '../../git/gitUri';
import type { GitBlame, GitBlameAuthor, GitBlameLine, GitBlameLines } from '../../git/models/blame';
import type { BranchSortOptions } from '../../git/models/branch';
import { GitBranch, sortBranches } from '../../git/models/branch';
import { getBranchId, GitBranch, sortBranches } from '../../git/models/branch';
import type { GitCommitLine } from '../../git/models/commit';
import { GitCommit, GitCommitIdentity } from '../../git/models/commit';
import { GitContributor } from '../../git/models/contributor';
@ -48,7 +48,6 @@ import type { GitFile } from '../../git/models/file';
import { GitFileChange, GitFileIndexStatus } from '../../git/models/file';
import type {
GitGraph,
GitGraphRefMetadata,
GitGraphRow,
GitGraphRowHead,
GitGraphRowRemoteHead,
@ -57,7 +56,6 @@ import type {
import { GitGraphRowType } from '../../git/models/graph';
import type { GitLog } from '../../git/models/log';
import type { GitMergeStatus } from '../../git/models/merge';
import type { PullRequest } from '../../git/models/pullRequest';
import type { GitRebaseStatus } from '../../git/models/rebase';
import type { GitBranchReference, GitReference } from '../../git/models/reference';
import { GitRevision } from '../../git/models/reference';
@ -1085,9 +1083,7 @@ export class GitHubGitProvider implements GitProvider, Disposable {
this.getTags(repoPath),
]);
const hasConnectedRemotes = getContext(ContextKeys.HasConnectedRemotes);
const avatars = new Map<string, string>();
const refMetadata = hasConnectedRemotes ? new Map<string, GitGraphRefMetadata>() : undefined;
const ids = new Set<string>();
return this.getCommitsForGraphCore(
@ -1098,7 +1094,6 @@ export class GitHubGitProvider implements GitProvider, Disposable {
getSettledValue(remotesResult)?.[0],
getSettledValue(tagsResult)?.values,
avatars,
refMetadata,
ids,
options,
);
@ -1112,7 +1107,6 @@ export class GitHubGitProvider implements GitProvider, Disposable {
remote: GitRemote | undefined,
tags: GitTag[] | undefined,
avatars: Map<string, string>,
refMetadata: Map<string, GitGraphRefMetadata> | undefined,
ids: Set<string>,
options?: {
branch?: string;
@ -1125,7 +1119,6 @@ export class GitHubGitProvider implements GitProvider, Disposable {
return {
repoPath: repoPath,
avatars: avatars,
refMetadata: refMetadata,
ids: ids,
rows: [],
};
@ -1136,7 +1129,6 @@ export class GitHubGitProvider implements GitProvider, Disposable {
return {
repoPath: repoPath,
avatars: avatars,
refMetadata: refMetadata,
ids: ids,
rows: [],
};
@ -1156,13 +1148,14 @@ export class GitHubGitProvider implements GitProvider, Disposable {
if (hasHeadShaAndRemote && commit.sha === branch.sha) {
refHeads = [
{
id: getBranchId(repoPath, false, branch.name),
name: branch.name,
isCurrentHead: true,
},
];
refRemoteHeads = [
{
id: remote.id,
id: getBranchId(repoPath, true, branch.name),
name: branch.name,
owner: remote.name,
url: remote.url,
@ -1222,7 +1215,6 @@ export class GitHubGitProvider implements GitProvider, Disposable {
return {
repoPath: repoPath,
avatars: avatars,
refMetadata: refMetadata,
ids: ids,
rows: rows,
id: options?.ref,
@ -1242,7 +1234,6 @@ export class GitHubGitProvider implements GitProvider, Disposable {
remote,
tags,
avatars,
refMetadata,
ids,
options,
);

+ 120
- 54
src/plus/webviews/graph/graphWebview.ts 查看文件

@ -22,6 +22,7 @@ import type {
CopyShaToClipboardCommandArgs,
OpenBranchOnRemoteCommandArgs,
OpenCommitOnRemoteCommandArgs,
OpenPullRequestOnRemoteCommandArgs,
ShowCommitsInViewCommandArgs,
} from '../../../commands';
import { parseCommandContext } from '../../../commands/base';
@ -32,10 +33,9 @@ import type { Container } from '../../../container';
import { getContext, onDidChangeContext, setContext } from '../../../context';
import { PlusFeatures } from '../../../features';
import { GitSearchError } from '../../../git/errors';
import type { GitBranch } from '../../../git/models/branch';
import type { GitCommit } from '../../../git/models/commit';
import { GitGraphRowType } from '../../../git/models/graph';
import type { GitGraph, GitGraphHostingServiceType } from '../../../git/models/graph';
import type { GitGraph } from '../../../git/models/graph';
import type {
GitBranchReference,
GitRevisionReference,
@ -77,12 +77,16 @@ import type {
DismissBannerParams,
EnsureRowParams,
GetMissingAvatarsParams,
GetMissingRefMetadataParams,
GetMissingRefsMetadataParams,
GetMoreRowsParams,
GraphColumnConfig,
GraphColumnName,
GraphColumnsSettings,
GraphComponentConfig,
GraphHostingServiceType,
GraphMissingRefsMetadataType,
GraphPullRequestMetadata,
GraphRefMetadata,
GraphRepository,
GraphSelectedRows,
GraphWorkingTreeStats,
@ -98,7 +102,7 @@ import {
DidChangeColumnsNotificationType,
DidChangeGraphConfigurationNotificationType,
DidChangeNotificationType,
DidChangeRefMetadataNotificationType,
DidChangeRefsMetadataNotificationType,
DidChangeRowsNotificationType,
DidChangeSelectionNotificationType,
DidChangeSubscriptionNotificationType,
@ -108,7 +112,7 @@ import {
DismissBannerCommandType,
EnsureRowCommandType,
GetMissingAvatarsCommandType,
GetMissingRefMetadataCommandType,
GetMissingRefsMetadataCommandType,
GetMoreRowsCommandType,
SearchCommandType,
SearchOpenInViewCommandType,
@ -158,6 +162,12 @@ export class GraphWebview extends WebviewBase {
value.onDidChange(this.onRepositoryChanged, this),
value.startWatchingFileSystem(),
value.onDidChangeFileSystem(this.onRepositoryFileSystemChanged, this),
onDidChangeContext(key => {
if (key !== ContextKeys.HasConnectedRemotes) return;
this.resetRefsMetadata();
this.updateRefsMetadata();
}),
);
}
@ -175,6 +185,7 @@ export class GraphWebview extends WebviewBase {
private _etagRepository?: number;
private _graph?: GitGraph;
private _pendingIpcNotifications = new Map<IpcNotificationType, IpcMessage | (() => Promise<boolean>)>();
private _refsMetadata: Map<string, GraphRefMetadata | null> | null | undefined;
private _search: GitSearch | undefined;
private _searchCancellation: CancellationTokenSource | undefined;
private _selectedId?: string;
@ -262,7 +273,7 @@ export class GraphWebview extends WebviewBase {
this.repository = context.node.repo;
}
if (this.repository != null) {
if (this.repository != null && this.isReady) {
this.updateState();
}
}
@ -318,6 +329,12 @@ export class GraphWebview extends WebviewBase {
registerCommand('gitlens.graph.createWorktree', this.createWorktree, this),
registerCommand('gitlens.graph.createPullRequest', this.createPullRequest, this),
registerCommand('gitlens.graph.openPullRequestOnRemote', this.openPullRequestOnRemote, this),
registerCommand(
'gitlens.graph.copyRemotePullRequestUrl',
item => this.openPullRequestOnRemote(item, true),
this,
),
registerCommand('gitlens.graph.copyMessage', this.copyMessage, this),
registerCommand('gitlens.graph.copySha', this.copySha, this),
@ -355,8 +372,8 @@ export class GraphWebview extends WebviewBase {
case GetMissingAvatarsCommandType.method:
onIpc(GetMissingAvatarsCommandType, e, params => this.onGetMissingAvatars(params));
break;
case GetMissingRefMetadataCommandType.method:
onIpc(GetMissingRefMetadataCommandType, e, params => this.onGetMissingRefMetadata(params));
case GetMissingRefsMetadataCommandType.method:
onIpc(GetMissingRefsMetadataCommandType, e, params => this.onGetMissingRefMetadata(params));
break;
case GetMoreRowsCommandType.method:
onIpc(GetMoreRowsCommandType, e, params => this.onGetMoreRows(params));
@ -542,41 +559,60 @@ export class GraphWebview extends WebviewBase {
}
}
private async onGetMissingRefMetadata(e: GetMissingRefMetadataParams) {
if (this._graph == null) return;
private async onGetMissingRefMetadata(e: GetMissingRefsMetadataParams) {
if (this._graph == null || this._refsMetadata === null || !getContext(ContextKeys.HasConnectedRemotes)) return;
const repoPath = this._graph.repoPath;
const hasConnectedRemotes = getContext(ContextKeys.HasConnectedRemotes);
if (!hasConnectedRemotes) return;
async function getRefMetadata(this: GraphWebview, id: string, type: string) {
const newRefMetadata = { ...(this._graph!.refMetadata ? this._graph!.refMetadata.get(id) : undefined) };
const foundBranchesResult = await this.container.git.getBranches(repoPath, { filter: b => b.id === id });
const foundBranches = foundBranchesResult?.values;
const refBranch: GitBranch | undefined = foundBranches?.length ? foundBranches[0] : undefined;
switch (type) {
case 'pullRequests':
newRefMetadata.pullRequests = null;
if (refBranch != null) {
const pullRequest = await refBranch.getAssociatedPullRequest();
if (pullRequest != null) {
const pullRequestMetadata = {
hostingServiceType: pullRequest.provider.name as GitGraphHostingServiceType,
id: Number.parseInt(pullRequest.id) || 0,
title: pullRequest.title
};
newRefMetadata.pullRequests = [ pullRequestMetadata ];
}
}
break;
default:
break;
async function getRefMetadata(this: GraphWebview, id: string, type: GraphMissingRefsMetadataType) {
if (this._refsMetadata == null) {
this._refsMetadata = new Map();
}
this._graph!.refMetadata?.set(id, newRefMetadata);
const metadata = { ...this._refsMetadata.get(id) };
if (type !== 'pullRequests') {
(metadata as any)[type] = null;
this._refsMetadata.set(id, metadata);
return;
}
const branch = (await this.container.git.getBranches(repoPath, { filter: b => b.id === id && b.remote }))
?.values?.[0];
const pr = await branch?.getAssociatedPullRequest();
if (pr == null) {
if (metadata.pullRequests === undefined || metadata.pullRequests?.length === 0) {
metadata.pullRequests = null;
}
this._refsMetadata.set(id, metadata);
return;
}
const prMetadata: GraphPullRequestMetadata = {
// TODO@eamodio: This is iffy, but works right now since `github` and `gitlab` are the only values possible currently
hostingServiceType: pr.provider.id as GraphHostingServiceType,
id: Number.parseInt(pr.id) || 0,
title: pr.title,
author: pr.author.name,
date: (pr.mergedDate ?? pr.closedDate ?? pr.date)?.getTime(),
state: pr.state,
url: pr.url,
context: serializeWebviewItemContext<GraphItemContext>({
webviewItem: 'gitlens:graph:pullrequest',
webviewItemValue: {
type: 'pullrequest',
id: pr.id,
url: pr.url,
},
}),
};
metadata.pullRequests = [prMetadata];
this._refsMetadata.set(id, metadata);
}
const promises: Promise<void>[] = [];
for (const [id, missingTypes] of Object.entries(e.missing)) {
for (const [id, missingTypes] of Object.entries(e.metadata)) {
for (const missingType of missingTypes) {
promises.push(getRefMetadata.call(this, id, missingType));
}
@ -584,8 +620,8 @@ export class GraphWebview extends WebviewBase {
if (promises.length) {
await Promise.allSettled(promises);
this.updateRefMetadata();
}
this.updateRefsMetadata();
}
@gate()
@ -814,30 +850,27 @@ export class GraphWebview extends WebviewBase {
});
}
private _notifyDidChangeRefMetadataDebounced: Deferrable<GraphWebview['notifyDidChangeRefMetadata']> | undefined =
undefined;
private _notifyDidChangeRefsMetadataDebounced: Deferrable<GraphWebview['notifyDidChangeRefsMetadata']> | undefined =
undefined;
@debug()
private updateRefMetadata(immediate: boolean = false) {
private updateRefsMetadata(immediate: boolean = false) {
if (immediate) {
void this.notifyDidChangeRefMetadata();
void this.notifyDidChangeRefsMetadata();
return;
}
if (this._notifyDidChangeRefMetadataDebounced == null) {
this._notifyDidChangeRefMetadataDebounced = debounce(this.notifyDidChangeRefMetadata.bind(this), 100);
if (this._notifyDidChangeRefsMetadataDebounced == null) {
this._notifyDidChangeRefsMetadataDebounced = debounce(this.notifyDidChangeRefsMetadata.bind(this), 100);
}
void this._notifyDidChangeRefMetadataDebounced();
void this._notifyDidChangeRefsMetadataDebounced();
}
@debug()
private async notifyDidChangeRefMetadata() {
if (this._graph == null) return;
const data = this._graph;
return this.notify(DidChangeRefMetadataNotificationType, {
refMetadata: data.refMetadata ? Object.fromEntries(data.refMetadata) : undefined,
private async notifyDidChangeRefsMetadata() {
return this.notify(DidChangeRefsMetadataNotificationType, {
metadata: this._refsMetadata != null ? Object.fromEntries(this._refsMetadata) : this._refsMetadata,
});
}
@ -877,7 +910,7 @@ export class GraphWebview extends WebviewBase {
{
rows: data.rows,
avatars: Object.fromEntries(data.avatars),
refMetadata: data.refMetadata != undefined ? Object.fromEntries(data.refMetadata) : undefined,
refsMetadata: this._refsMetadata != null ? Object.fromEntries(this._refsMetadata) : this._refsMetadata,
selectedRows: sendSelectedRows ? this._selectedRows : undefined,
paging: {
startingCursor: data.paging?.startingCursor,
@ -1169,7 +1202,7 @@ export class GraphWebview extends WebviewBase {
subscription: access?.subscription.current,
allowed: (access?.allowed ?? false) !== false,
avatars: data != null ? Object.fromEntries(data.avatars) : undefined,
refMetadata: data?.refMetadata != undefined ? Object.fromEntries(data.refMetadata) : undefined,
refsMetadata: this.resetRefsMetadata(),
loading: deferRows,
rows: data?.rows,
paging:
@ -1196,6 +1229,11 @@ export class GraphWebview extends WebviewBase {
void this.notifyDidChangeColumns();
}
private resetRefsMetadata(): null | undefined {
this._refsMetadata = getContext(ContextKeys.HasConnectedRemotes) ? undefined : null;
return this._refsMetadata;
}
private resetRepositoryState() {
this.setGraph(undefined);
this.setSelectedRows(undefined);
@ -1217,6 +1255,7 @@ export class GraphWebview extends WebviewBase {
private setGraph(graph: GitGraph | undefined) {
this._graph = graph;
if (graph == null) {
this.resetRefsMetadata();
this.resetSearchState();
}
}
@ -1579,6 +1618,23 @@ export class GraphWebview extends WebviewBase {
}
@debug()
private openPullRequestOnRemote(item: GraphItemContext, clipboard?: boolean) {
if (
isGraphItemContext(item) &&
typeof item.webviewItemValue === 'object' &&
item.webviewItemValue.type === 'pullrequest'
) {
const { url } = item.webviewItemValue;
return executeCommand<OpenPullRequestOnRemoteCommandArgs>(Commands.OpenPullRequestOnRemote, {
pr: { url: url },
clipboard: clipboard,
});
}
return Promise.resolve();
}
@debug()
private async toggleColumn(name: GraphColumnName, visible: boolean) {
let columns = this.container.storage.getWorkspace('graph:columns');
let column = columns?.[name];
@ -1613,7 +1669,11 @@ export type GraphItemRefContextValue =
| GraphCommitContextValue
| GraphStashContextValue
| GraphTagContextValue;
export type GraphItemContextValue = GraphAvatarContextValue | GraphColumnsContextValue | GraphItemRefContextValue;
export type GraphItemContextValue =
| GraphAvatarContextValue
| GraphColumnsContextValue
| GraphPullRequestContextValue
| GraphItemRefContextValue;
export interface GraphAvatarContextValue {
type: 'avatar';
@ -1622,6 +1682,12 @@ export interface GraphAvatarContextValue {
export type GraphColumnsContextValue = string;
export interface GraphPullRequestContextValue {
type: 'pullrequest';
id: string;
url: string;
}
export interface GraphBranchContextValue {
type: 'branch';
ref: GitBranchReference;

+ 24
- 12
src/plus/webviews/graph/protocol.ts 查看文件

@ -4,7 +4,10 @@ import type {
GraphContexts,
GraphRow,
GraphZoneType,
HostingServiceType,
PullRequestMetadata,
RefMetadata,
RefMetadataType,
Remote,
WorkDirStats,
} from '@gitkraken/gitkraken-components';
@ -19,8 +22,13 @@ import { IpcCommandType, IpcNotificationType } from '../../../webviews/protocol'
export type GraphColumnsSettings = Record<GraphColumnName, GraphColumnSetting>;
export type GraphSelectedRows = Record</*id*/ string, true>;
export type GraphAvatars = Record</*email*/ string, /*url*/ string>;
export type GraphRefMetadata = Record</* id */ string, RefMetadata>;
export type GraphMissingRefMetadata = Record</*id*/ string, /*missingType*/ string[]>;
export type GraphRefMetadata = RefMetadata | null;
export type GraphRefsMetadata = Record</* id */ string, GraphRefMetadata>;
export type GraphHostingServiceType = HostingServiceType;
export type GraphMissingRefsMetadataType = RefMetadataType;
export type GraphMissingRefsMetadata = Record</*id*/ string, /*missingType*/ GraphMissingRefsMetadataType[]>;
export type GraphPullRequestMetadata = PullRequestMetadata;
export interface State {
repositories?: GraphRepository[];
@ -31,7 +39,7 @@ export interface State {
allowed: boolean;
avatars?: GraphAvatars;
loading?: boolean;
refMetadata?: GraphRefMetadata;
refsMetadata?: GraphRefsMetadata | null;
rows?: GraphRow[];
paging?: GraphPaging;
columns?: GraphColumnsSettings;
@ -121,10 +129,12 @@ export interface GetMissingAvatarsParams {
}
export const GetMissingAvatarsCommandType = new IpcCommandType<GetMissingAvatarsParams>('graph/avatars/get');
export interface GetMissingRefMetadataParams {
missing: GraphMissingRefMetadata;
export interface GetMissingRefsMetadataParams {
metadata: GraphMissingRefsMetadata;
}
export const GetMissingRefMetadataCommandType = new IpcCommandType<GetMissingRefMetadataParams>('graph/refMetadata/get');
export const GetMissingRefsMetadataCommandType = new IpcCommandType<GetMissingRefsMetadataParams>(
'graph/refs/metadata/get',
);
export interface GetMoreRowsParams {
id?: string;
@ -185,17 +195,19 @@ export const DidChangeSubscriptionNotificationType = new IpcNotificationType
);
export interface DidChangeAvatarsParams {
avatars: { [email: string]: string };
avatars: GraphAvatars;
}
export const DidChangeAvatarsNotificationType = new IpcNotificationType<DidChangeAvatarsParams>(
'graph/avatars/didChange',
true,
);
export interface DidChangeRefMetadataParams {
refMetadata: GraphRefMetadata | undefined;
export interface DidChangeRefsMetadataParams {
metadata: GraphRefsMetadata | null | undefined;
}
export const DidChangeRefMetadataNotificationType = new IpcNotificationType<DidChangeRefMetadataParams>(
'graph/refMetadata/didChange',
export const DidChangeRefsMetadataNotificationType = new IpcNotificationType<DidChangeRefsMetadataParams>(
'graph/refs/didChangeMetadata',
true,
);
export interface DidChangeColumnsParams {
@ -211,7 +223,7 @@ export interface DidChangeRowsParams {
rows: GraphRow[];
avatars: { [email: string]: string };
paging?: GraphPaging;
refMetadata: GraphRefMetadata | undefined;
refsMetadata?: GraphRefsMetadata | null;
selectedRows?: GraphSelectedRows;
}
export const DidChangeRowsNotificationType = new IpcNotificationType<DidChangeRowsParams>('graph/rows/didChange');

+ 15
- 13
src/webviews/apps/plus/graph/GraphWrapper.tsx 查看文件

@ -16,9 +16,11 @@ import type {
DidEnsureRowParams,
DidSearchParams,
DismissBannerParams,
GraphAvatars,
GraphColumnConfig,
GraphColumnName,
GraphComponentConfig,
GraphMissingRefsMetadata,
GraphRepository,
GraphSearchResults,
GraphSearchResultsError,
@ -30,7 +32,7 @@ import {
DidChangeAvatarsNotificationType,
DidChangeColumnsNotificationType,
DidChangeGraphConfigurationNotificationType,
DidChangeRefMetadataNotificationType,
DidChangeRefsMetadataNotificationType,
DidChangeRowsNotificationType,
DidChangeSelectionNotificationType,
DidChangeSubscriptionNotificationType,
@ -53,7 +55,7 @@ export interface GraphWrapperProps {
onSelectRepository?: (repository: GraphRepository) => void;
onColumnChange?: (name: GraphColumnName, settings: GraphColumnConfig) => void;
onMissingAvatars?: (emails: { [email: string]: string }) => void;
onMissingRefMetadata?: (missing: { [id: string]: string[] }) => void;
onMissingRefsMetadata?: (metadata: GraphMissingRefsMetadata) => void;
onMoreRows?: (id?: string) => void;
onSearch?: (search: SearchQuery | undefined, options?: { limit?: number }) => void;
onSearchPromise?: (
@ -134,7 +136,7 @@ export function GraphWrapper({
onColumnChange,
onEnsureRowPromise,
onMissingAvatars,
onMissingRefMetadata,
onMissingRefsMetadata,
onMoreRows,
onSearch,
onSearchPromise,
@ -151,7 +153,7 @@ export function GraphWrapper({
const [rows, setRows] = useState(state.rows ?? []);
const [avatars, setAvatars] = useState(state.avatars);
const [refMetadata, setRefMetadata] = useState(state.refMetadata);
const [refsMetadata, setRefsMetadata] = useState(state.refsMetadata);
const [repos, setRepos] = useState(state.repositories ?? []);
const [repo, setRepo] = useState<GraphRepository | undefined>(
repos.find(item => item.path === state.selectedRepository),
@ -209,8 +211,8 @@ export function GraphWrapper({
case DidChangeAvatarsNotificationType:
setAvatars(state.avatars);
break;
case DidChangeRefMetadataNotificationType:
setRefMetadata(state.refMetadata);
case DidChangeRefsMetadataNotificationType:
setRefsMetadata(state.refsMetadata);
break;
case DidChangeColumnsNotificationType:
setColumns(state.columns);
@ -220,7 +222,7 @@ export function GraphWrapper({
setRows(state.rows ?? []);
setSelectedRows(state.selectedRows);
setAvatars(state.avatars);
setRefMetadata(state.refMetadata);
setRefsMetadata(state.refsMetadata);
setPagingHasMore(state.paging?.hasMore ?? false);
setIsLoading(state.loading);
break;
@ -256,7 +258,7 @@ export function GraphWrapper({
setSelectedRows(state.selectedRows);
setContext(state.context);
setAvatars(state.avatars ?? {});
setRefMetadata(state.refMetadata ?? {});
setRefsMetadata(state.refsMetadata);
setPagingHasMore(state.paging?.hasMore ?? false);
setRepos(state.repositories ?? []);
setRepo(repos.find(item => item.path === state.selectedRepository));
@ -442,12 +444,12 @@ export function GraphWrapper({
setRepoExpanded(!repoExpanded);
};
const handleMissingAvatars = (emails: { [email: string]: string }) => {
const handleMissingAvatars = (emails: GraphAvatars) => {
onMissingAvatars?.(emails);
};
const handleMissingRefMetadata = (missing: { [id: string]: string[] }) => {
onMissingRefMetadata?.(missing);
const handleMissingRefsMetadata = (metadata: GraphMissingRefsMetadata) => {
onMissingRefsMetadata?.(metadata);
};
const handleToggleColumnSettings = (event: React.MouseEvent<HTMLButtonElement, globalThis.MouseEvent>) => {
@ -701,10 +703,10 @@ export function GraphWrapper({
onColumnResized={handleOnColumnResized}
onSelectGraphRows={handleSelectGraphRows}
onEmailsMissingAvatarUrls={handleMissingAvatars}
onRefsMissingMetadata={handleMissingRefMetadata}
onRefsMissingMetadata={handleMissingRefsMetadata}
onShowMoreCommits={handleMoreCommits}
platform={clientPlatform}
refMetadataById={refMetadata}
refMetadataById={refsMetadata}
shaLength={graphConfig?.idLength}
themeOpacityFactor={styleProps?.themeOpacityFactor}
useAuthorInitialsForAvatars={!graphConfig?.avatars}

+ 14
- 10
src/webviews/apps/plus/graph/graph.tsx 查看文件

@ -6,8 +6,10 @@ import type { GitGraphRowType } from '../../../../git/models/graph';
import type { SearchQuery } from '../../../../git/search';
import type {
DismissBannerParams,
GraphAvatars,
GraphColumnConfig,
GraphColumnName,
GraphMissingRefsMetadata,
GraphRepository,
InternalNotificationType,
State,
@ -18,7 +20,7 @@ import {
DidChangeColumnsNotificationType,
DidChangeGraphConfigurationNotificationType,
DidChangeNotificationType,
DidChangeRefMetadataNotificationType,
DidChangeRefsMetadataNotificationType,
DidChangeRowsNotificationType,
DidChangeSelectionNotificationType,
DidChangeSubscriptionNotificationType,
@ -28,7 +30,7 @@ import {
DismissBannerCommandType,
EnsureRowCommandType,
GetMissingAvatarsCommandType,
GetMissingRefMetadataCommandType,
GetMissingRefsMetadataCommandType,
GetMoreRowsCommandType,
SearchCommandType,
SearchOpenInViewCommandType,
@ -87,7 +89,7 @@ export class GraphApp extends App {
250,
)}
onMissingAvatars={(...params) => this.onGetMissingAvatars(...params)}
onMissingRefMetadata={(...params) => this.onGetMissingRefMetadata(...params)}
onMissingRefsMetadata={(...params) => this.onGetMissingRefsMetadata(...params)}
onMoreRows={(...params) => this.onGetMoreRows(...params)}
onSearch={debounce<GraphApp['onSearch']>((search, options) => this.onSearch(search, options), 250)}
onSearchPromise={(...params) => this.onSearchPromise(...params)}
@ -144,9 +146,9 @@ export class GraphApp extends App {
});
break;
case DidChangeRefMetadataNotificationType.method:
onIpc(DidChangeRefMetadataNotificationType, msg, (params, type) => {
this.state.refMetadata = params.refMetadata;
case DidChangeRefsMetadataNotificationType.method:
onIpc(DidChangeRefsMetadataNotificationType, msg, (params, type) => {
this.state.refsMetadata = params.metadata;
this.setState(this.state, type);
});
break;
@ -215,7 +217,9 @@ export class GraphApp extends App {
}
this.state.avatars = params.avatars;
this.state.refMetadata = params.refMetadata;
if (params.refsMetadata !== undefined) {
this.state.refsMetadata = params.refsMetadata;
}
this.state.rows = rows;
this.state.paging = params.paging;
if (params.selectedRows != null) {
@ -372,12 +376,12 @@ export class GraphApp extends App {
});
}
private onGetMissingAvatars(emails: { [email: string]: string }) {
private onGetMissingAvatars(emails: GraphAvatars) {
this.sendCommand(GetMissingAvatarsCommandType, { emails: emails });
}
private onGetMissingRefMetadata(missing: { [id: string]: string[] }) {
this.sendCommand(GetMissingRefMetadataCommandType, { missing: missing });
private onGetMissingRefsMetadata(metadata: GraphMissingRefsMetadata) {
this.sendCommand(GetMissingRefsMetadataCommandType, { metadata: metadata });
}
private onGetMoreRows(sha?: string) {

正在加载...
取消
保存