@umituz/react-native-firebase 1.7.0 → 1.7.3

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.
Files changed (31) hide show
  1. package/package.json +9 -4
  2. package/src/analytics/application/ports/IAnalyticsService.ts +92 -0
  3. package/src/analytics/index.ts +19 -0
  4. package/src/analytics/infrastructure/adapters/index.ts +10 -0
  5. package/src/analytics/infrastructure/adapters/native-analytics.adapter.ts +91 -0
  6. package/src/analytics/infrastructure/adapters/web-analytics.adapter.ts +63 -0
  7. package/src/analytics/infrastructure/services/FirebaseAnalyticsService.ts +187 -0
  8. package/src/analytics/infrastructure/services/PerformanceTracker.ts +49 -0
  9. package/src/analytics/infrastructure/services/analytics-event.service.ts +72 -0
  10. package/src/analytics/infrastructure/services/analytics-initializer.service.ts +162 -0
  11. package/src/analytics/infrastructure/services/analytics-user.service.ts +98 -0
  12. package/src/analytics/infrastructure/services/index.ts +12 -0
  13. package/src/analytics/presentation/decorators/PerformanceDecorator.ts +72 -0
  14. package/src/analytics/presentation/decorators/TrackingDecorator.ts +38 -0
  15. package/src/analytics/presentation/hooks/useNavigationTracking.ts +70 -0
  16. package/src/analytics/presentation/hooks/useScreenTime.ts +69 -0
  17. package/src/analytics/presentation/hooks/useScreenView.ts +70 -0
  18. package/src/analytics/presentation/utils/analyticsUtils.ts +78 -0
  19. package/src/crashlytics/index.ts +9 -0
  20. package/src/crashlytics/infrastructure/adapters/index.ts +8 -0
  21. package/src/crashlytics/infrastructure/adapters/native-crashlytics.adapter.ts +125 -0
  22. package/src/crashlytics/infrastructure/services/FirebaseCrashlyticsService.ts +99 -0
  23. package/src/crashlytics/infrastructure/services/crashlytics-error.service.ts +67 -0
  24. package/src/crashlytics/infrastructure/services/crashlytics-initializer.service.ts +31 -0
  25. package/src/crashlytics/infrastructure/services/crashlytics-user.service.ts +91 -0
  26. package/src/crashlytics/infrastructure/services/index.ts +11 -0
  27. package/src/domain/errors/FirebaseError.ts +20 -38
  28. package/src/domain/value-objects/FirebaseConfig.ts +10 -40
  29. package/src/index.ts +18 -22
  30. package/src/infrastructure/config/FirebaseClient.ts +11 -2
  31. package/README.md +0 -221
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Analytics Initializer Service
3
+ * Single Responsibility: Handle analytics initialization logic
4
+ */
5
+
6
+ import { Platform } from 'react-native';
7
+ import { getFirebaseApp } from '../../../infrastructure/config/FirebaseClient';
8
+ import { webAnalyticsAdapter } from '../adapters/web-analytics.adapter';
9
+ import { nativeAnalyticsAdapter } from '../adapters/native-analytics.adapter';
10
+
11
+ export interface AnalyticsInstance {
12
+ instance: any;
13
+ platform: 'web' | 'native';
14
+ }
15
+
16
+ export class AnalyticsInitializerService {
17
+ private static nativeWarningLogged = false;
18
+
19
+ /**
20
+ * Initialize analytics instance
21
+ * Returns null if initialization fails
22
+ */
23
+ async initialize(): Promise<AnalyticsInstance | null> {
24
+ // Platform-specific initialization
25
+ // iOS/Android: Only use native
26
+ // Web: Only use web
27
+ if (Platform.OS === 'web') {
28
+ if (webAnalyticsAdapter) {
29
+ return this.initializeWeb();
30
+ }
31
+ return null;
32
+ }
33
+
34
+ // iOS/Android: Try native only
35
+ if (nativeAnalyticsAdapter) {
36
+ return this.initializeNative();
37
+ }
38
+
39
+ // Native not available on iOS/Android - return null
40
+ // Only log warning once
41
+ if (__DEV__ && !AnalyticsInitializerService.nativeWarningLogged) {
42
+ AnalyticsInitializerService.nativeWarningLogged = true;
43
+ /* eslint-disable-next-line no-console */
44
+ console.warn(
45
+ '⚠️ Firebase Analytics: Native module not available on iOS/Android (Expo Go limitation)',
46
+ );
47
+ }
48
+ return null;
49
+ }
50
+
51
+ private async initializeNative(): Promise<AnalyticsInstance | null> {
52
+ try {
53
+ if (!nativeAnalyticsAdapter) {
54
+ /* eslint-disable-next-line no-console */
55
+ if (__DEV__) {
56
+ console.warn(
57
+ '⚠️ Firebase Analytics: Native adapter not available - ensure @react-native-firebase/app and @react-native-firebase/analytics are installed',
58
+ );
59
+ }
60
+ return null;
61
+ }
62
+
63
+ // Try to get Firebase App instance to check if it's initialized
64
+ let firebaseApp: any = null;
65
+ try {
66
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
67
+ const firebaseAppModule = require('@react-native-firebase/app');
68
+ firebaseApp = firebaseAppModule.app();
69
+ } catch (appError: any) {
70
+ /* eslint-disable-next-line no-console */
71
+ if (__DEV__) {
72
+ const errorMessage = appError?.message || String(appError);
73
+ if (errorMessage.includes('No Firebase App') || errorMessage.includes('has been created')) {
74
+ console.warn(
75
+ '⚠️ Firebase Analytics: Firebase App not initialized. Please add GoogleService-Info.plist to ios/ directory and rebuild the app.',
76
+ );
77
+ } else {
78
+ console.warn('⚠️ Firebase Analytics: Firebase App check failed', appError);
79
+ }
80
+ }
81
+ return null;
82
+ }
83
+
84
+ const instance = nativeAnalyticsAdapter.getAnalytics();
85
+ if (!instance) {
86
+ /* eslint-disable-next-line no-console */
87
+ if (__DEV__) {
88
+ console.warn('⚠️ Firebase Analytics: getAnalytics() returned null');
89
+ }
90
+ return null;
91
+ }
92
+
93
+ /* eslint-disable-next-line no-console */
94
+ if (__DEV__) {
95
+ console.log('✅ Firebase Analytics initialized (native)');
96
+ }
97
+ return { instance, platform: 'native' };
98
+ } catch (error: any) {
99
+ /* eslint-disable-next-line no-console */
100
+ if (__DEV__) {
101
+ const errorMessage = error?.message || String(error);
102
+ if (errorMessage.includes('No Firebase App') || errorMessage.includes('has been created')) {
103
+ console.warn(
104
+ '⚠️ Firebase Analytics: Firebase App not initialized. Please add GoogleService-Info.plist to ios/ directory and rebuild the app.',
105
+ );
106
+ } else {
107
+ console.warn('⚠️ Firebase Analytics: Native initialization failed', error);
108
+ }
109
+ }
110
+ return null;
111
+ }
112
+ }
113
+
114
+ private async initializeWeb(): Promise<AnalyticsInstance | null> {
115
+ try {
116
+ // Check if Analytics is supported
117
+ if (webAnalyticsAdapter) {
118
+ const isSupported = await webAnalyticsAdapter.isSupported();
119
+ if (!isSupported) {
120
+ /* eslint-disable-next-line no-console */
121
+ if (__DEV__) {
122
+ console.warn('⚠️ Firebase Analytics: Not supported in this environment (web)');
123
+ }
124
+ return null;
125
+ }
126
+ }
127
+
128
+ const app = getFirebaseApp();
129
+ if (!app) {
130
+ /* eslint-disable-next-line no-console */
131
+ if (__DEV__) {
132
+ console.warn('⚠️ Firebase Analytics: Firebase app not available');
133
+ }
134
+ return null;
135
+ }
136
+
137
+ try {
138
+ const instance = webAnalyticsAdapter!.getAnalytics(app);
139
+ /* eslint-disable-next-line no-console */
140
+ if (__DEV__) {
141
+ console.log('✅ Firebase Analytics initialized (web)');
142
+ }
143
+ return { instance, platform: 'web' };
144
+ } catch (analyticsError) {
145
+ /* eslint-disable-next-line no-console */
146
+ if (__DEV__) {
147
+ console.warn('⚠️ Firebase Analytics: getAnalytics failed', analyticsError);
148
+ }
149
+ return null;
150
+ }
151
+ } catch (error) {
152
+ /* eslint-disable-next-line no-console */
153
+ if (__DEV__) {
154
+ console.warn('⚠️ Firebase Analytics: Web initialization failed', error);
155
+ }
156
+ return null;
157
+ }
158
+ }
159
+ }
160
+
161
+ export const analyticsInitializerService = new AnalyticsInitializerService();
162
+
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Analytics User Service
3
+ * Single Responsibility: Handle user-related analytics operations
4
+ */
5
+
6
+ import { webAnalyticsAdapter } from '../adapters/web-analytics.adapter';
7
+ import { nativeAnalyticsAdapter } from '../adapters/native-analytics.adapter';
8
+ import type { AnalyticsInstance } from './analytics-initializer.service';
9
+
10
+ export class AnalyticsUserService {
11
+ /**
12
+ * Set user ID
13
+ */
14
+ async setUserId(
15
+ analyticsInstance: AnalyticsInstance | null,
16
+ userId: string,
17
+ ): Promise<void> {
18
+ if (!analyticsInstance) {
19
+ return;
20
+ }
21
+
22
+ try {
23
+ if (analyticsInstance.platform === 'native' && nativeAnalyticsAdapter) {
24
+ await nativeAnalyticsAdapter.setUserId(analyticsInstance.instance, userId);
25
+ } else if (analyticsInstance.platform === 'web' && webAnalyticsAdapter) {
26
+ await webAnalyticsAdapter.setUserId(analyticsInstance.instance, userId);
27
+ }
28
+ } catch (_error) {
29
+ // Silent fail
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Set user property
35
+ */
36
+ async setUserProperty(
37
+ analyticsInstance: AnalyticsInstance | null,
38
+ key: string,
39
+ value: string,
40
+ ): Promise<void> {
41
+ if (!analyticsInstance) {
42
+ return;
43
+ }
44
+
45
+ try {
46
+ if (analyticsInstance.platform === 'native' && nativeAnalyticsAdapter) {
47
+ await nativeAnalyticsAdapter.setUserProperties(analyticsInstance.instance, {
48
+ [key]: value,
49
+ });
50
+ } else if (analyticsInstance.platform === 'web' && webAnalyticsAdapter) {
51
+ await webAnalyticsAdapter.setUserProperties(analyticsInstance.instance, {
52
+ [key]: value,
53
+ });
54
+ }
55
+ } catch (_error) {
56
+ // Silent fail
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Set multiple user properties
62
+ */
63
+ async setUserProperties(
64
+ analyticsInstance: AnalyticsInstance | null,
65
+ properties: Record<string, string>,
66
+ ): Promise<void> {
67
+ for (const [key, value] of Object.entries(properties)) {
68
+ await this.setUserProperty(analyticsInstance, key, value);
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Clear user data
74
+ */
75
+ async clearUserData(analyticsInstance: AnalyticsInstance | null): Promise<void> {
76
+ if (!analyticsInstance) {
77
+ return;
78
+ }
79
+
80
+ try {
81
+ if (analyticsInstance.platform === 'native' && nativeAnalyticsAdapter) {
82
+ await nativeAnalyticsAdapter.setUserId(analyticsInstance.instance, '');
83
+ await nativeAnalyticsAdapter.setUserProperties(analyticsInstance.instance, {});
84
+ if (nativeAnalyticsAdapter.resetAnalyticsData) {
85
+ await nativeAnalyticsAdapter.resetAnalyticsData(analyticsInstance.instance);
86
+ }
87
+ } else if (analyticsInstance.platform === 'web' && webAnalyticsAdapter) {
88
+ await webAnalyticsAdapter.setUserId(analyticsInstance.instance, '');
89
+ await webAnalyticsAdapter.setUserProperties(analyticsInstance.instance, {});
90
+ }
91
+ } catch (_error) {
92
+ // Silent fail
93
+ }
94
+ }
95
+ }
96
+
97
+ export const analyticsUserService = new AnalyticsUserService();
98
+
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Analytics Infrastructure Services
3
+ * Public API exports
4
+ */
5
+
6
+ export { firebaseAnalyticsService } from './FirebaseAnalyticsService';
7
+ export type { IAnalyticsService } from './FirebaseAnalyticsService';
8
+ export { analyticsInitializerService } from './analytics-initializer.service';
9
+ export type { AnalyticsInstance } from './analytics-initializer.service';
10
+ export { analyticsEventService } from './analytics-event.service';
11
+ export { analyticsUserService } from './analytics-user.service';
12
+
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Performance Decorator
3
+ *
4
+ * DDD Pattern: Decorator for automatic performance tracking
5
+ */
6
+
7
+ import { performanceTracker } from '../../infrastructure/services/PerformanceTracker';
8
+
9
+ export function TrackPerformance(operationName?: string) {
10
+ return function (
11
+ target: any,
12
+ propertyKey: string,
13
+ descriptor: PropertyDescriptor
14
+ ) {
15
+ const originalMethod = descriptor.value;
16
+ const operation = operationName || `${target?.constructor?.name || 'Unknown'}.${propertyKey}`;
17
+
18
+ descriptor.value = async function (...args: unknown[]) {
19
+ const trackingId = `${operation}_${Date.now()}`;
20
+ return await performanceTracker.track(trackingId, async () => {
21
+ return await originalMethod.apply(this, args);
22
+ });
23
+ };
24
+
25
+ return descriptor;
26
+ };
27
+ }
28
+
29
+ export function TrackOperation(
30
+ operationName: string,
31
+ errorType: 'database' | 'network' | 'auth' | 'cache' | 'generic' = 'generic'
32
+ ) {
33
+ return function (
34
+ _target: unknown,
35
+ propertyKey: string,
36
+ descriptor: PropertyDescriptor
37
+ ) {
38
+ const originalMethod = descriptor.value;
39
+
40
+ descriptor.value = async function (...args: unknown[]) {
41
+ const trackingId = `${operationName}_${Date.now()}`;
42
+ const startTime = Date.now();
43
+
44
+ try {
45
+ performanceTracker.start(trackingId);
46
+ const result = await originalMethod.apply(this, args);
47
+
48
+ await performanceTracker.end(trackingId, {
49
+ operation: operationName,
50
+ method: propertyKey,
51
+ success: true,
52
+ });
53
+
54
+ return result;
55
+ } catch (error) {
56
+ await performanceTracker.end(trackingId, {
57
+ operation: operationName,
58
+ method: propertyKey,
59
+ success: false,
60
+ duration_ms: Date.now() - startTime,
61
+ });
62
+
63
+
64
+
65
+ throw error;
66
+ }
67
+ };
68
+
69
+ return descriptor;
70
+ };
71
+ }
72
+
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Tracking Decorator
3
+ *
4
+ * DDD Pattern: Decorator for automatic event tracking
5
+ */
6
+
7
+ import { firebaseAnalyticsService } from '../../infrastructure/services/FirebaseAnalyticsService';
8
+
9
+ /**
10
+ * Decorator to automatically log an event when a method is called
11
+ */
12
+ export function TrackEvent(eventName: string, params?: Record<string, any>) {
13
+ return function (
14
+ _target: any,
15
+ _propertyKey: string,
16
+ descriptor: PropertyDescriptor
17
+ ) {
18
+ const originalMethod = descriptor.value;
19
+
20
+ descriptor.value = async function (...args: any[]) {
21
+ try {
22
+ await firebaseAnalyticsService.logEvent(eventName, params);
23
+ } catch (error) {
24
+ // Fail silently to not disrupt app flow
25
+ }
26
+ return originalMethod.apply(this, args);
27
+ };
28
+
29
+ return descriptor;
30
+ };
31
+ }
32
+
33
+ /**
34
+ * Functional utility to track an event
35
+ */
36
+ export async function trackEvent(eventName: string, params?: Record<string, any>): Promise<void> {
37
+ await firebaseAnalyticsService.logEvent(eventName, params);
38
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * useNavigationTracking Hook
3
+ *
4
+ * Tracks navigation between screens for user journey analysis.
5
+ * Logs navigation events when user navigates from one screen to another.
6
+ *
7
+ * Platform-agnostic: Works on Web, iOS, and Android
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { useNavigationTracking } from '@umituz/react-native-firebase-analytics';
12
+ *
13
+ * function MyScreen() {
14
+ * useNavigationTracking('home', 'HomeScreen');
15
+ * // ... rest of component
16
+ * }
17
+ * ```
18
+ */
19
+
20
+ import { useRef, useCallback } from "react";
21
+ import { useFocusEffect } from "@react-navigation/native";
22
+ import { firebaseAnalyticsService } from "../../infrastructure/services/FirebaseAnalyticsService";
23
+
24
+ /**
25
+ * Track navigation between screens
26
+ * @param screenName - Current screen name (e.g., 'home', 'decks', 'settings')
27
+ * @param screenClass - Current screen class name (e.g., 'HomeScreen', 'DecksScreen')
28
+ */
29
+ export function useNavigationTracking(
30
+ screenName: string,
31
+ screenClass?: string,
32
+ ): void {
33
+ const previousScreenRef = useRef<string | null>(null);
34
+ const isFirstFocusRef = useRef(true);
35
+
36
+ useFocusEffect(
37
+ useCallback(() => {
38
+ // Skip first focus (app start)
39
+ if (isFirstFocusRef.current) {
40
+ isFirstFocusRef.current = false;
41
+ previousScreenRef.current = screenName;
42
+ return;
43
+ }
44
+
45
+ // Log navigation if coming from another screen
46
+ if (previousScreenRef.current && previousScreenRef.current !== screenName) {
47
+ /* eslint-disable-next-line no-console */
48
+ if (__DEV__) {
49
+ console.log("📊 Navigation tracked:", {
50
+ from: previousScreenRef.current,
51
+ to: screenName,
52
+ });
53
+ }
54
+
55
+ firebaseAnalyticsService
56
+ .logNavigation({
57
+ from_screen: previousScreenRef.current,
58
+ to_screen: screenName,
59
+ screen_class: screenClass || screenName,
60
+ })
61
+ .catch(() => {
62
+ // Silent fail - analytics is non-critical
63
+ });
64
+ }
65
+
66
+ previousScreenRef.current = screenName;
67
+ }, [screenName, screenClass]),
68
+ );
69
+ }
70
+
@@ -0,0 +1,69 @@
1
+ /**
2
+ * useScreenTime Hook
3
+ *
4
+ * Tracks how long users spend on each screen.
5
+ * Automatically logs screen_time event when screen is unfocused.
6
+ *
7
+ * Platform-agnostic: Works on Web, iOS, and Android
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { useScreenTime } from '@umituz/react-native-firebase-analytics';
12
+ *
13
+ * function MyScreen() {
14
+ * useScreenTime('home', 'HomeScreen');
15
+ * // ... rest of component
16
+ * }
17
+ * ```
18
+ */
19
+
20
+ import { useRef, useCallback } from "react";
21
+ import { useFocusEffect } from "@react-navigation/native";
22
+ import { firebaseAnalyticsService } from "../../infrastructure/services/FirebaseAnalyticsService";
23
+
24
+ /**
25
+ * Track screen time when screen is focused/unfocused
26
+ * @param screenName - Screen name (e.g., 'home', 'decks', 'settings')
27
+ * @param screenClass - Screen class name (e.g., 'HomeScreen', 'DecksScreen')
28
+ */
29
+ export function useScreenTime(screenName: string, screenClass?: string): void {
30
+ const startTimeRef = useRef<number | null>(null);
31
+
32
+ useFocusEffect(
33
+ useCallback(() => {
34
+ // Record start time when screen is focused
35
+ startTimeRef.current = Date.now();
36
+
37
+ return () => {
38
+ // Calculate time spent when screen is unfocused
39
+ if (startTimeRef.current !== null) {
40
+ const timeSpent = Math.round((Date.now() - startTimeRef.current) / 1000); // seconds
41
+
42
+ // Only log if user spent at least 1 second on screen
43
+ if (timeSpent >= 1) {
44
+ /* eslint-disable-next-line no-console */
45
+ if (__DEV__) {
46
+ console.log("📊 Screen time tracked:", {
47
+ screen: screenName,
48
+ timeSpent: `${timeSpent}s`,
49
+ });
50
+ }
51
+
52
+ firebaseAnalyticsService
53
+ .logScreenTime({
54
+ screen_name: screenName,
55
+ screen_class: screenClass || screenName,
56
+ time_spent_seconds: timeSpent,
57
+ })
58
+ .catch(() => {
59
+ // Silent fail - analytics is non-critical
60
+ });
61
+ }
62
+
63
+ startTimeRef.current = null;
64
+ }
65
+ };
66
+ }, [screenName, screenClass]),
67
+ );
68
+ }
69
+
@@ -0,0 +1,70 @@
1
+ /**
2
+ * useScreenView Hook
3
+ *
4
+ * Comprehensive screen tracking hook that combines:
5
+ * - Screen view tracking (when screen is focused)
6
+ * - Screen time tracking (how long user stays on screen)
7
+ * - Navigation tracking (from/to screen transitions)
8
+ *
9
+ * Platform-agnostic: Works on Web, iOS, and Android
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { useScreenView } from '@umituz/react-native-firebase-analytics';
14
+ *
15
+ * function MyScreen() {
16
+ * useScreenView('home', 'HomeScreen');
17
+ * // ... rest of component
18
+ * }
19
+ * ```
20
+ */
21
+
22
+ import { useCallback } from "react";
23
+ import { useFocusEffect } from "@react-navigation/native";
24
+ import { InteractionManager } from "react-native";
25
+ import { firebaseAnalyticsService } from "../../infrastructure/services/FirebaseAnalyticsService";
26
+ import { useScreenTime } from "./useScreenTime";
27
+ import { useNavigationTracking } from "./useNavigationTracking";
28
+
29
+ /**
30
+ * Track screen view, time, and navigation when screen is focused
31
+ * @param screenName - Screen name (e.g., 'home', 'decks', 'settings')
32
+ * @param screenClass - Screen class name (e.g., 'HomeScreen', 'DecksScreen')
33
+ */
34
+ export function useScreenView(screenName: string, screenClass?: string): void {
35
+ // Track screen time (how long user stays on screen)
36
+ useScreenTime(screenName, screenClass);
37
+
38
+ // Track navigation (from/to screen transitions)
39
+ useNavigationTracking(screenName, screenClass);
40
+
41
+ // Track screen view (when screen is focused)
42
+ useFocusEffect(
43
+ useCallback(() => {
44
+ // Defer analytics until screen transition animation completes
45
+ const task = InteractionManager.runAfterInteractions(() => {
46
+ /* eslint-disable-next-line no-console */
47
+ if (__DEV__) {
48
+ console.log("📊 Screen view tracked:", {
49
+ screen: screenName,
50
+ screenClass: screenClass || screenName,
51
+ });
52
+ }
53
+
54
+ firebaseAnalyticsService
55
+ .logScreenView({
56
+ screen_name: screenName,
57
+ screen_class: screenClass || screenName,
58
+ })
59
+ .catch(() => {
60
+ // Silent fail - analytics is non-critical
61
+ });
62
+ });
63
+
64
+ return () => {
65
+ task.cancel();
66
+ };
67
+ }, [screenName, screenClass]),
68
+ );
69
+ }
70
+
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Analytics Utilities
3
+ *
4
+ * Helper functions for tracking user interactions and events.
5
+ * Platform-agnostic: Works on Web, iOS, and Android
6
+ */
7
+
8
+ import { firebaseAnalyticsService } from "../../infrastructure/services/FirebaseAnalyticsService";
9
+
10
+ /**
11
+ * Track button click
12
+ * @param buttonId - Unique button identifier (e.g., 'create_deck', 'delete_card')
13
+ * @param options - Optional parameters
14
+ */
15
+ export function trackButtonClick(
16
+ buttonId: string,
17
+ options?: {
18
+ buttonName?: string;
19
+ screenName?: string;
20
+ screenClass?: string;
21
+ [key: string]: string | number | boolean | null | undefined;
22
+ },
23
+ ): void {
24
+ const { buttonName, screenName, screenClass, ...additionalParams } =
25
+ options || {};
26
+
27
+ const params = {
28
+ button_id: buttonId,
29
+ button_name: buttonName || buttonId,
30
+ screen_name: screenName || "unknown",
31
+ screen_class: screenClass || screenName || "unknown",
32
+ ...additionalParams,
33
+ };
34
+
35
+ /* eslint-disable-next-line no-console */
36
+ if (__DEV__) {
37
+ console.log("📊 Button click tracked:", {
38
+ buttonId,
39
+ buttonName: buttonName || buttonId,
40
+ screenName: screenName || "unknown",
41
+ ...additionalParams,
42
+ });
43
+ }
44
+
45
+ firebaseAnalyticsService
46
+ .logButtonClick(params)
47
+ .catch(() => {
48
+ // Silent fail - analytics is non-critical
49
+ });
50
+ }
51
+
52
+ /**
53
+ * Track CRUD operation
54
+ * @param operation - Operation type ('create', 'update', 'delete', 'read')
55
+ * @param entityType - Entity type ('deck', 'card', 'category', etc.)
56
+ * @param entityId - Entity ID
57
+ * @param additionalParams - Additional parameters to track
58
+ */
59
+ export function trackCRUDOperation(
60
+ operation: "create" | "update" | "delete" | "read",
61
+ entityType: string,
62
+ entityId: string,
63
+ additionalParams?: Record<string, string | number | boolean | null>,
64
+ ): void {
65
+ const eventName = `${entityType}_${operation}d`;
66
+
67
+ firebaseAnalyticsService
68
+ .logEvent(eventName, {
69
+ [`${entityType}_id`]: entityId,
70
+ operation,
71
+ entity_type: entityType,
72
+ ...additionalParams,
73
+ })
74
+ .catch(() => {
75
+ // Silent fail - analytics is non-critical
76
+ });
77
+ }
78
+
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Firebase Crashlytics Module
3
+ */
4
+
5
+ export { firebaseCrashlyticsService } from './infrastructure/services/FirebaseCrashlyticsService';
6
+ export type { ICrashlyticsService } from './infrastructure/services/FirebaseCrashlyticsService';
7
+ export { crashlyticsInitializerService } from './infrastructure/services/crashlytics-initializer.service';
8
+ export { crashlyticsErrorService } from './infrastructure/services/crashlytics-error.service';
9
+ export { crashlyticsUserService } from './infrastructure/services/crashlytics-user.service';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Crashlytics Adapters
3
+ * Platform-specific implementations
4
+ */
5
+
6
+ export { nativeCrashlyticsAdapter } from './native-crashlytics.adapter';
7
+ export type { NativeCrashlyticsAdapter } from './native-crashlytics.adapter';
8
+