import React, { useState, useEffect, useCallback, useRef } from "react"; import { View, ActivityIndicator, Pressable } from "react-native"; import { useSirouRouter } from "@sirou/react-native"; import { useLocalSearchParams, Stack } from "expo-router"; import { AppRoutes } from "@/lib/routes"; import { Text } from "@/components/ui/text"; import { ScreenWrapper } from "@/components/ScreenWrapper"; import { Keypad } from "@/components/Keypad"; import { ArrowLeft } from "@/lib/icons"; import { api } from "@/lib/api"; import { usePinStore } from "@/lib/pin-store"; import { toast } from "@/lib/toast-store"; import { useColorScheme } from "nativewind"; type Step = "otp" | "pin" | "confirm"; export default function ForgotPinVerifyScreen() { const nav = useSirouRouter(); const { phone, verificationId } = useLocalSearchParams<{ phone: string; verificationId: string; }>(); const { colorScheme } = useColorScheme(); const isDark = colorScheme === "dark"; const setHasPin = usePinStore((s) => s.setHasPin); const unlock = usePinStore((s) => s.unlock); const mounted = useRef(true); useEffect(() => { return () => { mounted.current = false; }; }, []); const [step, setStep] = useState("otp"); const [otp, setOtp] = useState(""); const [pin, setPin] = useState(""); const [confirmPin, setConfirmPin] = useState(""); const [loading, setLoading] = useState(false); const currentValue = step === "otp" ? otp : step === "pin" ? pin : confirmPin; const setCurrentValue = step === "otp" ? setOtp : step === "pin" ? setPin : setConfirmPin; const arr = currentValue.split(""); useEffect(() => { if (currentValue.length === 6 && !loading) { if (step === "otp") { handleVerifyOtp(); } else if (step === "pin") { setStep("confirm"); setConfirmPin(""); } else { handleSetPin(); } } }, [currentValue]); const handleVerifyOtp = async () => { setLoading(true); try { await api.auth.forgetPinVerify({ body: { phone: phone as string, code: otp, verificationId }, }); if (mounted.current) setLoading(false); setStep("pin"); setPin(""); } catch (err: any) { if (mounted.current) setLoading(false); setOtp(""); toast.error("Invalid Code", err.message || "Verification code is invalid or expired"); } }; const handleSetPin = async () => { if (confirmPin !== pin) { toast.error("Mismatch", "PINs do not match"); setStep("pin"); setPin(""); setConfirmPin(""); return; } setLoading(true); try { await api.auth.setPin({ body: { pin } }); setHasPin(true); unlock(); toast.success("PIN Reset", "Your PIN has been updated"); nav.go("(tabs)"); } catch (err: any) { if (mounted.current) setLoading(false); toast.error("Failed", err.message || "Could not set PIN"); } }; const handlePress = useCallback((key: string) => { setCurrentValue((prev: string) => (prev + key).slice(0, 6)); }, [step]); const handleDelete = useCallback(() => { setCurrentValue((prev: string) => prev.slice(0, -1)); }, [step]); const getTitle = () => { switch (step) { case "otp": return "Verify Code"; case "pin": return "New PIN"; case "confirm": return "Confirm PIN"; } }; const getSubtitle = () => { switch (step) { case "otp": return "Enter the 6-digit code sent to your phone"; case "pin": return "Create a new 6-digit PIN"; case "confirm": return "Enter the new PIN again"; } }; return ( { if (step === "pin") { setStep("otp"); setOtp(""); } else if (step === "confirm") { setStep("pin"); setPin(""); setConfirmPin(""); } else nav.back(); }} className="h-10 w-10 rounded-lg items-center justify-center" > {step === "otp" ? "Step 1 of 3" : step === "pin" ? "Step 2 of 3" : "Step 3 of 3"} {getTitle()} {getSubtitle()} {[0, 1, 2, 3, 4, 5].map((i) => ( {step === "otp" ? ( {arr[i] ?? ""} ) : ( )} ))} {loading && } ); }