import { Link, useRouter } from "expo-router"; import { useCallback, useState } from "react"; import { Alert, Image, KeyboardAvoidingView, Platform, ScrollView, Text, TouchableOpacity, View, } from "react-native"; import ReactNativeModal from "react-native-modal"; import { CustomButton } from "@/components/custom-button"; import { InputField } from "@/components/input-field"; import { OAuth } from "@/components/oauth"; import { icons, images } from "@/constants"; import { fetchAPI } from "@/lib/fetch"; import { useSession } from "@/lib/session"; const SignIn = () => { const router = useRouter(); const { isLoaded, setSession } = useSession(); const [form, setForm] = useState({ email: "", password: "", }); // Forgot-password flow: "request" collects the email, "reset" collects the // emailed code and a new password. const [reset, setReset] = useState({ state: "closed" as "closed" | "request" | "reset", email: "", code: "", password: "", devCode: "", error: "", busy: false, }); const openReset = () => setReset({ state: "request", email: form.email, code: "", password: "", devCode: "", error: "", busy: false, }); const closeReset = () => setReset((prev) => ({ ...prev, state: "closed" })); const onRequestReset = async () => { if (!reset.email.trim()) { setReset((prev) => ({ ...prev, error: "Enter your email address." })); return; } setReset((prev) => ({ ...prev, busy: true, error: "" })); try { const response = await fetchAPI("/(api)/auth/forgot-password", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: reset.email.trim() }), }); setReset((prev) => ({ ...prev, state: "reset", busy: false, devCode: (response as { data?: { devCode?: string } })?.data?.devCode ?? "", })); } catch { // The endpoint hides whether the email exists, so move on regardless. setReset((prev) => ({ ...prev, state: "reset", busy: false })); } }; const onSubmitReset = async () => { if (!/^\d{6}$/.test(reset.code)) { setReset((prev) => ({ ...prev, error: "Enter the 6-digit code." })); return; } if (reset.password.length < 8) { setReset((prev) => ({ ...prev, error: "Password must be at least 8 characters.", })); return; } setReset((prev) => ({ ...prev, busy: true, error: "" })); try { const response = await fetchAPI("/(api)/auth/reset-password", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: reset.email.trim(), code: reset.code, password: reset.password, }), }); await setSession(response.data); setReset((prev) => ({ ...prev, state: "closed", busy: false })); router.replace("/"); } catch (err: any) { setReset((prev) => ({ ...prev, busy: false, error: String(err?.message ?? "").includes("400") ? "Invalid or expired reset code." : "Could not reset your password. Please try again.", })); } }; const onSignInPress = useCallback(async () => { try { const response = await fetchAPI("/(api)/auth/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: form.email, password: form.password, }), }); await setSession(response.data); router.replace("/"); } catch (err: any) { const status = String(err?.message ?? ""); const message = status.includes("403") ? "Please verify your email first." : status.includes("401") ? "Invalid email or password." : "Could not sign in. Please try again."; Alert.alert("Error", message); setForm((prevForm) => ({ ...prevForm, password: "", })); } }, [isLoaded, form.email, form.password, setSession, router]); return ( Car Welcome 👋 setForm((prevForm) => ({ ...prevForm, email: value, })) } keyboardType="email-address" /> setForm((prevForm) => ({ ...prevForm, password: value, })) } /> Forgot password? Don't have an account? Sign up Reset password Enter your email and we'll send you a 6-digit reset code. setReset((prev) => ({ ...prev, email })) } /> {reset.error ? ( {reset.error} ) : null} Enter new password We've sent a reset code to {reset.email} {reset.devCode ? ( Email delivery is not configured on this server. Your reset code is {reset.devCode} ) : null} setReset((prev) => ({ ...prev, code }))} /> setReset((prev) => ({ ...prev, password })) } /> {reset.error ? ( {reset.error} ) : null} ); }; export default SignIn;