132 lines
4.2 KiB
TypeScript

import React, { useEffect, useRef } from 'react';
import { StatusBar, AppState, AppStateStatus, Platform } from 'react-native';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { RootNavigator } from './navigation/rootNavigator';
import { ThemeProvider, useTheme } from './theme';
import { Provider } from 'react-redux';
import { store, persistor, useAppDispatch, useAppSelector, getStatusList, getSourceList, getCountryList, getLanguageList, getCurrencyList } from '@store';
import { PersistGate } from 'redux-persist/integration/react';
import { NotificationService } from '@services';
import BootSplash from 'react-native-bootsplash';
import messaging from '@react-native-firebase/messaging';
import { getStaffNotifications } from './features/notification/thunk';
import { navigateToLeadDetails } from '@utils';
const ThemedStatusBar = () => {
const { theme: colors, isDark } = useTheme();
useEffect(() => {
StatusBar.setBarStyle(isDark ? 'light-content' : 'dark-content', true);
StatusBar.setBackgroundColor(colors.background, true);
if (Platform.OS === 'android') {
StatusBar.setTranslucent(false);
}
}, [colors, isDark]);
return (
<StatusBar
barStyle={isDark ? 'light-content' : 'dark-content'}
backgroundColor={colors.background}
translucent={false}
/>
);
};
/** Dispatches startup data fetches once the Redux store is ready. */
const AppInit = () => {
const dispatch = useAppDispatch();
const token = useAppSelector(state => state.auth.token);
useEffect(() => {
if (token) {
dispatch(getStatusList());
dispatch(getSourceList());
dispatch(getCountryList());
dispatch(getLanguageList());
dispatch(getCurrencyList());
}
}, [dispatch, token]);
// When a push notification arrives (foreground), re-fetch from the server
// so the list and unread badge are always accurate
useEffect(() => {
const unsubscribe = messaging().onMessage(async () => {
const staffId = (store.getState() as any).auth?.user_data?.staffid;
if (staffId) {
dispatch(getStaffNotifications(staffId));
}
});
return unsubscribe;
}, [dispatch]);
// When the app comes back to the foreground from background (e.g., user taps
// a background push notification), re-fetch so count and list are up to date
const appState = useRef<AppStateStatus>(AppState.currentState);
useEffect(() => {
const subscription = AppState.addEventListener('change', nextState => {
if (appState.current.match(/inactive|background/) && nextState === 'active') {
const staffId = (store.getState() as any).auth?.user_data?.staffid;
if (staffId) {
dispatch(getStaffNotifications(staffId));
}
}
appState.current = nextState;
});
return () => subscription.remove();
}, [dispatch]);
return null;
};
const App = () => {
useEffect(() => {
let cleanup: (() => void) | undefined;
const initApp = async () => {
try {
const cleanupFn = await NotificationService.initialize({
onNotificationOpen: data => {
console.log('[App] Notification opened with data:', data);
// Deep-link: if the notification carries a lead_id, open LeadDetails
if (data?.type === 'lead' && data?.lead_id) {
navigateToLeadDetails(data.lead_id);
}
},
onTokenRefreshed: token => {
// TODO: Send the refreshed token to your backend so the server
// always has the latest FCM token for this device.
console.log('[App] FCM token refreshed — sync to server:', token);
},
});
cleanup = cleanupFn;
} catch (err) {
console.error('[App] NotificationService.initialize error:', err);
} finally {
await BootSplash.hide({ fade: true });
}
};
initApp();
return () => {
cleanup?.();
};
}, []);
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<SafeAreaProvider>
<ThemeProvider>
<ThemedStatusBar />
<AppInit />
<RootNavigator />
</ThemeProvider>
</SafeAreaProvider>
</PersistGate>
</Provider>
);
};
export default App;