@umituz/react-native-firebase 1.13.6 → 1.13.7
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 +3 -4
- package/src/index.ts +2 -10
- package/src/analytics/application/ports/IAnalyticsService.ts +0 -92
- package/src/analytics/index.ts +0 -20
- package/src/analytics/infrastructure/adapters/index.ts +0 -10
- package/src/analytics/infrastructure/adapters/native-analytics.adapter.ts +0 -63
- package/src/analytics/infrastructure/adapters/web-analytics.adapter.ts +0 -63
- package/src/analytics/infrastructure/services/FirebaseAnalyticsService.ts +0 -165
- package/src/analytics/infrastructure/services/PerformanceTracker.ts +0 -49
- package/src/analytics/infrastructure/services/analytics-event.service.ts +0 -72
- package/src/analytics/infrastructure/services/analytics-initializer.service.ts +0 -141
- package/src/analytics/infrastructure/services/analytics-user.service.ts +0 -98
- package/src/analytics/infrastructure/services/index.ts +0 -12
- package/src/analytics/presentation/decorators/PerformanceDecorator.ts +0 -73
- package/src/analytics/presentation/decorators/TrackingDecorator.ts +0 -38
- package/src/analytics/presentation/hooks/useNavigationAnalytics.ts +0 -118
- package/src/analytics/presentation/hooks/useNavigationTracking.ts +0 -62
- package/src/analytics/presentation/hooks/useScreenTime.ts +0 -69
- package/src/analytics/presentation/hooks/useScreenView.ts +0 -70
- package/src/analytics/presentation/utils/analyticsUtils.ts +0 -78
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-firebase",
|
|
3
|
-
"version": "1.13.
|
|
4
|
-
"description": "Unified Firebase package for React Native apps -
|
|
3
|
+
"version": "1.13.7",
|
|
4
|
+
"description": "Unified Firebase package for React Native apps - Auth and Firestore services using Firebase JS SDK (no native modules).",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
7
7
|
"scripts": {
|
|
@@ -14,13 +14,12 @@
|
|
|
14
14
|
"keywords": [
|
|
15
15
|
"react-native",
|
|
16
16
|
"firebase",
|
|
17
|
-
"firebase-
|
|
17
|
+
"firebase-js-sdk",
|
|
18
18
|
"auth",
|
|
19
19
|
"authentication",
|
|
20
20
|
"firestore",
|
|
21
21
|
"database",
|
|
22
22
|
"repository",
|
|
23
|
-
"analytics",
|
|
24
23
|
"ddd",
|
|
25
24
|
"domain-driven-design"
|
|
26
25
|
],
|
package/src/index.ts
CHANGED
|
@@ -4,15 +4,13 @@
|
|
|
4
4
|
* Domain-Driven Design (DDD) Architecture
|
|
5
5
|
*
|
|
6
6
|
* This package provides Firebase App initialization and core services:
|
|
7
|
-
* - Auth
|
|
8
|
-
* - Firestore
|
|
9
|
-
* - Analytics
|
|
7
|
+
* - Auth (Firebase JS SDK)
|
|
8
|
+
* - Firestore (Firebase JS SDK)
|
|
10
9
|
*
|
|
11
10
|
* Usage:
|
|
12
11
|
* import { initializeFirebase, getFirebaseApp } from '@umituz/react-native-firebase';
|
|
13
12
|
* import { useFirebaseAuth } from '@umituz/react-native-firebase';
|
|
14
13
|
* import { getFirestore, BaseRepository } from '@umituz/react-native-firebase';
|
|
15
|
-
* import { firebaseAnalyticsService } from '@umituz/react-native-firebase';
|
|
16
14
|
*/
|
|
17
15
|
|
|
18
16
|
// =============================================================================
|
|
@@ -58,9 +56,3 @@ export * from './auth';
|
|
|
58
56
|
|
|
59
57
|
export * from './firestore';
|
|
60
58
|
|
|
61
|
-
// =============================================================================
|
|
62
|
-
// ANALYTICS MODULE
|
|
63
|
-
// =============================================================================
|
|
64
|
-
|
|
65
|
-
export * from './analytics';
|
|
66
|
-
|
|
@@ -1,92 +0,0 @@
|
|
|
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
|
-
}
|
package/src/analytics/index.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
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
|
-
export { useNavigationAnalytics } from './presentation/hooks/useNavigationAnalytics';
|
|
18
|
-
|
|
19
|
-
// Utility Functions
|
|
20
|
-
export { trackButtonClick, trackCRUDOperation } from './presentation/utils/analyticsUtils';
|
|
@@ -1,10 +0,0 @@
|
|
|
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
|
-
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Native Analytics Adapter
|
|
3
|
-
* Single Responsibility: Handle Firebase Analytics native implementation
|
|
4
|
-
* Uses React Native Firebase v23+ modular API
|
|
5
|
-
*
|
|
6
|
-
* NOTE: This adapter uses optional import to support Expo Go.
|
|
7
|
-
* Native modules are only available in development builds or standalone apps.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
// Optional import - will be null in Expo Go
|
|
11
|
-
let nativeAnalytics: any = null;
|
|
12
|
-
try {
|
|
13
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
14
|
-
nativeAnalytics = require('@react-native-firebase/analytics');
|
|
15
|
-
} catch {
|
|
16
|
-
// Native module not available (e.g., Expo Go)
|
|
17
|
-
// eslint-disable-next-line no-console
|
|
18
|
-
if (__DEV__) console.warn('⚠️ Firebase Analytics: Native module not available');
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface NativeAnalyticsAdapter {
|
|
22
|
-
getAnalytics(): any;
|
|
23
|
-
logEvent(analytics: any, eventName: string, params?: Record<string, any>): Promise<void>;
|
|
24
|
-
setUserId(analytics: any, userId: string): Promise<void>;
|
|
25
|
-
setUserProperties(analytics: any, properties: Record<string, string>): Promise<void>;
|
|
26
|
-
resetAnalyticsData(analytics: any): Promise<void>;
|
|
27
|
-
setAnalyticsCollectionEnabled(analytics: any, enabled: boolean): Promise<void>;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
declare const __DEV__: boolean;
|
|
31
|
-
|
|
32
|
-
export const nativeAnalyticsAdapter: NativeAnalyticsAdapter | null = nativeAnalytics
|
|
33
|
-
? {
|
|
34
|
-
getAnalytics(): any {
|
|
35
|
-
return nativeAnalytics.getAnalytics();
|
|
36
|
-
},
|
|
37
|
-
async logEvent(
|
|
38
|
-
analytics: any,
|
|
39
|
-
eventName: string,
|
|
40
|
-
params?: Record<string, any>,
|
|
41
|
-
): Promise<void> {
|
|
42
|
-
await nativeAnalytics.logEvent(analytics, eventName, params);
|
|
43
|
-
},
|
|
44
|
-
async setUserId(analytics: any, userId: string): Promise<void> {
|
|
45
|
-
await nativeAnalytics.setUserId(analytics, userId);
|
|
46
|
-
},
|
|
47
|
-
async setUserProperties(
|
|
48
|
-
analytics: any,
|
|
49
|
-
properties: Record<string, string>,
|
|
50
|
-
): Promise<void> {
|
|
51
|
-
await nativeAnalytics.setUserProperties(analytics, properties);
|
|
52
|
-
},
|
|
53
|
-
async resetAnalyticsData(analytics: any): Promise<void> {
|
|
54
|
-
await nativeAnalytics.resetAnalyticsData(analytics);
|
|
55
|
-
},
|
|
56
|
-
async setAnalyticsCollectionEnabled(
|
|
57
|
-
analytics: any,
|
|
58
|
-
enabled: boolean,
|
|
59
|
-
): Promise<void> {
|
|
60
|
-
await nativeAnalytics.setAnalyticsCollectionEnabled(analytics, enabled);
|
|
61
|
-
},
|
|
62
|
-
}
|
|
63
|
-
: null;
|
|
@@ -1,63 +0,0 @@
|
|
|
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
|
-
|
|
@@ -1,165 +0,0 @@
|
|
|
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
|
-
} catch (_error) {
|
|
43
|
-
// Analytics is non-critical, fail silently
|
|
44
|
-
} finally {
|
|
45
|
-
this.isInitialized = true;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async setAnalyticsCollectionEnabled(enabled: boolean): Promise<void> {
|
|
50
|
-
if (!this.analyticsInstance) {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
try {
|
|
55
|
-
if (this.analyticsInstance.platform === 'native' && nativeAnalyticsAdapter) {
|
|
56
|
-
await nativeAnalyticsAdapter.setAnalyticsCollectionEnabled(
|
|
57
|
-
this.analyticsInstance.instance,
|
|
58
|
-
enabled,
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
} catch (_error) {
|
|
62
|
-
// Fail silently
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async setUserId(userId: string): Promise<void> {
|
|
67
|
-
if (!this.isInitialized || !this.analyticsInstance) {
|
|
68
|
-
// Not initialized yet, will be set during init
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (this.userId === userId) {
|
|
73
|
-
// Already set to this user ID
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
try {
|
|
78
|
-
this.userId = userId;
|
|
79
|
-
await analyticsUserService.setUserId(this.analyticsInstance, userId);
|
|
80
|
-
} catch (_error) {
|
|
81
|
-
// Analytics is non-critical, fail silently
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async logEvent(
|
|
86
|
-
eventName: string,
|
|
87
|
-
params?: Record<string, string | number | boolean | null>,
|
|
88
|
-
): Promise<void> {
|
|
89
|
-
if (!this.isInitialized) {
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
await analyticsEventService.logEvent(this.analyticsInstance, eventName, params);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
async setUserProperty(key: string, value: string): Promise<void> {
|
|
96
|
-
this.userProperties[key] = value;
|
|
97
|
-
await analyticsUserService.setUserProperty(this.analyticsInstance, key, value);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
async setUserProperties(properties: Record<string, string>): Promise<void> {
|
|
101
|
-
await analyticsUserService.setUserProperties(this.analyticsInstance, properties);
|
|
102
|
-
Object.assign(this.userProperties, properties);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
async clearUserData(): Promise<void> {
|
|
106
|
-
await analyticsUserService.clearUserData(this.analyticsInstance);
|
|
107
|
-
this.userId = null;
|
|
108
|
-
this.userProperties = {};
|
|
109
|
-
this.isInitialized = false;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
getCurrentUserId(): string | null {
|
|
113
|
-
return this.userId;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
async logScreenView(params: {
|
|
117
|
-
screen_name: string;
|
|
118
|
-
screen_class?: string;
|
|
119
|
-
}): Promise<void> {
|
|
120
|
-
await this.logEvent('screen_view', {
|
|
121
|
-
screen_name: params.screen_name,
|
|
122
|
-
screen_class: params.screen_class || params.screen_name,
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
async logScreenTime(params: {
|
|
127
|
-
screen_name: string;
|
|
128
|
-
screen_class?: string;
|
|
129
|
-
time_spent_seconds: number;
|
|
130
|
-
}): Promise<void> {
|
|
131
|
-
await this.logEvent('screen_time', {
|
|
132
|
-
screen_name: params.screen_name,
|
|
133
|
-
screen_class: params.screen_class || params.screen_name,
|
|
134
|
-
time_spent_seconds: params.time_spent_seconds,
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
async logNavigation(params: {
|
|
139
|
-
from_screen: string;
|
|
140
|
-
to_screen: string;
|
|
141
|
-
screen_class?: string;
|
|
142
|
-
}): Promise<void> {
|
|
143
|
-
await this.logEvent('navigation', {
|
|
144
|
-
from_screen: params.from_screen,
|
|
145
|
-
to_screen: params.to_screen,
|
|
146
|
-
screen_class: params.screen_class || params.to_screen,
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
async logButtonClick(params: {
|
|
151
|
-
button_id: string;
|
|
152
|
-
button_name?: string;
|
|
153
|
-
screen_name: string;
|
|
154
|
-
screen_class?: string;
|
|
155
|
-
}): Promise<void> {
|
|
156
|
-
await this.logEvent('button_click', {
|
|
157
|
-
button_id: params.button_id,
|
|
158
|
-
button_name: params.button_name || params.button_id,
|
|
159
|
-
screen_name: params.screen_name,
|
|
160
|
-
screen_class: params.screen_class || params.screen_name,
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
export const firebaseAnalyticsService = new FirebaseAnalyticsService();
|
|
@@ -1,49 +0,0 @@
|
|
|
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();
|
|
@@ -1,72 +0,0 @@
|
|
|
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
|
-
|
|
@@ -1,141 +0,0 @@
|
|
|
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
|
-
const instance = nativeAnalyticsAdapter.getAnalytics();
|
|
64
|
-
if (!instance) {
|
|
65
|
-
/* eslint-disable-next-line no-console */
|
|
66
|
-
if (__DEV__) {
|
|
67
|
-
console.warn('⚠️ Firebase Analytics: getAnalytics() returned null');
|
|
68
|
-
}
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/* eslint-disable-next-line no-console */
|
|
73
|
-
if (__DEV__) {
|
|
74
|
-
console.log('✅ Firebase Analytics initialized (native)');
|
|
75
|
-
}
|
|
76
|
-
return { instance, platform: 'native' };
|
|
77
|
-
} catch (error: any) {
|
|
78
|
-
/* eslint-disable-next-line no-console */
|
|
79
|
-
if (__DEV__) {
|
|
80
|
-
const errorMessage = error?.message || String(error);
|
|
81
|
-
if (errorMessage.includes('No Firebase App') || errorMessage.includes('has been created')) {
|
|
82
|
-
console.warn(
|
|
83
|
-
'⚠️ Firebase Analytics: Firebase App not initialized. Please add GoogleService-Info.plist to ios/ directory and rebuild the app.',
|
|
84
|
-
);
|
|
85
|
-
} else {
|
|
86
|
-
console.warn('⚠️ Firebase Analytics: Native initialization failed', error);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
private async initializeWeb(): Promise<AnalyticsInstance | null> {
|
|
94
|
-
try {
|
|
95
|
-
// Check if Analytics is supported
|
|
96
|
-
if (webAnalyticsAdapter) {
|
|
97
|
-
const isSupported = await webAnalyticsAdapter.isSupported();
|
|
98
|
-
if (!isSupported) {
|
|
99
|
-
/* eslint-disable-next-line no-console */
|
|
100
|
-
if (__DEV__) {
|
|
101
|
-
console.warn('⚠️ Firebase Analytics: Not supported in this environment (web)');
|
|
102
|
-
}
|
|
103
|
-
return null;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const app = getFirebaseApp();
|
|
108
|
-
if (!app) {
|
|
109
|
-
/* eslint-disable-next-line no-console */
|
|
110
|
-
if (__DEV__) {
|
|
111
|
-
console.warn('⚠️ Firebase Analytics: Firebase app not available');
|
|
112
|
-
}
|
|
113
|
-
return null;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
try {
|
|
117
|
-
const instance = webAnalyticsAdapter!.getAnalytics(app);
|
|
118
|
-
/* eslint-disable-next-line no-console */
|
|
119
|
-
if (__DEV__) {
|
|
120
|
-
console.log('✅ Firebase Analytics initialized (web)');
|
|
121
|
-
}
|
|
122
|
-
return { instance, platform: 'web' };
|
|
123
|
-
} catch (analyticsError) {
|
|
124
|
-
/* eslint-disable-next-line no-console */
|
|
125
|
-
if (__DEV__) {
|
|
126
|
-
console.warn('⚠️ Firebase Analytics: getAnalytics failed', analyticsError);
|
|
127
|
-
}
|
|
128
|
-
return null;
|
|
129
|
-
}
|
|
130
|
-
} catch (error) {
|
|
131
|
-
/* eslint-disable-next-line no-console */
|
|
132
|
-
if (__DEV__) {
|
|
133
|
-
console.warn('⚠️ Firebase Analytics: Web initialization failed', error);
|
|
134
|
-
}
|
|
135
|
-
return null;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export const analyticsInitializerService = new AnalyticsInitializerService();
|
|
141
|
-
|
|
@@ -1,98 +0,0 @@
|
|
|
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
|
-
|
|
@@ -1,12 +0,0 @@
|
|
|
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
|
-
|
|
@@ -1,73 +0,0 @@
|
|
|
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
|
-
errorType,
|
|
61
|
-
duration_ms: Date.now() - startTime,
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
throw error;
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
return descriptor;
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
|
|
@@ -1,38 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useNavigationAnalytics Hook
|
|
3
|
-
*
|
|
4
|
-
* Tracks navigation analytics at app level for NavigationContainer
|
|
5
|
-
* Migrated to Firebase modular SDK (v22+)
|
|
6
|
-
*
|
|
7
|
-
* Features:
|
|
8
|
-
* - Screen view tracking
|
|
9
|
-
* - Screen time measurement
|
|
10
|
-
* - Navigation flow analytics
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```typescript
|
|
14
|
-
* import { useNavigationAnalytics } from '@umituz/react-native-firebase';
|
|
15
|
-
*
|
|
16
|
-
* function App() {
|
|
17
|
-
* const { handleNavigationReady, handleNavigationStateChange } = useNavigationAnalytics();
|
|
18
|
-
*
|
|
19
|
-
* return (
|
|
20
|
-
* <NavigationContainer
|
|
21
|
-
* onReady={() => handleNavigationReady(navigationRef)}
|
|
22
|
-
* onStateChange={() => handleNavigationStateChange(navigationRef)}
|
|
23
|
-
* >
|
|
24
|
-
* <AppNavigator />
|
|
25
|
-
* </NavigationContainer>
|
|
26
|
-
* );
|
|
27
|
-
* }
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
|
-
import { useRef } from "react";
|
|
32
|
-
import { firebaseAnalyticsService } from "../../infrastructure/services/FirebaseAnalyticsService";
|
|
33
|
-
|
|
34
|
-
export const useNavigationAnalytics = () => {
|
|
35
|
-
const routeNameRef = useRef<string | undefined>(undefined);
|
|
36
|
-
const screenStartTimeRef = useRef<number | undefined>(undefined);
|
|
37
|
-
|
|
38
|
-
const getActiveRouteName = (state: any): string | undefined => {
|
|
39
|
-
if (!state || !state.routes) return undefined;
|
|
40
|
-
const route = state.routes[state.index];
|
|
41
|
-
if (route.state) {
|
|
42
|
-
return getActiveRouteName(route.state);
|
|
43
|
-
}
|
|
44
|
-
return route.name;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const handleNavigationReady = (navigationRef: any) => {
|
|
48
|
-
const currentRouteName = getActiveRouteName(
|
|
49
|
-
navigationRef.current?.getRootState(),
|
|
50
|
-
);
|
|
51
|
-
routeNameRef.current = currentRouteName;
|
|
52
|
-
screenStartTimeRef.current = Date.now();
|
|
53
|
-
|
|
54
|
-
if (currentRouteName) {
|
|
55
|
-
firebaseAnalyticsService
|
|
56
|
-
.logScreenView({
|
|
57
|
-
screen_name: currentRouteName,
|
|
58
|
-
screen_class: currentRouteName,
|
|
59
|
-
})
|
|
60
|
-
.catch(() => {
|
|
61
|
-
// Silent fail - analytics is non-critical
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
const handleNavigationStateChange = async (navigationRef: any) => {
|
|
67
|
-
const previousRouteName = routeNameRef.current;
|
|
68
|
-
const currentRouteName = getActiveRouteName(
|
|
69
|
-
navigationRef.current?.getRootState(),
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
if (previousRouteName !== currentRouteName && currentRouteName) {
|
|
73
|
-
if (previousRouteName && screenStartTimeRef.current) {
|
|
74
|
-
const timeSpent = Math.round(
|
|
75
|
-
(Date.now() - screenStartTimeRef.current) / 1000,
|
|
76
|
-
);
|
|
77
|
-
await firebaseAnalyticsService
|
|
78
|
-
.logScreenTime({
|
|
79
|
-
screen_name: previousRouteName,
|
|
80
|
-
screen_class: previousRouteName,
|
|
81
|
-
time_spent_seconds: timeSpent,
|
|
82
|
-
})
|
|
83
|
-
.catch(() => {
|
|
84
|
-
// Silent fail - analytics is non-critical
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
await firebaseAnalyticsService
|
|
89
|
-
.logScreenView({
|
|
90
|
-
screen_name: currentRouteName,
|
|
91
|
-
screen_class: currentRouteName,
|
|
92
|
-
})
|
|
93
|
-
.catch(() => {
|
|
94
|
-
// Silent fail - analytics is non-critical
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
if (previousRouteName) {
|
|
98
|
-
await firebaseAnalyticsService
|
|
99
|
-
.logNavigation({
|
|
100
|
-
from_screen: previousRouteName,
|
|
101
|
-
to_screen: currentRouteName,
|
|
102
|
-
screen_class: currentRouteName,
|
|
103
|
-
})
|
|
104
|
-
.catch(() => {
|
|
105
|
-
// Silent fail - analytics is non-critical
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
routeNameRef.current = currentRouteName;
|
|
110
|
-
screenStartTimeRef.current = Date.now();
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
return {
|
|
115
|
-
handleNavigationReady,
|
|
116
|
-
handleNavigationStateChange,
|
|
117
|
-
};
|
|
118
|
-
};
|
|
@@ -1,62 +0,0 @@
|
|
|
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
|
-
firebaseAnalyticsService
|
|
48
|
-
.logNavigation({
|
|
49
|
-
from_screen: previousScreenRef.current,
|
|
50
|
-
to_screen: screenName,
|
|
51
|
-
screen_class: screenClass || screenName,
|
|
52
|
-
})
|
|
53
|
-
.catch(() => {
|
|
54
|
-
// Silent fail - analytics is non-critical
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
previousScreenRef.current = screenName;
|
|
59
|
-
}, [screenName, screenClass]),
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
|
|
@@ -1,69 +0,0 @@
|
|
|
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
|
-
|
|
@@ -1,70 +0,0 @@
|
|
|
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
|
-
|
|
@@ -1,78 +0,0 @@
|
|
|
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
|
-
|