/** * useFCM Hook - Platform-aware FCM token management * * Native: Full FCM support with token registration and notification handling * Web: Gracefully skips FCM (web push requires additional service worker setup) */ import { useEffect, useRef } from 'react'; import { Platform, AppState, AppStateStatus } from 'react-native'; import { useAuthStore } from '../stores/authStore'; // Only import FCMService and messaging on native platforms let FCMService: any = null; let messaging: any = null; if (Platform.OS !== 'web') { FCMService = require('../services/fcmService').FCMService; messaging = require('@react-native-firebase/messaging').default; } /** * Hook to manage FCM token registration and notification handling * Call this hook once at the app root level when user is authenticated */ export const useFCM = () => { const { user } = useAuthStore(); const foregroundUnsubscribeRef = useRef<(() => void) | null>(null); const notificationOpenedUnsubscribeRef = useRef<(() => void) | null>(null); const initializedUserIdRef = useRef(null); useEffect(() => { // Skip FCM on web platform if (Platform.OS === 'web' || !FCMService) { return; } if (!user) { // User logged out - clean up if (initializedUserIdRef.current) { FCMService.cleanup(); if (foregroundUnsubscribeRef.current) { foregroundUnsubscribeRef.current(); foregroundUnsubscribeRef.current = null; } if (notificationOpenedUnsubscribeRef.current) { notificationOpenedUnsubscribeRef.current(); notificationOpenedUnsubscribeRef.current = null; } initializedUserIdRef.current = null; } return; } // User is authenticated - initialize FCM // Clean up previous user's listeners if user changed if (initializedUserIdRef.current && initializedUserIdRef.current !== user.uid) { FCMService.cleanup(); if (foregroundUnsubscribeRef.current) { foregroundUnsubscribeRef.current(); foregroundUnsubscribeRef.current = null; } if (notificationOpenedUnsubscribeRef.current) { notificationOpenedUnsubscribeRef.current(); notificationOpenedUnsubscribeRef.current = null; } } initializedUserIdRef.current = user.uid; // Update FCM token for the existing user FCMService.updateTokenForExistingUser(user.uid).catch((error: any) => { console.error('Failed to update FCM token:', error); }); // Set up handlers only if not already set up for this user if (!foregroundUnsubscribeRef.current) { // Set up foreground message handler foregroundUnsubscribeRef.current = FCMService.setupForegroundMessageHandler(); // Set up notification opened handler notificationOpenedUnsubscribeRef.current = FCMService.setupNotificationOpenedHandler( (remoteMessage: any) => { console.log('Notification opened:', remoteMessage); } ); // Check if app was opened from a notification FCMService.getInitialNotification().then((remoteMessage: any) => { if (remoteMessage) { console.log('App opened from notification:', remoteMessage); } }); } // Cleanup on unmount return () => { if (foregroundUnsubscribeRef.current) { foregroundUnsubscribeRef.current(); foregroundUnsubscribeRef.current = null; } if (notificationOpenedUnsubscribeRef.current) { notificationOpenedUnsubscribeRef.current(); notificationOpenedUnsubscribeRef.current = null; } }; }, [user]); // Save token when app comes to foreground useEffect(() => { // Skip on web if (Platform.OS === 'web' || !FCMService || !user) { return; } const handleAppStateChange = (nextAppState: AppStateStatus) => { if (nextAppState === 'active' && user) { FCMService.updateTokenForExistingUser(user.uid).catch((error: any) => { console.error('Failed to refresh FCM token on app foreground:', error); }); } }; const subscription = AppState.addEventListener('change', handleAppStateChange); return () => { subscription.remove(); }; }, [user]); // Clean up on unmount useEffect(() => { // Skip on web if (Platform.OS === 'web' || !FCMService) { return; } return () => { FCMService.cleanup(); if (foregroundUnsubscribeRef.current) { foregroundUnsubscribeRef.current(); } if (notificationOpenedUnsubscribeRef.current) { notificationOpenedUnsubscribeRef.current(); } }; }, []); };