import { Ionicons } from "@expo/vector-icons"; import { router, useLocalSearchParams } from "expo-router"; import { useEffect, useMemo, useState } from "react"; import { KeyboardAvoidingView, Platform, ScrollView, StyleSheet, Text, TouchableOpacity, View, } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; import { apiClient } from "../../api/client"; import { t } from "../../lib/locales"; import { colors } from "../../lib/theme"; import { Button, H1, Muted, TextInput } from "../../lib/ui"; type Mode = "request" | "confirm"; export default function ResetPassword() { const params = useLocalSearchParams<{ token?: string | string[] }>(); const tokenFromParams = useMemo( () => (Array.isArray(params.token) ? params.token[0] : params.token) || "", [params.token], ); const [mode, setMode] = useState(tokenFromParams ? "confirm" : "request"); const [email, setEmail] = useState(""); const [token, setToken] = useState(tokenFromParams); const [password, setPassword] = useState(""); const [confirmPassword, setConfirmPassword] = useState(""); const [loading, setLoading] = useState(false); const [error, setError] = useState(""); const [success, setSuccess] = useState(""); const [emailError, setEmailError] = useState(""); const [tokenError, setTokenError] = useState(""); const [passwordError, setPasswordError] = useState(""); const [confirmPasswordError, setConfirmPasswordError] = useState(""); useEffect(() => { if (!tokenFromParams) return; setToken(tokenFromParams); setMode("confirm"); }, [tokenFromParams]); const resetFieldErrors = () => { setEmailError(""); setTokenError(""); setPasswordError(""); setConfirmPasswordError(""); }; const validateRequest = () => { resetFieldErrors(); const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { setEmailError(t("invalidEmail")); return false; } return true; }; const validateConfirm = () => { resetFieldErrors(); let valid = true; if (!token.trim()) { setTokenError(t("resetTokenRequired")); valid = false; } if (!password) { setPasswordError(t("passwordRequired")); valid = false; } else if (password.length < 8) { setPasswordError(t("passwordTooShort")); valid = false; } if (password !== confirmPassword) { setConfirmPasswordError(t("passwordsDoNotMatch")); valid = false; } return valid; }; const handleRequestReset = async () => { if (!validateRequest()) return; setLoading(true); setError(""); setSuccess(""); try { const result = await apiClient.requestPasswordReset(email.trim()); if (!result.success) { setError(result.reason || t("resetPasswordError")); return; } setSuccess(t("passwordResetEmailSent")); } catch { setError(t("resetPasswordError")); } finally { setLoading(false); } }; const handleConfirmReset = async () => { if (!validateConfirm()) return; setLoading(true); setError(""); setSuccess(""); try { const result = await apiClient.confirmPasswordReset(token.trim(), password); if (!result.success) { setError(result.reason || t("resetPasswordError")); return; } setSuccess(t("passwordResetSuccess")); setTimeout(() => router.replace("/(auth)/signin"), 500); } catch { setError(t("resetPasswordError")); } finally { setLoading(false); } }; const switchMode = () => { resetFieldErrors(); setError(""); setSuccess(""); setMode((current) => (current === "request" ? "confirm" : "request")); }; return ( router.back()}>

{t("resetPassword")}

{t("resetPasswordHelp")}
{mode === "request" ? ( ) : ( <> )} {error ? {error} : null} {success ? {success} : null}