Skip to content

Commit 01a5248

Browse files
committed
[dashboard] Make analytics types explicit
1 parent 93d41f1 commit 01a5248

File tree

1 file changed

+76
-58
lines changed

1 file changed

+76
-58
lines changed

components/dashboard/src/Analytics.tsx

Lines changed: 76 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -11,41 +11,60 @@ import { v4 } from "uuid";
1111

1212

1313
export type Event = "invite_url_requested" | "organisation_authorised";
14+
type InternalEvent = Event | "path_changed" | "dashboard_clicked";
15+
16+
export type EventProperties =
17+
TrackOrgAuthorised
18+
| TrackInviteUrlRequested
19+
;
20+
type InternalEventProperties = (
21+
EventProperties
22+
| TrackDashboardClick
23+
| TrackPathChanged
24+
);
25+
26+
export interface TrackOrgAuthorised {
27+
installation_id: string,
28+
setup_action: string | undefined,
29+
}
30+
31+
export interface TrackInviteUrlRequested {
32+
invite_url: string,
33+
}
1434

15-
export type TrackingMsg = {
35+
interface TrackDashboardClick {
1636
dnt?: boolean,
1737
path: string,
1838
button_type?: string,
1939
label?: string,
20-
destination?: string
40+
destination?: string,
41+
};
42+
43+
interface TrackPathChanged {
44+
prev: string,
45+
path: string,
2146
}
2247

2348
//call this to track all events outside of button and anchor clicks
24-
export const trackEvent = (event: Event, properties: any) => {
25-
getGitpodService().server.trackEvent({
26-
event: event,
27-
properties: properties
28-
})
49+
export const trackEvent = (event: Event, properties: EventProperties) => {
50+
trackEventInternal(event, properties);
2951
}
3052

31-
export const getAnonymousId = (): string => {
32-
let anonymousId = Cookies.get('ajs_anonymous_id');
33-
if (anonymousId) {
34-
return anonymousId.replace(/^"(.+(?="$))"$/, '$1'); //strip enclosing double quotes before returning
35-
}
36-
else {
37-
anonymousId = v4();
38-
Cookies.set('ajs_anonymous_id', anonymousId, {domain: '.'+window.location.hostname, expires: 365});
39-
};
40-
return anonymousId;
41-
}
53+
const trackEventInternal = (event: InternalEvent, properties: InternalEventProperties, userKnown?: boolean) => {
54+
getGitpodService().server.trackEvent({
55+
//if the user is authenticated, let server determine the id. else, pass anonymousId explicitly.
56+
anonymousId: userKnown ? undefined : getAnonymousId(),
57+
event,
58+
properties,
59+
});
60+
};
4261

4362
export const trackButtonOrAnchor = (target: HTMLAnchorElement | HTMLButtonElement | HTMLDivElement, userKnown: boolean) => {
4463
//read manually passed analytics props from 'data-analytics' attribute of event target
45-
let passedProps: TrackingMsg | undefined;
64+
let passedProps: TrackDashboardClick | undefined;
4665
if (target.dataset.analytics) {
4766
try {
48-
passedProps = JSON.parse(target.dataset.analytics) as TrackingMsg;
67+
passedProps = JSON.parse(target.dataset.analytics) as TrackDashboardClick;
4968
if (passedProps.dnt) {
5069
return;
5170
}
@@ -55,10 +74,10 @@ export const trackButtonOrAnchor = (target: HTMLAnchorElement | HTMLButtonElemen
5574

5675
}
5776

58-
let trackingMsg: TrackingMsg = {
77+
let trackingMsg: TrackDashboardClick = {
5978
path: window.location.pathname,
6079
label: target.textContent || undefined
61-
}
80+
};
6281

6382
if (target instanceof HTMLButtonElement || target instanceof HTMLDivElement) {
6483
//parse button data
@@ -79,60 +98,59 @@ export const trackButtonOrAnchor = (target: HTMLAnchorElement | HTMLButtonElemen
7998
trackingMsg.destination = anchor.href;
8099
}
81100

82-
const getAncestorProps = (curr: HTMLElement | null): TrackingMsg | undefined => {
101+
const getAncestorProps = (curr: HTMLElement | null): TrackDashboardClick | undefined => {
83102
if (!curr || curr instanceof Document) {
84103
return;
85104
}
86-
const ancestorProps: TrackingMsg | undefined = getAncestorProps(curr.parentElement);
87-
const currProps = JSON.parse(curr.dataset.analytics || "{}");
88-
return {...ancestorProps, ...currProps} as TrackingMsg;
105+
const ancestorProps: TrackDashboardClick | undefined = getAncestorProps(curr.parentElement);
106+
const currProps = JSON.parse(curr.dataset.analytics || "{}") as TrackDashboardClick;
107+
return {...ancestorProps, ...currProps};
89108
}
90109

91110
const ancestorProps = getAncestorProps(target);
92111

93112
//props that were passed directly to the event target take precedence over those passed to ancestor elements, which take precedence over those implicitly determined.
94113
trackingMsg = {...trackingMsg, ...ancestorProps, ...passedProps};
95114

96-
//if the user is authenticated, let server determine the id. else, pass anonymousId explicitly.
97-
if (userKnown) {
98-
getGitpodService().server.trackEvent({
99-
event: "dashboard_clicked",
100-
properties: trackingMsg
101-
});
102-
} else {
103-
getGitpodService().server.trackEvent({
104-
anonymousId: getAnonymousId(),
105-
event: "dashboard_clicked",
106-
properties: trackingMsg
107-
});
108-
}
115+
trackEventInternal("dashboard_clicked", trackingMsg, userKnown);
109116
}
110117

111118
//call this when the path changes. Complete page call is unnecessary for SPA after initial call
112-
export const trackPathChange = (props: { prev: string, path: string }) => {
113-
getGitpodService().server.trackEvent({
114-
event: "path_changed",
115-
properties: props
116-
});
119+
export const trackPathChange = (props: TrackPathChanged) => {
120+
trackEventInternal("path_changed", props);
117121
}
118122

123+
124+
type TrackLocationProperties = {
125+
referrer: string,
126+
path: string,
127+
host: string,
128+
url: string,
129+
};
130+
119131
export const trackLocation = async (userKnown: boolean) => {
120-
const props = {
132+
const props: TrackLocationProperties = {
121133
referrer: document.referrer,
122134
path: window.location.pathname,
123135
host: window.location.hostname,
124-
url: window.location.href
125-
}
126-
if (userKnown) {
127-
//if the user is known, make server call
128-
getGitpodService().server.trackLocation({
129-
properties: props
130-
});
131-
} else {
132-
//make privacy preserving page call (automatically interpreted as such by server if anonymousId is passed)
133-
getGitpodService().server.trackLocation({
134-
anonymousId: getAnonymousId(),
135-
properties: props
136-
});
136+
url: window.location.href,
137+
};
138+
139+
getGitpodService().server.trackLocation({
140+
//if the user is authenticated, let server determine the id. else, pass anonymousId explicitly.
141+
anonymousId: userKnown ? undefined : getAnonymousId(),
142+
properties: props
143+
});
144+
}
145+
146+
const getAnonymousId = (): string => {
147+
let anonymousId = Cookies.get('ajs_anonymous_id');
148+
if (anonymousId) {
149+
return anonymousId.replace(/^"(.+(?="$))"$/, '$1'); //strip enclosing double quotes before returning
137150
}
151+
else {
152+
anonymousId = v4();
153+
Cookies.set('ajs_anonymous_id', anonymousId, {domain: '.'+window.location.hostname, expires: 365});
154+
};
155+
return anonymousId;
138156
}

0 commit comments

Comments
 (0)