Skip to content

Commit bd9d882

Browse files
committed
Improves Graph render performance
- Only sends changed props to react - Limits work on selection
1 parent 9e66e07 commit bd9d882

File tree

6 files changed

+390
-344
lines changed

6 files changed

+390
-344
lines changed

docs/telemetry-events.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -909,7 +909,7 @@ or
909909

910910
### graph/row/hovered
911911

912-
> Sent when the user hovers over a row on the Commit Graph
912+
> Sent when the user hovers over a row on the Commit Graph (first time and every 100 times after)
913913
914914
```typescript
915915
{
@@ -921,13 +921,14 @@ or
921921
'context.webview.host': 'editor' | 'view',
922922
'context.webview.id': string,
923923
'context.webview.instanceId': string,
924-
'context.webview.type': string
924+
'context.webview.type': string,
925+
'count': number
925926
}
926927
```
927928

928929
### graph/row/selected
929930

930-
> Sent when the user selects (clicks on) a row or rows on the Commit Graph
931+
> Sent when the user selects (clicks on) a row or rows on the Commit Graph (first time and every 100 times after)
931932
932933
```typescript
933934
{
@@ -940,6 +941,7 @@ or
940941
'context.webview.id': string,
941942
'context.webview.instanceId': string,
942943
'context.webview.type': string,
944+
'count': number,
943945
'rows': number
944946
}
945947
```

src/constants.telemetry.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,9 @@ export interface TelemetryEvents extends WebviewShowAbortedEvents, WebviewShownE
135135
/** Sent when the user changes the current repository on the Commit Graph */
136136
'graph/repository/changed': GraphRepositoryChangedEvent;
137137

