Browse Source

Send remote ids and handle ref metadata requests when remote connected

main
Ramin Tadayon 2 years ago
committed by Eric Amodio
parent
commit
0a17784b42
7 changed files with 157 additions and 7 deletions
  1. +10
    -4
      src/env/node/git/localGitProvider.ts
  2. +5
    -1
      src/git/models/graph.ts
  3. +12
    -1
      src/plus/github/githubGitProvider.ts
  4. +83
    -1
      src/plus/webviews/graph/graphWebview.ts
  5. +17
    -0
      src/plus/webviews/graph/protocol.ts
  6. +15
    -0
      src/webviews/apps/plus/graph/GraphWrapper.tsx
  7. +15
    -0
      src/webviews/apps/plus/graph/graph.tsx

+ 10
- 4
src/env/node/git/localGitProvider.ts View File

@ -14,8 +14,9 @@ import type {
} from '../../../@types/vscode.git';
import { getCachedAvatarUri } from '../../../avatars';
import { configuration } from '../../../configuration';
import { CoreGitConfiguration, GlyphChars, Schemes } from '../../../constants';
import { ContextKeys, 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';
@ -61,6 +62,7 @@ import type { GitFile, GitFileStatus } from '../../../git/models/file';
import { GitFileChange } from '../../../git/models/file';
import type {
GitGraph,
GitGraphRefMetadata,
GitGraphRow,
GitGraphRowContexts,
GitGraphRowHead,
@ -1660,7 +1662,9 @@ 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>();
@ -1706,7 +1710,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, rows: [] };
return { repoPath: repoPath, avatars: avatars, ids: ids, refMetadata: refMetadata, rows: [] };
}
size = data.length;
@ -1732,7 +1736,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
}
}
if (!data) return { repoPath: repoPath, avatars: avatars, ids: ids, rows: [] };
if (!data) return { repoPath: repoPath, avatars: avatars, ids: ids, refMetadata: refMetadata, rows: [] };
log = data;
if (limit !== 0) {
@ -1812,6 +1816,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
}
}
branch = branchMap.get(tip);
remoteName = getRemoteNameFromBranchName(tip);
if (remoteName) {
remote = remoteMap.get(remoteName);
@ -1820,6 +1825,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
if (branchName === 'HEAD') continue;
refRemoteHeads.push({
id: branch?.id ?? remote.id,
name: branchName,
owner: remote.name,
url: remote.url,
@ -1845,7 +1851,6 @@ export class LocalGitProvider implements GitProvider, Disposable {
}
}
branch = branchMap.get(tip);
refHeads.push({
name: tip,
isCurrentHead: current,
@ -1963,6 +1968,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
avatars: avatars,
ids: ids,
skippedIds: skippedIds,
refMetadata: refMetadata,
rows: rows,
id: sha,

+ 5
- 1
src/git/models/graph.ts View File

@ -1,9 +1,11 @@
import type { GraphRow, Head, Remote, RowContexts, Tag } from '@gitkraken/gitkraken-components';
import type { GraphRow, Head, HostingServiceType, RefMetadata, 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',
@ -25,6 +27,8 @@ 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 */

+ 12
- 1
src/plus/github/githubGitProvider.ts View File

@ -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 { setContext } from '../../context';
import { getContext, setContext } from '../../context';
import { emojify } from '../../emojis';
import {
AuthenticationError,
@ -48,6 +48,7 @@ import type { GitFile } from '../../git/models/file';
import { GitFileChange, GitFileIndexStatus } from '../../git/models/file';
import type {
GitGraph,
GitGraphRefMetadata,
GitGraphRow,
GitGraphRowHead,
GitGraphRowRemoteHead,
@ -56,6 +57,7 @@ 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';
@ -1083,7 +1085,9 @@ 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(
@ -1094,6 +1098,7 @@ export class GitHubGitProvider implements GitProvider, Disposable {
getSettledValue(remotesResult)?.[0],
getSettledValue(tagsResult)?.values,
avatars,
refMetadata,
ids,
options,
);
@ -1107,6 +1112,7 @@ 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;
@ -1119,6 +1125,7 @@ export class GitHubGitProvider implements GitProvider, Disposable {
return {
repoPath: repoPath,
avatars: avatars,
refMetadata: refMetadata,
ids: ids,
rows: [],
};
@ -1129,6 +1136,7 @@ export class GitHubGitProvider implements GitProvider, Disposable {
return {
repoPath: repoPath,
avatars: avatars,
refMetadata: refMetadata,
ids: ids,
rows: [],
};
@ -1154,6 +1162,7 @@ export class GitHubGitProvider implements GitProvider, Disposable {
];
refRemoteHeads = [
{
id: remote.id,
name: branch.name,
owner: remote.name,
url: remote.url,
@ -1213,6 +1222,7 @@ export class GitHubGitProvider implements GitProvider, Disposable {
return {
repoPath: repoPath,
avatars: avatars,
refMetadata: refMetadata,
ids: ids,
rows: rows,
id: options?.ref,
@ -1232,6 +1242,7 @@ export class GitHubGitProvider implements GitProvider, Disposable {
remote,
tags,
avatars,
refMetadata,
ids,
options,
);

+ 83
- 1
src/plus/webviews/graph/graphWebview.ts View File

@ -32,9 +32,10 @@ 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 } from '../../../git/models/graph';
import type { GitGraph, GitGraphHostingServiceType } from '../../../git/models/graph';
import type {
GitBranchReference,
GitRevisionReference,
@ -76,6 +77,7 @@ import type {
DismissBannerParams,
EnsureRowParams,
GetMissingAvatarsParams,
GetMissingRefMetadataParams,
GetMoreRowsParams,
GraphColumnConfig,
GraphColumnName,
@ -96,6 +98,7 @@ import {
DidChangeColumnsNotificationType,
DidChangeGraphConfigurationNotificationType,
DidChangeNotificationType,
DidChangeRefMetadataNotificationType,
DidChangeRowsNotificationType,
DidChangeSelectionNotificationType,
DidChangeSubscriptionNotificationType,
@ -105,6 +108,7 @@ import {
DismissBannerCommandType,
EnsureRowCommandType,
GetMissingAvatarsCommandType,
GetMissingRefMetadataCommandType,
GetMoreRowsCommandType,
SearchCommandType,
SearchOpenInViewCommandType,
@ -351,6 +355,9 @@ 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));
break;
case GetMoreRowsCommandType.method:
onIpc(GetMoreRowsCommandType, e, params => this.onGetMoreRows(params));
break;
@ -535,6 +542,52 @@ export class GraphWebview extends WebviewBase {
}
}
private async onGetMissingRefMetadata(e: GetMissingRefMetadataParams) {
if (this._graph == null) 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;
}
this._graph!.refMetadata?.set(id, newRefMetadata);
}
const promises: Promise<void>[] = [];
for (const [id, missingTypes] of Object.entries(e.missing)) {
for (const missingType of missingTypes) {
promises.push(getRefMetadata.call(this, id, missingType));
}
}
if (promises.length) {
await Promise.allSettled(promises);
this.updateRefMetadata();
}
}
@gate()
@debug()
private async onGetMoreRows(e: GetMoreRowsParams, sendSelectedRows: boolean = false) {
@ -761,6 +814,33 @@ export class GraphWebview extends WebviewBase {
});
}
private _notifyDidChangeRefMetadataDebounced: Deferrable<GraphWebview['notifyDidChangeRefMetadata']> | undefined =
undefined;
@debug()
private updateRefMetadata(immediate: boolean = false) {
if (immediate) {
void this.notifyDidChangeRefMetadata();
return;
}
if (this._notifyDidChangeRefMetadataDebounced == null) {
this._notifyDidChangeRefMetadataDebounced = debounce(this.notifyDidChangeRefMetadata.bind(this), 100);
}
void this._notifyDidChangeRefMetadataDebounced();
}
@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,
});
}
@debug()
private async notifyDidChangeColumns() {
if (!this.isReady || !this.visible) {
@ -797,6 +877,7 @@ export class GraphWebview extends WebviewBase {
{
rows: data.rows,
avatars: Object.fromEntries(data.avatars),
refMetadata: data.refMetadata != undefined ? Object.fromEntries(data.refMetadata) : undefined,
selectedRows: sendSelectedRows ? this._selectedRows : undefined,
paging: {
startingCursor: data.paging?.startingCursor,
@ -1088,6 +1169,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,
loading: deferRows,
rows: data?.rows,
paging:

+ 17
- 0
src/plus/webviews/graph/protocol.ts View File

@ -4,6 +4,7 @@ import type {
GraphContexts,
GraphRow,
GraphZoneType,
RefMetadata,
Remote,
WorkDirStats,
} from '@gitkraken/gitkraken-components';
@ -18,6 +19,8 @@ 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 interface State {
repositories?: GraphRepository[];
@ -28,6 +31,7 @@ export interface State {
allowed: boolean;
avatars?: GraphAvatars;
loading?: boolean;
refMetadata?: GraphRefMetadata;
rows?: GraphRow[];
paging?: GraphPaging;
columns?: GraphColumnsSettings;
@ -117,6 +121,11 @@ export interface GetMissingAvatarsParams {
}
export const GetMissingAvatarsCommandType = new IpcCommandType<GetMissingAvatarsParams>('graph/avatars/get');
export interface GetMissingRefMetadataParams {
missing: GraphMissingRefMetadata;
}
export const GetMissingRefMetadataCommandType = new IpcCommandType<GetMissingRefMetadataParams>('graph/refMetadata/get');
export interface GetMoreRowsParams {
id?: string;
}
@ -182,6 +191,13 @@ export const DidChangeAvatarsNotificationType = new IpcNotificationType
'graph/avatars/didChange',
);
export interface DidChangeRefMetadataParams {
refMetadata: GraphRefMetadata | undefined;
}
export const DidChangeRefMetadataNotificationType = new IpcNotificationType<DidChangeRefMetadataParams>(
'graph/refMetadata/didChange',
);
export interface DidChangeColumnsParams {
columns: GraphColumnsSettings | undefined;
context?: string;
@ -195,6 +211,7 @@ export interface DidChangeRowsParams {
rows: GraphRow[];
avatars: { [email: string]: string };
paging?: GraphPaging;
refMetadata: GraphRefMetadata | undefined;
selectedRows?: GraphSelectedRows;
}
export const DidChangeRowsNotificationType = new IpcNotificationType<DidChangeRowsParams>('graph/rows/didChange');

+ 15
- 0
src/webviews/apps/plus/graph/GraphWrapper.tsx View File

@ -30,6 +30,7 @@ import {
DidChangeAvatarsNotificationType,
DidChangeColumnsNotificationType,
DidChangeGraphConfigurationNotificationType,
DidChangeRefMetadataNotificationType,
DidChangeRowsNotificationType,
DidChangeSelectionNotificationType,
DidChangeSubscriptionNotificationType,
@ -52,6 +53,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;
onMoreRows?: (id?: string) => void;
onSearch?: (search: SearchQuery | undefined, options?: { limit?: number }) => void;
onSearchPromise?: (
@ -132,6 +134,7 @@ export function GraphWrapper({
onColumnChange,
onEnsureRowPromise,
onMissingAvatars,
onMissingRefMetadata,
onMoreRows,
onSearch,
onSearchPromise,
@ -148,6 +151,7 @@ export function GraphWrapper({
const [rows, setRows] = useState(state.rows ?? []);
const [avatars, setAvatars] = useState(state.avatars);
const [refMetadata, setRefMetadata] = useState(state.refMetadata);
const [repos, setRepos] = useState(state.repositories ?? []);
const [repo, setRepo] = useState<GraphRepository | undefined>(
repos.find(item => item.path === state.selectedRepository),
@ -205,6 +209,9 @@ export function GraphWrapper({
case DidChangeAvatarsNotificationType:
setAvatars(state.avatars);
break;
case DidChangeRefMetadataNotificationType:
setRefMetadata(state.refMetadata);
break;
case DidChangeColumnsNotificationType:
setColumns(state.columns);
setContext(state.context);
@ -213,6 +220,7 @@ export function GraphWrapper({
setRows(state.rows ?? []);
setSelectedRows(state.selectedRows);
setAvatars(state.avatars);
setRefMetadata(state.refMetadata);
setPagingHasMore(state.paging?.hasMore ?? false);
setIsLoading(state.loading);
break;
@ -248,6 +256,7 @@ export function GraphWrapper({
setSelectedRows(state.selectedRows);
setContext(state.context);
setAvatars(state.avatars ?? {});
setRefMetadata(state.refMetadata ?? {});
setPagingHasMore(state.paging?.hasMore ?? false);
setRepos(state.repositories ?? []);
setRepo(repos.find(item => item.path === state.selectedRepository));
@ -437,6 +446,10 @@ export function GraphWrapper({
onMissingAvatars?.(emails);
};
const handleMissingRefMetadata = (missing: { [id: string]: string[] }) => {
onMissingRefMetadata?.(missing);
};
const handleToggleColumnSettings = (event: React.MouseEvent<HTMLButtonElement, globalThis.MouseEvent>) => {
const e = event.nativeEvent;
const evt = new MouseEvent('contextmenu', {
@ -688,8 +701,10 @@ export function GraphWrapper({
onColumnResized={handleOnColumnResized}
onSelectGraphRows={handleSelectGraphRows}
onEmailsMissingAvatarUrls={handleMissingAvatars}
onRefsMissingMetadata={handleMissingRefMetadata}
onShowMoreCommits={handleMoreCommits}
platform={clientPlatform}
refMetadataById={refMetadata}
shaLength={graphConfig?.idLength}
themeOpacityFactor={styleProps?.themeOpacityFactor}
useAuthorInitialsForAvatars={!graphConfig?.avatars}

+ 15
- 0
src/webviews/apps/plus/graph/graph.tsx View File

@ -18,6 +18,7 @@ import {
DidChangeColumnsNotificationType,
DidChangeGraphConfigurationNotificationType,
DidChangeNotificationType,
DidChangeRefMetadataNotificationType,
DidChangeRowsNotificationType,
DidChangeSelectionNotificationType,
DidChangeSubscriptionNotificationType,
@ -27,6 +28,7 @@ import {
DismissBannerCommandType,
EnsureRowCommandType,
GetMissingAvatarsCommandType,
GetMissingRefMetadataCommandType,
GetMoreRowsCommandType,
SearchCommandType,
SearchOpenInViewCommandType,
@ -85,6 +87,7 @@ export class GraphApp extends App {
250,
)}
onMissingAvatars={(...params) => this.onGetMissingAvatars(...params)}
onMissingRefMetadata={(...params) => this.onGetMissingRefMetadata(...params)}
onMoreRows={(...params) => this.onGetMoreRows(...params)}
onSearch={debounce<GraphApp['onSearch']>((search, options) => this.onSearch(search, options), 250)}
onSearchPromise={(...params) => this.onSearchPromise(...params)}
@ -141,6 +144,13 @@ export class GraphApp extends App {
});
break;
case DidChangeRefMetadataNotificationType.method:
onIpc(DidChangeRefMetadataNotificationType, msg, (params, type) => {
this.state.refMetadata = params.refMetadata;
this.setState(this.state, type);
});
break;
case DidChangeRowsNotificationType.method:
onIpc(DidChangeRowsNotificationType, msg, (params, type) => {
let rows;
@ -205,6 +215,7 @@ export class GraphApp extends App {
}
this.state.avatars = params.avatars;
this.state.refMetadata = params.refMetadata;
this.state.rows = rows;
this.state.paging = params.paging;
if (params.selectedRows != null) {
@ -365,6 +376,10 @@ export class GraphApp extends App {
this.sendCommand(GetMissingAvatarsCommandType, { emails: emails });
}
private onGetMissingRefMetadata(missing: { [id: string]: string[] }) {
this.sendCommand(GetMissingRefMetadataCommandType, { missing: missing });
}
private onGetMoreRows(sha?: string) {
return this.sendCommand(GetMoreRowsCommandType, { id: sha });
}

Loading…
Cancel
Save