import React, { useState } from "react"; import { ScrollView, View } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; import { type Href } from "expo-router"; import { useNavigation } from "../../context/NavigationContext"; import { StatusBar } from "expo-status-bar"; import { Ionicons } from "@expo/vector-icons"; import { useColorScheme } from "nativewind"; import * as Haptics from "expo-haptics"; import { MutedText, Text } from "../../components/text"; import { Button, IconButton } from "../../components/button"; import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "../../components/card"; import { withClickTracking } from "../../services/telemetry"; import { EmailInput } from "../../components/input"; import AuthService from "../../services/auth.service"; import { validateEmail } from "../../utils/validation"; import { useAppConfig } from "../../context/AppConfigContext"; export function ForgotPasswordScreen() { const [email, setEmail] = useState(""); const [error, setError] = useState(""); const [isLoading, setIsLoading] = useState(false); const [sent, setSent] = useState(false); const { colorScheme } = useColorScheme(); const isDark = colorScheme === "dark"; const router = useNavigation(); const { routes } = useAppConfig(); const handleSubmit = async () => { // Re-entry guard against double-submit (button + form-submit // keypress) firing before React flushes `setIsLoading(true)` // and disables the button. if (isLoading) { return; } if (!email) { setError("Email is required"); return; } if (!validateEmail(email)) { setError("Please enter a valid email address"); return; } setIsLoading(true); try { await AuthService.forgotPassword(email); } catch (e: unknown) { // Deliberately swallow — the server is supposed to respond // identically for known and unknown emails, but a 5xx (or a // rate-limit, network blip, etc.) would otherwise let the UI // branch on "did this email exist or not?". Surface the same // confirmation regardless; the server retries on its own // schedule and ops can read the failure from the server logs. if (__DEV__) { console.warn("[forgot-password] request failed; showing generic confirmation:", e); } } await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); setSent(true); setIsLoading(false); }; return ( router.back()} accessibilityLabel="Go back" accessibilityRole="button" > {sent ? "Check your email" : "Forgot password?"} {sent ? `If an account exists for ${email}, you will receive a password reset code.` : "Enter your email to reset your password"} {!sent ? ( <> { setEmail(text); if (error) { setError(""); } }} error={error} autoFocus returnKeyType="done" onSubmitEditing={handleSubmit} accessibilityLabel="Email address" /> Remember your password?{" "} router.push(routes.login) )} accessibilityRole="link" accessibilityLabel="Sign in" > Sign In ) : ( Didn't receive the code?{" "} { setSent(false); setError(""); })} accessibilityRole="button" accessibilityLabel="Resend reset email" > Resend )} ); } export default ForgotPasswordScreen;