247 lines
8.6 KiB
TypeScript
247 lines
8.6 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,
|
|
StyleSheet,
|
|
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);
|
|
|
|
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 === null) {
|
|
return <View style={{ flex: 1, backgroundColor: '#000' }} />;
|
|
}
|
|
|
|
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 set once (we ensure it's available before first render)
|
|
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>
|
|
|
|
{/* Splash transition */}
|
|
{isSplashVisible && (
|
|
<Animated.View
|
|
style={{
|
|
...StyleSheet.absoluteFillObject,
|
|
transform: [{ translateX: slideAnim }],
|
|
zIndex: 10,
|
|
}}
|
|
>
|
|
<SplashScreen onFinish={handleSplashFinish} />
|
|
</Animated.View>
|
|
)}
|
|
|
|
<Toast config={toastConfig} />
|
|
</View>
|
|
</I18nextProvider>
|
|
</QueryClientProvider>
|
|
);
|
|
}
|