@umituz/react-native-firebase 1.7.0 → 1.7.2
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/package.json +8 -3
- package/src/analytics/application/ports/IAnalyticsService.ts +92 -0
- package/src/analytics/index.ts +19 -0
- package/src/analytics/infrastructure/adapters/index.ts +10 -0
- package/src/analytics/infrastructure/adapters/native-analytics.adapter.ts +91 -0
- package/src/analytics/infrastructure/adapters/web-analytics.adapter.ts +63 -0
- package/src/analytics/infrastructure/services/FirebaseAnalyticsService.ts +187 -0
- package/src/analytics/infrastructure/services/PerformanceTracker.ts +49 -0
- package/src/analytics/infrastructure/services/analytics-event.service.ts +72 -0
- package/src/analytics/infrastructure/services/analytics-initializer.service.ts +162 -0
- package/src/analytics/infrastructure/services/analytics-user.service.ts +98 -0
- package/src/analytics/infrastructure/services/index.ts +12 -0
- package/src/analytics/presentation/decorators/PerformanceDecorator.ts +72 -0
- package/src/analytics/presentation/decorators/TrackingDecorator.ts +38 -0
- package/src/analytics/presentation/hooks/useNavigationTracking.ts +70 -0
- package/src/analytics/presentation/hooks/useScreenTime.ts +69 -0
- package/src/analytics/presentation/hooks/useScreenView.ts +70 -0
- package/src/analytics/presentation/utils/analyticsUtils.ts +78 -0
- package/src/crashlytics/index.ts +9 -0
- package/src/crashlytics/infrastructure/adapters/index.ts +8 -0
- package/src/crashlytics/infrastructure/adapters/native-crashlytics.adapter.ts +125 -0
- package/src/crashlytics/infrastructure/services/FirebaseCrashlyticsService.ts +99 -0
- package/src/crashlytics/infrastructure/services/crashlytics-error.service.ts +67 -0
- package/src/crashlytics/infrastructure/services/crashlytics-initializer.service.ts +31 -0
- package/src/crashlytics/infrastructure/services/crashlytics-user.service.ts +91 -0
- package/src/crashlytics/infrastructure/services/index.ts +11 -0
- package/src/domain/errors/FirebaseError.ts +20 -38
- package/src/domain/value-objects/FirebaseConfig.ts +10 -40
- package/src/index.ts +18 -22
- package/src/infrastructure/config/FirebaseClient.ts +11 -2
- package/README.md +0 -221
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-firebase",
|
|
3
|
-
"version": "1.7.
|
|
4
|
-
"description": "Firebase
|
|
3
|
+
"version": "1.7.2",
|
|
4
|
+
"description": "Unified Firebase package for React Native apps - Centralized initialization and core services (Analytics, Crashlytics).",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
7
7
|
"scripts": {
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
"react-native",
|
|
13
13
|
"firebase",
|
|
14
14
|
"firebase-core",
|
|
15
|
-
"
|
|
15
|
+
"analytics",
|
|
16
|
+
"crashlytics",
|
|
16
17
|
"ddd",
|
|
17
18
|
"domain-driven-design"
|
|
18
19
|
],
|
|
@@ -23,6 +24,10 @@
|
|
|
23
24
|
"url": "https://github.com/umituz/react-native-firebase"
|
|
24
25
|
},
|
|
25
26
|
"peerDependencies": {
|
|
27
|
+
"@react-native-firebase/analytics": ">=23.0.0",
|
|
28
|
+
"@react-native-firebase/app": ">=23.0.0",
|
|
29
|
+
"@react-native-firebase/crashlytics": ">=23.0.0",
|
|
30
|
+
"@react-navigation/native": ">=6.0.0",
|
|
26
31
|
"firebase": ">=11.0.0",
|
|
27
32
|
"react": ">=18.2.0",
|
|
28
33
|
"react-native": ">=0.74.0"
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics Service Interface
|
|
3
|
+
*
|
|
4
|
+
* Application Layer - Port
|
|
5
|
+
* Defines the contract for analytics operations
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface IAnalyticsService {
|
|
9
|
+
/**
|
|
10
|
+
* Initialize the analytics service
|
|
11
|
+
* @param userId Optional user ID to associate with analytics
|
|
12
|
+
*/
|
|
13
|
+
init(userId?: string): Promise<void>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Set the current user ID
|
|
17
|
+
* @param userId The unique user identifier
|
|
18
|
+
*/
|
|
19
|
+
setUserId(userId: string): Promise<void>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Log a custom event
|
|
23
|
+
* @param eventName Name of the event
|
|
24
|
+
* @param params Optional key-value pairs of parameters
|
|
25
|
+
*/
|
|
26
|
+
logEvent(
|
|
27
|
+
eventName: string,
|
|
28
|
+
params?: Record<string, string | number | boolean | null>,
|
|
29
|
+
): Promise<void>;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Set a user property
|
|
33
|
+
* @param key Property key
|
|
34
|
+
* @param value Property value
|
|
35
|
+
*/
|
|
36
|
+
setUserProperty(key: string, value: string): Promise<void>;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Set multiple user properties at once
|
|
40
|
+
* @param properties Key-value pairs of user properties
|
|
41
|
+
*/
|
|
42
|
+
setUserProperties(properties: Record<string, string>): Promise<void>;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Clear all user data and reset initialization state
|
|
46
|
+
*/
|
|
47
|
+
clearUserData(): Promise<void>;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Get the current user ID if set
|
|
51
|
+
*/
|
|
52
|
+
getCurrentUserId(): string | null;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Log a screen view event
|
|
56
|
+
*/
|
|
57
|
+
logScreenView(params: { screen_name: string; screen_class?: string }): Promise<void>;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Log time spent on a screen
|
|
61
|
+
*/
|
|
62
|
+
logScreenTime(params: {
|
|
63
|
+
screen_name: string;
|
|
64
|
+
screen_class?: string;
|
|
65
|
+
time_spent_seconds: number;
|
|
66
|
+
}): Promise<void>;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Log a navigation event between screens
|
|
70
|
+
*/
|
|
71
|
+
logNavigation(params: {
|
|
72
|
+
from_screen: string;
|
|
73
|
+
to_screen: string;
|
|
74
|
+
screen_class?: string;
|
|
75
|
+
}): Promise<void>;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Log a button click event
|
|
79
|
+
*/
|
|
80
|
+
logButtonClick(params: {
|
|
81
|
+
button_id: string;
|
|
82
|
+
button_name?: string;
|
|
83
|
+
screen_name: string;
|
|
84
|
+
screen_class?: string;
|
|
85
|
+
}): Promise<void>;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Enable or disable analytics collection
|
|
89
|
+
* @param enabled Whether collection should be enabled
|
|
90
|
+
*/
|
|
91
|
+
setAnalyticsCollectionEnabled(enabled: boolean): Promise<void>;
|
|
92
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firebase Analytics Module
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { firebaseAnalyticsService } from './infrastructure/services/FirebaseAnalyticsService';
|
|
6
|
+
export type { IAnalyticsService } from './infrastructure/services/FirebaseAnalyticsService';
|
|
7
|
+
export { analyticsInitializerService } from './infrastructure/services/analytics-initializer.service';
|
|
8
|
+
export type { AnalyticsInstance } from './infrastructure/services/analytics-initializer.service';
|
|
9
|
+
export { analyticsEventService } from './infrastructure/services/analytics-event.service';
|
|
10
|
+
export { analyticsUserService } from './infrastructure/services/analytics-user.service';
|
|
11
|
+
export { performanceTracker } from './infrastructure/services/PerformanceTracker';
|
|
12
|
+
|
|
13
|
+
// Hooks
|
|
14
|
+
export { useScreenView } from './presentation/hooks/useScreenView';
|
|
15
|
+
export { useScreenTime } from './presentation/hooks/useScreenTime';
|
|
16
|
+
export { useNavigationTracking } from './presentation/hooks/useNavigationTracking';
|
|
17
|
+
|
|
18
|
+
// Utility Functions
|
|
19
|
+
export { trackButtonClick, trackCRUDOperation } from './presentation/utils/analyticsUtils';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics Adapters
|
|
3
|
+
* Platform-specific implementations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { webAnalyticsAdapter } from './web-analytics.adapter';
|
|
7
|
+
export type { WebAnalyticsAdapter } from './web-analytics.adapter';
|
|
8
|
+
export { nativeAnalyticsAdapter } from './native-analytics.adapter';
|
|
9
|
+
export type { NativeAnalyticsAdapter } from './native-analytics.adapter';
|
|
10
|
+
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native Analytics Adapter
|
|
3
|
+
* Single Responsibility: Handle Firebase Analytics native implementation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface NativeAnalyticsAdapter {
|
|
7
|
+
getAnalytics(): any;
|
|
8
|
+
logEvent(analytics: any, eventName: string, params?: Record<string, any>): Promise<void>;
|
|
9
|
+
setUserId(analytics: any, userId: string): Promise<void>;
|
|
10
|
+
setUserProperties(analytics: any, properties: Record<string, string>): Promise<void>;
|
|
11
|
+
resetAnalyticsData(analytics: any): Promise<void>;
|
|
12
|
+
setAnalyticsCollectionEnabled(analytics: any, enabled: boolean): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let nativeAnalyticsModule: any = null;
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
19
|
+
require('@react-native-firebase/app');
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
21
|
+
const analyticsModule = require('@react-native-firebase/analytics');
|
|
22
|
+
|
|
23
|
+
// @react-native-firebase/analytics returns a factory function via createModuleNamespace
|
|
24
|
+
// Handle CommonJS (require) and ES6 (import) module formats
|
|
25
|
+
// The module can be:
|
|
26
|
+
// 1. Direct function: analyticsModule()
|
|
27
|
+
// 2. Object with default: analyticsModule.default()
|
|
28
|
+
// 3. Object with __esModule: analyticsModule.default() or analyticsModule()
|
|
29
|
+
|
|
30
|
+
if (typeof analyticsModule === 'function') {
|
|
31
|
+
// Direct function export
|
|
32
|
+
nativeAnalyticsModule = analyticsModule;
|
|
33
|
+
} else if (analyticsModule && typeof analyticsModule.default === 'function') {
|
|
34
|
+
// ES6 default export in CommonJS format
|
|
35
|
+
nativeAnalyticsModule = analyticsModule.default;
|
|
36
|
+
} else if (analyticsModule && typeof analyticsModule === 'object') {
|
|
37
|
+
// Try to use the module itself if it's callable
|
|
38
|
+
// Some bundlers wrap the function in an object
|
|
39
|
+
nativeAnalyticsModule = analyticsModule;
|
|
40
|
+
}
|
|
41
|
+
} catch (error) {
|
|
42
|
+
/* eslint-disable-next-line no-console */
|
|
43
|
+
if (__DEV__) {
|
|
44
|
+
console.warn('⚠️ Firebase Analytics: Native module not available', error);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const nativeAnalyticsAdapter: NativeAnalyticsAdapter | null =
|
|
49
|
+
nativeAnalyticsModule && (typeof nativeAnalyticsModule === 'function' || typeof nativeAnalyticsModule === 'object')
|
|
50
|
+
? {
|
|
51
|
+
getAnalytics(): any {
|
|
52
|
+
// Try calling as function first, then as object method
|
|
53
|
+
if (typeof nativeAnalyticsModule === 'function') {
|
|
54
|
+
return nativeAnalyticsModule();
|
|
55
|
+
}
|
|
56
|
+
// If it's an object, try calling it directly (some modules are callable objects)
|
|
57
|
+
if (typeof nativeAnalyticsModule === 'object' && nativeAnalyticsModule.default) {
|
|
58
|
+
return typeof nativeAnalyticsModule.default === 'function'
|
|
59
|
+
? nativeAnalyticsModule.default()
|
|
60
|
+
: nativeAnalyticsModule.default;
|
|
61
|
+
}
|
|
62
|
+
return nativeAnalyticsModule;
|
|
63
|
+
},
|
|
64
|
+
async logEvent(
|
|
65
|
+
analytics: any,
|
|
66
|
+
eventName: string,
|
|
67
|
+
params?: Record<string, any>,
|
|
68
|
+
): Promise<void> {
|
|
69
|
+
await analytics.logEvent(eventName, params);
|
|
70
|
+
},
|
|
71
|
+
async setUserId(analytics: any, userId: string): Promise<void> {
|
|
72
|
+
await analytics.setUserId(userId);
|
|
73
|
+
},
|
|
74
|
+
async setUserProperties(
|
|
75
|
+
analytics: any,
|
|
76
|
+
properties: Record<string, string>,
|
|
77
|
+
): Promise<void> {
|
|
78
|
+
await analytics.setUserProperties(properties);
|
|
79
|
+
},
|
|
80
|
+
async resetAnalyticsData(analytics: any): Promise<void> {
|
|
81
|
+
await analytics.resetAnalyticsData();
|
|
82
|
+
},
|
|
83
|
+
async setAnalyticsCollectionEnabled(
|
|
84
|
+
analytics: any,
|
|
85
|
+
enabled: boolean,
|
|
86
|
+
): Promise<void> {
|
|
87
|
+
await analytics.setAnalyticsCollectionEnabled(enabled);
|
|
88
|
+
},
|
|
89
|
+
}
|
|
90
|
+
: null;
|
|
91
|
+
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web Analytics Adapter
|
|
3
|
+
* Single Responsibility: Handle Firebase Analytics web implementation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface WebAnalyticsAdapter {
|
|
7
|
+
isSupported(): Promise<boolean>;
|
|
8
|
+
getAnalytics(app: any): any;
|
|
9
|
+
logEvent(analytics: any, eventName: string, params?: Record<string, any>): Promise<void>;
|
|
10
|
+
setUserId(analytics: any, userId: string): Promise<void>;
|
|
11
|
+
setUserProperties(analytics: any, properties: Record<string, string>): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let webAnalyticsModule: any = null;
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
18
|
+
const {
|
|
19
|
+
getAnalytics,
|
|
20
|
+
logEvent,
|
|
21
|
+
setUserId,
|
|
22
|
+
setUserProperties,
|
|
23
|
+
isSupported,
|
|
24
|
+
} = require('firebase/analytics');
|
|
25
|
+
webAnalyticsModule = {
|
|
26
|
+
getAnalytics,
|
|
27
|
+
logEvent,
|
|
28
|
+
setUserId,
|
|
29
|
+
setUserProperties,
|
|
30
|
+
isSupported,
|
|
31
|
+
};
|
|
32
|
+
} catch {
|
|
33
|
+
// firebase/analytics not available
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const webAnalyticsAdapter: WebAnalyticsAdapter | null = webAnalyticsModule
|
|
37
|
+
? {
|
|
38
|
+
async isSupported(): Promise<boolean> {
|
|
39
|
+
if (!webAnalyticsModule.isSupported) return true;
|
|
40
|
+
return webAnalyticsModule.isSupported();
|
|
41
|
+
},
|
|
42
|
+
getAnalytics(app: any): any {
|
|
43
|
+
return webAnalyticsModule.getAnalytics(app);
|
|
44
|
+
},
|
|
45
|
+
async logEvent(
|
|
46
|
+
analytics: any,
|
|
47
|
+
eventName: string,
|
|
48
|
+
params?: Record<string, any>,
|
|
49
|
+
): Promise<void> {
|
|
50
|
+
await webAnalyticsModule.logEvent(analytics, eventName, params);
|
|
51
|
+
},
|
|
52
|
+
async setUserId(analytics: any, userId: string): Promise<void> {
|
|
53
|
+
await webAnalyticsModule.setUserId(analytics, userId);
|
|
54
|
+
},
|
|
55
|
+
async setUserProperties(
|
|
56
|
+
analytics: any,
|
|
57
|
+
properties: Record<string, string>,
|
|
58
|
+
): Promise<void> {
|
|
59
|
+
await webAnalyticsModule.setUserProperties(analytics, properties);
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
: null;
|
|
63
|
+
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firebase Analytics Service
|
|
3
|
+
* Single Responsibility: Orchestrate analytics operations
|
|
4
|
+
* Delegates to specialized services for initialization, events, and user management
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { analyticsInitializerService } from './analytics-initializer.service';
|
|
8
|
+
import { analyticsEventService } from './analytics-event.service';
|
|
9
|
+
import { analyticsUserService } from './analytics-user.service';
|
|
10
|
+
import type { AnalyticsInstance } from './analytics-initializer.service';
|
|
11
|
+
import type { IAnalyticsService } from '../../application/ports/IAnalyticsService';
|
|
12
|
+
import { nativeAnalyticsAdapter } from '../adapters/native-analytics.adapter';
|
|
13
|
+
|
|
14
|
+
export type { IAnalyticsService };
|
|
15
|
+
|
|
16
|
+
class FirebaseAnalyticsService implements IAnalyticsService {
|
|
17
|
+
private isInitialized = false;
|
|
18
|
+
private userId: string | null = null;
|
|
19
|
+
private userProperties: Record<string, string> = {};
|
|
20
|
+
private analyticsInstance: AnalyticsInstance | null = null;
|
|
21
|
+
|
|
22
|
+
async init(userId?: string): Promise<void> {
|
|
23
|
+
if (this.isInitialized) {
|
|
24
|
+
if (userId && userId !== this.userId) {
|
|
25
|
+
await this.setUserId(userId);
|
|
26
|
+
}
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
this.analyticsInstance = await analyticsInitializerService.initialize();
|
|
32
|
+
|
|
33
|
+
if (this.analyticsInstance) {
|
|
34
|
+
if (userId) {
|
|
35
|
+
this.userId = userId;
|
|
36
|
+
await analyticsUserService.setUserId(this.analyticsInstance, userId);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Explicitly enable analytics collection to ensure it's active
|
|
40
|
+
await this.setAnalyticsCollectionEnabled(true);
|
|
41
|
+
|
|
42
|
+
/* eslint-disable-next-line no-console */
|
|
43
|
+
if (__DEV__) {
|
|
44
|
+
console.log('✅ Firebase Analytics initialized successfully', {
|
|
45
|
+
platform: this.analyticsInstance.platform,
|
|
46
|
+
userId: userId || 'guest',
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
/* eslint-disable-next-line no-console */
|
|
51
|
+
if (__DEV__) {
|
|
52
|
+
console.warn('⚠️ Firebase Analytics: Initialization returned null instance', {
|
|
53
|
+
reason: 'Native module not available or web analytics not supported',
|
|
54
|
+
note: 'Events will be logged to console but not sent to Firebase',
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
} catch (_error) {
|
|
59
|
+
// Analytics is non-critical, fail silently
|
|
60
|
+
} finally {
|
|
61
|
+
this.isInitialized = true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async setAnalyticsCollectionEnabled(enabled: boolean): Promise<void> {
|
|
66
|
+
if (!this.analyticsInstance) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
if (this.analyticsInstance.platform === 'native' && nativeAnalyticsAdapter) {
|
|
72
|
+
await nativeAnalyticsAdapter.setAnalyticsCollectionEnabled(
|
|
73
|
+
this.analyticsInstance.instance,
|
|
74
|
+
enabled,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
} catch (_error) {
|
|
78
|
+
// Fail silently
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async setUserId(userId: string): Promise<void> {
|
|
83
|
+
if (!this.isInitialized || !this.analyticsInstance) {
|
|
84
|
+
// Not initialized yet, will be set during init
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (this.userId === userId) {
|
|
89
|
+
// Already set to this user ID
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
this.userId = userId;
|
|
95
|
+
await analyticsUserService.setUserId(this.analyticsInstance, userId);
|
|
96
|
+
} catch (_error) {
|
|
97
|
+
// Analytics is non-critical, fail silently
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async logEvent(
|
|
102
|
+
eventName: string,
|
|
103
|
+
params?: Record<string, string | number | boolean | null>,
|
|
104
|
+
): Promise<void> {
|
|
105
|
+
if (!this.isInitialized) {
|
|
106
|
+
/* eslint-disable-next-line no-console */
|
|
107
|
+
if (__DEV__) {
|
|
108
|
+
console.warn('⚠️ Firebase Analytics: Cannot log event - Service not initialized', {
|
|
109
|
+
eventName,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
await analyticsEventService.logEvent(this.analyticsInstance, eventName, params);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async setUserProperty(key: string, value: string): Promise<void> {
|
|
118
|
+
this.userProperties[key] = value;
|
|
119
|
+
await analyticsUserService.setUserProperty(this.analyticsInstance, key, value);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async setUserProperties(properties: Record<string, string>): Promise<void> {
|
|
123
|
+
await analyticsUserService.setUserProperties(this.analyticsInstance, properties);
|
|
124
|
+
Object.assign(this.userProperties, properties);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async clearUserData(): Promise<void> {
|
|
128
|
+
await analyticsUserService.clearUserData(this.analyticsInstance);
|
|
129
|
+
this.userId = null;
|
|
130
|
+
this.userProperties = {};
|
|
131
|
+
this.isInitialized = false;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
getCurrentUserId(): string | null {
|
|
135
|
+
return this.userId;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async logScreenView(params: {
|
|
139
|
+
screen_name: string;
|
|
140
|
+
screen_class?: string;
|
|
141
|
+
}): Promise<void> {
|
|
142
|
+
await this.logEvent('screen_view', {
|
|
143
|
+
screen_name: params.screen_name,
|
|
144
|
+
screen_class: params.screen_class || params.screen_name,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async logScreenTime(params: {
|
|
149
|
+
screen_name: string;
|
|
150
|
+
screen_class?: string;
|
|
151
|
+
time_spent_seconds: number;
|
|
152
|
+
}): Promise<void> {
|
|
153
|
+
await this.logEvent('screen_time', {
|
|
154
|
+
screen_name: params.screen_name,
|
|
155
|
+
screen_class: params.screen_class || params.screen_name,
|
|
156
|
+
time_spent_seconds: params.time_spent_seconds,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async logNavigation(params: {
|
|
161
|
+
from_screen: string;
|
|
162
|
+
to_screen: string;
|
|
163
|
+
screen_class?: string;
|
|
164
|
+
}): Promise<void> {
|
|
165
|
+
await this.logEvent('navigation', {
|
|
166
|
+
from_screen: params.from_screen,
|
|
167
|
+
to_screen: params.to_screen,
|
|
168
|
+
screen_class: params.screen_class || params.to_screen,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async logButtonClick(params: {
|
|
173
|
+
button_id: string;
|
|
174
|
+
button_name?: string;
|
|
175
|
+
screen_name: string;
|
|
176
|
+
screen_class?: string;
|
|
177
|
+
}): Promise<void> {
|
|
178
|
+
await this.logEvent('button_click', {
|
|
179
|
+
button_id: params.button_id,
|
|
180
|
+
button_name: params.button_name || params.button_id,
|
|
181
|
+
screen_name: params.screen_name,
|
|
182
|
+
screen_class: params.screen_class || params.screen_name,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export const firebaseAnalyticsService = new FirebaseAnalyticsService();
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance Tracker Service
|
|
3
|
+
*
|
|
4
|
+
* Single Responsibility: Track performance metrics of operations
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export class PerformanceTracker {
|
|
8
|
+
private activeTracks = new Map<string, number>();
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Start tracking an operation
|
|
12
|
+
*/
|
|
13
|
+
start(trackingId: string): void {
|
|
14
|
+
this.activeTracks.set(trackingId, Date.now());
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* End tracking and return duration
|
|
19
|
+
*/
|
|
20
|
+
async end(trackingId: string, params: Record<string, any> = {}): Promise<number> {
|
|
21
|
+
const startTime = this.activeTracks.get(trackingId);
|
|
22
|
+
if (!startTime) return 0;
|
|
23
|
+
|
|
24
|
+
const duration = Date.now() - startTime;
|
|
25
|
+
this.activeTracks.delete(trackingId);
|
|
26
|
+
|
|
27
|
+
// In a full implementation, this could send to Firebase Performance
|
|
28
|
+
// For now, we just log it or handle it as a custom metric
|
|
29
|
+
if (__DEV__) {
|
|
30
|
+
console.log(`⏱️ [Performance] ${trackingId}: ${duration}ms`, params);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return duration;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Track a function execution
|
|
38
|
+
*/
|
|
39
|
+
async track<T>(trackingId: string, fn: () => Promise<T>): Promise<T> {
|
|
40
|
+
this.start(trackingId);
|
|
41
|
+
try {
|
|
42
|
+
return await fn();
|
|
43
|
+
} finally {
|
|
44
|
+
await this.end(trackingId);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const performanceTracker = new PerformanceTracker();
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics Event Service
|
|
3
|
+
* Single Responsibility: Handle event logging 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 AnalyticsEventService {
|
|
11
|
+
/**
|
|
12
|
+
* Log event to analytics
|
|
13
|
+
*/
|
|
14
|
+
async logEvent(
|
|
15
|
+
analyticsInstance: AnalyticsInstance | null,
|
|
16
|
+
eventName: string,
|
|
17
|
+
params?: Record<string, string | number | boolean | null>,
|
|
18
|
+
): Promise<void> {
|
|
19
|
+
if (!analyticsInstance) {
|
|
20
|
+
/* eslint-disable-next-line no-console */
|
|
21
|
+
if (__DEV__) {
|
|
22
|
+
console.warn('⚠️ Firebase Analytics: Cannot log event - Analytics not initialized', {
|
|
23
|
+
eventName,
|
|
24
|
+
reason: 'Analytics instance is null (native module not available in Expo Go)',
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
if (analyticsInstance.platform === 'native' && nativeAnalyticsAdapter) {
|
|
32
|
+
await nativeAnalyticsAdapter.logEvent(
|
|
33
|
+
analyticsInstance.instance,
|
|
34
|
+
eventName,
|
|
35
|
+
params,
|
|
36
|
+
);
|
|
37
|
+
/* eslint-disable-next-line no-console */
|
|
38
|
+
if (__DEV__) {
|
|
39
|
+
console.log('📊 Firebase Analytics Event (native):', {
|
|
40
|
+
eventName,
|
|
41
|
+
params,
|
|
42
|
+
platform: 'iOS/Android',
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
} else if (analyticsInstance.platform === 'web' && webAnalyticsAdapter) {
|
|
46
|
+
await webAnalyticsAdapter.logEvent(
|
|
47
|
+
analyticsInstance.instance,
|
|
48
|
+
eventName,
|
|
49
|
+
params,
|
|
50
|
+
);
|
|
51
|
+
/* eslint-disable-next-line no-console */
|
|
52
|
+
if (__DEV__) {
|
|
53
|
+
console.log('📊 Firebase Analytics Event:', {
|
|
54
|
+
eventName,
|
|
55
|
+
params,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
} catch (error) {
|
|
60
|
+
/* eslint-disable-next-line no-console */
|
|
61
|
+
if (__DEV__) {
|
|
62
|
+
console.warn('⚠️ Firebase Analytics: Failed to log event', {
|
|
63
|
+
eventName,
|
|
64
|
+
error: error instanceof Error ? error.message : String(error),
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const analyticsEventService = new AnalyticsEventService();
|
|
72
|
+
|