feat: smooth navigation transitions + config updates

- Migrate MainStack from JS stack to native-stack for native-thread,
  jank-free screen transitions (slide_from_right, fade for auth screens)
- Add shift animation to bottom tab switching
- Install @react-navigation/native-stack
- Update API URLs, Axios, Login and Android network security config

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Abin Toppo
2026-06-11 11:19:32 +05:30
co-authored by Claude Opus 4.8
parent 44e84ff6f1
commit 9acec328a7
14 changed files with 260 additions and 157 deletions
+70 -9
View File
@@ -1,5 +1,5 @@
import React, { useState, useEffect, useCallback } from 'react';
import { View, Text, StyleSheet, KeyboardAvoidingView, Platform, ScrollView, TouchableOpacity, Dimensions, StatusBar, TextInput, Image } from 'react-native';
import { View, Text, StyleSheet, KeyboardAvoidingView, Platform, ScrollView, TouchableOpacity, Dimensions, StatusBar, TextInput, Image, ActivityIndicator, Keyboard } from 'react-native';
import { Formik } from 'formik';
import * as Yup from 'yup';
import { useDispatch } from 'react-redux';
@@ -43,7 +43,7 @@ const Login = () => {
const [passwordVisible, setPasswordVisible] = useState(false);
const [showForm, setShowForm] = useState(false);
const [activeSlide, setActiveSlide] = useState(0);
const { mutateAsync: registrationStateMutate } = useRagistrationState();
const { mutateAsync: registrationStateMutate, isPending: isResolvingRoute } = useRagistrationState();
useEffect(() => {
if (showForm) return;
@@ -211,6 +211,9 @@ const Login = () => {
const handleSuccess = (res: any) => {
dispatch(setToken({ ...res.data, token: res.token, userType: 'tender' }));
console.log('handleSuccess', res)
console.log('==================== 🔑 AUTH TOKEN ====================');
console.log('TOKEN →', res?.token);
console.log('=======================================================');
if (res.statusCode === 200) {
handleGetData()
} else {
@@ -219,12 +222,18 @@ const Login = () => {
};
const handleLogin = (values: any) => {
if (isLoading) return; // guard against double submit
Keyboard.dismiss(); // smooth the navigation transition (no keyboard mid-replace)
const payload = { emailAdd: values.email, password: values.password, device: "mobileApp" };
console.log('payload------------', payload)
loginMutation.mutate(payload);
};
const isLoading = loginMutation.isPending;
// Covers the WHOLE sign-in flow: login request + registration-state resolve
// (+ the brief moment before navigation.replace swaps the screen). Without
// this, the button reverts to "SIGN IN" between the two requests, which is
// the flicker/glitch the user sees.
const isLoading = loginMutation.isPending || isResolvingRoute;
// Minimal Input Component (Enhanced for better aesthetics)
const MinimalInput = ({ placeholder, value, onChangeText, onBlur, icon, secureTextEntry, rightIcon, onRightIconPress }: any) => {
@@ -375,7 +384,7 @@ const Login = () => {
<View style={styles.cardInnerContent}>
<View style={styles.formHeader}>
<Text style={[styles.formTitle, { color: colors.text }]}>Welcome Back</Text>
<Text style={[styles.formTitle, { color: colors.text }]}>Welcome</Text>
<Text style={styles.formSubtitle}>Sign in to your account</Text>
</View>
<Formik
@@ -413,18 +422,28 @@ const Login = () => {
<TouchableOpacity
activeOpacity={0.8}
onPress={() => handleSubmit()}
disabled={isLoading}
style={styles.loginBtnContainer}
>
<LinearGradient
colors={[colors.primary, '#f59953']}
colors={isLoading ? ['#e8a44c99', '#f5995399'] : [colors.primary, '#f59953']}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
style={styles.loginBtn}
>
<Text style={styles.loginBtnText}>
{isLoading ? 'Wait...' : 'SIGN IN'}
</Text>
{!isLoading && <ArrowRight size={22} color={'#FFF'} style={styles.arrowIcon} />}
{isLoading ? (
<>
<ActivityIndicator size="small" color={'#FFF'} />
<Text style={[styles.loginBtnText, styles.loginBtnTextLoading]}>
SIGNING IN
</Text>
</>
) : (
<>
<Text style={styles.loginBtnText}>SIGN IN</Text>
<ArrowRight size={22} color={'#FFF'} style={styles.arrowIcon} />
</>
)}
</LinearGradient>
</TouchableOpacity>
</View>
@@ -445,6 +464,17 @@ const Login = () => {
</ScrollView>
</KeyboardAvoidingView>
{/* Full-screen loader — covers the whole login→resolve→navigate flow so
the user never sees a half-rendered state or the navigation flicker */}
{isLoading && (
<View style={styles.loaderOverlay} pointerEvents="auto">
<View style={styles.loaderCard}>
<ActivityIndicator size="large" color={colors.primary} />
<Text style={[styles.loaderText, { color: colors.text }]}>Signing in…</Text>
</View>
</View>
)}
</AppBackground >
);
};
@@ -688,10 +718,41 @@ const styles = StyleSheet.create({
letterSpacing: 1.5,
textTransform: 'uppercase',
},
loginBtnTextLoading: {
marginLeft: 12,
},
arrowIcon: {
marginLeft: 12,
},
// FULL-SCREEN LOADER OVERLAY
loaderOverlay: {
...StyleSheet.absoluteFillObject,
backgroundColor: 'rgba(0, 0, 0, 0.35)',
alignItems: 'center',
justifyContent: 'center',
zIndex: 999,
},
loaderCard: {
paddingVertical: 24,
paddingHorizontal: 32,
borderRadius: 20,
backgroundColor: 'rgba(255, 255, 255, 0.95)',
alignItems: 'center',
justifyContent: 'center',
shadowColor: '#000',
shadowOffset: { width: 0, height: 8 },
shadowOpacity: 0.2,
shadowRadius: 16,
elevation: 10,
},
loaderText: {
marginTop: 12,
fontSize: 15,
fontWeight: '600',
fontFamily: FontsFamily.poppinsMedium,
},
// FOOTER
footer: {
flexDirection: 'row',