@croacroa/react-native-template 1.0.0
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/.env.example +18 -0
- package/.eslintrc.js +55 -0
- package/.github/workflows/ci.yml +184 -0
- package/.github/workflows/eas-build.yml +55 -0
- package/.github/workflows/eas-update.yml +50 -0
- package/.gitignore +62 -0
- package/.prettierrc +11 -0
- package/.storybook/main.ts +28 -0
- package/.storybook/preview.tsx +30 -0
- package/CHANGELOG.md +106 -0
- package/CONTRIBUTING.md +377 -0
- package/README.md +399 -0
- package/__tests__/components/Button.test.tsx +74 -0
- package/__tests__/hooks/useAuth.test.tsx +499 -0
- package/__tests__/services/api.test.ts +535 -0
- package/__tests__/utils/cn.test.ts +39 -0
- package/app/(auth)/_layout.tsx +36 -0
- package/app/(auth)/home.tsx +117 -0
- package/app/(auth)/profile.tsx +152 -0
- package/app/(auth)/settings.tsx +147 -0
- package/app/(public)/_layout.tsx +21 -0
- package/app/(public)/forgot-password.tsx +127 -0
- package/app/(public)/login.tsx +120 -0
- package/app/(public)/onboarding.tsx +5 -0
- package/app/(public)/register.tsx +139 -0
- package/app/_layout.tsx +97 -0
- package/app/index.tsx +21 -0
- package/app.config.ts +72 -0
- package/assets/images/.gitkeep +7 -0
- package/assets/images/adaptive-icon.png +0 -0
- package/assets/images/favicon.png +0 -0
- package/assets/images/icon.png +0 -0
- package/assets/images/notification-icon.png +0 -0
- package/assets/images/splash.png +0 -0
- package/babel.config.js +10 -0
- package/components/ErrorBoundary.tsx +169 -0
- package/components/forms/FormInput.tsx +78 -0
- package/components/forms/index.ts +1 -0
- package/components/onboarding/OnboardingScreen.tsx +370 -0
- package/components/onboarding/index.ts +2 -0
- package/components/ui/AnimatedButton.tsx +156 -0
- package/components/ui/AnimatedCard.tsx +108 -0
- package/components/ui/Avatar.tsx +316 -0
- package/components/ui/Badge.tsx +416 -0
- package/components/ui/BottomSheet.tsx +307 -0
- package/components/ui/Button.stories.tsx +115 -0
- package/components/ui/Button.tsx +104 -0
- package/components/ui/Card.stories.tsx +84 -0
- package/components/ui/Card.tsx +32 -0
- package/components/ui/Checkbox.tsx +261 -0
- package/components/ui/Input.stories.tsx +106 -0
- package/components/ui/Input.tsx +117 -0
- package/components/ui/Modal.tsx +98 -0
- package/components/ui/OptimizedImage.tsx +369 -0
- package/components/ui/Select.tsx +240 -0
- package/components/ui/Skeleton.tsx +180 -0
- package/components/ui/index.ts +18 -0
- package/constants/config.ts +54 -0
- package/docs/adr/001-state-management.md +79 -0
- package/docs/adr/002-styling-approach.md +130 -0
- package/docs/adr/003-data-fetching.md +155 -0
- package/docs/adr/004-auth-adapter-pattern.md +144 -0
- package/docs/adr/README.md +78 -0
- package/eas.json +47 -0
- package/global.css +10 -0
- package/hooks/index.ts +25 -0
- package/hooks/useApi.ts +236 -0
- package/hooks/useAuth.tsx +290 -0
- package/hooks/useBiometrics.ts +295 -0
- package/hooks/useDeepLinking.ts +256 -0
- package/hooks/useNotifications.ts +138 -0
- package/hooks/useOffline.ts +69 -0
- package/hooks/usePerformance.ts +434 -0
- package/hooks/useTheme.tsx +85 -0
- package/hooks/useUpdates.ts +358 -0
- package/i18n/index.ts +77 -0
- package/i18n/locales/en.json +101 -0
- package/i18n/locales/fr.json +101 -0
- package/jest.config.js +32 -0
- package/maestro/README.md +113 -0
- package/maestro/config.yaml +35 -0
- package/maestro/flows/login.yaml +62 -0
- package/maestro/flows/navigation.yaml +68 -0
- package/maestro/flows/offline.yaml +60 -0
- package/maestro/flows/register.yaml +94 -0
- package/metro.config.js +6 -0
- package/nativewind-env.d.ts +1 -0
- package/package.json +170 -0
- package/scripts/init.ps1 +162 -0
- package/scripts/init.sh +174 -0
- package/services/analytics.ts +428 -0
- package/services/api.ts +340 -0
- package/services/authAdapter.ts +333 -0
- package/services/index.ts +22 -0
- package/services/queryClient.ts +97 -0
- package/services/sentry.ts +131 -0
- package/services/storage.ts +82 -0
- package/stores/appStore.ts +54 -0
- package/stores/index.ts +2 -0
- package/stores/notificationStore.ts +40 -0
- package/tailwind.config.js +47 -0
- package/tsconfig.json +26 -0
- package/types/index.ts +42 -0
- package/types/user.ts +63 -0
- package/utils/accessibility.ts +446 -0
- package/utils/cn.ts +14 -0
- package/utils/index.ts +43 -0
- package/utils/toast.ts +113 -0
- package/utils/validation.ts +67 -0
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics Adapter Pattern
|
|
3
|
+
*
|
|
4
|
+
* This module provides an abstraction layer for analytics providers.
|
|
5
|
+
* Supports multiple analytics backends simultaneously.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { IS_DEV, ENABLE_ANALYTICS } from "@/constants/config";
|
|
9
|
+
import { captureException as sentryCapture, addBreadcrumb } from "./sentry";
|
|
10
|
+
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Types
|
|
13
|
+
// ============================================================================
|
|
14
|
+
|
|
15
|
+
export interface AnalyticsAdapter {
|
|
16
|
+
/**
|
|
17
|
+
* Track a custom event
|
|
18
|
+
*/
|
|
19
|
+
track(event: string, properties?: Record<string, unknown>): void;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Identify a user
|
|
23
|
+
*/
|
|
24
|
+
identify(userId: string, traits?: Record<string, unknown>): void;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Track a screen view
|
|
28
|
+
*/
|
|
29
|
+
screen(name: string, properties?: Record<string, unknown>): void;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Reset the analytics state (on logout)
|
|
33
|
+
*/
|
|
34
|
+
reset(): void;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Set user properties that persist across events
|
|
38
|
+
*/
|
|
39
|
+
setUserProperties?(properties: Record<string, unknown>): void;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Track revenue/purchase
|
|
43
|
+
*/
|
|
44
|
+
trackRevenue?(
|
|
45
|
+
amount: number,
|
|
46
|
+
currency: string,
|
|
47
|
+
productId?: string,
|
|
48
|
+
properties?: Record<string, unknown>
|
|
49
|
+
): void;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Start a timed event
|
|
53
|
+
*/
|
|
54
|
+
startTimer?(event: string): void;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* End a timed event and track duration
|
|
58
|
+
*/
|
|
59
|
+
endTimer?(event: string, properties?: Record<string, unknown>): void;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ============================================================================
|
|
63
|
+
// Console Adapter (Development)
|
|
64
|
+
// ============================================================================
|
|
65
|
+
|
|
66
|
+
const consoleAdapter: AnalyticsAdapter = {
|
|
67
|
+
track(event, properties) {
|
|
68
|
+
console.log(`[Analytics] Track: ${event}`, properties);
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
identify(userId, traits) {
|
|
72
|
+
console.log(`[Analytics] Identify: ${userId}`, traits);
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
screen(name, properties) {
|
|
76
|
+
console.log(`[Analytics] Screen: ${name}`, properties);
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
reset() {
|
|
80
|
+
console.log("[Analytics] Reset");
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
setUserProperties(properties) {
|
|
84
|
+
console.log("[Analytics] User Properties:", properties);
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
trackRevenue(amount, currency, productId, properties) {
|
|
88
|
+
console.log(`[Analytics] Revenue: ${amount} ${currency}`, {
|
|
89
|
+
productId,
|
|
90
|
+
...properties,
|
|
91
|
+
});
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// ============================================================================
|
|
96
|
+
// Sentry Adapter (Error Tracking + Basic Analytics)
|
|
97
|
+
// ============================================================================
|
|
98
|
+
|
|
99
|
+
const sentryAdapter: AnalyticsAdapter = {
|
|
100
|
+
track(event, properties) {
|
|
101
|
+
addBreadcrumb("analytics", event, properties);
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
identify(userId, traits) {
|
|
105
|
+
// Sentry user is set via setUser in sentry.ts
|
|
106
|
+
addBreadcrumb("user", `Identified: ${userId}`, traits);
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
screen(name, properties) {
|
|
110
|
+
addBreadcrumb("navigation", `Screen: ${name}`, properties);
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
reset() {
|
|
114
|
+
addBreadcrumb("user", "User reset");
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// ============================================================================
|
|
119
|
+
// Example: Mixpanel Adapter
|
|
120
|
+
// ============================================================================
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Example Mixpanel implementation:
|
|
124
|
+
*
|
|
125
|
+
* import { Mixpanel } from 'mixpanel-react-native';
|
|
126
|
+
*
|
|
127
|
+
* const mixpanel = new Mixpanel('YOUR_PROJECT_TOKEN', true);
|
|
128
|
+
* mixpanel.init();
|
|
129
|
+
*
|
|
130
|
+
* const mixpanelAdapter: AnalyticsAdapter = {
|
|
131
|
+
* track(event, properties) {
|
|
132
|
+
* mixpanel.track(event, properties);
|
|
133
|
+
* },
|
|
134
|
+
*
|
|
135
|
+
* identify(userId, traits) {
|
|
136
|
+
* mixpanel.identify(userId);
|
|
137
|
+
* if (traits) {
|
|
138
|
+
* mixpanel.getPeople().set(traits);
|
|
139
|
+
* }
|
|
140
|
+
* },
|
|
141
|
+
*
|
|
142
|
+
* screen(name, properties) {
|
|
143
|
+
* mixpanel.track('Screen View', { screen_name: name, ...properties });
|
|
144
|
+
* },
|
|
145
|
+
*
|
|
146
|
+
* reset() {
|
|
147
|
+
* mixpanel.reset();
|
|
148
|
+
* },
|
|
149
|
+
*
|
|
150
|
+
* setUserProperties(properties) {
|
|
151
|
+
* mixpanel.getPeople().set(properties);
|
|
152
|
+
* },
|
|
153
|
+
*
|
|
154
|
+
* trackRevenue(amount, currency, productId, properties) {
|
|
155
|
+
* mixpanel.getPeople().trackCharge(amount, { currency, productId, ...properties });
|
|
156
|
+
* },
|
|
157
|
+
* };
|
|
158
|
+
*/
|
|
159
|
+
|
|
160
|
+
// ============================================================================
|
|
161
|
+
// Example: Amplitude Adapter
|
|
162
|
+
// ============================================================================
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Example Amplitude implementation:
|
|
166
|
+
*
|
|
167
|
+
* import { Amplitude } from '@amplitude/analytics-react-native';
|
|
168
|
+
*
|
|
169
|
+
* Amplitude.init('YOUR_API_KEY');
|
|
170
|
+
*
|
|
171
|
+
* const amplitudeAdapter: AnalyticsAdapter = {
|
|
172
|
+
* track(event, properties) {
|
|
173
|
+
* Amplitude.track(event, properties);
|
|
174
|
+
* },
|
|
175
|
+
*
|
|
176
|
+
* identify(userId, traits) {
|
|
177
|
+
* Amplitude.setUserId(userId);
|
|
178
|
+
* if (traits) {
|
|
179
|
+
* const identifyObj = new Amplitude.Identify();
|
|
180
|
+
* Object.entries(traits).forEach(([key, value]) => {
|
|
181
|
+
* identifyObj.set(key, value);
|
|
182
|
+
* });
|
|
183
|
+
* Amplitude.identify(identifyObj);
|
|
184
|
+
* }
|
|
185
|
+
* },
|
|
186
|
+
*
|
|
187
|
+
* screen(name, properties) {
|
|
188
|
+
* Amplitude.track('Screen View', { screen_name: name, ...properties });
|
|
189
|
+
* },
|
|
190
|
+
*
|
|
191
|
+
* reset() {
|
|
192
|
+
* Amplitude.reset();
|
|
193
|
+
* },
|
|
194
|
+
*
|
|
195
|
+
* setUserProperties(properties) {
|
|
196
|
+
* const identifyObj = new Amplitude.Identify();
|
|
197
|
+
* Object.entries(properties).forEach(([key, value]) => {
|
|
198
|
+
* identifyObj.set(key, value);
|
|
199
|
+
* });
|
|
200
|
+
* Amplitude.identify(identifyObj);
|
|
201
|
+
* },
|
|
202
|
+
*
|
|
203
|
+
* trackRevenue(amount, currency, productId, properties) {
|
|
204
|
+
* const revenue = new Amplitude.Revenue()
|
|
205
|
+
* .setPrice(amount)
|
|
206
|
+
* .setProductId(productId || 'unknown')
|
|
207
|
+
* .setRevenueType('purchase');
|
|
208
|
+
* Amplitude.revenue(revenue);
|
|
209
|
+
* },
|
|
210
|
+
* };
|
|
211
|
+
*/
|
|
212
|
+
|
|
213
|
+
// ============================================================================
|
|
214
|
+
// Multi-Provider Analytics Manager
|
|
215
|
+
// ============================================================================
|
|
216
|
+
|
|
217
|
+
class Analytics implements AnalyticsAdapter {
|
|
218
|
+
private adapters: AnalyticsAdapter[] = [];
|
|
219
|
+
private timers: Map<string, number> = new Map();
|
|
220
|
+
private superProperties: Record<string, unknown> = {};
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Add an analytics adapter
|
|
224
|
+
*/
|
|
225
|
+
addAdapter(adapter: AnalyticsAdapter): void {
|
|
226
|
+
this.adapters.push(adapter);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Remove all adapters
|
|
231
|
+
*/
|
|
232
|
+
clearAdapters(): void {
|
|
233
|
+
this.adapters = [];
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Set properties that will be sent with every event
|
|
238
|
+
*/
|
|
239
|
+
setSuperProperties(properties: Record<string, unknown>): void {
|
|
240
|
+
this.superProperties = { ...this.superProperties, ...properties };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Clear super properties
|
|
245
|
+
*/
|
|
246
|
+
clearSuperProperties(): void {
|
|
247
|
+
this.superProperties = {};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
track(event: string, properties?: Record<string, unknown>): void {
|
|
251
|
+
if (!ENABLE_ANALYTICS && !IS_DEV) return;
|
|
252
|
+
|
|
253
|
+
const mergedProperties = { ...this.superProperties, ...properties };
|
|
254
|
+
|
|
255
|
+
this.adapters.forEach((adapter) => {
|
|
256
|
+
try {
|
|
257
|
+
adapter.track(event, mergedProperties);
|
|
258
|
+
} catch (error) {
|
|
259
|
+
console.error(`Analytics track error:`, error);
|
|
260
|
+
sentryCapture(error as Error, { event, properties: mergedProperties });
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
identify(userId: string, traits?: Record<string, unknown>): void {
|
|
266
|
+
if (!ENABLE_ANALYTICS && !IS_DEV) return;
|
|
267
|
+
|
|
268
|
+
this.adapters.forEach((adapter) => {
|
|
269
|
+
try {
|
|
270
|
+
adapter.identify(userId, traits);
|
|
271
|
+
} catch (error) {
|
|
272
|
+
console.error(`Analytics identify error:`, error);
|
|
273
|
+
sentryCapture(error as Error, { userId });
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
screen(name: string, properties?: Record<string, unknown>): void {
|
|
279
|
+
if (!ENABLE_ANALYTICS && !IS_DEV) return;
|
|
280
|
+
|
|
281
|
+
const mergedProperties = { ...this.superProperties, ...properties };
|
|
282
|
+
|
|
283
|
+
this.adapters.forEach((adapter) => {
|
|
284
|
+
try {
|
|
285
|
+
adapter.screen(name, mergedProperties);
|
|
286
|
+
} catch (error) {
|
|
287
|
+
console.error(`Analytics screen error:`, error);
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
reset(): void {
|
|
293
|
+
this.clearSuperProperties();
|
|
294
|
+
this.timers.clear();
|
|
295
|
+
|
|
296
|
+
this.adapters.forEach((adapter) => {
|
|
297
|
+
try {
|
|
298
|
+
adapter.reset();
|
|
299
|
+
} catch (error) {
|
|
300
|
+
console.error(`Analytics reset error:`, error);
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
setUserProperties(properties: Record<string, unknown>): void {
|
|
306
|
+
if (!ENABLE_ANALYTICS && !IS_DEV) return;
|
|
307
|
+
|
|
308
|
+
this.adapters.forEach((adapter) => {
|
|
309
|
+
try {
|
|
310
|
+
adapter.setUserProperties?.(properties);
|
|
311
|
+
} catch (error) {
|
|
312
|
+
console.error(`Analytics setUserProperties error:`, error);
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
trackRevenue(
|
|
318
|
+
amount: number,
|
|
319
|
+
currency: string,
|
|
320
|
+
productId?: string,
|
|
321
|
+
properties?: Record<string, unknown>
|
|
322
|
+
): void {
|
|
323
|
+
if (!ENABLE_ANALYTICS && !IS_DEV) return;
|
|
324
|
+
|
|
325
|
+
this.adapters.forEach((adapter) => {
|
|
326
|
+
try {
|
|
327
|
+
adapter.trackRevenue?.(amount, currency, productId, properties);
|
|
328
|
+
} catch (error) {
|
|
329
|
+
console.error(`Analytics trackRevenue error:`, error);
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
// Also track as a regular event for providers that don't support revenue
|
|
334
|
+
this.track("Purchase", {
|
|
335
|
+
amount,
|
|
336
|
+
currency,
|
|
337
|
+
productId,
|
|
338
|
+
...properties,
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
startTimer(event: string): void {
|
|
343
|
+
this.timers.set(event, Date.now());
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
endTimer(event: string, properties?: Record<string, unknown>): void {
|
|
347
|
+
const startTime = this.timers.get(event);
|
|
348
|
+
if (startTime) {
|
|
349
|
+
const duration = Date.now() - startTime;
|
|
350
|
+
this.timers.delete(event);
|
|
351
|
+
this.track(event, { ...properties, duration_ms: duration });
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// ============================================================================
|
|
357
|
+
// Singleton Instance
|
|
358
|
+
// ============================================================================
|
|
359
|
+
|
|
360
|
+
export const analytics = new Analytics();
|
|
361
|
+
|
|
362
|
+
// Initialize with default adapters
|
|
363
|
+
if (IS_DEV) {
|
|
364
|
+
analytics.addAdapter(consoleAdapter);
|
|
365
|
+
}
|
|
366
|
+
analytics.addAdapter(sentryAdapter);
|
|
367
|
+
|
|
368
|
+
// ============================================================================
|
|
369
|
+
// Convenience Exports
|
|
370
|
+
// ============================================================================
|
|
371
|
+
|
|
372
|
+
export const track = analytics.track.bind(analytics);
|
|
373
|
+
export const identify = analytics.identify.bind(analytics);
|
|
374
|
+
export const screen = analytics.screen.bind(analytics);
|
|
375
|
+
export const resetAnalytics = analytics.reset.bind(analytics);
|
|
376
|
+
export const setUserProperties = analytics.setUserProperties.bind(analytics);
|
|
377
|
+
export const trackRevenue = analytics.trackRevenue.bind(analytics);
|
|
378
|
+
export const startTimer = analytics.startTimer.bind(analytics);
|
|
379
|
+
export const endTimer = analytics.endTimer.bind(analytics);
|
|
380
|
+
|
|
381
|
+
// ============================================================================
|
|
382
|
+
// Pre-defined Events (Type Safety)
|
|
383
|
+
// ============================================================================
|
|
384
|
+
|
|
385
|
+
export const AnalyticsEvents = {
|
|
386
|
+
// Auth
|
|
387
|
+
SIGN_UP_STARTED: "Sign Up Started",
|
|
388
|
+
SIGN_UP_COMPLETED: "Sign Up Completed",
|
|
389
|
+
SIGN_UP_FAILED: "Sign Up Failed",
|
|
390
|
+
SIGN_IN_STARTED: "Sign In Started",
|
|
391
|
+
SIGN_IN_COMPLETED: "Sign In Completed",
|
|
392
|
+
SIGN_IN_FAILED: "Sign In Failed",
|
|
393
|
+
SIGN_OUT: "Sign Out",
|
|
394
|
+
PASSWORD_RESET_REQUESTED: "Password Reset Requested",
|
|
395
|
+
|
|
396
|
+
// Onboarding
|
|
397
|
+
ONBOARDING_STARTED: "Onboarding Started",
|
|
398
|
+
ONBOARDING_STEP_COMPLETED: "Onboarding Step Completed",
|
|
399
|
+
ONBOARDING_COMPLETED: "Onboarding Completed",
|
|
400
|
+
ONBOARDING_SKIPPED: "Onboarding Skipped",
|
|
401
|
+
|
|
402
|
+
// Navigation
|
|
403
|
+
SCREEN_VIEW: "Screen View",
|
|
404
|
+
TAB_CHANGED: "Tab Changed",
|
|
405
|
+
DEEP_LINK_OPENED: "Deep Link Opened",
|
|
406
|
+
|
|
407
|
+
// User Actions
|
|
408
|
+
PROFILE_UPDATED: "Profile Updated",
|
|
409
|
+
SETTINGS_CHANGED: "Settings Changed",
|
|
410
|
+
NOTIFICATION_ENABLED: "Notification Enabled",
|
|
411
|
+
NOTIFICATION_DISABLED: "Notification Disabled",
|
|
412
|
+
BIOMETRIC_ENABLED: "Biometric Enabled",
|
|
413
|
+
BIOMETRIC_DISABLED: "Biometric Disabled",
|
|
414
|
+
|
|
415
|
+
// Errors
|
|
416
|
+
ERROR_OCCURRED: "Error Occurred",
|
|
417
|
+
API_ERROR: "API Error",
|
|
418
|
+
NETWORK_ERROR: "Network Error",
|
|
419
|
+
|
|
420
|
+
// Engagement
|
|
421
|
+
FEATURE_USED: "Feature Used",
|
|
422
|
+
BUTTON_CLICKED: "Button Clicked",
|
|
423
|
+
SEARCH_PERFORMED: "Search Performed",
|
|
424
|
+
CONTENT_SHARED: "Content Shared",
|
|
425
|
+
} as const;
|
|
426
|
+
|
|
427
|
+
export type AnalyticsEvent =
|
|
428
|
+
(typeof AnalyticsEvents)[keyof typeof AnalyticsEvents];
|