diff --git a/App.tsx b/App.tsx index 0de681b..919bdfc 100644 --- a/App.tsx +++ b/App.tsx @@ -5,17 +5,20 @@ import { SafeAreaProvider } from 'react-native-safe-area-context'; import { PaperProvider } from 'react-native-paper'; import AppNavigator from './src/navigation/AppNavigator'; import notifee from '@notifee/react-native'; -import { backgroundHandler, foregroundListener, requestPermission, tokenRefreshHandler } from './src/utils/firebase/firebaseServices'; +import { backgroundHandler, foregroundListener, requestPermission, setupFirebaseHandlers, setupNotificationPressHandlers, tokenRefreshHandler } from './src/utils/firebase/firebaseServices'; function App() { const isDarkMode = useColorScheme() === 'dark'; + useEffect(() => { // 🔹 Ask for permission requestPermission(); // 🔹 Handle background + quit state - backgroundHandler(); + backgroundHandler(); + setupFirebaseHandlers() + setupNotificationPressHandlers() // 🔹 Listen for foreground messages const unsubscribe = foregroundListener(); diff --git a/src/navigation/AppNavigator.tsx b/src/navigation/AppNavigator.tsx index 0096fb1..58cfcfe 100644 --- a/src/navigation/AppNavigator.tsx +++ b/src/navigation/AppNavigator.tsx @@ -22,6 +22,8 @@ import TransactionsList from "../screens/transaction/TransactionList"; import RandomSelectUserScreen from "../screens/management/RandomSelectUserScreen"; import Notification from "../screens/notification/Notification"; import ManagementScreen from "../screens/management/ManagementScreen"; +import { navigationRef } from "../utils/navigationService"; +import NotificationRedirection from "../screens/NotificationRedirection"; export type RootStackParamList = { Splash: undefined; @@ -44,6 +46,7 @@ export type RootStackParamList = { TransactionList: undefined RandomSelectUserScreen: { groupId: string | number, cycleNumber: number }; Notification: undefined; + NotificationRedirection:{ data: any }; Management: | { actionTab: "My Groups" } | { actionTab1: "Joined Groups" } @@ -88,8 +91,8 @@ export default function AppNavigator() { return ( - - + + @@ -111,6 +114,7 @@ export default function AppNavigator() { + ); diff --git a/src/screens/NotificationRedirection.tsx b/src/screens/NotificationRedirection.tsx new file mode 100644 index 0000000..b6f3781 --- /dev/null +++ b/src/screens/NotificationRedirection.tsx @@ -0,0 +1,99 @@ +// src/screens/NotificationRedirection.tsx + +import React, { useEffect } from 'react'; +import { View, StyleSheet, ActivityIndicator, Text } from 'react-native'; +import { useRoute, RouteProp, useNavigation } from '@react-navigation/native'; +import { StackNavigationProp } from '@react-navigation/stack'; +import { RootStackParamList } from '../navigation/types'; + +// Define the specific route prop type for this screen +type NotificationRedirectionRouteProp = RouteProp; + +const NotificationRedirection: React.FC = () => { + const route = useRoute(); + const navigation = useNavigation>(); + const { data } = route.params; + + useEffect(() => { + // This effect runs once when the component mounts + if (data) { + console.log('🔄 Processing notification data:', data); + + // --- Your Redirection Logic Goes Here --- + // Use the 'data' object to decide where to go. + // For this example, we'll navigate based on 'msgType'. + + let destination: keyof RootStackParamList = 'Home'; // Default destination + let params: object | undefined = undefined; + + switch (data.msgType) { + case 'JOIN_REQUEST': + destination = 'Home'; + params = { + screen: 'Management', + params: { actionTab: 'Requests' }, + }; + break; + case 'JOIN_APPROVED': + destination = 'Home'; + params = { + screen: 'Management', + params: { actionTab: 'Joined Groups' }, + }; + break; + case 'GROUP_STARTED': + destination = 'GroupDetails'; + params = { + item: {id: data?.groupId, + }, + }; + break; + // 'ManagementTabs', { groupId: group.id } + case 'CONTRIBUTION': + destination = 'ManagementTabs'; + params = { + screen:'Payments', + groupId: data.groupId + }; + break; + // navigation.navigate('ManagementTabs', { groupId: group.id }); + + default: + // Keep the default destination as 'Home' + break; + } + + // Use replace to prevent the user from navigating back to this loading screen + navigation.replace(destination, params); + + } else { + // If no data is passed for some reason, just go to the home screen + console.warn('⚠ No data found in notification, redirecting to Home.'); + navigation.replace('Home'); + } + }, [data, navigation]); // Effect dependencies + + // This is the only UI the user will see: a clean loading indicator + return ( + + + Loading... + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: '#f5f5f5', + }, + loadingText: { + marginTop: 10, + fontSize: 16, + color: '#666', + }, +}); + +export default NotificationRedirection; \ No newline at end of file diff --git a/src/utils/firebase/firebaseServices.ts b/src/utils/firebase/firebaseServices.ts index 3cd4f95..eefe30d 100644 --- a/src/utils/firebase/firebaseServices.ts +++ b/src/utils/firebase/firebaseServices.ts @@ -129,11 +129,12 @@ import messaging from '@react-native-firebase/messaging'; -import notifee, { AndroidImportance } from '@notifee/react-native'; +import notifee, { AndroidImportance, EventType } from '@notifee/react-native'; import { apiCall } from '../ApiHelper'; import { REMOVE_FCM_TOKEN, STORE_FCM_TOKEN } from '../ApiUrls'; import { store } from '../../redux/store'; // Import store directly import { addNotification } from '../../redux/notificationSlice'; +import { navigate } from '../navigationService'; // 🔹 Request notification permission export const requestPermission = async () => { @@ -257,7 +258,7 @@ export const foregroundListener = () => { if (!remoteMessage) return; - const { notification, data } = remoteMessage; + const { notification, data } = remoteMessage; const title = notification?.title || data?.title || 'New Message'; const body = notification?.body || data?.body || ''; @@ -313,6 +314,38 @@ export const tokenRefreshHandler = () => { }); }; +export const handleNotificationNavigation = (data: any) => { + if (!data || !data.msgType) { + return console.log('❌ No msgType in notification data for navigation.'); + } + + console.log('🧭 Navigating based on msgType:', data.msgType); + navigate('NotificationRedirection',{data:data}); +}; + +export const setupFirebaseHandlers = () => { + messaging().onNotificationOpenedApp(remoteMessage => { + console.log('📱 App opened from background via FCM tap'); + handleNotificationNavigation(remoteMessage.data); + }); + + messaging().getInitialNotification().then(remoteMessage => { + if (remoteMessage) { + console.log('💀 App opened from quit state via FCM tap'); + handleNotificationNavigation(remoteMessage.data); + } + }); +}; + +export const setupNotificationPressHandlers = () => { + notifee.onForegroundEvent(({ type, detail }) => { + if (type === EventType.PRESS) { + console.log('👆 User pressed a Notifee notification in the foreground'); + console.log("Notification data:", detail.notification?.data); + handleNotificationNavigation(detail.notification?.data); +    } +  }); +}; // Optional: Export all functions as a single object for easier imports export default { requestPermission, @@ -322,4 +355,5 @@ export default { foregroundListener, backgroundHandler, tokenRefreshHandler, + handleNotificationNavigation }; \ No newline at end of file diff --git a/src/utils/navigationService.ts b/src/utils/navigationService.ts new file mode 100644 index 0000000..4591ba0 --- /dev/null +++ b/src/utils/navigationService.ts @@ -0,0 +1,40 @@ +// src/utils/navigationService.ts +import { createNavigationContainerRef } from '@react-navigation/native'; +import { RootStackParamList } from '../navigation/types'; // ✅ Your import path + +export const navigationRef = createNavigationContainerRef(); + +/** + * A type-safe navigation function with debug logs. + * It correctly infers the parameters required for each route in your RootStack. + */ +export function navigate( + ...args: undefined extends RootStackParamList[RouteName] + // Handles routes with optional or no params + ? [screen: RouteName, params?: RootStackParamList[RouteName]] + // Handles routes that require params + : [screen: RouteName, params: RootStackParamList[RouteName]] +) { + // Log the initial call to the navigate function + console.log('⏩ [navigationService] Navigate called with:', args); + + if (navigationRef.isReady()) { + // Log immediate navigation + console.log('✅ [navigationService] Ref is ready. Navigating now.'); + navigationRef.navigate(...(args as [string, object])); + } else { + // Warn that navigation is being delayed + console.warn('⚠ [navigationService] Ref not ready. Retrying in 300ms...'); + + setTimeout(() => { + if (navigationRef.isReady()) { + // Log the successful retry + console.log('✅ [navigationService] Retry successful. Navigating now.'); + navigationRef.navigate(...(args as [string, object])); + } else { + // Log a critical failure if the retry also fails + console.error('❌ [navigationService] Navigation failed on retry. Ref still not ready.'); + } + }, 300); + } +} \ No newline at end of file