Skip to content

Commit cba1141

Browse files
committed
Tracks welcome page carousel views
Adds telemetry to track the number of carousel pages viewed on the welcome page. This data will help us understand user engagement with the welcome page features. (#4769, #4773, PLG-138)
1 parent 776e752 commit cba1141

File tree

2 files changed

+85
-3
lines changed

2 files changed

+85
-3
lines changed

src/webviews/apps/home/components/welcome-page.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,17 @@ const helpBlameUrl =
2626
const helpRevisionNavigationUrl =
2727
'https://help.gitkraken.com/gitlens/gitlens-features/?utm_source=gitlens-extension&utm_medium=in-app-links#revision-navigation';
2828

29+
type TelemetryData = {
30+
viewedCarouselPages: number;
31+
};
32+
2933
@customElement('gl-welcome-page')
3034
export class GlWelcomePage extends LitElement {
3135
static override styles = [scrollableBase, welcomeStyles];
32-
//static override styles = welcomeStyles;
36+
37+
private telemetryData: TelemetryData = {
38+
viewedCarouselPages: 0,
39+
};
3340

3441
@property({ type: Boolean })
3542
closeable = false;
@@ -58,6 +65,7 @@ export class GlWelcomePage extends LitElement {
5865
type: 'command',
5966
name: 'plus/sign-up',
6067
command: command,
68+
viewedCarouselPages: this.telemetryData.viewedCarouselPages,
6169
},
6270
source: { source: 'welcome' },
6371
});
@@ -68,6 +76,10 @@ export class GlWelcomePage extends LitElement {
6876
this.dispatchEvent(new CustomEvent('close'));
6977
}
7078

79+
private onFeatureAppeared() {
80+
this.telemetryData.viewedCarouselPages++;
81+
}
82+
7183
override render(): unknown {
7284
const themeSuffix = this.isLightTheme ? 'light' : 'dark';
7385
return html`
@@ -85,7 +97,7 @@ export class GlWelcomePage extends LitElement {
8597
</div>
8698
8799
<div class="section">
88-
<gl-feature-carousel>
100+
<gl-feature-carousel @gl-feature-appeared=${this.onFeatureAppeared}>
89101
<gl-feature-card class="card">
90102
<img
91103
slot="image"

src/webviews/apps/home/components/welcome-parts.ts

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { css, html, LitElement } from 'lit';
2-
import { customElement, queryAssignedElements, state } from 'lit/decorators.js';
2+
import { customElement, property, queryAssignedElements, state } from 'lit/decorators.js';
33
import '../../shared/components/button';
44
import '../../shared/components/code-icon';
55

@@ -250,6 +250,42 @@ export class GlFeatureCard extends LitElement {
250250
`,
251251
];
252252

253+
private hasBeenVisible: boolean = false;
254+
255+
override updated(changedProperties: Map<PropertyKey, unknown>): void {
256+
super.updated(changedProperties);
257+
258+
const isVisible = isElementVisible(this);
259+
if (!isVisible || this.hasBeenVisible) return;
260+
261+
const isInViewport = isElementInViewport(this);
262+
const isPartiallyInViewport = isElementPartiallyInViewport(this);
263+
const visible = isVisible && (isInViewport || isPartiallyInViewport);
264+
if (visible && !this.hasBeenVisible) {
265+
this.hasBeenVisible = true;
266+
267+
// Dispatch a custom event when any property changes
268+
this.dispatchEvent(
269+
new CustomEvent('gl-feature-appeared', {
270+
detail: {
271+
changedProperties: Array.from(changedProperties.keys()),
272+
visibility: {
273+
isVisible: isVisible,
274+
isInViewport: isInViewport,
275+
isPartiallyInViewport: isPartiallyInViewport,
276+
},
277+
},
278+
bubbles: true,
279+
composed: true,
280+
}),
281+
);
282+
}
283+
}
284+
285+
/** is used to make the component reactive to 'data-active' attribute changes */
286+
@property({ type: Boolean, reflect: true, attribute: 'data-active' })
287+
private _dataActive: boolean = false;
288+
253289
override render(): unknown {
254290
return html`
255291
<div class="image">
@@ -370,3 +406,37 @@ export class GlScrollableFeatures extends LitElement {
370406
return html`<div class="content"><slot></slot></div>`;
371407
}
372408
}
409+
410+
function isElementVisible(element: HTMLElement): boolean {
411+
// Check if element is hidden by display: none or visibility: hidden
412+
const style = window.getComputedStyle(element);
413+
if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') {
414+
return false;
415+
}
416+
417+
// Check if element has zero dimensions
418+
const rect = element.getBoundingClientRect();
419+
if (rect.width === 0 || rect.height === 0) {
420+
return false;
421+
}
422+
423+
return true;
424+
}
425+
426+
function isElementInViewport(element: HTMLElement): boolean {
427+
const rect = element.getBoundingClientRect();
428+
return (
429+
rect.top >= 0 &&
430+
rect.left >= 0 &&
431+
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
432+
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
433+
);
434+
}
435+
436+
function isElementPartiallyInViewport(element: HTMLElement): boolean {
437+
const rect = element.getBoundingClientRect();
438+
const windowHeight = window.innerHeight || document.documentElement.clientHeight;
439+
const windowWidth = window.innerWidth || document.documentElement.clientWidth;
440+
441+
return rect.bottom > 0 && rect.right > 0 && rect.top < windowHeight && rect.left < windowWidth;
442+
}

0 commit comments

Comments
 (0)