again push for merge code
This commit is contained in:
@@ -5,17 +5,20 @@ import { SafeAreaProvider } from 'react-native-safe-area-context';
|
|||||||
import { PaperProvider } from 'react-native-paper';
|
import { PaperProvider } from 'react-native-paper';
|
||||||
import AppNavigator from './src/navigation/AppNavigator';
|
import AppNavigator from './src/navigation/AppNavigator';
|
||||||
import notifee from '@notifee/react-native';
|
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() {
|
function App() {
|
||||||
const isDarkMode = useColorScheme() === 'dark';
|
const isDarkMode = useColorScheme() === 'dark';
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 🔹 Ask for permission
|
// 🔹 Ask for permission
|
||||||
requestPermission();
|
requestPermission();
|
||||||
|
|
||||||
// 🔹 Handle background + quit state
|
// 🔹 Handle background + quit state
|
||||||
backgroundHandler();
|
backgroundHandler();
|
||||||
|
setupFirebaseHandlers()
|
||||||
|
setupNotificationPressHandlers()
|
||||||
|
|
||||||
// 🔹 Listen for foreground messages
|
// 🔹 Listen for foreground messages
|
||||||
const unsubscribe = foregroundListener();
|
const unsubscribe = foregroundListener();
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ import TransactionsList from "../screens/transaction/TransactionList";
|
|||||||
import RandomSelectUserScreen from "../screens/management/RandomSelectUserScreen";
|
import RandomSelectUserScreen from "../screens/management/RandomSelectUserScreen";
|
||||||
import Notification from "../screens/notification/Notification";
|
import Notification from "../screens/notification/Notification";
|
||||||
import ManagementScreen from "../screens/management/ManagementScreen";
|
import ManagementScreen from "../screens/management/ManagementScreen";
|
||||||
|
import { navigationRef } from "../utils/navigationService";
|
||||||
|
import NotificationRedirection from "../screens/NotificationRedirection";
|
||||||
|
|
||||||
export type RootStackParamList = {
|
export type RootStackParamList = {
|
||||||
Splash: undefined;
|
Splash: undefined;
|
||||||
@@ -44,6 +46,7 @@ export type RootStackParamList = {
|
|||||||
TransactionList: undefined
|
TransactionList: undefined
|
||||||
RandomSelectUserScreen: { groupId: string | number, cycleNumber: number };
|
RandomSelectUserScreen: { groupId: string | number, cycleNumber: number };
|
||||||
Notification: undefined;
|
Notification: undefined;
|
||||||
|
NotificationRedirection:{ data: any };
|
||||||
Management:
|
Management:
|
||||||
| { actionTab: "My Groups" }
|
| { actionTab: "My Groups" }
|
||||||
| { actionTab1: "Joined Groups" }
|
| { actionTab1: "Joined Groups" }
|
||||||
@@ -88,8 +91,8 @@ export default function AppNavigator() {
|
|||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NavigationContainer linking={linking}>
|
<NavigationContainer linking={linking} ref={navigationRef}>
|
||||||
<Stack.Navigator initialRouteName="Splash">
|
<Stack.Navigator initialRouteName="Splash" >
|
||||||
<Stack.Screen name="Splash" component={SplashScreen} options={{ headerShown: false }} />
|
<Stack.Screen name="Splash" component={SplashScreen} options={{ headerShown: false }} />
|
||||||
<Stack.Screen name="Home" component={BottomTabs} options={{ headerShown: false }} />
|
<Stack.Screen name="Home" component={BottomTabs} options={{ headerShown: false }} />
|
||||||
<Stack.Screen name="Login" component={LoginScreen} options={{ headerShown: false }} />
|
<Stack.Screen name="Login" component={LoginScreen} options={{ headerShown: false }} />
|
||||||
@@ -111,6 +114,7 @@ export default function AppNavigator() {
|
|||||||
<Stack.Screen name="RandomSelectUserScreen" component={RandomSelectUserScreen} options={{ headerShown: false }} />
|
<Stack.Screen name="RandomSelectUserScreen" component={RandomSelectUserScreen} options={{ headerShown: false }} />
|
||||||
<Stack.Screen name="Notification" component={Notification} options={{ headerShown: false }} />
|
<Stack.Screen name="Notification" component={Notification} options={{ headerShown: false }} />
|
||||||
<Stack.Screen name="Management" component={ManagementScreen} options={{ headerShown: false }} />
|
<Stack.Screen name="Management" component={ManagementScreen} options={{ headerShown: false }} />
|
||||||
|
<Stack.Screen name="NotificationRedirection" component={NotificationRedirection} options={{ headerShown: false }} />
|
||||||
</Stack.Navigator>
|
</Stack.Navigator>
|
||||||
</NavigationContainer>
|
</NavigationContainer>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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<RootStackParamList, 'NotificationRedirection'>;
|
||||||
|
|
||||||
|
const NotificationRedirection: React.FC = () => {
|
||||||
|
const route = useRoute<NotificationRedirectionRouteProp>();
|
||||||
|
const navigation = useNavigation<StackNavigationProp<RootStackParamList>>();
|
||||||
|
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 (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<ActivityIndicator size="large" color="#007AFF" />
|
||||||
|
<Text style={styles.loadingText}>Loading...</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: '#f5f5f5',
|
||||||
|
},
|
||||||
|
loadingText: {
|
||||||
|
marginTop: 10,
|
||||||
|
fontSize: 16,
|
||||||
|
color: '#666',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default NotificationRedirection;
|
||||||
@@ -129,11 +129,12 @@
|
|||||||
|
|
||||||
|
|
||||||
import messaging from '@react-native-firebase/messaging';
|
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 { apiCall } from '../ApiHelper';
|
||||||
import { REMOVE_FCM_TOKEN, STORE_FCM_TOKEN } from '../ApiUrls';
|
import { REMOVE_FCM_TOKEN, STORE_FCM_TOKEN } from '../ApiUrls';
|
||||||
import { store } from '../../redux/store'; // Import store directly
|
import { store } from '../../redux/store'; // Import store directly
|
||||||
import { addNotification } from '../../redux/notificationSlice';
|
import { addNotification } from '../../redux/notificationSlice';
|
||||||
|
import { navigate } from '../navigationService';
|
||||||
|
|
||||||
// 🔹 Request notification permission
|
// 🔹 Request notification permission
|
||||||
export const requestPermission = async () => {
|
export const requestPermission = async () => {
|
||||||
@@ -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
|
// Optional: Export all functions as a single object for easier imports
|
||||||
export default {
|
export default {
|
||||||
requestPermission,
|
requestPermission,
|
||||||
@@ -322,4 +355,5 @@ export default {
|
|||||||
foregroundListener,
|
foregroundListener,
|
||||||
backgroundHandler,
|
backgroundHandler,
|
||||||
tokenRefreshHandler,
|
tokenRefreshHandler,
|
||||||
|
handleNotificationNavigation
|
||||||
};
|
};
|
||||||
@@ -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<RootStackParamList>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A type-safe navigation function with debug logs.
|
||||||
|
* It correctly infers the parameters required for each route in your RootStack.
|
||||||
|
*/
|
||||||
|
export function navigate<RouteName extends keyof RootStackParamList>(
|
||||||
|
...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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user