Skip to content

Commit b3d65f6

Browse files
authored
Analytics updates (#151)
* Add device for tracking * tracking connect and account modal viewing. Handling for address and userid being undefined * Add connect and disconnect events * add changeset * Get some device info by default
1 parent a379082 commit b3d65f6

File tree

8 files changed

+111
-7
lines changed

8 files changed

+111
-7
lines changed

.changeset/long-otters-punch.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@treasure-dev/tdk-react": patch
3+
"@treasure-dev/tdk-core": patch
4+
---
5+
6+
Updated analytics, device tracking + auto default analytics tracking

packages/core/src/analytics/AnalyticsManager.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@ import {
66
getCachedEvents,
77
removeOldEvents,
88
} from "./storage";
9-
import type { AnalyticsPayload, AppInfo, TrackableEvent } from "./types";
10-
import { getEventId, getServerTime } from "./utils";
9+
import type {
10+
AnalyticsPayload,
11+
AppInfo,
12+
Device,
13+
TrackableEvent,
14+
} from "./types";
15+
import { getDevice, getEventId, getServerTime } from "./utils";
1116

1217
export class AnalyticsManager {
1318
static _instance: AnalyticsManager;
@@ -22,6 +27,8 @@ export class AnalyticsManager {
2227

2328
cartridgeTag!: string;
2429

30+
device?: Device;
31+
2532
private constructor() {}
2633

2734
public static get instance(): AnalyticsManager {
@@ -37,11 +44,23 @@ export class AnalyticsManager {
3744
apiKey,
3845
app,
3946
cartridgeTag,
40-
}: { apiUri?: string; apiKey: string; app: AppInfo; cartridgeTag: string }) {
47+
device,
48+
}: {
49+
apiUri?: string;
50+
apiKey: string;
51+
app: AppInfo;
52+
cartridgeTag: string;
53+
device?: Device;
54+
}) {
4155
this.apiUri = apiUri;
4256
this.apiKey = apiKey;
4357
this.app = app;
4458
this.cartridgeTag = cartridgeTag;
59+
const defaultDevice = getDevice();
60+
this.device = {
61+
...defaultDevice,
62+
...device,
63+
};
4564
this.initialized = true;
4665

4766
setInterval(
@@ -96,6 +115,7 @@ export class AnalyticsManager {
96115
time_server: serverTime,
97116
time_local: localTime,
98117
app: this.app,
118+
device: this.device,
99119
tdk_flavour: "tdk-js",
100120
tdk_version: pjson.version,
101121
};

packages/core/src/analytics/storage.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { v4 as uuidv4 } from "uuid";
12
import type { AnalyticsPayload } from "./types";
23

34
function getCachedEventIds(): string[] {
@@ -69,3 +70,14 @@ export function removeOldEvents(): void {
6970
}
7071
localStorage.setItem("tdk-analytics-event-ids", JSON.stringify(eventIds));
7172
}
73+
74+
export function getDeviceUniqueId(): string {
75+
const deviceUIDKey = "tdk-analytics-device-unique-id";
76+
const stored = localStorage.getItem(deviceUIDKey);
77+
if (stored) {
78+
return stored;
79+
}
80+
const newUID = `device-${uuidv4()}`;
81+
localStorage.setItem(deviceUIDKey, newUID);
82+
return newUID;
83+
}

packages/core/src/analytics/utils.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { v4 as uuidv4 } from "uuid";
2+
import { getDeviceUniqueId } from "./storage";
3+
import type { Device } from "./types";
24

35
export async function getServerTime(apiUri: string): Promise<string> {
46
const result = await fetch(`${apiUri}/utils/time-unix`);
@@ -12,3 +14,25 @@ export async function getServerTime(apiUri: string): Promise<string> {
1214
export function getEventId(): string {
1315
return `event-${uuidv4()}`;
1416
}
17+
18+
export function getDevice(): Device {
19+
if (typeof window !== "undefined" && window.navigator) {
20+
const deviceID = getDeviceUniqueId();
21+
return {
22+
device_unique_id: deviceID,
23+
device_os: navigator.userAgent.includes("Windows")
24+
? "Windows"
25+
: navigator.userAgent.includes("Mac")
26+
? "MacOS"
27+
: navigator.userAgent.includes("Linux")
28+
? "Linux"
29+
: "Unknown",
30+
};
31+
}
32+
const os = require("node:os");
33+
return {
34+
device_name: os.hostname(),
35+
device_model: `${os.type()} ${os.release()}`,
36+
device_os: os.platform(),
37+
};
38+
}

packages/react/src/contexts/treasure.tsx

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ import { startUserSessionViaLauncher } from "@treasure-dev/launcher";
3939
import { useLauncher } from "../hooks/useLauncher";
4040
import { i18n } from "../i18n";
4141
import type { AnalyticsEvent, Config, ContextValues } from "../types";
42+
import {
43+
EVT_TREASURECONNECT_CONNECTED,
44+
EVT_TREASURECONNECT_DISCONNECTED,
45+
} from "../utils/defaultAnalytics";
4246
import {
4347
clearStoredAuthToken,
4448
getStoredAuthToken,
@@ -113,6 +117,7 @@ const TreasureProviderInner = ({
113117
apiKey: analyticsOptions.apiKey,
114118
app: analyticsOptions.appInfo,
115119
cartridgeTag: analyticsOptions.cartridgeTag,
120+
device: analyticsOptions.device,
116121
});
117122

118123
return AnalyticsManager.instance;
@@ -126,10 +131,11 @@ const TreasureProviderInner = ({
126131
);
127132
}
128133

129-
const address = event.address ?? user?.address;
134+
let address = event.address ?? user?.address;
130135

131136
if (address === undefined && event.userId === undefined) {
132-
throw new Error("Cannot track event without userId or address");
137+
address = "";
138+
event.userId = "";
133139
}
134140

135141
// After the previous check one must be non-null so this works
@@ -149,7 +155,7 @@ const TreasureProviderInner = ({
149155
const trackableEvent: TrackableEvent = {
150156
...playerId,
151157
name: event.name,
152-
properties: event.properties,
158+
properties: event.properties ?? {},
153159
};
154160
return analyticsManager.trackCustomEvent(trackableEvent);
155161
},
@@ -175,6 +181,12 @@ const TreasureProviderInner = ({
175181
});
176182

177183
const logOut = () => {
184+
trackCustomEvent({
185+
name: EVT_TREASURECONNECT_DISCONNECTED,
186+
properties: {
187+
isUsingTreasureLauncher,
188+
},
189+
});
178190
setUser(undefined);
179191
tdk.clearAuthToken();
180192
clearStoredAuthToken();
@@ -227,6 +239,13 @@ const TreasureProviderInner = ({
227239
setUser(nextUser);
228240
setStoredAuthToken(nextAuthToken as string);
229241

242+
trackCustomEvent({
243+
name: EVT_TREASURECONNECT_CONNECTED,
244+
properties: {
245+
isUsingTreasureLauncher,
246+
},
247+
});
248+
230249
// Trigger completion callback
231250
onConnect?.(nextUser);
232251
return nextUser;

packages/react/src/hooks/useConnect.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ import {
1616
} from "../components/connect/ConnectModal";
1717
import { useTreasure } from "../contexts/treasure";
1818
import { getLocaleId } from "../i18n";
19+
import {
20+
EVT_TREASURECONNECT_UI_ACCOUNT,
21+
EVT_TREASURECONNECT_UI_LOGIN,
22+
} from "../utils/defaultAnalytics";
1923

2024
export type Options = ConnectModalOptions & {
2125
supportedChainIds?: number[];
@@ -72,6 +76,7 @@ export const useConnect = (props?: Props) => {
7276
setRootElement,
7377
isUsingTreasureLauncher,
7478
openLauncherAccountModal,
79+
trackCustomEvent,
7580
} = useTreasure();
7681
const { open: openWalletDetailsModal } = useWalletDetailsModal();
7782
const {
@@ -94,6 +99,11 @@ export const useConnect = (props?: Props) => {
9499
);
95100
return;
96101
}
102+
103+
trackCustomEvent({
104+
name: EVT_TREASURECONNECT_UI_LOGIN,
105+
});
106+
97107
setRootElement(
98108
<ConnectModal
99109
open
@@ -105,6 +115,13 @@ export const useConnect = (props?: Props) => {
105115
};
106116

107117
const openAccountModal = () => {
118+
trackCustomEvent({
119+
name: EVT_TREASURECONNECT_UI_ACCOUNT,
120+
properties: {
121+
isUsingTreasureLauncher,
122+
},
123+
});
124+
108125
if (isUsingTreasureLauncher) {
109126
openLauncherAccountModal(connectModalSize);
110127
return;

packages/react/src/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type {
22
AddressString,
33
AppInfo,
44
Contract,
5+
Device,
56
EcosystemIdString,
67
PropertyValue,
78
SessionOptions,
@@ -18,7 +19,7 @@ export type AnalyticsEvent = {
1819
name: string;
1920
userId?: string;
2021
address?: string;
21-
properties: { [key: string]: PropertyValue | PropertyValue[] };
22+
properties?: { [key: string]: PropertyValue | PropertyValue[] };
2223
};
2324

2425
type LauncherOptions = {
@@ -30,6 +31,7 @@ type AnalyticsOptions = {
3031
apiKey: string;
3132
appInfo: AppInfo;
3233
cartridgeTag: string;
34+
device?: Device;
3335
};
3436

3537
export type Config = {
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const EVT_TREASURECONNECT_CONNECTED = "tc_connected";
2+
export const EVT_TREASURECONNECT_DISCONNECTED = "tc_disconnected";
3+
export const EVT_TREASURECONNECT_UI_LOGIN = "tc_ui_login";
4+
export const EVT_TREASURECONNECT_UI_ACCOUNT = "tc_ui_account";

0 commit comments

Comments
 (0)