Files
cpost-mobile/App.tsx
Samandar Turgunboyev c426b729b9 screen bug fix
2025-12-10 11:02:58 +05:00

238 lines
8.3 KiB
TypeScript

// App.tsx
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 i18n from 'i18n/i18n';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { I18nextProvider } from 'react-i18next';
import { Animated, Dimensions, LogBox, StatusBar, View } from 'react-native';
import Toast from 'react-native-toast-message';
import SplashScreen from './src/components/SplashScreen';
// Screens
import {
authEvents,
getLanguage,
getToken,
loadInitialAuthData,
storage,
} from 'helpers/event';
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 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([
'ViewPropTypes will be removed',
// NOTE: I recommend NOT ignoring "Non-serializable values were found in the navigation state"
// because it hides bugs that can cause crashes. Keep only if you understand the implications.
]);
const Stack = createNativeStackNavigator();
const screenWidth = Dimensions.get('window').width;
const queryClient = new QueryClient();
export default function App() {
const [initialRoute, setInitialRoute] = useState<string | null>(null);
const slideAnim = useRef(new Animated.Value(0)).current;
const [isSplashVisible, setIsSplashVisible] = useState(true);
const isMounted = useRef(false);
const currentAnimation = useRef<Animated.CompositeAnimation | null>(null);
const token = getToken();
useEffect(() => {
loadInitialAuthData();
}, []);
useEffect(() => {
const logoutListener = () => {
if (navigationRef.isReady()) {
navigationRef.reset({
index: 0,
routes: [{ name: 'Login' }],
});
}
};
authEvents.on('logout', logoutListener);
return () => {
authEvents.removeListener('logout', logoutListener);
};
}, []);
useEffect(() => {
isMounted.current = true;
return () => {
isMounted.current = false;
if (currentAnimation.current) {
currentAnimation.current.stop();
}
};
}, []);
useEffect(() => {
const initializeApp = async () => {
try {
const seen = storage.getString('hasSeenOnboarding');
const token = getToken();
const lang = getLanguage();
if (lang) {
try {
await i18n.changeLanguage(lang);
} catch (e) {
console.warn('i18n.changeLanguage failed:', e);
}
}
const initialRouteName = !seen
? 'Onboarding'
: token
? 'Home'
: 'select-auth';
if (isMounted.current) {
setInitialRoute(initialRouteName);
}
} catch (error) {
console.error('App initialization error:', error);
if (isMounted.current) setInitialRoute('select-auth');
}
};
initializeApp();
}, []);
const handleSplashFinish = useMemo(
() => () => {
// create animation and keep ref so we can stop it on unmount
const animation = Animated.timing(slideAnim, {
toValue: -screenWidth,
duration: 600,
useNativeDriver: true,
});
currentAnimation.current = animation;
animation.start(() => {
// ensure component still mounted before setting state
if (isMounted.current) {
setIsSplashVisible(false);
}
currentAnimation.current = null;
});
},
[slideAnim],
);
const handleOnboardingFinish = useMemo(
() => async (navigation: any) => {
try {
storage.set('hasSeenOnboarding', 'true');
} catch (e) {
console.warn('Failed to set hasSeenOnboarding', e);
}
navigation.replace('select-auth');
},
[],
);
if (!initialRoute || isSplashVisible || !currentAnimation) {
return (
<View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#ffff',
}}
>
<SplashScreen onFinish={handleSplashFinish} />
</View>
);
}
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: 'slide_from_right',
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} />
<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>
<Toast config={toastConfig} />
</View>
</I18nextProvider>
</QueryClientProvider>
);
}