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 { EXPO_LOCAL, EXPO_STAGING, EXPO_NO_TELEMETRY } 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    EXPO_STAGING || EXPO_LOCAL ? '24TKICqYKilXM480mA7ktgVDdea' : '24TKR7CQAaGgIrLTgu3Fp4OdOkI', // expo unified
29    'https://cdp.expo.dev/v1/batch',
30    {
31      flushInterval: 300,
32    }
33  );
34
35  // Install flush on exit...
36  process.on('SIGINT', () => client?.flush?.());
37  process.on('SIGTERM', () => client?.flush?.());
38
39  return client;
40}
41
42export async function setUserDataAsync(userId: string, traits: Record<string, any>): Promise<void> {
43  if (EXPO_NO_TELEMETRY) {
44    return;
45  }
46
47  const deviceId = await UserSettings.getAnonymousIdentifierAsync();
48
49  identifyData = {
50    userId,
51    deviceId,
52    traits,
53  };
54
55  ensureIdentified();
56}
57
58export function logEvent(
59  event: // TODO: revise
60  | 'action'
61    | 'Open Url on Device'
62    | 'Start Project'
63    | 'Serve Manifest'
64    | 'dev client start command'
65    | 'Serve Expo Updates Manifest',
66  properties: Record<string, any> = {}
67): void {
68  if (EXPO_NO_TELEMETRY) {
69    return;
70  }
71  ensureIdentified();
72
73  const { userId, deviceId } = identifyData ?? {};
74  const commonEventProperties = { source_version: process.env.__EXPO_VERSION, source: 'expo' };
75
76  const identity = { userId: userId ?? undefined, anonymousId: deviceId ?? uuidv4() };
77  getClient().track({
78    event,
79    properties: { ...properties, ...commonEventProperties },
80    ...identity,
81    context: getContext(),
82  });
83}
84
85function ensureIdentified(): void {
86  if (EXPO_NO_TELEMETRY || identified || !identifyData) {
87    return;
88  }
89
90  getClient().identify({
91    userId: identifyData.userId,
92    anonymousId: identifyData.deviceId,
93    traits: identifyData.traits,
94  });
95  identified = true;
96}
97
98function getContext(): Record<string, any> {
99  const platform = PLATFORM_TO_ANALYTICS_PLATFORM[os.platform()] || os.platform();
100  return {
101    os: { name: platform, version: os.release() },
102    device: { type: platform, model: platform },
103    app: { name: 'expo', version: process.env.__EXPO_VERSION },
104  };
105}
106