mirror of
https://github.com/trezor/trezor-suite.git
synced 2026-03-21 22:57:17 +01:00
135 lines
4.0 KiB
TypeScript
135 lines
4.0 KiB
TypeScript
import type {
|
|
Event as AnalyticsEvent,
|
|
AnalyticsOptions,
|
|
App,
|
|
InitOptions,
|
|
ReportConfig,
|
|
} from './types';
|
|
import { encodeDataToQueryString, getRandomId, getUrl, reportEvent } from './utils';
|
|
|
|
export class Analytics<T extends AnalyticsEvent> {
|
|
private enabled?: boolean;
|
|
|
|
private useQueue = false;
|
|
private queue = new Array<T>();
|
|
|
|
private version: string;
|
|
private app: App;
|
|
|
|
private instanceId?: string;
|
|
private sessionId?: string;
|
|
private commitId?: string;
|
|
private url?: string;
|
|
|
|
private callbacks?: InitOptions['callbacks'];
|
|
|
|
constructor({ version, app, useQueue = false }: AnalyticsOptions) {
|
|
this.version = version;
|
|
this.app = app;
|
|
this.useQueue = useQueue;
|
|
}
|
|
|
|
public init = (enabled: boolean | undefined, options: InitOptions) => {
|
|
this.enabled = enabled;
|
|
|
|
this.instanceId = options.instanceId || getRandomId();
|
|
this.sessionId = options.sessionId || getRandomId();
|
|
this.commitId = options.commitId;
|
|
this.url = getUrl(this.app, options.isDev, options.environment);
|
|
this.callbacks = options.callbacks;
|
|
|
|
// Call flushQueue only if 'enabled' is explicitly set (true or false).
|
|
// If 'enabled' is undefined, do not call flushQueue since the analytics
|
|
// status (enabled/disabled) will be determined later.
|
|
if (this.enabled !== undefined) {
|
|
this.flushQueue();
|
|
}
|
|
};
|
|
|
|
public enable = () => {
|
|
this.enabled = true;
|
|
|
|
this.callbacks?.onEnable?.();
|
|
|
|
this.flushQueue();
|
|
};
|
|
|
|
private flushQueue = () => {
|
|
if (this.useQueue) {
|
|
this.useQueue = false;
|
|
this.queue.map(data => this.report(data));
|
|
this.queue = [];
|
|
}
|
|
};
|
|
|
|
public disable = () => {
|
|
this.enabled = false;
|
|
|
|
this.callbacks?.onDisable?.();
|
|
|
|
if (this.useQueue) {
|
|
this.useQueue = false;
|
|
this.queue = [];
|
|
}
|
|
};
|
|
|
|
public isEnabled = () => !!this.enabled;
|
|
|
|
public report = (data: T, config?: ReportConfig) => {
|
|
// Add a timestamp to each event to track its actual occurrence time, considering possible queuing delays.
|
|
if (!data.timestamp) {
|
|
data.timestamp = Date.now().toString();
|
|
}
|
|
|
|
const isMissingFields =
|
|
!this.url || !this.instanceId || !this.sessionId || !this.commitId || !this.version;
|
|
|
|
if (!this.useQueue && isMissingFields) {
|
|
const listOfMissingFields =
|
|
`${!this.url ? 'url, ' : ''}` +
|
|
`${!this.instanceId ? 'instanceId, ' : ''}` +
|
|
`${!this.sessionId ? 'sessionId, ' : ''}` +
|
|
`${!this.commitId ? 'commitId, ' : ''}` +
|
|
`${!this.version ? 'version, ' : ''}`;
|
|
|
|
console.error(
|
|
`Unable to report ${data.type}. Analytics is not initialized! Missing: ${listOfMissingFields}`,
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
const { anonymize, force } = config ?? {};
|
|
|
|
if (this.useQueue && this.enabled === undefined && !force) {
|
|
this.queue.push(data);
|
|
}
|
|
|
|
if ((!this.enabled && !force) || isMissingFields) {
|
|
return;
|
|
}
|
|
|
|
const qs = encodeDataToQueryString(
|
|
// Random ID is better than constant because it helps clean the data in case it is accidentally logged multiple times.
|
|
anonymize ? getRandomId() : this.instanceId!,
|
|
anonymize ? getRandomId() : this.sessionId!,
|
|
this.commitId!,
|
|
this.version,
|
|
data,
|
|
);
|
|
|
|
// try to report analytics event once again if the previous one fails
|
|
// easy solution which should solve missing events on the launch of the app
|
|
// if it does not help, then queue or more sophisticated solution should be implemented
|
|
reportEvent({
|
|
type: data.type,
|
|
url: `${this.url}?${qs}`,
|
|
options: {
|
|
method: 'GET',
|
|
keepalive: true,
|
|
},
|
|
retry: true,
|
|
});
|
|
};
|
|
}
|