Files
cpost-mobile/src/screens/auth/registeration/ui/Confirm.tsx
Samandar Turgunboyev 684d09e6b5 added login modal
2025-11-24 10:52:21 +05:00

352 lines
9.5 KiB
TypeScript

import AsyncStorage from '@react-native-async-storage/async-storage';
// import { getApp } from '@react-native-firebase/app';
// import { getMessaging, getToken } from '@react-native-firebase/messaging';
import { useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { useMutation } from '@tanstack/react-query';
import { authApi } from 'api/auth';
import { otpPayload, resendPayload } from 'api/auth/type';
import AppText from 'components/AppText';
import ErrorNotification from 'components/ErrorNotification';
import formatPhone from 'helpers/formatPhone';
import React, { useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
ActivityIndicator,
ImageBackground,
KeyboardAvoidingView,
Platform,
StyleSheet,
TextInput,
TouchableOpacity,
View,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import Logo from 'screens/../../assets/bootsplash/logo_512.png';
import LanguageSelector from 'screens/auth/select-language/SelectLang';
import ArrowLeft from 'svg/ArrowLeft';
import { RootStackParamList } from 'types/types';
import { useUserStore } from '../lib/userstore';
import { RegisterStyle } from './styled';
type VerificationCodeScreenNavigationProp = NativeStackNavigationProp<
RootStackParamList,
'Confirm'
>;
const OTP_LENGTH = 4;
const Confirm = ({
setStep,
}: {
setStep: React.Dispatch<React.SetStateAction<number>>;
}) => {
const navigation = useNavigation<VerificationCodeScreenNavigationProp>();
const { t } = useTranslation();
const [code, setCode] = useState<string[]>(new Array(OTP_LENGTH).fill(''));
const [timer, setTimer] = useState(60);
const [canResend, setCanResend] = useState(false);
const [errorConfirm, setErrorConfirm] = useState<string | null>(null);
const inputRefs = useRef<Array<TextInput | null>>([]);
const { phoneNumber } = useUserStore(state => state);
const [visible, setVisible] = useState(false);
const [error, setError] = useState<string>('Xatolik yuz berdi');
// 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;
// }
// };
// useEffect(() => {
// getDeviceData().then(data => {
// setFirebseToken(data);
// });
// }, []);
const { mutate, isPending } = useMutation({
mutationFn: (payload: otpPayload) => authApi.verifyOtp(payload),
onSuccess: async res => {
await AsyncStorage.setItem('token', res.data.accessToken);
navigation.navigate('Confirm');
setErrorConfirm(null);
},
onError: (err: any) => {
setError(err?.response?.data?.message || 'Xatolik yuz berdi');
setVisible(true);
},
});
const { mutate: resendMutate } = useMutation({
mutationFn: (payload: resendPayload) => authApi.resendOtp(payload),
onSuccess: async res => {
setTimer(60);
setCanResend(false);
setCode(new Array(OTP_LENGTH).fill(''));
inputRefs.current[0]?.focus();
setErrorConfirm(null);
},
onError: (err: any) => {
setError(err?.response?.data?.message || 'Xatolik yuz berdi');
setVisible(true);
},
});
const handleCodeChange = (text: string, index: number) => {
const newCode = [...code];
newCode[index] = text;
setCode(newCode);
if (text.length > 0 && index < OTP_LENGTH - 1) {
inputRefs.current[index + 1]?.focus();
}
if (text.length === 0 && index > 0) {
inputRefs.current[index - 1]?.focus();
}
};
const handleKeyPress = ({ nativeEvent }: any, index: number) => {
if (nativeEvent.key === 'Backspace' && code[index] === '' && index > 0) {
inputRefs.current[index - 1]?.focus();
}
};
const handleResendCode = () => {
resendMutate({
phoneNumber: phoneNumber,
otpType: 'REGISTRATION',
});
};
const handleVerifyCode = () => {
const enteredCode = code.join('');
mutate({
phoneNumber,
otp: enteredCode,
otpType: 'REGISTRATION',
deviceId: '',
deviceName: '',
fcmToken: '',
});
};
return (
<ImageBackground
source={Logo}
style={RegisterStyle.background}
resizeMode="contain"
imageStyle={{
opacity: 0.1,
height: '100%',
width: '100%',
transform: [{ scale: 1 }],
}}
>
<SafeAreaView style={{ flex: 1 }}>
<View style={styles.langContainer}>
<TouchableOpacity onPress={() => setStep(1)}>
<ArrowLeft color={'#000'} />
</TouchableOpacity>
<LanguageSelector />
</View>
<ErrorNotification
setVisible={setVisible}
error={error}
visible={visible}
/>
<KeyboardAvoidingView
style={styles.container}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
>
<View style={styles.content}>
<AppText style={styles.title}>
{t('Tasdiqlash kodini kiriting')}
</AppText>
<AppText style={styles.message}>
{formatPhone(phoneNumber)} {t('raqamiga yuborilgan')} {OTP_LENGTH}{' '}
{t('xonali kodni kiriting.')}
</AppText>
<View style={styles.otpContainer}>
{code.map((digit, index) => (
<TextInput
key={index}
ref={ref => {
inputRefs.current[index] = ref;
}}
style={styles.otpInput}
keyboardType="number-pad"
maxLength={1}
onChangeText={text => handleCodeChange(text, index)}
onKeyPress={e => handleKeyPress(e, index)}
value={digit}
autoFocus={index === 0}
/>
))}
</View>
{errorConfirm !== null && (
<AppText style={styles.errorText}>{errorConfirm}</AppText>
)}
<View style={styles.resendContainer}>
{canResend ? (
<TouchableOpacity
onPress={handleResendCode}
style={styles.resendButton}
>
<AppText style={styles.resendButtonText}>
{t('Kodni qayta yuborish')}
</AppText>
</TouchableOpacity>
) : (
<AppText style={styles.timerText}>
{t('Kodni qayta yuborish vaqti')} ({timer}s)
</AppText>
)}
</View>
<TouchableOpacity
style={styles.verifyButton}
onPress={handleVerifyCode}
>
{isPending ? (
<ActivityIndicator color="#fff" />
) : (
<AppText
style={[
RegisterStyle.btnText,
isPending && RegisterStyle.buttonTextDisabled,
]}
>
{t('Tasdiqlash')}
</AppText>
)}
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
</SafeAreaView>
</ImageBackground>
);
};
const styles = StyleSheet.create({
safeArea: {
flex: 1,
backgroundColor: '#f8f9fa',
},
errorText: {
color: 'red',
fontSize: 14,
fontWeight: '500',
marginTop: 10,
textAlign: 'center',
},
langContainer: {
width: '100%',
marginTop: 10,
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingHorizontal: 16,
},
headerTitle: {
fontSize: 18,
fontWeight: 'bold',
color: '#333',
},
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
},
content: {
width: '100%',
alignItems: 'center',
},
title: {
fontSize: 24,
fontWeight: 'bold',
color: '#333',
marginBottom: 10,
textAlign: 'center',
},
message: {
fontSize: 15,
color: '#666',
textAlign: 'center',
marginBottom: 30,
lineHeight: 22,
},
otpContainer: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
gap: 10,
},
otpInput: {
width: 45,
height: 55,
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 8,
textAlign: 'center',
fontSize: 22,
fontWeight: 'bold',
color: '#333',
backgroundColor: '#fff',
},
resendContainer: {
marginBottom: 30,
},
timerText: {
fontSize: 15,
color: '#999',
},
resendButton: {
paddingVertical: 10,
paddingHorizontal: 20,
borderRadius: 8,
},
resendButtonText: {
fontSize: 15,
color: '#007bff',
fontWeight: 'bold',
},
verifyButton: {
backgroundColor: '#007bff',
paddingVertical: 15,
paddingHorizontal: 40,
borderRadius: 8,
width: '100%',
alignItems: 'center',
elevation: 3,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2,
shadowRadius: 4,
},
verifyButtonText: {
color: '#fff',
fontSize: 18,
fontFamily: 'GolosText-Bold',
},
});
export default Confirm;