@formo/analytics 1.11.7 → 1.11.9-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -8
- package/dist/223.index.umd.min.js +125 -0
- package/dist/223.index.umd.min.js.map +1 -0
- package/dist/cjs/src/FormoAnalytics.d.ts +26 -33
- package/dist/cjs/src/FormoAnalytics.d.ts.map +1 -1
- package/dist/cjs/src/FormoAnalytics.js +157 -205
- package/dist/cjs/src/FormoAnalytics.js.map +1 -1
- package/dist/cjs/src/FormoAnalyticsProvider.d.ts.map +1 -1
- package/dist/cjs/src/FormoAnalyticsProvider.js.map +1 -1
- package/dist/cjs/src/types/base.d.ts +1 -1
- package/dist/cjs/src/types/base.d.ts.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/src/FormoAnalytics.d.ts +26 -33
- package/dist/esm/src/FormoAnalytics.d.ts.map +1 -1
- package/dist/esm/src/FormoAnalytics.js +157 -205
- package/dist/esm/src/FormoAnalytics.js.map +1 -1
- package/dist/esm/src/FormoAnalyticsProvider.d.ts.map +1 -1
- package/dist/esm/src/FormoAnalyticsProvider.js.map +1 -1
- package/dist/esm/src/types/base.d.ts +1 -1
- package/dist/esm/src/types/base.d.ts.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/index.umd.min.js +1 -1
- package/dist/index.umd.min.js.map +1 -1
- package/package.json +2 -2
- package/src/FormoAnalytics.ts +168 -222
- package/src/FormoAnalyticsProvider.tsx +0 -3
- package/src/types/base.ts +1 -1
- package/dist/387.index.umd.min.js +0 -125
- package/dist/387.index.umd.min.js.map +0 -1
- package/dist/cjs/src/utils/index.d.ts +0 -2
- package/dist/cjs/src/utils/index.d.ts.map +0 -1
- package/dist/cjs/src/utils/index.js +0 -18
- package/dist/cjs/src/utils/index.js.map +0 -1
- package/dist/cjs/src/utils/isNotEmptyObject.d.ts +0 -2
- package/dist/cjs/src/utils/isNotEmptyObject.d.ts.map +0 -1
- package/dist/cjs/src/utils/isNotEmptyObject.js +0 -9
- package/dist/cjs/src/utils/isNotEmptyObject.js.map +0 -1
- package/dist/esm/src/utils/index.d.ts +0 -2
- package/dist/esm/src/utils/index.d.ts.map +0 -1
- package/dist/esm/src/utils/index.js +0 -2
- package/dist/esm/src/utils/index.js.map +0 -1
- package/dist/esm/src/utils/isNotEmptyObject.d.ts +0 -2
- package/dist/esm/src/utils/isNotEmptyObject.d.ts.map +0 -1
- package/dist/esm/src/utils/isNotEmptyObject.js +0 -6
- package/dist/esm/src/utils/isNotEmptyObject.js.map +0 -1
- package/src/utils/index.ts +0 -1
- package/src/utils/isNotEmptyObject.ts +0 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@formo/analytics",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.9-alpha.1",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/getformo/sdk.git"
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"sinon-chai": "^3.7.0",
|
|
64
64
|
"ts-loader": "^9.3.1",
|
|
65
65
|
"ts-node": "^10.8.2",
|
|
66
|
-
"typescript": "^5.
|
|
66
|
+
"typescript": "^5.7.2",
|
|
67
67
|
"webpack": "^5.74.0",
|
|
68
68
|
"webpack-cli": "^4.10.0"
|
|
69
69
|
},
|
package/src/FormoAnalytics.ts
CHANGED
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
SESSION_STORAGE_ID_KEY,
|
|
6
6
|
Event,
|
|
7
7
|
} from './constants';
|
|
8
|
-
import { isNotEmpty } from './utils';
|
|
9
8
|
import { H } from 'highlight.run';
|
|
10
9
|
import { ChainID, EIP1193Provider, Options } from './types';
|
|
11
10
|
|
|
@@ -13,12 +12,10 @@ interface IFormoAnalytics {
|
|
|
13
12
|
/**
|
|
14
13
|
* Initializes the FormoAnalytics instance with the provided API key and project ID.
|
|
15
14
|
*/
|
|
16
|
-
init(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
*/
|
|
21
|
-
identify(userData: Record<string, any>): void;
|
|
15
|
+
init(
|
|
16
|
+
apiKey: string,
|
|
17
|
+
options?: Options
|
|
18
|
+
): Promise<FormoAnalytics>;
|
|
22
19
|
|
|
23
20
|
/**
|
|
24
21
|
* Tracks page visit events.
|
|
@@ -28,22 +25,22 @@ interface IFormoAnalytics {
|
|
|
28
25
|
/**
|
|
29
26
|
* Connects to a wallet with the specified chain ID and address.
|
|
30
27
|
*/
|
|
31
|
-
connect(params: {
|
|
28
|
+
connect(params: { chainId: ChainID; address: string }): Promise<void>;
|
|
32
29
|
|
|
33
30
|
/**
|
|
34
31
|
* Disconnects the current wallet and clears the session information.
|
|
35
32
|
*/
|
|
36
|
-
disconnect(
|
|
33
|
+
disconnect(params?: { chainId?: ChainID; address?: string }): void;
|
|
37
34
|
|
|
38
35
|
/**
|
|
39
|
-
*
|
|
36
|
+
* Switches the blockchain chain context and optionally logs additional params.
|
|
40
37
|
*/
|
|
41
|
-
|
|
38
|
+
chain(params: { chainId: ChainID; address?: string }): void;
|
|
42
39
|
|
|
43
40
|
/**
|
|
44
|
-
*
|
|
41
|
+
* Tracks a specific event with a name and associated data.
|
|
45
42
|
*/
|
|
46
|
-
|
|
43
|
+
track(eventName: string, eventData: Record<string, any>): void;
|
|
47
44
|
}
|
|
48
45
|
interface Config {
|
|
49
46
|
token: string;
|
|
@@ -61,7 +58,7 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
61
58
|
private timezoneToCountry: Record<string, string> = COUNTRY_LIST;
|
|
62
59
|
|
|
63
60
|
currentChainId?: string | null;
|
|
64
|
-
|
|
61
|
+
currentConnectedAddress?: string;
|
|
65
62
|
|
|
66
63
|
private constructor(
|
|
67
64
|
public readonly apiKey: string,
|
|
@@ -71,7 +68,8 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
71
68
|
token: this.apiKey,
|
|
72
69
|
};
|
|
73
70
|
|
|
74
|
-
const provider =
|
|
71
|
+
const provider =
|
|
72
|
+
window?.ethereum || window.web3?.currentProvider || options?.provider;
|
|
75
73
|
if (provider) {
|
|
76
74
|
this.trackProvider(provider);
|
|
77
75
|
}
|
|
@@ -79,7 +77,7 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
79
77
|
|
|
80
78
|
static async init(
|
|
81
79
|
apiKey: string,
|
|
82
|
-
options
|
|
80
|
+
options?: Options
|
|
83
81
|
): Promise<FormoAnalytics> {
|
|
84
82
|
const config = {
|
|
85
83
|
token: apiKey,
|
|
@@ -94,10 +92,6 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
94
92
|
return this._provider;
|
|
95
93
|
}
|
|
96
94
|
|
|
97
|
-
private identifyUser(userData: any) {
|
|
98
|
-
this.trackEvent(Event.IDENTIFY, userData);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
95
|
private getSessionId() {
|
|
102
96
|
const existingSessionId = this.getCookieValue(this.sessionIdKey);
|
|
103
97
|
|
|
@@ -116,7 +110,9 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
116
110
|
// Function to set the session cookie
|
|
117
111
|
private setSessionCookie(): void {
|
|
118
112
|
const sessionId = this.getSessionId();
|
|
119
|
-
let cookieValue = `${
|
|
113
|
+
let cookieValue = `${
|
|
114
|
+
this.sessionIdKey
|
|
115
|
+
}=${sessionId}; Max-Age=1800; path=/; secure; domain=${this.getOrigin()}`;
|
|
120
116
|
document.cookie = cookieValue;
|
|
121
117
|
}
|
|
122
118
|
|
|
@@ -147,20 +143,23 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
147
143
|
address: address,
|
|
148
144
|
session_id: this.getSessionId(),
|
|
149
145
|
timestamp: new Date().toISOString(),
|
|
150
|
-
action
|
|
146
|
+
action,
|
|
151
147
|
version: '1',
|
|
152
|
-
payload
|
|
148
|
+
payload,
|
|
153
149
|
};
|
|
154
150
|
|
|
155
151
|
const sendRequest = async (): Promise<void> => {
|
|
156
152
|
try {
|
|
157
|
-
const response = await axios.post(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
153
|
+
const response = await axios.post(
|
|
154
|
+
EVENTS_API_URL,
|
|
155
|
+
JSON.stringify(requestData),
|
|
156
|
+
{
|
|
157
|
+
headers: {
|
|
158
|
+
'Content-Type': 'application/json',
|
|
159
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
160
|
+
},
|
|
161
|
+
}
|
|
162
|
+
);
|
|
164
163
|
|
|
165
164
|
if (response.status >= 200 && response.status < 300) {
|
|
166
165
|
console.log('Event sent successfully:', action);
|
|
@@ -193,72 +192,6 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
193
192
|
await sendRequest();
|
|
194
193
|
}
|
|
195
194
|
|
|
196
|
-
// Function to mask sensitive data in the payload
|
|
197
|
-
private maskSensitiveData(
|
|
198
|
-
data: string | undefined | null
|
|
199
|
-
): Record<string, any> | null {
|
|
200
|
-
// Check if data is null or undefined
|
|
201
|
-
if (data === null || data === undefined) {
|
|
202
|
-
console.warn('Data is null or undefined, returning null');
|
|
203
|
-
return null;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Check if data is a string; if so, parse it to an object
|
|
207
|
-
if (typeof data === 'string') {
|
|
208
|
-
let parsedData: Record<string, any>;
|
|
209
|
-
try {
|
|
210
|
-
parsedData = JSON.parse(data);
|
|
211
|
-
} catch (error) {
|
|
212
|
-
console.error('Failed to parse JSON:', error);
|
|
213
|
-
return null; // Return null if parsing fails
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const sensitiveFields = [
|
|
217
|
-
'username',
|
|
218
|
-
'user',
|
|
219
|
-
'user_id',
|
|
220
|
-
'password',
|
|
221
|
-
'email',
|
|
222
|
-
'phone',
|
|
223
|
-
];
|
|
224
|
-
|
|
225
|
-
// Create a new object to store masked data
|
|
226
|
-
const maskedData = { ...parsedData };
|
|
227
|
-
|
|
228
|
-
// Mask sensitive fields
|
|
229
|
-
sensitiveFields.forEach((field) => {
|
|
230
|
-
if (field in maskedData) {
|
|
231
|
-
maskedData[field] = '********'; // Replace value with masked string
|
|
232
|
-
}
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
return maskedData; // Return the new object with masked fields
|
|
236
|
-
} else if (typeof data === 'object') {
|
|
237
|
-
// If data is already an object, handle masking directly
|
|
238
|
-
const sensitiveFields = [
|
|
239
|
-
'username',
|
|
240
|
-
'user',
|
|
241
|
-
'user_id',
|
|
242
|
-
'password',
|
|
243
|
-
'email',
|
|
244
|
-
'phone',
|
|
245
|
-
];
|
|
246
|
-
|
|
247
|
-
const maskedData = { ...(data as Record<string, any>) };
|
|
248
|
-
|
|
249
|
-
// Mask sensitive fields
|
|
250
|
-
sensitiveFields.forEach((field) => {
|
|
251
|
-
if (field in maskedData) {
|
|
252
|
-
maskedData[field] = '********'; // Replace value with masked string
|
|
253
|
-
}
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
return maskedData; // Return the new object with masked fields
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
return data;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
195
|
// Function to track page hits
|
|
263
196
|
private trackPageHit() {
|
|
264
197
|
if (window.__nightmare || window.navigator.webdriver || window.Cypress)
|
|
@@ -282,6 +215,7 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
282
215
|
const params = new URLSearchParams(url.search);
|
|
283
216
|
this.trackEvent(Event.PAGE, {
|
|
284
217
|
'user-agent': window.navigator.userAgent,
|
|
218
|
+
address: this.currentConnectedAddress,
|
|
285
219
|
locale: language,
|
|
286
220
|
location: location,
|
|
287
221
|
referrer: document.referrer,
|
|
@@ -297,11 +231,12 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
297
231
|
|
|
298
232
|
private trackProvider(provider: EIP1193Provider) {
|
|
299
233
|
if (provider === this._provider) {
|
|
234
|
+
console.log('Provider already tracked.');
|
|
300
235
|
return;
|
|
301
236
|
}
|
|
302
237
|
|
|
303
238
|
this.currentChainId = undefined;
|
|
304
|
-
this.
|
|
239
|
+
this.currentConnectedAddress = undefined;
|
|
305
240
|
|
|
306
241
|
if (this._provider) {
|
|
307
242
|
const eventNames = Object.keys(this._registeredProviderListeners);
|
|
@@ -314,13 +249,84 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
314
249
|
}
|
|
315
250
|
}
|
|
316
251
|
|
|
252
|
+
console.log('Tracking new provider:', provider);
|
|
317
253
|
this._provider = provider;
|
|
318
254
|
|
|
319
255
|
this.getCurrentWallet();
|
|
320
|
-
this.
|
|
256
|
+
this.registerAddressChangedListener();
|
|
321
257
|
this.registerChainChangedListener();
|
|
322
258
|
}
|
|
323
259
|
|
|
260
|
+
private async getCurrentWallet() {
|
|
261
|
+
if (!this.provider) {
|
|
262
|
+
console.warn('FormoAnalytics::getCurrentWallet: the provider is not set');
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const sessionData = sessionStorage.getItem(this.walletAddressSessionKey);
|
|
267
|
+
|
|
268
|
+
if (!sessionData) {
|
|
269
|
+
console.warn(
|
|
270
|
+
'Session data missing. Attempting to fetch address from provider.'
|
|
271
|
+
);
|
|
272
|
+
try {
|
|
273
|
+
const accounts = await this.provider.request<string[]>({
|
|
274
|
+
method: 'eth_accounts',
|
|
275
|
+
});
|
|
276
|
+
if (accounts && accounts.length > 0) {
|
|
277
|
+
const address = accounts[0];
|
|
278
|
+
this.storeWalletAddress(address);
|
|
279
|
+
return address;
|
|
280
|
+
}
|
|
281
|
+
} catch (err) {
|
|
282
|
+
console.error('Failed to fetch accounts from provider:', err);
|
|
283
|
+
}
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const parsedData = JSON.parse(sessionData);
|
|
288
|
+
const sessionExpiry = 30 * 60 * 1000; // 30 minutes
|
|
289
|
+
const currentTime = Date.now();
|
|
290
|
+
|
|
291
|
+
if (currentTime - parsedData.timestamp > sessionExpiry) {
|
|
292
|
+
console.warn('Session expired. Ignoring wallet address.');
|
|
293
|
+
sessionStorage.removeItem(this.walletAddressSessionKey); // Clear expired session data
|
|
294
|
+
return '';
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
this.onAddressConnected(parsedData.address);
|
|
298
|
+
return parsedData.address || '';
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
private async getCurrentChainId(): Promise<string> {
|
|
302
|
+
if (!this.provider) {
|
|
303
|
+
console.error('FormoAnalytics::getCurrentChainId: provider not set');
|
|
304
|
+
}
|
|
305
|
+
const chainIdHex = await this.provider?.request<string>({
|
|
306
|
+
method: 'eth_chainId',
|
|
307
|
+
});
|
|
308
|
+
// Because we're connected, the chainId cannot be null
|
|
309
|
+
if (!chainIdHex) {
|
|
310
|
+
console.error(
|
|
311
|
+
`FormoAnalytics::getCurrentChainId: chainIdHex is: ${chainIdHex}`
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return parseInt(chainIdHex as string, 16).toString();
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
private registerAddressChangedListener() {
|
|
319
|
+
const listener = (...args: unknown[]) =>
|
|
320
|
+
this.onAddressChanged(args[0] as string[]);
|
|
321
|
+
|
|
322
|
+
this._provider?.on('accountsChanged', listener);
|
|
323
|
+
this._registeredProviderListeners['accountsChanged'] = listener;
|
|
324
|
+
|
|
325
|
+
const onAddressDisconnected = this.onAddressDisconnected.bind(this);
|
|
326
|
+
this._provider?.on('disconnect', onAddressDisconnected);
|
|
327
|
+
this._registeredProviderListeners['disconnect'] = onAddressDisconnected;
|
|
328
|
+
}
|
|
329
|
+
|
|
324
330
|
private registerChainChangedListener() {
|
|
325
331
|
const listener = (...args: unknown[]) =>
|
|
326
332
|
this.onChainChanged(args[0] as string);
|
|
@@ -328,25 +334,50 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
328
334
|
this._registeredProviderListeners['chainChanged'] = listener;
|
|
329
335
|
}
|
|
330
336
|
|
|
331
|
-
private
|
|
332
|
-
if (
|
|
337
|
+
private async onAddressChanged(addresses: string[]) {
|
|
338
|
+
if (addresses.length > 0) {
|
|
339
|
+
const newAccount = addresses[0];
|
|
340
|
+
if (newAccount !== this.currentConnectedAddress) {
|
|
341
|
+
this.onAddressConnected(newAccount);
|
|
342
|
+
}
|
|
343
|
+
} else {
|
|
344
|
+
this.onAddressDisconnected();
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
private async onAddressConnected(address: string) {
|
|
349
|
+
if (address === this.currentConnectedAddress) {
|
|
350
|
+
// We have already reported this address
|
|
333
351
|
return;
|
|
352
|
+
} else {
|
|
353
|
+
this.currentConnectedAddress = address;
|
|
334
354
|
}
|
|
335
355
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
356
|
+
this.currentChainId = await this.getCurrentChainId();
|
|
357
|
+
|
|
358
|
+
this.connect({ chainId: this.currentChainId, address });
|
|
359
|
+
this.storeWalletAddress(address);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
private onAddressDisconnected() {
|
|
363
|
+
if (!this.currentConnectedAddress) {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const payload = {
|
|
368
|
+
chain_id: this.currentChainId,
|
|
369
|
+
address: this.currentConnectedAddress,
|
|
339
370
|
};
|
|
340
371
|
this.currentChainId = undefined;
|
|
341
|
-
this.
|
|
372
|
+
this.currentConnectedAddress = undefined;
|
|
342
373
|
this.clearWalletAddress();
|
|
343
374
|
|
|
344
|
-
return this.trackEvent(Event.DISCONNECT,
|
|
375
|
+
return this.trackEvent(Event.DISCONNECT, payload);
|
|
345
376
|
}
|
|
346
377
|
|
|
347
378
|
private async onChainChanged(chainIdHex: string) {
|
|
348
379
|
this.currentChainId = parseInt(chainIdHex).toString();
|
|
349
|
-
if (!this.
|
|
380
|
+
if (!this.currentConnectedAddress) {
|
|
350
381
|
if (!this.provider) {
|
|
351
382
|
console.error(
|
|
352
383
|
'error',
|
|
@@ -367,7 +398,7 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
367
398
|
return;
|
|
368
399
|
}
|
|
369
400
|
|
|
370
|
-
this.
|
|
401
|
+
this.currentConnectedAddress = res[0];
|
|
371
402
|
} catch (err) {
|
|
372
403
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
373
404
|
if ((err as any).code !== 4001) {
|
|
@@ -384,90 +415,10 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
384
415
|
|
|
385
416
|
return this.chain({
|
|
386
417
|
chainId: this.currentChainId,
|
|
387
|
-
|
|
418
|
+
address: this.currentConnectedAddress,
|
|
388
419
|
});
|
|
389
420
|
}
|
|
390
421
|
|
|
391
|
-
private async onAccountsChanged(accounts: string[]) {
|
|
392
|
-
if (accounts.length > 0) {
|
|
393
|
-
const newAccount = accounts[0];
|
|
394
|
-
if (newAccount !== this.currentConnectedAccount) {
|
|
395
|
-
this.handleAccountConnected(newAccount);
|
|
396
|
-
}
|
|
397
|
-
} else {
|
|
398
|
-
this.handleAccountDisconnected();
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
private registerAccountsChangedListener() {
|
|
403
|
-
const listener = (...args: unknown[]) =>
|
|
404
|
-
this.onAccountsChanged(args[0] as string[]);
|
|
405
|
-
|
|
406
|
-
this._provider?.on('accountsChanged', listener);
|
|
407
|
-
this._registeredProviderListeners['accountsChanged'] = listener;
|
|
408
|
-
|
|
409
|
-
const handleAccountDisconnected = this.handleAccountDisconnected.bind(this);
|
|
410
|
-
this._provider?.on('disconnect', handleAccountDisconnected);
|
|
411
|
-
this._registeredProviderListeners['disconnect'] = handleAccountDisconnected;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
private async getCurrentChainId(): Promise<string> {
|
|
415
|
-
if (!this.provider) {
|
|
416
|
-
console.error('FormoAnalytics::getCurrentChainId: provider not set');
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
const chainIdHex = await this.provider?.request<string>({
|
|
420
|
-
method: 'eth_chainId',
|
|
421
|
-
});
|
|
422
|
-
// Because we're connected, the chainId cannot be null
|
|
423
|
-
if (!chainIdHex) {
|
|
424
|
-
console.error(
|
|
425
|
-
`FormoAnalytics::getCurrentChainId: chainIdHex is: ${chainIdHex}`
|
|
426
|
-
);
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
return parseInt(chainIdHex as string, 16).toString();
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
private async handleAccountConnected(account: string) {
|
|
433
|
-
if (account === this.currentConnectedAccount) {
|
|
434
|
-
// We have already reported this account
|
|
435
|
-
return;
|
|
436
|
-
} else {
|
|
437
|
-
this.currentConnectedAccount = account;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
this.currentChainId = await this.getCurrentChainId();
|
|
441
|
-
|
|
442
|
-
this.connect({ account, chainId: this.currentChainId });
|
|
443
|
-
this.storeWalletAddress(account);
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
private async getCurrentWallet() {
|
|
447
|
-
if (!this.provider) {
|
|
448
|
-
console.warn('FormoAnalytics::getCurrentWallet: the provider is not set');
|
|
449
|
-
return;
|
|
450
|
-
}
|
|
451
|
-
const sessionData = sessionStorage.getItem(this.walletAddressSessionKey);
|
|
452
|
-
|
|
453
|
-
if (!sessionData) {
|
|
454
|
-
return null;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
const parsedData = JSON.parse(sessionData);
|
|
458
|
-
const sessionExpiry = 30 * 60 * 1000; // 30 minutes
|
|
459
|
-
const currentTime = Date.now();
|
|
460
|
-
|
|
461
|
-
if (currentTime - parsedData.timestamp > sessionExpiry) {
|
|
462
|
-
console.warn('Session expired. Ignoring wallet address.');
|
|
463
|
-
sessionStorage.removeItem(this.walletAddressSessionKey); // Clear expired session data
|
|
464
|
-
return '';
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
this.handleAccountConnected(parsedData.address);
|
|
468
|
-
return parsedData.address || '';
|
|
469
|
-
}
|
|
470
|
-
|
|
471
422
|
/**
|
|
472
423
|
* Stores the wallet address in session storage when connected.
|
|
473
424
|
* @param address - The wallet address to store.
|
|
@@ -483,7 +434,10 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
483
434
|
timestamp: Date.now(),
|
|
484
435
|
};
|
|
485
436
|
|
|
486
|
-
sessionStorage.setItem(
|
|
437
|
+
sessionStorage.setItem(
|
|
438
|
+
this.walletAddressSessionKey,
|
|
439
|
+
JSON.stringify(sessionData)
|
|
440
|
+
);
|
|
487
441
|
}
|
|
488
442
|
|
|
489
443
|
/**
|
|
@@ -493,54 +447,55 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
493
447
|
sessionStorage.removeItem(this.walletAddressSessionKey);
|
|
494
448
|
}
|
|
495
449
|
|
|
496
|
-
|
|
450
|
+
init(apiKey: string, options: Options): Promise<FormoAnalytics> {
|
|
451
|
+
const instance = new FormoAnalytics(apiKey, options);
|
|
452
|
+
return Promise.resolve(instance);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
connect({ chainId, address }: { chainId: ChainID; address: string }) {
|
|
497
456
|
if (!chainId) {
|
|
498
|
-
throw new Error('FormoAnalytics::connect:
|
|
457
|
+
throw new Error('FormoAnalytics::connect: chain ID cannot be empty');
|
|
499
458
|
}
|
|
500
|
-
if (!
|
|
501
|
-
throw new Error('FormoAnalytics::connect:
|
|
459
|
+
if (!address) {
|
|
460
|
+
throw new Error('FormoAnalytics::connect: address cannot be empty');
|
|
502
461
|
}
|
|
503
462
|
|
|
504
463
|
this.currentChainId = chainId.toString();
|
|
505
|
-
this.
|
|
464
|
+
this.currentConnectedAddress = address;
|
|
506
465
|
|
|
507
466
|
return this.trackEvent(Event.CONNECT, {
|
|
508
|
-
chainId,
|
|
509
|
-
address:
|
|
467
|
+
chain_id: chainId,
|
|
468
|
+
address: address,
|
|
510
469
|
});
|
|
511
470
|
}
|
|
512
471
|
|
|
513
|
-
disconnect(
|
|
514
|
-
const
|
|
515
|
-
if (!
|
|
472
|
+
disconnect(params?: { chainId?: ChainID; address?: string }) {
|
|
473
|
+
const address = params?.address || this.currentConnectedAddress;
|
|
474
|
+
if (!address) {
|
|
516
475
|
// We have most likely already reported this disconnection with the automatic
|
|
517
476
|
// `disconnect` detection
|
|
518
477
|
return;
|
|
519
478
|
}
|
|
520
479
|
|
|
521
|
-
const
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
...(chainId && { chainId }),
|
|
480
|
+
const payload = {
|
|
481
|
+
chain_id: params?.chainId || this.currentChainId,
|
|
482
|
+
address,
|
|
525
483
|
};
|
|
526
|
-
|
|
527
484
|
this.currentChainId = undefined;
|
|
528
|
-
this.
|
|
485
|
+
this.currentConnectedAddress = undefined;
|
|
529
486
|
|
|
530
|
-
return this.trackEvent(Event.DISCONNECT,
|
|
487
|
+
return this.trackEvent(Event.DISCONNECT, payload);
|
|
531
488
|
}
|
|
532
489
|
|
|
533
|
-
chain({ chainId,
|
|
490
|
+
chain({ chainId, address }: { chainId: ChainID; address?: string }) {
|
|
534
491
|
if (!chainId || Number(chainId) === 0) {
|
|
535
492
|
throw new Error('FormoAnalytics::chain: chainId cannot be empty or 0');
|
|
536
493
|
}
|
|
537
|
-
|
|
538
|
-
if (!account && !this.currentConnectedAccount) {
|
|
494
|
+
if (!address && !this.currentConnectedAddress) {
|
|
539
495
|
throw new Error(
|
|
540
|
-
'FormoAnalytics::chain:
|
|
496
|
+
'FormoAnalytics::chain: address was empty and no previous address has been recorded. You can either pass an address or call connect() first'
|
|
541
497
|
);
|
|
542
498
|
}
|
|
543
|
-
|
|
544
499
|
if (isNaN(Number(chainId))) {
|
|
545
500
|
throw new Error(
|
|
546
501
|
'FormoAnalytics::chain: chainId must be a valid hex or decimal number'
|
|
@@ -550,20 +505,11 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
550
505
|
this.currentChainId = chainId.toString();
|
|
551
506
|
|
|
552
507
|
return this.trackEvent(Event.CHAIN_CHANGED, {
|
|
553
|
-
chainId,
|
|
554
|
-
|
|
508
|
+
chain_id: chainId,
|
|
509
|
+
address: address || this.currentConnectedAddress,
|
|
555
510
|
});
|
|
556
511
|
}
|
|
557
512
|
|
|
558
|
-
init(apiKey: string, options: Options): Promise<FormoAnalytics> {
|
|
559
|
-
const instance = new FormoAnalytics(apiKey, options);
|
|
560
|
-
return Promise.resolve(instance);
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
identify(userData: any) {
|
|
564
|
-
this.identifyUser(userData);
|
|
565
|
-
}
|
|
566
|
-
|
|
567
513
|
page() {
|
|
568
514
|
this.trackPageHit();
|
|
569
515
|
}
|
|
@@ -32,15 +32,12 @@ export const FormoAnalyticsProvider = ({
|
|
|
32
32
|
console.error('FormoAnalyticsProvider: No API key provided');
|
|
33
33
|
return;
|
|
34
34
|
}
|
|
35
|
-
|
|
36
35
|
if (disabled) {
|
|
37
36
|
console.warn('FormoAnalytics is disabled');
|
|
38
37
|
return;
|
|
39
38
|
}
|
|
40
|
-
|
|
41
39
|
if (initializedStartedRef.current) return;
|
|
42
40
|
initializedStartedRef.current = true;
|
|
43
|
-
|
|
44
41
|
// Initialize Highlight.run if project ID is available
|
|
45
42
|
if (HIGHLIGHT_PROJECT_ID) {
|
|
46
43
|
try {
|