Browse Source

Adds dblclick/enter/space actions to open details

main
Eric Amodio 2 years ago
parent
commit
5f050f55b3
4 changed files with 109 additions and 30 deletions
  1. +34
    -22
      src/plus/webviews/graph/graphWebview.ts
  2. +12
    -4
      src/plus/webviews/graph/protocol.ts
  3. +41
    -2
      src/webviews/apps/plus/graph/GraphWrapper.tsx
  4. +22
    -2
      src/webviews/apps/plus/graph/graph.tsx

+ 34
- 22
src/plus/webviews/graph/graphWebview.ts View File

@ -78,7 +78,7 @@ import { arePlusFeaturesEnabled, ensurePlusFeaturesEnabled } from '../../subscri
import type {
DimMergeCommitsParams,
DismissBannerParams,
DoubleClickedRefParams,
DoubleClickedParams,
EnsureRowParams,
GetMissingAvatarsParams,
GetMissingRefsMetadataParams,
@ -125,7 +125,7 @@ import {
DidSearchNotificationType,
DimMergeCommitsCommandType,
DismissBannerCommandType,
DoubleClickedRefCommandType,
DoubleClickedCommandType,
EnsureRowCommandType,
GetMissingAvatarsCommandType,
GetMissingRefsMetadataCommandType,
@ -415,8 +415,8 @@ export class GraphWebview extends WebviewBase {
case DismissBannerCommandType.method:
onIpc(DismissBannerCommandType, e, params => this.dismissBanner(params));
break;
case DoubleClickedRefCommandType.method:
onIpc(DoubleClickedRefCommandType, e, params => this.onDoubleClickRef(params));
case DoubleClickedCommandType.method:
onIpc(DoubleClickedCommandType, e, params => this.onDoubleClick(params));
break;
case EnsureRowCommandType.method:
onIpc(EnsureRowCommandType, e, params => this.onEnsureRow(params, e.completionId));
@ -624,8 +624,8 @@ export class GraphWebview extends WebviewBase {
this.updateExcludedRefs(this._graph, e.refs, e.visible);
}
private onDoubleClickRef(e: DoubleClickedRefParams) {
if (e.ref.context) {
private onDoubleClick(e: DoubleClickedParams) {
if (e.type === 'ref' && e.ref.context) {
const item = typeof e.ref.context === 'string' ? JSON.parse(e.ref.context) : e.ref.context;
if (!('webview' in item)) {
item.webview = this.id;
@ -643,6 +643,9 @@ export class GraphWebview extends WebviewBase {
configuration.isUnset('gitCommands.skipConfirmations') ? true : undefined,
);
}
} else if (e.type === 'row' && e.row) {
const commit = this.getRevisionReference(this.repository?.path, e.row.id, e.row.type);
if (commit != null) return GitActions.Commit.showDetailsView(commit, { preserveFocus: e.preserveFocus });
}
return Promise.resolve();
@ -931,22 +934,8 @@ export class GraphWebview extends WebviewBase {
private fireSelectionChanged(id: string | undefined, type: GitGraphRowType | undefined) {
if (this.repository == null) return;
let commits: GitRevisionReference[] | undefined;
if (id != null) {
if (type === GitGraphRowType.Stash) {
commits = [
GitReference.create(id, this.repository.path, {
refType: 'stash',
name: id,
number: undefined,
}),
];
} else if (type === GitGraphRowType.Working) {
commits = [GitReference.create(GitRevision.uncommitted, this.repository.path, { refType: 'revision' })];
} else {
commits = [GitReference.create(id, this.repository.path, { refType: 'revision' })];
}
}
const commit = this.getRevisionReference(this.repository.path, id, type);
const commits = commit != null ? [commit] : undefined;
this._selection = commits;
this._onDidChangeSelection.fire({ selection: commits ?? [] });
@ -965,6 +954,29 @@ export class GraphWebview extends WebviewBase {
private _notifyDidChangeStateDebounced: Deferrable<GraphWebview['notifyDidChangeState']> | undefined = undefined;
private getRevisionReference(
repoPath: string | undefined,
id: string | undefined,
type: GitGraphRowType | undefined,
) {
if (repoPath == null || id == null) return undefined;
switch (type) {
case GitGraphRowType.Stash:
return GitReference.create(id, repoPath, {
refType: 'stash',
name: id,
number: undefined,
});
case GitGraphRowType.Working:
return GitReference.create(GitRevision.uncommitted, repoPath, { refType: 'revision' });
default:
return GitReference.create(id, repoPath, { refType: 'revision' });
}
}
@debug()
private updateState(immediate: boolean = false) {
this._pendingIpcNotifications.clear();

+ 12
- 4
src/plus/webviews/graph/protocol.ts View File

@ -154,10 +154,17 @@ export interface DismissBannerParams {
}
export const DismissBannerCommandType = new IpcCommandType<DismissBannerParams>('graph/dismissBanner');
export interface DoubleClickedRefParams {
ref: GraphRef;
}
export const DoubleClickedRefCommandType = new IpcCommandType<DoubleClickedRefParams>('graph/ref/doubleclick');
export type DoubleClickedParams =
| {
type: 'ref';
ref: GraphRef;
}
| {
type: 'row';
row: { id: string; type: GitGraphRowType };
preserveFocus?: boolean;
};
export const DoubleClickedCommandType = new IpcCommandType<DoubleClickedParams>('graph/dblclick');
export interface EnsureRowParams {
id: string;
@ -228,6 +235,7 @@ export interface UpdateSelectionParams {
export const UpdateSelectionCommandType = new IpcCommandType<UpdateSelectionParams>('graph/selection/update');
// Notifications
export interface DidChangeParams {
state: State;
}

+ 41
- 2
src/webviews/apps/plus/graph/GraphWrapper.tsx View File

@ -1,4 +1,4 @@
import GraphContainer from '@gitkraken/gitkraken-components';
import GraphContainer, { GRAPH_ZONE_TYPE, REF_ZONE_TYPE } from '@gitkraken/gitkraken-components';
import type {
GraphColumnSetting,
GraphColumnsSettings,
@ -8,6 +8,7 @@ import type {
GraphRefGroup,
GraphRefOptData,
GraphRow,
GraphZoneType,
OnFormatCommitDateTime,
} from '@gitkraken/gitkraken-components';
import { VSCodeCheckbox, VSCodeRadio, VSCodeRadioGroup } from '@vscode/webview-ui-toolkit/react';
@ -69,6 +70,7 @@ export interface GraphWrapperProps {
onColumnsChange?: (colsSettings: GraphColumnsConfig) => void;
onDimMergeCommits?: (dim: boolean) => void;
onDoubleClickRef?: (ref: GraphRef) => void;
onDoubleClickRow?: (row: GraphRow, preserveFocus?: boolean) => void;
onMissingAvatars?: (emails: { [email: string]: string }) => void;
onMissingRefsMetadata?: (metadata: GraphMissingRefsMetadata) => void;
onMoreRows?: (id?: string) => void;
@ -144,6 +146,7 @@ export function GraphWrapper({
onColumnsChange,
onDimMergeCommits,
onDoubleClickRef,
onDoubleClickRow,
onEnsureRowPromise,
onMissingAvatars,
onMissingRefsMetadata,
@ -309,6 +312,30 @@ export function GraphWrapper({
useEffect(() => subscriber?.(updateState), []);
useEffect(() => {
window.addEventListener('keydown', handleKeyDown);
return () => {
window.removeEventListener('keydown', handleKeyDown);
};
}, [activeRow]);
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Enter' || e.key === ' ') {
const sha = getActiveRowInfo(activeRow ?? state.activeRow)?.id;
if (sha == null) return;
// TODO@eamodio a bit of a hack since the graph container ref isn't exposed in the types
const graph = (graphRef.current as any)?.graphContainerRef.current;
if (!e.composedPath().some(el => el === graph)) return;
const row = rows.find(r => r.sha === sha);
if (row == null) return;
onDoubleClickRow?.(row, e.key !== 'Enter');
}
};
useEffect(() => {
if (searchResultsError != null || searchResults == null || searchResults.count === 0 || searchQuery == null) {
return;
}
@ -573,14 +600,25 @@ export function GraphWrapper({
};
const handleOnDoubleClickRef = (
event: React.MouseEvent<HTMLButtonElement, globalThis.MouseEvent>,
_event: React.MouseEvent<HTMLButtonElement, globalThis.MouseEvent>,
refGroup: GraphRefGroup,
_row: GraphRow,
) => {
if (refGroup.length > 0) {
onDoubleClickRef?.(refGroup[0]);
}
};
const handleOnDoubleClickRow = (
_event: React.MouseEvent<HTMLButtonElement, globalThis.MouseEvent>,
graphZoneType: GraphZoneType,
row: GraphRow,
) => {
if (graphZoneType === REF_ZONE_TYPE || graphZoneType === GRAPH_ZONE_TYPE) return;
onDoubleClickRow?.(row, true);
};
const handleSelectGraphRows = (rows: GraphRow[]) => {
const active = rows[0];
const activeKey = active != null ? `${active.sha}|${active.date}` : undefined;
@ -986,6 +1024,7 @@ export function GraphWrapper({
isSelectedBySha={selectedRows}
nonce={nonce}
onColumnResized={handleOnColumnResized}
onDoubleClickGraphRow={handleOnDoubleClickRow}
onDoubleClickGraphRef={handleOnDoubleClickRef}
onGraphColumnsReOrdered={handleOnGraphColumnsReOrdered}
onSelectGraphRows={handleSelectGraphRows}

+ 22
- 2
src/webviews/apps/plus/graph/graph.tsx View File

@ -33,7 +33,7 @@ import {
DidSearchNotificationType,
DimMergeCommitsCommandType,
DismissBannerCommandType,
DoubleClickedRefCommandType,
DoubleClickedCommandType,
EnsureRowCommandType,
GetMissingAvatarsCommandType,
GetMissingRefsMetadataCommandType,
@ -76,6 +76,7 @@ export class GraphApp extends App {
protected override onBind() {
const disposables = super.onBind?.() ?? [];
// disposables.push(DOM.on(window, 'keyup', e => this.onKeyUp(e)));
this.log(`${this.appName}.onBind`);
@ -98,6 +99,7 @@ export class GraphApp extends App {
}
onChooseRepository={debounce<GraphApp['onChooseRepository']>(() => this.onChooseRepository(), 250)}
onDoubleClickRef={ref => this.onDoubleClickRef(ref)}
onDoubleClickRow={(row, preserveFocus) => this.onDoubleClickRow(row, preserveFocus)}
onMissingAvatars={(...params) => this.onGetMissingAvatars(...params)}
onMissingRefsMetadata={(...params) => this.onGetMissingRefsMetadata(...params)}
onMoreRows={(...params) => this.onGetMoreRows(...params)}
@ -123,6 +125,15 @@ export class GraphApp extends App {
return disposables;
}
// private onKeyUp(e: KeyboardEvent) {
// if (e.key === 'Enter' || e.key === ' ') {
// const inputFocused = e.composedPath().some(el => (el as HTMLElement).tagName === 'INPUT');
// if (!inputFocused) return;
// const $target = e.target as HTMLElement;
// }
// }
protected override onMessageReceived(e: MessageEvent) {
const msg = e.data as IpcMessage;
this.log(`${this.appName}.onMessageReceived(${msg.id}): name=${msg.method}`);
@ -422,11 +433,20 @@ export class GraphApp extends App {
}
private onDoubleClickRef(ref: GraphRef) {
this.sendCommand(DoubleClickedRefCommandType, {
this.sendCommand(DoubleClickedCommandType, {
type: 'ref',
ref: ref,
});
}
private onDoubleClickRow(row: GraphRow, preserveFocus?: boolean) {
this.sendCommand(DoubleClickedCommandType, {
type: 'row',
row: { id: row.sha, type: row.type as GitGraphRowType },
preserveFocus: preserveFocus,
});
}
private onGetMissingAvatars(emails: GraphAvatars) {
this.sendCommand(GetMissingAvatarsCommandType, { emails: emails });
}

Loading…
Cancel
Save