109 lines
3.1 KiB
TypeScript
109 lines
3.1 KiB
TypeScript
import { useState, useEffect, useCallback } from 'react';
|
|
import { Alert } from 'react-native';
|
|
import { useNavigation, useRoute, RouteProp } from '@react-navigation/native';
|
|
import { StackNavigationProp } from '@react-navigation/stack';
|
|
import { RootState, useAppDispatch, useAppSelector, verifyOtp } from '@store';
|
|
import { AuthStackParamList } from '@navigation/authStack';
|
|
|
|
type OtpNavProp = StackNavigationProp<AuthStackParamList, 'OtpScreen'>;
|
|
type OtpRouteProp = RouteProp<AuthStackParamList, 'OtpScreen'>;
|
|
|
|
const OTP_LENGTH = 6;
|
|
const RESEND_TIMER = 30;
|
|
|
|
export const useOtpScreen = () => {
|
|
const navigation = useNavigation<OtpNavProp>();
|
|
const route = useRoute<OtpRouteProp>();
|
|
const dispatch = useAppDispatch();
|
|
const { mobileNumber } = route.params;
|
|
|
|
const { loginData, user } = useAppSelector((state: RootState) => state.auth);
|
|
|
|
const [otp, setOtp] = useState<string[]>(Array(OTP_LENGTH).fill(''));
|
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
const [timer, setTimer] = useState(RESEND_TIMER);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
|
|
useEffect(() => {
|
|
if (timer > 0) {
|
|
const interval = setInterval(() => setTimer(t => t - 1), 1000);
|
|
return () => clearInterval(interval);
|
|
}
|
|
}, [timer]);
|
|
|
|
const resetOtp = useCallback(() => {
|
|
setOtp(Array(OTP_LENGTH).fill(''));
|
|
setActiveIndex(0);
|
|
}, []);
|
|
|
|
const handleKeyPress = useCallback(
|
|
(key: string) => {
|
|
if (key === 'backspace') {
|
|
if (otp[activeIndex] || activeIndex > 0) {
|
|
const newOtp = [...otp];
|
|
if (otp[activeIndex]) {
|
|
newOtp[activeIndex] = '';
|
|
} else if (activeIndex > 0) {
|
|
newOtp[activeIndex - 1] = '';
|
|
setActiveIndex(activeIndex - 1);
|
|
}
|
|
setOtp(newOtp);
|
|
}
|
|
} else if (key >= '0' && key <= '9' && activeIndex < OTP_LENGTH) {
|
|
const newOtp = [...otp];
|
|
newOtp[activeIndex] = key;
|
|
setOtp(newOtp);
|
|
if (activeIndex < OTP_LENGTH - 1) {
|
|
setActiveIndex(activeIndex + 1);
|
|
}
|
|
}
|
|
},
|
|
[otp, activeIndex],
|
|
);
|
|
|
|
const handleResend = useCallback(() => {
|
|
setTimer(RESEND_TIMER);
|
|
resetOtp();
|
|
// TODO: dispatch resend/loginWithPhone thunk here if needed
|
|
}, [resetOtp]);
|
|
|
|
const handleVerify = useCallback(async () => {
|
|
const otpString = otp.join('');
|
|
if (otpString.length !== OTP_LENGTH) return;
|
|
|
|
setIsLoading(true);
|
|
try {
|
|
await dispatch(
|
|
verifyOtp({ phone: mobileNumber, code: otpString, role: 'CUSTOMER' }),
|
|
).unwrap();
|
|
|
|
navigation.navigate('SetLocationScreen');
|
|
} catch (error) {
|
|
const message =
|
|
typeof error === 'string' ? error : 'Invalid OTP. Please try again.';
|
|
Alert.alert('Verification Failed', message);
|
|
resetOtp();
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
}, [dispatch, mobileNumber, navigation, otp, resetOtp]);
|
|
|
|
return {
|
|
// data
|
|
mobileNumber,
|
|
loginData,
|
|
user,
|
|
otp,
|
|
activeIndex,
|
|
timer,
|
|
isLoading,
|
|
otpLength: OTP_LENGTH,
|
|
isOtpComplete: otp.join('').length === OTP_LENGTH,
|
|
|
|
// actions
|
|
handleKeyPress,
|
|
handleVerify,
|
|
handleResend,
|
|
};
|
|
};
|