UNPKG

advanced-games-library

Version:

Advanced Gaming Library for React Native - Four Complete Games with iOS Compatibility Fixes

363 lines (323 loc) 8.59 kB
import { AnalyticsEvent } from '../core/types'; /** * AnalyticsService - handles event tracking and analytics */ export class AnalyticsService { private isEnabled: boolean; private eventQueue: AnalyticsEvent[] = []; private isOnline: boolean = true; private flushInterval?: NodeJS.Timeout; private readonly QUEUE_LIMIT = 100; private readonly FLUSH_INTERVAL = 30000; // 30 seconds constructor(enabled: boolean = true) { this.isEnabled = enabled; } /** * Initialize the analytics service */ async initialize(): Promise<void> { if (!this.isEnabled) { return; } // Set up periodic flush this.flushInterval = setInterval(() => { this.flush(); }, this.FLUSH_INTERVAL); // Load queued events from storage await this.loadQueuedEvents(); // Listen for network state changes (if available) this.setupNetworkListener(); } /** * Track an analytics event */ trackEvent(event: AnalyticsEvent): void { if (!this.isEnabled) { return; } // Add timestamp if not provided if (!event.timestamp) { event.timestamp = new Date(); } // Add to queue this.eventQueue.push(event); // Limit queue size if (this.eventQueue.length > this.QUEUE_LIMIT) { this.eventQueue = this.eventQueue.slice(-this.QUEUE_LIMIT); } // Try to flush if online if (this.isOnline) { this.flush(); } } /** * Track game start */ trackGameStart(gameId: string, properties: Record<string, any> = {}): void { this.trackEvent({ event: 'game_started', gameId, properties: { ...properties, timestamp: new Date() }, timestamp: new Date() }); } /** * Track game completion */ trackGameComplete(gameId: string, result: any, properties: Record<string, any> = {}): void { this.trackEvent({ event: 'game_completed', gameId, properties: { ...properties, score: result.score, timeSpent: result.timeSpent, completed: result.completed, difficulty: result.difficulty }, timestamp: new Date() }); } /** * Track custom event */ trackCustomEvent(eventName: string, properties: Record<string, any> = {}): void { this.trackEvent({ event: eventName, properties, timestamp: new Date() }); } /** * Track error */ trackError(error: Error | string, context: Record<string, any> = {}): void { this.trackEvent({ event: 'error_occurred', properties: { error: typeof error === 'string' ? error : error.message, stack: typeof error === 'object' ? error.stack : undefined, ...context }, timestamp: new Date() }); } /** * Track performance metric */ trackPerformance(metric: string, value: number, unit: string = 'ms'): void { this.trackEvent({ event: 'performance_metric', properties: { metric, value, unit, timestamp: new Date() }, timestamp: new Date() }); } /** * Flush queued events */ async flush(): Promise<void> { if (!this.isEnabled || this.eventQueue.length === 0) { return; } const eventsToSend = [...this.eventQueue]; this.eventQueue = []; try { await this.sendEvents(eventsToSend); } catch (error) { console.warn('Failed to send analytics events:', error); // Add events back to queue on failure this.eventQueue = [...eventsToSend, ...this.eventQueue]; // Limit queue size if (this.eventQueue.length > this.QUEUE_LIMIT) { this.eventQueue = this.eventQueue.slice(-this.QUEUE_LIMIT); } } } /** * Enable/disable analytics */ setEnabled(enabled: boolean): void { this.isEnabled = enabled; if (!enabled) { this.eventQueue = []; if (this.flushInterval) { clearInterval(this.flushInterval); this.flushInterval = undefined; } } else if (!this.flushInterval) { this.flushInterval = setInterval(() => { this.flush(); }, this.FLUSH_INTERVAL); } } /** * Get analytics statistics */ getStats(): { queuedEvents: number; isEnabled: boolean; isOnline: boolean; } { return { queuedEvents: this.eventQueue.length, isEnabled: this.isEnabled, isOnline: this.isOnline }; } /** * Private methods */ private async sendEvents(events: AnalyticsEvent[]): Promise<void> { // This is where you would send events to your analytics service // For example: Firebase Analytics, Mixpanel, custom backend, etc. // For now, just log to console in development if (__DEV__) { console.log('Analytics Events:', events); } // Example implementation for a custom backend: /* try { await fetch('https://your-analytics-endpoint.com/events', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(events), }); } catch (error) { throw new Error(`Failed to send analytics: ${error}`); } */ // For Firebase Analytics: /* try { const analytics = await import('@react-native-firebase/analytics'); for (const event of events) { await analytics.default().logEvent(event.event, event.properties); } } catch (error) { throw new Error(`Failed to send to Firebase: ${error}`); } */ // Simulate network delay await new Promise(resolve => setTimeout(resolve, 100)); } private async loadQueuedEvents(): Promise<void> { try { // Try to load events from storage (implement based on your storage solution) // const { StorageService } = await import('./StorageService'); // const storage = new StorageService(); // const queuedEvents = await storage.getData('analytics_queue'); // if (queuedEvents) { // this.eventQueue = queuedEvents; // } } catch (error) { console.warn('Failed to load queued analytics events:', error); } } private setupNetworkListener(): void { try { // Listen for network state changes (React Native NetInfo) // const NetInfo = require('@react-native-community/netinfo'); // NetInfo.addEventListener(state => { // this.isOnline = state.isConnected; // if (this.isOnline && this.eventQueue.length > 0) { // this.flush(); // } // }); } catch (error) { console.warn('Network listener not available:', error); } } /** * Cleanup */ destroy(): void { if (this.flushInterval) { clearInterval(this.flushInterval); this.flushInterval = undefined; } // Final flush this.flush(); } } /** * Analytics event builders for common events */ export class AnalyticsEventBuilder { static gameStarted(gameId: string, properties: Record<string, any> = {}): AnalyticsEvent { return { event: 'game_started', gameId, properties: { ...properties, timestamp: new Date() }, timestamp: new Date() }; } static gameCompleted(gameId: string, result: any): AnalyticsEvent { return { event: 'game_completed', gameId, properties: { score: result.score, timeSpent: result.timeSpent, completed: result.completed, difficulty: result.difficulty }, timestamp: new Date() }; } static scoreAchieved(gameId: string, score: number, isHighScore: boolean = false): AnalyticsEvent { return { event: 'score_achieved', gameId, properties: { score, isHighScore }, timestamp: new Date() }; } static achievementUnlocked(achievementId: string, gameId?: string): AnalyticsEvent { return { event: 'achievement_unlocked', gameId, properties: { achievementId }, timestamp: new Date() }; } static userAction(action: string, gameId?: string, properties: Record<string, any> = {}): AnalyticsEvent { return { event: 'user_action', gameId, properties: { action, ...properties }, timestamp: new Date() }; } static performanceMetric(metric: string, value: number, gameId?: string): AnalyticsEvent { return { event: 'performance', gameId, properties: { metric, value }, timestamp: new Date() }; } }