1import RudderAnalytics from '@expo/rudder-sdk-node';
2import os from 'os';
3import { v4 as uuidv4 } from 'uuid';
4
5import UserSettings from '../../api/user/UserSettings';
6import { env } from '../env';
7
8const PLATFORM_TO_ANALYTICS_PLATFORM: { [platform: string]: string } = {
9  darwin: 'Mac',
10  win32: 'Windows',
11  linux: 'Linux',
12};
13
14let client: RudderAnalytics | null = null;
15let identified = false;
16let identifyData: {
17  userId: string;
18  deviceId: string;
19  traits: Record<string, any>;
20} | null = null;
21
22function getClient(): RudderAnalytics {
23  if (client) {
24    return client;
25  }
26
27  client = new RudderAnalytics(
28    env.EXPO_STAGING || env.EXPO_LOCAL
29      ? '24TKICqYKilXM480mA7ktgVDdea'
30      : '24TKR7CQAaGgIrLTgu3Fp4OdOkI', // expo unified
31    'https://cdp.expo.dev/v1/batch',
32    {
33      flushInterval: 300,
34    }
35  );
36
37  // Install flush on exit...
38  process.on('SIGINT', () => client?.flush?.());
39  process.on('SIGTERM', () => client?.flush?.());
40
41  return client;
42}
43
44export async function setUserDataAsync(userId: string, traits: Record<string, any>): Promise<void> {
45  if (env.EXPO_NO_TELEMETRY) {
46    return;
47  }
48
49  const deviceId = await UserSettings.getAnonymousIdentifierAsync();
50
51  identifyData = {
52    userId,
53    deviceId,
54    traits,
55  };
56
57  ensureIdentified();
58}
59
60export function logEvent(
61  event: // TODO: revise
62  | 'action'
63    | 'Open Url on Device'
64    | 'Start Project'
65    | 'Serve Manifest'
66    | 'dev client start command'
67    | 'Serve Expo Updates Manifest',
68  properties: Record<string, any> = {}
69): void {
70  if (env.EXPO_NO_TELEMETRY) {
71    return;
72  }
73  ensureIdentified();
74
75  const { userId, deviceId } = identifyData ?? {};
76  const commonEventProperties = { source_version: process.env.__EXPO_VERSION, source: 'expo' };
77
78  const identity = { userId: userId ?? undefined, anonymousId: deviceId ?? uuidv4() };
79  getClient().track({
80    event,
81    properties: { ...properties, ...commonEventProperties },
82    ...identity,
83    context: getContext(),
84  });
85}
86
87function ensureIdentified(): void {
88  if (env.EXPO_NO_TELEMETRY || identified || !identifyData) {
89    return;
90  }
91
92  getClient().identify({
93    userId: identifyData.userId,
94    anonymousId: identifyData.deviceId,
95    traits: identifyData.traits,
96  });
97  identified = true;
98}
99
100function getContext(): Record<string, any> {
101  const platform = PLATFORM_TO_ANALYTICS_PLATFORM[os.platform()] || os.platform();
102  return {
103    os: { name: platform, version: os.release() },
104    device: { type: platform, model: platform },
105    app: { name: 'expo', version: process.env.__EXPO_VERSION },
106  };
107}
108