Files
cpost-mobile/App.tsx
Samandar Turgunboyev f41451c6b8 update
2025-09-04 18:38:08 +05:00

325 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// App.tsx
import AsyncStorage from '@react-native-async-storage/async-storage';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { toastConfig } from 'components/CustomAlertModal';
import { navigationRef } from 'components/NavigationRef';
import SplashScreen from 'components/SplashScreen';
import i18n from 'i18n/i18n';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { I18nextProvider } from 'react-i18next';
import {
Animated,
Dimensions,
LogBox,
PermissionsAndroid,
Platform,
StatusBar,
StyleSheet,
View,
} from 'react-native';
import Toast from 'react-native-toast-message';
// Screens
import notifee, { AndroidImportance } from '@notifee/react-native';
import { getApp } from '@react-native-firebase/app';
import {
getInitialNotification,
getMessaging,
getToken,
onMessage,
onNotificationOpenedApp,
} from '@react-native-firebase/messaging';
import DeviceInfo from 'react-native-device-info';
import Login from 'screens/auth/login/ui';
import Confirm from 'screens/auth/login/ui/Confirm';
import Register from 'screens/auth/registeration/ui';
import SecondStep from 'screens/auth/registeration/ui/SecondStep';
import TermsAndConditions from 'screens/auth/registeration/ui/TermsAndConditions';
import SelectAuth from 'screens/auth/select-auth/SelectAuth';
import Branches from 'screens/home/branches/ui/Branches';
import ListBranches from 'screens/home/branches/ui/ListBranches';
import CargoPrices from 'screens/home/cargoPrices/ui/CargoPrices';
import Home from 'screens/home/home/ui/Home';
import RestrictedProduct from 'screens/home/restrictedProduct/ui/RestrictedProduct';
import CreatePassword from 'screens/passport/createPassport/ui/CreatePassword';
import Passport from 'screens/passport/myPassport/ui/Passport';
import Profile from 'screens/profile/myProfile/ui/Profile';
import Notifications from 'screens/profile/notifications/ui/Notifications';
import AddedLock from 'screens/profile/settings/ui/AddedLock';
import Settings from 'screens/profile/settings/ui/Settings';
import SettingsLock from 'screens/profile/settings/ui/SettingsLock';
import Support from 'screens/profile/support/ui/Support';
import Warehouses from 'screens/profile/warehouses/ui/Warehouses';
import Status from 'screens/status/ui/Status';
import EnterCard from 'screens/wallet/enterCard/ui/EnterCard';
import Wallet from 'screens/wallet/payment/ui/Wallet';
import PaymentMethod from 'screens/wallet/paymentMethod/ui/PaymentMethod';
import PaymentQrCode from 'screens/wallet/successPayment/ui/PaymentQrCode';
import Onboarding from 'screens/welcome/Onboarding';
LogBox.ignoreLogs([
'Non-serializable values were found in the navigation state',
'ViewPropTypes will be removed',
]);
const Stack = createNativeStackNavigator();
const screenWidth = Dimensions.get('window').width;
const queryClient = new QueryClient();
const saveNotification = async (remoteMessage: any) => {
try {
const stored = await AsyncStorage.getItem('notifications');
const notifications = stored ? JSON.parse(stored) : [];
const newNotification = {
id: Date.now(),
title:
remoteMessage.notification?.title ||
remoteMessage.data?.title ||
'Yangi bildirishnoma',
message:
remoteMessage.notification?.body ||
remoteMessage.data?.body ||
'Matn yoq',
sentTime: remoteMessage.sentTime || Date.now(),
};
await AsyncStorage.setItem(
'notifications',
JSON.stringify([newNotification, ...notifications]),
);
} catch (e) {
console.error('Notification saqlashda xato:', e);
}
};
async function onDisplayNotification(remoteMessage: any) {
const channelId = await notifee.createChannel({
id: 'default',
name: 'Umumiy bildirishnomalar',
sound: 'default',
importance: AndroidImportance.HIGH,
});
await notifee.displayNotification({
title:
remoteMessage.notification?.title ||
remoteMessage.data?.title ||
'Yangi xabar',
body:
remoteMessage.notification?.body ||
remoteMessage.data?.body ||
'Matn yoq',
android: {
channelId,
largeIcon: 'ic_launcher_foreground',
sound: 'default',
pressAction: {
id: 'default',
},
},
});
}
async function requestNotificationPermission() {
if (Platform.OS === 'android' && Platform.Version >= 33) {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS,
);
console.log('POST_NOTIFICATIONS permission:', granted);
}
}
export default function App() {
const [initialRoute, setInitialRoute] = useState<string | null>(null);
const slideAnim = useRef(new Animated.Value(0)).current;
const [isSplashVisible, setIsSplashVisible] = useState(true);
useEffect(() => {
requestNotificationPermission();
const messagingInstance = getMessaging();
const unsubscribe = onMessage(messagingInstance, async remoteMessage => {
console.log('Foreground message:', remoteMessage);
await saveNotification(remoteMessage);
await onDisplayNotification(remoteMessage);
});
const unsubscribeOpened = onNotificationOpenedApp(
messagingInstance,
remoteMessage => {
console.log('Backgrounddan ochildi:', remoteMessage);
saveNotification(remoteMessage);
},
);
(async () => {
const remoteMessage = await getInitialNotification(messagingInstance);
if (remoteMessage) {
console.log('Killeddan ochildi:', remoteMessage);
saveNotification(remoteMessage);
}
})();
return () => {
unsubscribe();
unsubscribeOpened();
};
}, []);
useEffect(() => {
const initializeApp = async () => {
try {
const [seen, token, lang] = await Promise.all([
AsyncStorage.getItem('hasSeenOnboarding'),
AsyncStorage.getItem('token'),
AsyncStorage.getItem('language'),
]);
if (lang) await i18n.changeLanguage(lang);
const initialRouteName = !seen
? 'Onboarding'
: token
? 'Home'
: 'select-auth';
setInitialRoute(initialRouteName);
} catch (error) {
console.error('App initialization error:', error);
setInitialRoute('select-auth');
}
};
initializeApp();
}, []);
const handleSplashFinish = useMemo(
() => () => {
Animated.timing(slideAnim, {
toValue: -screenWidth,
duration: 600,
useNativeDriver: true,
}).start(() => setIsSplashVisible(false));
},
[slideAnim],
);
const handleOnboardingFinish = useMemo(
() => async (navigation: any) => {
await AsyncStorage.setItem('hasSeenOnboarding', 'true');
navigation.replace('select-auth');
},
[],
);
const [firebaseToken, setFirebseToken] = useState<{
fcmToken: string;
deviceId: string;
deviceName: string;
} | null>();
const app = getApp();
const messaging = getMessaging(app);
const getDeviceData = async () => {
try {
const fcmToken = await getToken(messaging);
return {
fcmToken,
deviceId: await DeviceInfo.getUniqueId(),
deviceName: await DeviceInfo.getDeviceName(),
};
} catch (e) {
console.log('Xato:', e);
return null;
}
};
console.log(firebaseToken);
useEffect(() => {
getDeviceData().then(data => {
setFirebseToken(data);
});
}, []);
if (!initialRoute) return null;
return (
<QueryClientProvider client={queryClient}>
<I18nextProvider i18n={i18n}>
<View style={{ flex: 1 }}>
<StatusBar barStyle="dark-content" backgroundColor="#fff" />
<NavigationContainer ref={navigationRef}>
<Stack.Navigator
screenOptions={{
headerShown: false,
animation: 'none',
gestureEnabled: false,
}}
initialRouteName={initialRoute}
>
<Stack.Screen name="Onboarding">
{props => (
<Onboarding
{...props}
onFinish={() => handleOnboardingFinish(props.navigation)}
/>
)}
</Stack.Screen>
<Stack.Screen name="select-auth" component={SelectAuth} />
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="Login-Confirm" component={Confirm} />
<Stack.Screen name="Register" component={Register} />
<Stack.Screen name="Confirm" component={SecondStep} />
<Stack.Screen name="SettingsLock" component={SettingsLock} />
<Stack.Screen name="AddLock" component={AddedLock} />
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Status" component={Status} />
<Stack.Screen name="Passports" component={Passport} />
<Stack.Screen name="CargoPrices" component={CargoPrices} />
<Stack.Screen name="create-password" component={CreatePassword} />
<Stack.Screen name="Wallet" component={Wallet} />
<Stack.Screen name="PaymentMethod" component={PaymentMethod} />
<Stack.Screen name="EnterCard" component={EnterCard} />
<Stack.Screen name="PaymentQrCode" component={PaymentQrCode} />
<Stack.Screen name="Profile" component={Profile} />
<Stack.Screen name="Settings" component={Settings} />
{Platform.OS === 'android' && (
<Stack.Screen name="Notifications" component={Notifications} />
)}
<Stack.Screen name="Warehouses" component={Warehouses} />
<Stack.Screen name="Support" component={Support} />
<Stack.Screen name="ListBranches" component={ListBranches} />
<Stack.Screen name="Branches" component={Branches} />
<Stack.Screen
name="RestrictedProduct"
component={RestrictedProduct}
/>
<Stack.Screen
name="TermsAndConditions"
component={TermsAndConditions}
/>
</Stack.Navigator>
</NavigationContainer>
{/* Splash transition */}
{isSplashVisible && (
<Animated.View
style={{
...StyleSheet.absoluteFillObject,
transform: [{ translateX: slideAnim }],
zIndex: 10,
}}
>
<SplashScreen onFinish={handleSplashFinish} />
</Animated.View>
)}
<Toast config={toastConfig} />
</View>
</I18nextProvider>
</QueryClientProvider>
);
}