138-
/** Sent when the user hovers over a row on the Commit Graph */
139-
'graph/row/hovered': GraphContextEventData;
140-
/** Sent when the user selects (clicks on) a row or rows on the Commit Graph */
138+
/** Sent when the user hovers over a row on the Commit Graph (first time and every 100 times after) */
139+
'graph/row/hovered': GraphRowHoveredEvent;
140+
/** Sent when the user selects (clicks on) a row or rows on the Commit Graph (first time and every 100 times after) */
141141
'graph/row/selected': GraphRowSelectedEvent;
142142
/** Sent when rows are loaded into the Commit Graph */
143143
'graph/rows/loaded': GraphRowsLoadedEvent;
@@ -545,8 +545,13 @@ interface GraphFiltersChangedEvent extends GraphContextEventData {
545545

546546
interface GraphRepositoryChangedEvent extends RepositoryEventData, GraphContextEventData {}
547547

548+
interface GraphRowHoveredEvent extends GraphContextEventData {
549+
count: number;
550+
}
551+
548552
interface GraphRowSelectedEvent extends GraphContextEventData {
549553
rows: number;
554+
count: number;
550555
}
551556

552557
interface GraphRowsLoadedEvent extends GraphContextEventData {

src/webviews/apps/plus/graph/graph-app.ts

Lines changed: 114 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,30 @@
1+
import type { GraphRow } from '@gitkraken/gitkraken-components';
2+
import { refZone } from '@gitkraken/gitkraken-components';
13
import { consume } from '@lit/context';
24
import { SignalWatcher } from '@lit-labs/signals';
35
import { html, LitElement } from 'lit';
46
import { customElement, query } from 'lit/decorators.js';
57
import { when } from 'lit/directives/when.js';
8+
import type { GitGraphRowType } from '../../../../git/models/graph';
9+
import { getScopedCounter } from '../../../../system/counter';
10+
import { GetRowHoverRequest } from '../../../plus/graph/protocol';
611
import type { CustomEventType } from '../../shared/components/element';
12+
import { ipcContext } from '../../shared/contexts/ipc';
13+
import type { TelemetryContext } from '../../shared/contexts/telemetry';
14+
import { telemetryContext } from '../../shared/contexts/telemetry';
715
import { emitTelemetrySentEvent } from '../../shared/telemetry';
816
import { stateContext } from './context';
917
import type { GlGraphWrapper } from './graph-wrapper/graph-wrapper';
18+
import type { GlGraphHover } from './hover/graphHover';
1019
import type { GraphMinimapDaySelectedEventDetail } from './minimap/minimap';
1120
import type { GlGraphMinimapContainer } from './minimap/minimap-container';
1221
import { graphStateContext } from './stateProvider';
13-
import './minimap/minimap-container';
22+
import './gate';
23+
import './graph-header';
1424
import './graph-wrapper/graph-wrapper';
25+
import './hover/graphHover';
26+
import './minimap/minimap-container';
1527
import './sidebar/sidebar';
16-
import './graph-header';
17-
import './gate';
1828

1929
@customElement('gl-graph-app-wc')
2030
export class GraphAppWC extends SignalWatcher(LitElement) {
@@ -28,20 +38,28 @@ export class GraphAppWC extends SignalWatcher(LitElement) {
2838
@consume({ context: graphStateContext, subscribe: true })
2939
graphApp!: typeof graphStateContext.__context__;
3040

31-
@query('gl-graph-minimap-container')
32-
minimapEl: GlGraphMinimapContainer | undefined;
41+
@consume({ context: ipcContext })
42+
private readonly _ipc!: typeof ipcContext.__context__;
43+
44+
@consume({ context: telemetryContext as any })
45+
private readonly _telemetry!: TelemetryContext;
3346

3447
@query('gl-graph-wrapper')
35-
graphWrapper!: GlGraphWrapper;
48+
graph!: GlGraphWrapper;
49+
50+
@query('gl-graph-hover#commit-hover')
51+
private readonly graphHover!: GlGraphHover;
52+
53+
@query('gl-graph-minimap-container')
54+
minimapEl: GlGraphMinimapContainer | undefined;
3655

3756
private handleHeaderSearchNavigation(e: CustomEventType<'gl-select-commits'>) {
38-
this.graphWrapper.selectCommits([e.detail], false, true);
57+
this.graph.selectCommits([e.detail], false, true);
3958
}
4059

4160
private handleMinimapDaySelected(e: CustomEvent<GraphMinimapDaySelectedEventDetail>) {
42-
if (!this.state.rows) {
43-
return;
44-
}
61+
if (!this.state.rows) return;
62+
4563
let { sha } = e.detail;
4664
if (sha == null) {
4765
const date = e.detail.date?.getTime();
@@ -54,32 +72,99 @@ export class GraphAppWC extends SignalWatcher(LitElement) {
5472
sha = closest.sha;
5573
}
5674

57-
this.graphWrapper.selectCommits([sha], false, true);
75+
this.graph.selectCommits([sha], false, true);
5876

59-
queueMicrotask(
60-
() =>
61-
e.target &&
62-
emitTelemetrySentEvent<'graph/minimap/day/selected'>(e.target, {
77+
if (e.target != null) {
78+
const { target } = e;
79+
queueMicrotask(() =>
80+
emitTelemetrySentEvent<'graph/minimap/day/selected'>(target, {
6381
name: 'graph/minimap/day/selected',
6482
data: {},
6583
}),
66-
);
84+
);
85+
}
86+
}
87+
88+
private handleGraphSelectionChanged(_e: CustomEventType<'gl-graph-change-selection'>) {
89+
this.graphHover.hide();
90+
}
91+
92+
private handleGraphVisibleDaysChanged({ detail }: CustomEventType<'gl-graph-change-visible-days'>) {
93+
this.graphApp.visibleDays = detail;
94+
}
95+
96+
private handleGraphRowContextMenu(_e: CustomEventType<'gl-graph-row-context-menu'>) {
97+
this.graphHover.hide();
6798
}
6899

69-
private handleGraphVisibleDaysChanged(e: CustomEventType<'gl-graph-change-visible-days'>) {
70-
this.graphApp.visibleDays = e.detail;
100+
private handleGraphRowHover({
101+
detail: { graphZoneType, graphRow, clientX, currentTarget },
102+
}: CustomEventType<'gl-graph-row-hover'>) {
103+
if (graphZoneType === refZone) return;
104+
105+
const hover = this.graphHover;
106+
if (hover == null) return;
107+
108+
const rect = currentTarget.getBoundingClientRect();
109+
const x = clientX;
110+
const y = rect.top;
111+
const height = rect.height;
112+
const width = 60; // Add some width, so `skidding` will be able to apply
113+
const anchor = {
114+
getBoundingClientRect: function () {
115+
return {
116+
width: width,
117+
height: height,
118+
x: x,
119+
y: y,
120+
top: y,
121+
left: x,
122+
right: x + width,
123+
bottom: y + height,
124+
};
125+
},
126+
};
127+
128+
hover.requestMarkdown ??= this.getRowHoverPromise.bind(this);
129+
hover.onRowHovered(graphRow, anchor);
130+
131+
this.minimapEl?.select(graphRow.date, true);
71132
}
72133

73-
private handleGraphRowHovered(e: CustomEventType<'gl-graph-hovered-row'>) {
74-
this.minimapEl?.select(e.detail.graphRow.date, true);
134+
private handleGraphRowUnhover({
135+
detail: { graphZoneType, graphRow, relatedTarget },
136+
}: CustomEventType<'gl-graph-row-unhover'>): void {
137+
if (graphZoneType === refZone) return;
138+
139+
this.graphHover.onRowUnhovered(graphRow, relatedTarget);
140+
}
141+
142+
private _hoverCounter = getScopedCounter();
143+
144+
private async getRowHoverPromise(row: GraphRow) {
145+
try {
146+
const request = await this._ipc.sendRequest(GetRowHoverRequest, {
147+
type: row.type as GitGraphRowType,
148+
id: row.sha,
149+
});
150+
151+
const count = this._hoverCounter.next();
152+
if (count === 1 || count % 100 === 0) {
153+
queueMicrotask(() => this._telemetry.sendEvent({ name: 'graph/row/hovered', data: { count: count } }));
154+
}
155+
156+
return request;
157+
} catch (ex) {
158+
return { id: row.sha, markdown: { status: 'rejected' as const, reason: ex } };
159+
}
75160
}
76161

77-
private handleGraphMouseLeaved() {
162+
private handleGraphMouseLeave() {
78163
this.minimapEl?.unselect(undefined, true);
79164
}
80165

81166
resetHover() {
82-
this.graphWrapper.resetHover();
167+
this.graphHover.reset();
83168
}
84169

85170
override render() {
@@ -105,19 +190,20 @@ export class GraphAppWC extends SignalWatcher(LitElement) {
105190
.markerTypes=${this.state.config?.minimapMarkerTypes ?? []}
106191
.refMetadata=${this.state.refsMetadata}
107192
.searchResults=${this.graphApp.searchResults}
193+
.visibleDays=${this.graphApp.visibleDays}
108194
@gl-graph-minimap-selected=${this.handleMinimapDaySelected}
109-
.visibleDays=${this.graphApp.visibleDays && {
110-
top: this.graphApp.visibleDays.top,
111-
bottom: this.graphApp.visibleDays.bottom,
112-
}}
113195
></gl-graph-minimap-container>
114196
`,
115197
)}
116198
${when(this.state.config?.sidebar, () => html`<gl-graph-sidebar></gl-graph-sidebar>`)}
199+
<gl-graph-hover id="commit-hover" distance=${0} skidding=${15}></gl-graph-hover>
117200
<gl-graph-wrapper
201+
@gl-graph-change-selection=${this.handleGraphSelectionChanged}
118202
@gl-graph-change-visible-days=${this.handleGraphVisibleDaysChanged}
119-
@gl-graph-hovered-row=${this.handleGraphRowHovered}
120-
@gl-graph-mouse-leave=${this.handleGraphMouseLeaved}
203+
@gl-graph-mouse-leave=${this.handleGraphMouseLeave}
204+
@gl-graph-row-context-menu=${this.handleGraphRowContextMenu}
205+
@gl-graph-row-hover=${this.handleGraphRowHover}
206+
@gl-graph-row-unhover=${this.handleGraphRowUnhover}
121207
></gl-graph-wrapper>
122208
</div>
123209
<!-- future: commit details -->

0 commit comments

Comments
 (0)