@formo/analytics 1.11.8 → 1.11.9
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 +27 -33
- package/dist/cjs/src/FormoAnalytics.d.ts.map +1 -1
- package/dist/cjs/src/FormoAnalytics.js +162 -208
- package/dist/cjs/src/FormoAnalytics.js.map +1 -1
- package/dist/cjs/src/FormoAnalyticsProvider.d.ts +1 -1
- package/dist/cjs/src/FormoAnalyticsProvider.d.ts.map +1 -1
- package/dist/cjs/src/FormoAnalyticsProvider.js +2 -2
- package/dist/cjs/src/FormoAnalyticsProvider.js.map +1 -1
- package/dist/cjs/src/types/base.d.ts +2 -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 +27 -33
- package/dist/esm/src/FormoAnalytics.d.ts.map +1 -1
- package/dist/esm/src/FormoAnalytics.js +162 -208
- package/dist/esm/src/FormoAnalytics.js.map +1 -1
- package/dist/esm/src/FormoAnalyticsProvider.d.ts +1 -1
- package/dist/esm/src/FormoAnalyticsProvider.d.ts.map +1 -1
- package/dist/esm/src/FormoAnalyticsProvider.js +2 -2
- package/dist/esm/src/FormoAnalyticsProvider.js.map +1 -1
- package/dist/esm/src/types/base.d.ts +2 -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 +172 -222
- package/src/FormoAnalyticsProvider.tsx +2 -4
- package/src/types/base.ts +2 -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",
|
|
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,11 @@ 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
|
+
projectId: string,
|
|
18
|
+
options?: Options
|
|
19
|
+
): Promise<FormoAnalytics>;
|
|
22
20
|
|
|
23
21
|
/**
|
|
24
22
|
* Tracks page visit events.
|
|
@@ -28,22 +26,22 @@ interface IFormoAnalytics {
|
|
|
28
26
|
/**
|
|
29
27
|
* Connects to a wallet with the specified chain ID and address.
|
|
30
28
|
*/
|
|
31
|
-
connect(params: {
|
|
29
|
+
connect(params: { chainId: ChainID; address: string }): Promise<void>;
|
|
32
30
|
|
|
33
31
|
/**
|
|
34
32
|
* Disconnects the current wallet and clears the session information.
|
|
35
33
|
*/
|
|
36
|
-
disconnect(
|
|
34
|
+
disconnect(params?: { chainId?: ChainID; address?: string }): void;
|
|
37
35
|
|
|
38
36
|
/**
|
|
39
|
-
*
|
|
37
|
+
* Switches the blockchain chain context and optionally logs additional params.
|
|
40
38
|
*/
|
|
41
|
-
|
|
39
|
+
chain(params: { chainId: ChainID; address?: string }): void;
|
|
42
40
|
|
|
43
41
|
/**
|
|
44
|
-
*
|
|
42
|
+
* Tracks a specific event with a name and associated data.
|
|
45
43
|
*/
|
|
46
|
-
|
|
44
|
+
track(eventName: string, eventData: Record<string, any>): void;
|
|
47
45
|
}
|
|
48
46
|
interface Config {
|
|
49
47
|
token: string;
|
|
@@ -61,17 +59,19 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
61
59
|
private timezoneToCountry: Record<string, string> = COUNTRY_LIST;
|
|
62
60
|
|
|
63
61
|
currentChainId?: string | null;
|
|
64
|
-
|
|
62
|
+
currentConnectedAddress?: string;
|
|
65
63
|
|
|
66
64
|
private constructor(
|
|
67
65
|
public readonly apiKey: string,
|
|
66
|
+
public readonly projectId: string,
|
|
68
67
|
public options: Options = {}
|
|
69
68
|
) {
|
|
70
69
|
this.config = {
|
|
71
70
|
token: this.apiKey,
|
|
72
71
|
};
|
|
73
72
|
|
|
74
|
-
const provider =
|
|
73
|
+
const provider =
|
|
74
|
+
window?.ethereum || window.web3?.currentProvider || options?.provider;
|
|
75
75
|
if (provider) {
|
|
76
76
|
this.trackProvider(provider);
|
|
77
77
|
}
|
|
@@ -79,12 +79,13 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
79
79
|
|
|
80
80
|
static async init(
|
|
81
81
|
apiKey: string,
|
|
82
|
-
|
|
82
|
+
projectId: string,
|
|
83
|
+
options?: Options
|
|
83
84
|
): Promise<FormoAnalytics> {
|
|
84
85
|
const config = {
|
|
85
86
|
token: apiKey,
|
|
86
87
|
};
|
|
87
|
-
const instance = new FormoAnalytics(apiKey, options);
|
|
88
|
+
const instance = new FormoAnalytics(apiKey, projectId, options);
|
|
88
89
|
instance.config = config;
|
|
89
90
|
|
|
90
91
|
return instance;
|
|
@@ -94,10 +95,6 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
94
95
|
return this._provider;
|
|
95
96
|
}
|
|
96
97
|
|
|
97
|
-
private identifyUser(userData: any) {
|
|
98
|
-
this.trackEvent(Event.IDENTIFY, userData);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
98
|
private getSessionId() {
|
|
102
99
|
const existingSessionId = this.getCookieValue(this.sessionIdKey);
|
|
103
100
|
|
|
@@ -116,7 +113,9 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
116
113
|
// Function to set the session cookie
|
|
117
114
|
private setSessionCookie(): void {
|
|
118
115
|
const sessionId = this.getSessionId();
|
|
119
|
-
let cookieValue = `${
|
|
116
|
+
let cookieValue = `${
|
|
117
|
+
this.sessionIdKey
|
|
118
|
+
}=${sessionId}; Max-Age=1800; path=/; secure; domain=${this.getOrigin()}`;
|
|
120
119
|
document.cookie = cookieValue;
|
|
121
120
|
}
|
|
122
121
|
|
|
@@ -147,19 +146,23 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
147
146
|
address: address,
|
|
148
147
|
session_id: this.getSessionId(),
|
|
149
148
|
timestamp: new Date().toISOString(),
|
|
150
|
-
action
|
|
149
|
+
action,
|
|
151
150
|
version: '1',
|
|
152
|
-
payload
|
|
151
|
+
payload,
|
|
153
152
|
};
|
|
154
153
|
|
|
155
154
|
const sendRequest = async (): Promise<void> => {
|
|
156
155
|
try {
|
|
157
|
-
const response = await axios.post(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
156
|
+
const response = await axios.post(
|
|
157
|
+
EVENTS_API_URL,
|
|
158
|
+
JSON.stringify(requestData),
|
|
159
|
+
{
|
|
160
|
+
headers: {
|
|
161
|
+
'Content-Type': 'application/json',
|
|
162
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
163
|
+
},
|
|
164
|
+
}
|
|
165
|
+
);
|
|
163
166
|
|
|
164
167
|
if (response.status >= 200 && response.status < 300) {
|
|
165
168
|
console.log('Event sent successfully:', action);
|
|
@@ -192,72 +195,6 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
192
195
|
await sendRequest();
|
|
193
196
|
}
|
|
194
197
|
|
|
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
198
|
// Function to track page hits
|
|
262
199
|
private trackPageHit() {
|
|
263
200
|
if (window.__nightmare || window.navigator.webdriver || window.Cypress)
|
|
@@ -281,6 +218,7 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
281
218
|
const params = new URLSearchParams(url.search);
|
|
282
219
|
this.trackEvent(Event.PAGE, {
|
|
283
220
|
'user-agent': window.navigator.userAgent,
|
|
221
|
+
address: this.currentConnectedAddress,
|
|
284
222
|
locale: language,
|
|
285
223
|
location: location,
|
|
286
224
|
referrer: document.referrer,
|
|
@@ -296,11 +234,12 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
296
234
|
|
|
297
235
|
private trackProvider(provider: EIP1193Provider) {
|
|
298
236
|
if (provider === this._provider) {
|
|
237
|
+
console.log('Provider already tracked.');
|
|
299
238
|
return;
|
|
300
239
|
}
|
|
301
240
|
|
|
302
241
|
this.currentChainId = undefined;
|
|
303
|
-
this.
|
|
242
|
+
this.currentConnectedAddress = undefined;
|
|
304
243
|
|
|
305
244
|
if (this._provider) {
|
|
306
245
|
const eventNames = Object.keys(this._registeredProviderListeners);
|
|
@@ -313,13 +252,84 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
313
252
|
}
|
|
314
253
|
}
|
|
315
254
|
|
|
255
|
+
console.log('Tracking new provider:', provider);
|
|
316
256
|
this._provider = provider;
|
|
317
257
|
|
|
318
258
|
this.getCurrentWallet();
|
|
319
|
-
this.
|
|
259
|
+
this.registerAddressChangedListener();
|
|
320
260
|
this.registerChainChangedListener();
|
|
321
261
|
}
|
|
322
262
|
|
|
263
|
+
private async getCurrentWallet() {
|
|
264
|
+
if (!this.provider) {
|
|
265
|
+
console.warn('FormoAnalytics::getCurrentWallet: the provider is not set');
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const sessionData = sessionStorage.getItem(this.walletAddressSessionKey);
|
|
270
|
+
|
|
271
|
+
if (!sessionData) {
|
|
272
|
+
console.warn(
|
|
273
|
+
'Session data missing. Attempting to fetch address from provider.'
|
|
274
|
+
);
|
|
275
|
+
try {
|
|
276
|
+
const accounts = await this.provider.request<string[]>({
|
|
277
|
+
method: 'eth_accounts',
|
|
278
|
+
});
|
|
279
|
+
if (accounts && accounts.length > 0) {
|
|
280
|
+
const address = accounts[0];
|
|
281
|
+
this.storeWalletAddress(address);
|
|
282
|
+
return address;
|
|
283
|
+
}
|
|
284
|
+
} catch (err) {
|
|
285
|
+
console.error('Failed to fetch accounts from provider:', err);
|
|
286
|
+
}
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const parsedData = JSON.parse(sessionData);
|
|
291
|
+
const sessionExpiry = 30 * 60 * 1000; // 30 minutes
|
|
292
|
+
const currentTime = Date.now();
|
|
293
|
+
|
|
294
|
+
if (currentTime - parsedData.timestamp > sessionExpiry) {
|
|
295
|
+
console.warn('Session expired. Ignoring wallet address.');
|
|
296
|
+
sessionStorage.removeItem(this.walletAddressSessionKey); // Clear expired session data
|
|
297
|
+
return '';
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
this.onAddressConnected(parsedData.address);
|
|
301
|
+
return parsedData.address || '';
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
private async getCurrentChainId(): Promise<string> {
|
|
305
|
+
if (!this.provider) {
|
|
306
|
+
console.error('FormoAnalytics::getCurrentChainId: provider not set');
|
|
307
|
+
}
|
|
308
|
+
const chainIdHex = await this.provider?.request<string>({
|
|
309
|
+
method: 'eth_chainId',
|
|
310
|
+
});
|
|
311
|
+
// Because we're connected, the chainId cannot be null
|
|
312
|
+
if (!chainIdHex) {
|
|
313
|
+
console.error(
|
|
314
|
+
`FormoAnalytics::getCurrentChainId: chainIdHex is: ${chainIdHex}`
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return parseInt(chainIdHex as string, 16).toString();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
private registerAddressChangedListener() {
|
|
322
|
+
const listener = (...args: unknown[]) =>
|
|
323
|
+
this.onAddressChanged(args[0] as string[]);
|
|
324
|
+
|
|
325
|
+
this._provider?.on('accountsChanged', listener);
|
|
326
|
+
this._registeredProviderListeners['accountsChanged'] = listener;
|
|
327
|
+
|
|
328
|
+
const onAddressDisconnected = this.onAddressDisconnected.bind(this);
|
|
329
|
+
this._provider?.on('disconnect', onAddressDisconnected);
|
|
330
|
+
this._registeredProviderListeners['disconnect'] = onAddressDisconnected;
|
|
331
|
+
}
|
|
332
|
+
|
|
323
333
|
private registerChainChangedListener() {
|
|
324
334
|
const listener = (...args: unknown[]) =>
|
|
325
335
|
this.onChainChanged(args[0] as string);
|
|
@@ -327,25 +337,50 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
327
337
|
this._registeredProviderListeners['chainChanged'] = listener;
|
|
328
338
|
}
|
|
329
339
|
|
|
330
|
-
private
|
|
331
|
-
if (
|
|
340
|
+
private async onAddressChanged(addresses: string[]) {
|
|
341
|
+
if (addresses.length > 0) {
|
|
342
|
+
const newAccount = addresses[0];
|
|
343
|
+
if (newAccount !== this.currentConnectedAddress) {
|
|
344
|
+
this.onAddressConnected(newAccount);
|
|
345
|
+
}
|
|
346
|
+
} else {
|
|
347
|
+
this.onAddressDisconnected();
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
private async onAddressConnected(address: string) {
|
|
352
|
+
if (address === this.currentConnectedAddress) {
|
|
353
|
+
// We have already reported this address
|
|
332
354
|
return;
|
|
355
|
+
} else {
|
|
356
|
+
this.currentConnectedAddress = address;
|
|
333
357
|
}
|
|
334
358
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
359
|
+
this.currentChainId = await this.getCurrentChainId();
|
|
360
|
+
|
|
361
|
+
this.connect({ chainId: this.currentChainId, address });
|
|
362
|
+
this.storeWalletAddress(address);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
private onAddressDisconnected() {
|
|
366
|
+
if (!this.currentConnectedAddress) {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const payload = {
|
|
371
|
+
chain_id: this.currentChainId,
|
|
372
|
+
address: this.currentConnectedAddress,
|
|
338
373
|
};
|
|
339
374
|
this.currentChainId = undefined;
|
|
340
|
-
this.
|
|
375
|
+
this.currentConnectedAddress = undefined;
|
|
341
376
|
this.clearWalletAddress();
|
|
342
377
|
|
|
343
|
-
return this.trackEvent(Event.DISCONNECT,
|
|
378
|
+
return this.trackEvent(Event.DISCONNECT, payload);
|
|
344
379
|
}
|
|
345
380
|
|
|
346
381
|
private async onChainChanged(chainIdHex: string) {
|
|
347
382
|
this.currentChainId = parseInt(chainIdHex).toString();
|
|
348
|
-
if (!this.
|
|
383
|
+
if (!this.currentConnectedAddress) {
|
|
349
384
|
if (!this.provider) {
|
|
350
385
|
console.error(
|
|
351
386
|
'error',
|
|
@@ -366,7 +401,7 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
366
401
|
return;
|
|
367
402
|
}
|
|
368
403
|
|
|
369
|
-
this.
|
|
404
|
+
this.currentConnectedAddress = res[0];
|
|
370
405
|
} catch (err) {
|
|
371
406
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
372
407
|
if ((err as any).code !== 4001) {
|
|
@@ -383,88 +418,8 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
383
418
|
|
|
384
419
|
return this.chain({
|
|
385
420
|
chainId: this.currentChainId,
|
|
386
|
-
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
|
|
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',
|
|
421
|
+
address: this.currentConnectedAddress,
|
|
420
422
|
});
|
|
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
423
|
}
|
|
469
424
|
|
|
470
425
|
/**
|
|
@@ -482,7 +437,10 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
482
437
|
timestamp: Date.now(),
|
|
483
438
|
};
|
|
484
439
|
|
|
485
|
-
sessionStorage.setItem(
|
|
440
|
+
sessionStorage.setItem(
|
|
441
|
+
this.walletAddressSessionKey,
|
|
442
|
+
JSON.stringify(sessionData)
|
|
443
|
+
);
|
|
486
444
|
}
|
|
487
445
|
|
|
488
446
|
/**
|
|
@@ -492,54 +450,55 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
492
450
|
sessionStorage.removeItem(this.walletAddressSessionKey);
|
|
493
451
|
}
|
|
494
452
|
|
|
495
|
-
|
|
453
|
+
init(apiKey: string, projectId: string, options: Options): Promise<FormoAnalytics> {
|
|
454
|
+
const instance = new FormoAnalytics(apiKey, projectId, options);
|
|
455
|
+
return Promise.resolve(instance);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
connect({ chainId, address }: { chainId: ChainID; address: string }) {
|
|
496
459
|
if (!chainId) {
|
|
497
|
-
throw new Error('FormoAnalytics::connect:
|
|
460
|
+
throw new Error('FormoAnalytics::connect: chain ID cannot be empty');
|
|
498
461
|
}
|
|
499
|
-
if (!
|
|
500
|
-
throw new Error('FormoAnalytics::connect:
|
|
462
|
+
if (!address) {
|
|
463
|
+
throw new Error('FormoAnalytics::connect: address cannot be empty');
|
|
501
464
|
}
|
|
502
465
|
|
|
503
466
|
this.currentChainId = chainId.toString();
|
|
504
|
-
this.
|
|
467
|
+
this.currentConnectedAddress = address;
|
|
505
468
|
|
|
506
469
|
return this.trackEvent(Event.CONNECT, {
|
|
507
|
-
chainId,
|
|
508
|
-
address:
|
|
470
|
+
chain_id: chainId,
|
|
471
|
+
address: address,
|
|
509
472
|
});
|
|
510
473
|
}
|
|
511
474
|
|
|
512
|
-
disconnect(
|
|
513
|
-
const
|
|
514
|
-
if (!
|
|
475
|
+
disconnect(params?: { chainId?: ChainID; address?: string }) {
|
|
476
|
+
const address = params?.address || this.currentConnectedAddress;
|
|
477
|
+
if (!address) {
|
|
515
478
|
// We have most likely already reported this disconnection with the automatic
|
|
516
479
|
// `disconnect` detection
|
|
517
480
|
return;
|
|
518
481
|
}
|
|
519
482
|
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
...(chainId && { chainId }),
|
|
483
|
+
const payload = {
|
|
484
|
+
chain_id: params?.chainId || this.currentChainId,
|
|
485
|
+
address,
|
|
524
486
|
};
|
|
525
|
-
|
|
526
487
|
this.currentChainId = undefined;
|
|
527
|
-
this.
|
|
488
|
+
this.currentConnectedAddress = undefined;
|
|
528
489
|
|
|
529
|
-
return this.trackEvent(Event.DISCONNECT,
|
|
490
|
+
return this.trackEvent(Event.DISCONNECT, payload);
|
|
530
491
|
}
|
|
531
492
|
|
|
532
|
-
chain({ chainId,
|
|
493
|
+
chain({ chainId, address }: { chainId: ChainID; address?: string }) {
|
|
533
494
|
if (!chainId || Number(chainId) === 0) {
|
|
534
495
|
throw new Error('FormoAnalytics::chain: chainId cannot be empty or 0');
|
|
535
496
|
}
|
|
536
|
-
|
|
537
|
-
if (!account && !this.currentConnectedAccount) {
|
|
497
|
+
if (!address && !this.currentConnectedAddress) {
|
|
538
498
|
throw new Error(
|
|
539
|
-
'FormoAnalytics::chain:
|
|
499
|
+
'FormoAnalytics::chain: address was empty and no previous address has been recorded. You can either pass an address or call connect() first'
|
|
540
500
|
);
|
|
541
501
|
}
|
|
542
|
-
|
|
543
502
|
if (isNaN(Number(chainId))) {
|
|
544
503
|
throw new Error(
|
|
545
504
|
'FormoAnalytics::chain: chainId must be a valid hex or decimal number'
|
|
@@ -549,20 +508,11 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
549
508
|
this.currentChainId = chainId.toString();
|
|
550
509
|
|
|
551
510
|
return this.trackEvent(Event.CHAIN_CHANGED, {
|
|
552
|
-
chainId,
|
|
553
|
-
|
|
511
|
+
chain_id: chainId,
|
|
512
|
+
address: address || this.currentConnectedAddress,
|
|
554
513
|
});
|
|
555
514
|
}
|
|
556
515
|
|
|
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
516
|
page() {
|
|
567
517
|
this.trackPageHit();
|
|
568
518
|
}
|
|
@@ -19,6 +19,7 @@ export const FormoAnalyticsContext = createContext<FormoAnalytics | undefined>(
|
|
|
19
19
|
export const FormoAnalyticsProvider = ({
|
|
20
20
|
apiKey,
|
|
21
21
|
options,
|
|
22
|
+
projectId,
|
|
22
23
|
disabled,
|
|
23
24
|
children,
|
|
24
25
|
}: FormoAnalyticsProviderProps) => {
|
|
@@ -32,15 +33,12 @@ export const FormoAnalyticsProvider = ({
|
|
|
32
33
|
console.error('FormoAnalyticsProvider: No API key provided');
|
|
33
34
|
return;
|
|
34
35
|
}
|
|
35
|
-
|
|
36
36
|
if (disabled) {
|
|
37
37
|
console.warn('FormoAnalytics is disabled');
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
40
|
-
|
|
41
40
|
if (initializedStartedRef.current) return;
|
|
42
41
|
initializedStartedRef.current = true;
|
|
43
|
-
|
|
44
42
|
// Initialize Highlight.run if project ID is available
|
|
45
43
|
if (HIGHLIGHT_PROJECT_ID) {
|
|
46
44
|
try {
|
|
@@ -64,7 +62,7 @@ export const FormoAnalyticsProvider = ({
|
|
|
64
62
|
|
|
65
63
|
// Initialize FormoAnalytics
|
|
66
64
|
try {
|
|
67
|
-
const sdkInstance = await FormoAnalytics.init(apiKey, options);
|
|
65
|
+
const sdkInstance = await FormoAnalytics.init(apiKey, projectId, options);
|
|
68
66
|
setSdk(sdkInstance);
|
|
69
67
|
console.log('FormoAnalytics SDK initialized successfully');
|
|
70
68
|
} catch (error) {
|