@formo/analytics 1.11.8 → 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 +1 -1
- 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 +158 -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 +158 -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 -221
- 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,19 +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
|
-
|
|
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
|
+
);
|
|
163
163
|
|
|
164
164
|
if (response.status >= 200 && response.status < 300) {
|
|
165
165
|
console.log('Event sent successfully:', action);
|
|
@@ -192,72 +192,6 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
192
192
|
await sendRequest();
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
-
// Function to mask sensitive data in the payload
|
|
196
|
-
private maskSensitiveData(
|
|
197
|
-
data: string | undefined | null
|
|
198
|
-
): Record<string, any> | null {
|
|
199
|
-
// Check if data is null or undefined
|
|
200
|
-
if (data === null || data === undefined) {
|
|
201
|
-
console.warn('Data is null or undefined, returning null');
|
|
202
|
-
return null;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// Check if data is a string; if so, parse it to an object
|
|
206
|
-
if (typeof data === 'string') {
|
|
207
|
-
let parsedData: Record<string, any>;
|
|
208
|
-
try {
|
|
209
|
-
parsedData = JSON.parse(data);
|
|
210
|
-
} catch (error) {
|
|
211
|
-
console.error('Failed to parse JSON:', error);
|
|
212
|
-
return null; // Return null if parsing fails
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const sensitiveFields = [
|
|
216
|
-
'username',
|
|
217
|
-
'user',
|
|
218
|
-
'user_id',
|
|
219
|
-
'password',
|
|
220
|
-
'email',
|
|
221
|
-
'phone',
|
|
222
|
-
];
|
|
223
|
-
|
|
224
|
-
// Create a new object to store masked data
|
|
225
|
-
const maskedData = { ...parsedData };
|
|
226
|
-
|
|
227
|
-
// Mask sensitive fields
|
|
228
|
-
sensitiveFields.forEach((field) => {
|
|
229
|
-
if (field in maskedData) {
|
|
230
|
-
maskedData[field] = '********'; // Replace value with masked string
|
|
231
|
-
}
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
return maskedData; // Return the new object with masked fields
|
|
235
|
-
} else if (typeof data === 'object') {
|
|
236
|
-
// If data is already an object, handle masking directly
|
|
237
|
-
const sensitiveFields = [
|
|
238
|
-
'username',
|
|
239
|
-
'user',
|
|
240
|
-
'user_id',
|
|
241
|
-
'password',
|
|
242
|
-
'email',
|
|
243
|
-
'phone',
|
|
244
|
-
];
|
|
245
|
-
|
|
246
|
-
const maskedData = { ...(data as Record<string, any>) };
|
|
247
|
-
|
|
248
|
-
// Mask sensitive fields
|
|
249
|
-
sensitiveFields.forEach((field) => {
|
|
250
|
-
if (field in maskedData) {
|
|
251
|
-
maskedData[field] = '********'; // Replace value with masked string
|
|
252
|
-
}
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
return maskedData; // Return the new object with masked fields
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
return data;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
195
|
// Function to track page hits
|
|
262
196
|
private trackPageHit() {
|
|
263
197
|
if (window.__nightmare || window.navigator.webdriver || window.Cypress)
|
|
@@ -281,6 +215,7 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
281
215
|
const params = new URLSearchParams(url.search);
|
|
282
216
|
this.trackEvent(Event.PAGE, {
|
|
283
217
|
'user-agent': window.navigator.userAgent,
|
|
218
|
+
address: this.currentConnectedAddress,
|
|
284
219
|
locale: language,
|
|
285
220
|
location: location,
|
|
286
221
|
referrer: document.referrer,
|
|
@@ -296,11 +231,12 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
296
231
|
|
|
297
232
|
private trackProvider(provider: EIP1193Provider) {
|
|
298
233
|
if (provider === this._provider) {
|
|
234
|
+
console.log('Provider already tracked.');
|
|
299
235
|
return;
|
|
300
236
|
}
|
|
301
237
|
|
|
302
238
|
this.currentChainId = undefined;
|
|
303
|
-
this.
|
|
239
|
+
this.currentConnectedAddress = undefined;
|
|
304
240
|
|
|
305
241
|
if (this._provider) {
|
|
306
242
|
const eventNames = Object.keys(this._registeredProviderListeners);
|
|
@@ -313,13 +249,84 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
313
249
|
}
|
|
314
250
|
}
|
|
315
251
|
|
|
252
|
+
console.log('Tracking new provider:', provider);
|
|
316
253
|
this._provider = provider;
|
|
317
254
|
|
|
318
255
|
this.getCurrentWallet();
|
|
319
|
-
this.
|
|
256
|
+
this.registerAddressChangedListener();
|
|
320
257
|
this.registerChainChangedListener();
|
|
321
258
|
}
|
|
322
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
|
+
|
|
323
330
|
private registerChainChangedListener() {
|
|
324
331
|
const listener = (...args: unknown[]) =>
|
|
325
332
|
this.onChainChanged(args[0] as string);
|
|
@@ -327,25 +334,50 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
327
334
|
this._registeredProviderListeners['chainChanged'] = listener;
|
|
328
335
|
}
|
|
329
336
|
|
|
330
|
-
private
|
|
331
|
-
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
|
|
332
351
|
return;
|
|
352
|
+
} else {
|
|
353
|
+
this.currentConnectedAddress = address;
|
|
333
354
|
}
|
|
334
355
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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,
|
|
338
370
|
};
|
|
339
371
|
this.currentChainId = undefined;
|
|
340
|
-
this.
|
|
372
|
+
this.currentConnectedAddress = undefined;
|
|
341
373
|
this.clearWalletAddress();
|
|
342
374
|
|
|
343
|
-
return this.trackEvent(Event.DISCONNECT,
|
|
375
|
+
return this.trackEvent(Event.DISCONNECT, payload);
|
|
344
376
|
}
|
|
345
377
|
|
|
346
378
|
private async onChainChanged(chainIdHex: string) {
|
|
347
379
|
this.currentChainId = parseInt(chainIdHex).toString();
|
|
348
|
-
if (!this.
|
|
380
|
+
if (!this.currentConnectedAddress) {
|
|
349
381
|
if (!this.provider) {
|
|
350
382
|
console.error(
|
|
351
383
|
'error',
|
|
@@ -366,7 +398,7 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
366
398
|
return;
|
|
367
399
|
}
|
|
368
400
|
|
|
369
|
-
this.
|
|
401
|
+
this.currentConnectedAddress = res[0];
|
|
370
402
|
} catch (err) {
|
|
371
403
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
372
404
|
if ((err as any).code !== 4001) {
|
|
@@ -383,90 +415,10 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
383
415
|
|
|
384
416
|
return this.chain({
|
|
385
417
|
chainId: this.currentChainId,
|
|
386
|
-
|
|
418
|
+
address: this.currentConnectedAddress,
|
|
387
419
|
});
|
|
388
420
|
}
|
|
389
421
|
|
|
390
|
-
private async onAccountsChanged(accounts: string[]) {
|
|
391
|
-
if (accounts.length > 0) {
|
|
392
|
-
const newAccount = accounts[0];
|
|
393
|
-
if (newAccount !== this.currentConnectedAccount) {
|
|
394
|
-
this.handleAccountConnected(newAccount);
|
|
395
|
-
}
|
|
396
|
-
} else {
|
|
397
|
-
this.handleAccountDisconnected();
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
private registerAccountsChangedListener() {
|
|
402
|
-
const listener = (...args: unknown[]) =>
|
|
403
|
-
this.onAccountsChanged(args[0] as string[]);
|
|
404
|
-
|
|
405
|
-
this._provider?.on('accountsChanged', listener);
|
|
406
|
-
this._registeredProviderListeners['accountsChanged'] = listener;
|
|
407
|
-
|
|
408
|
-
const handleAccountDisconnected = this.handleAccountDisconnected.bind(this);
|
|
409
|
-
this._provider?.on('disconnect', handleAccountDisconnected);
|
|
410
|
-
this._registeredProviderListeners['disconnect'] = handleAccountDisconnected;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
private async getCurrentChainId(): Promise<string> {
|
|
414
|
-
if (!this.provider) {
|
|
415
|
-
console.error('FormoAnalytics::getCurrentChainId: provider not set');
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
const chainIdHex = await this.provider?.request<string>({
|
|
419
|
-
method: 'eth_chainId',
|
|
420
|
-
});
|
|
421
|
-
// Because we're connected, the chainId cannot be null
|
|
422
|
-
if (!chainIdHex) {
|
|
423
|
-
console.error(
|
|
424
|
-
`FormoAnalytics::getCurrentChainId: chainIdHex is: ${chainIdHex}`
|
|
425
|
-
);
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
return parseInt(chainIdHex as string, 16).toString();
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
private async handleAccountConnected(account: string) {
|
|
432
|
-
if (account === this.currentConnectedAccount) {
|
|
433
|
-
// We have already reported this account
|
|
434
|
-
return;
|
|
435
|
-
} else {
|
|
436
|
-
this.currentConnectedAccount = account;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
this.currentChainId = await this.getCurrentChainId();
|
|
440
|
-
|
|
441
|
-
this.connect({ account, chainId: this.currentChainId });
|
|
442
|
-
this.storeWalletAddress(account);
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
private async getCurrentWallet() {
|
|
446
|
-
if (!this.provider) {
|
|
447
|
-
console.warn('FormoAnalytics::getCurrentWallet: the provider is not set');
|
|
448
|
-
return;
|
|
449
|
-
}
|
|
450
|
-
const sessionData = sessionStorage.getItem(this.walletAddressSessionKey);
|
|
451
|
-
|
|
452
|
-
if (!sessionData) {
|
|
453
|
-
return null;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
const parsedData = JSON.parse(sessionData);
|
|
457
|
-
const sessionExpiry = 30 * 60 * 1000; // 30 minutes
|
|
458
|
-
const currentTime = Date.now();
|
|
459
|
-
|
|
460
|
-
if (currentTime - parsedData.timestamp > sessionExpiry) {
|
|
461
|
-
console.warn('Session expired. Ignoring wallet address.');
|
|
462
|
-
sessionStorage.removeItem(this.walletAddressSessionKey); // Clear expired session data
|
|
463
|
-
return '';
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
this.handleAccountConnected(parsedData.address);
|
|
467
|
-
return parsedData.address || '';
|
|
468
|
-
}
|
|
469
|
-
|
|
470
422
|
/**
|
|
471
423
|
* Stores the wallet address in session storage when connected.
|
|
472
424
|
* @param address - The wallet address to store.
|
|
@@ -482,7 +434,10 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
482
434
|
timestamp: Date.now(),
|
|
483
435
|
};
|
|
484
436
|
|
|
485
|
-
sessionStorage.setItem(
|
|
437
|
+
sessionStorage.setItem(
|
|
438
|
+
this.walletAddressSessionKey,
|
|
439
|
+
JSON.stringify(sessionData)
|
|
440
|
+
);
|
|
486
441
|
}
|
|
487
442
|
|
|
488
443
|
/**
|
|
@@ -492,54 +447,55 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
492
447
|
sessionStorage.removeItem(this.walletAddressSessionKey);
|
|
493
448
|
}
|
|
494
449
|
|
|
495
|
-
|
|
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 }) {
|
|
496
456
|
if (!chainId) {
|
|
497
|
-
throw new Error('FormoAnalytics::connect:
|
|
457
|
+
throw new Error('FormoAnalytics::connect: chain ID cannot be empty');
|
|
498
458
|
}
|
|
499
|
-
if (!
|
|
500
|
-
throw new Error('FormoAnalytics::connect:
|
|
459
|
+
if (!address) {
|
|
460
|
+
throw new Error('FormoAnalytics::connect: address cannot be empty');
|
|
501
461
|
}
|
|
502
462
|
|
|
503
463
|
this.currentChainId = chainId.toString();
|
|
504
|
-
this.
|
|
464
|
+
this.currentConnectedAddress = address;
|
|
505
465
|
|
|
506
466
|
return this.trackEvent(Event.CONNECT, {
|
|
507
|
-
chainId,
|
|
508
|
-
address:
|
|
467
|
+
chain_id: chainId,
|
|
468
|
+
address: address,
|
|
509
469
|
});
|
|
510
470
|
}
|
|
511
471
|
|
|
512
|
-
disconnect(
|
|
513
|
-
const
|
|
514
|
-
if (!
|
|
472
|
+
disconnect(params?: { chainId?: ChainID; address?: string }) {
|
|
473
|
+
const address = params?.address || this.currentConnectedAddress;
|
|
474
|
+
if (!address) {
|
|
515
475
|
// We have most likely already reported this disconnection with the automatic
|
|
516
476
|
// `disconnect` detection
|
|
517
477
|
return;
|
|
518
478
|
}
|
|
519
479
|
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
...(chainId && { chainId }),
|
|
480
|
+
const payload = {
|
|
481
|
+
chain_id: params?.chainId || this.currentChainId,
|
|
482
|
+
address,
|
|
524
483
|
};
|
|
525
|
-
|
|
526
484
|
this.currentChainId = undefined;
|
|
527
|
-
this.
|
|
485
|
+
this.currentConnectedAddress = undefined;
|
|
528
486
|
|
|
529
|
-
return this.trackEvent(Event.DISCONNECT,
|
|
487
|
+
return this.trackEvent(Event.DISCONNECT, payload);
|
|
530
488
|
}
|
|
531
489
|
|
|
532
|
-
chain({ chainId,
|
|
490
|
+
chain({ chainId, address }: { chainId: ChainID; address?: string }) {
|
|
533
491
|
if (!chainId || Number(chainId) === 0) {
|
|
534
492
|
throw new Error('FormoAnalytics::chain: chainId cannot be empty or 0');
|
|
535
493
|
}
|
|
536
|
-
|
|
537
|
-
if (!account && !this.currentConnectedAccount) {
|
|
494
|
+
if (!address && !this.currentConnectedAddress) {
|
|
538
495
|
throw new Error(
|
|
539
|
-
'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'
|
|
540
497
|
);
|
|
541
498
|
}
|
|
542
|
-
|
|
543
499
|
if (isNaN(Number(chainId))) {
|
|
544
500
|
throw new Error(
|
|
545
501
|
'FormoAnalytics::chain: chainId must be a valid hex or decimal number'
|
|
@@ -549,20 +505,11 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
549
505
|
this.currentChainId = chainId.toString();
|
|
550
506
|
|
|
551
507
|
return this.trackEvent(Event.CHAIN_CHANGED, {
|
|
552
|
-
chainId,
|
|
553
|
-
|
|
508
|
+
chain_id: chainId,
|
|
509
|
+
address: address || this.currentConnectedAddress,
|
|
554
510
|
});
|
|
555
511
|
}
|
|
556
512
|
|
|
557
|
-
init(apiKey: string, options: Options): Promise<FormoAnalytics> {
|
|
558
|
-
const instance = new FormoAnalytics(apiKey, options);
|
|
559
|
-
return Promise.resolve(instance);
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
identify(userData: any) {
|
|
563
|
-
this.identifyUser(userData);
|
|
564
|
-
}
|
|
565
|
-
|
|
566
513
|
page() {
|
|
567
514
|
this.trackPageHit();
|
|
568
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 {
|