You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

377 lines
9.8 KiB

3 weeks ago
import React, { useState, useRef, useEffect } from "react";
import {
View,
Text,
StyleSheet,
TouchableOpacity,
TextInput,
Modal,
ActivityIndicator,
Platform,
} from "react-native";
import { useTranslation } from "react-i18next";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { VerificationCodeInput } from "./VerificationCodeInput";
type ForgotEmailPasswordProps = {
visible?: boolean;
onClose?: () => void;
email?: string;
};
export const ForgotEmailPassword = ({
visible = true,
onClose = () => {},
email = ""
}: ForgotEmailPasswordProps) => {
const { t } = useTranslation();
// States
const [userEmail, setUserEmail] = useState(email);
const [emailError, setEmailError] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [showVerificationModal, setShowVerificationModal] = useState(false);
// Refs
const emailInputRef = useRef<TextInput>(null);
// Focus email input when modal opens
useEffect(() => {
if (visible && !email) {
const timer = setTimeout(() => {
if (emailInputRef.current) {
emailInputRef.current.focus();
}
}, 300);
return () => clearTimeout(timer);
}
}, [visible, email]);
// Set initial email value if provided
useEffect(() => {
if (email) {
setUserEmail(email);
}
}, [email]);
// Validate email
const validateEmail = (email: string): boolean => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
};
// Handle email change
const handleEmailChange = (text: string) => {
setUserEmail(text);
if (text) {
setEmailError(!validateEmail(text));
} else {
setEmailError(false);
}
setError(null);
};
// Handle submit
const handleSubmit = async () => {
if (!validateEmail(userEmail)) {
setEmailError(true);
return;
}
try {
setLoading(true);
// TODO: Replace with actual API call to send reset code
// For example: await userApi.sendEmailPasswordResetCode({ email: userEmail });
// Log reset method
console.log("Password reset method: Email");
try {
// Store reset method in AsyncStorage or other storage
await AsyncStorage.setItem("@password_reset_method", "email");
} catch (storageError) {
console.error("Failed to store reset method:", storageError);
}
// Simulate API call success
setTimeout(() => {
setLoading(false);
setShowVerificationModal(true);
}, 1500);
} catch (error) {
setLoading(false);
setError('Failed to send reset code. Please try again.');
}
};
// Handle verification code submission
const handleVerifyCode = async (code: string): Promise<boolean> => {
// TODO: Replace with actual API call to verify code
// For example: return await userApi.verifyEmailPasswordResetCode({
// email: userEmail,
// code: code
// });
// Simulate verification for demo
return new Promise((resolve) => {
setTimeout(() => {
// For demo: code "123456" is valid, others are invalid
resolve(code === "123456");
}, 1500);
});
};
// Handle resend code
const handleResendCode = async (): Promise<void> => {
// TODO: Replace with actual API call to resend code
// For example: await userApi.sendEmailPasswordResetCode({
// email: userEmail
// });
// Simulate resend for demo
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 1500);
});
};
// Handle reset password
const handleResetPassword = async (password: string): Promise<boolean> => {
// TODO: Replace with actual API call to reset password
// For example: return await userApi.resetEmailPassword({
// email: userEmail,
// password: password
// });
// Simulate API call for demo
return new Promise((resolve) => {
setTimeout(() => {
// On success, close this modal too
if (onClose) onClose();
resolve(true); // Always succeed for demo
}, 1500);
});
};
return (
<Modal
visible={visible}
animationType="slide"
transparent={true}
onRequestClose={onClose}
statusBarTranslucent={true}
>
<View style={styles.modalContainer}>
<View style={styles.forgotPasswordContainer}>
<View style={styles.forgotPasswordHeader}>
<TouchableOpacity
style={styles.forgotPasswordCloseButton}
onPress={onClose}
activeOpacity={0.7}
>
<Text style={styles.forgotPasswordCloseButtonText}></Text>
</TouchableOpacity>
<Text style={styles.forgotPasswordTitle}>{t("login.forgotPassword.title")}</Text>
</View>
<View style={styles.forgotPasswordContent}>
<Text style={styles.forgotPasswordDescription}>
{t("login.forgotPassword.emailDescription")}
</Text>
<View style={styles.emailInputContainer}>
<TextInput
ref={emailInputRef}
style={styles.emailInput}
placeholder={t("email")}
value={userEmail}
onChangeText={handleEmailChange}
keyboardType="email-address"
autoCapitalize="none"
autoCorrect={false}
autoFocus={!email}
/>
{userEmail.length > 0 && (
<TouchableOpacity
style={styles.emailClearButton}
onPress={() => {
setUserEmail("");
setEmailError(false);
setError(null);
}}
activeOpacity={0.7}
>
<Text style={styles.emailClearButtonText}></Text>
</TouchableOpacity>
)}
</View>
{emailError && (
<Text style={styles.emailErrorText}>
{t("login.forgotPassword.invalidEmail")}
</Text>
)}
{error && (
<Text style={styles.errorText}>
{error}
</Text>
)}
<TouchableOpacity
style={[
styles.submitButton,
(!userEmail.trim() || emailError) && styles.disabledButton,
]}
onPress={handleSubmit}
disabled={!userEmail.trim() || emailError || loading}
activeOpacity={0.7}
>
{loading ? (
<ActivityIndicator size="small" color="#fff" />
) : (
<Text style={styles.submitButtonText}>
{t("login.forgotPassword.submit")}
</Text>
)}
</TouchableOpacity>
</View>
</View>
{/* Verification Code Modal */}
<VerificationCodeInput
visible={showVerificationModal}
onClose={() => setShowVerificationModal(false)}
phoneNumber={userEmail} // We're using phoneNumber prop for email too
onVerify={handleVerifyCode}
onResend={handleResendCode}
onResetPassword={handleResetPassword}
/>
</View>
</Modal>
);
};
const styles = StyleSheet.create({
modalContainer: {
flex: 1,
backgroundColor: "rgba(0,0,0,0.5)",
justifyContent: "flex-end",
zIndex: 9999,
},
forgotPasswordContainer: {
backgroundColor: "#fff",
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
height: "80%",
shadowColor: "#000",
shadowOffset: { width: 0, height: -2 },
shadowOpacity: 0.1,
shadowRadius: 5,
elevation: 5,
},
forgotPasswordHeader: {
flexDirection: "row",
alignItems: "center",
paddingTop: 20,
paddingHorizontal: 16,
paddingBottom: 15,
borderBottomWidth: 1,
borderBottomColor: "#f0f0f0",
},
forgotPasswordCloseButton: {
padding: 8,
width: 36,
height: 36,
justifyContent: "center",
alignItems: "center",
},
forgotPasswordCloseButtonText: {
fontSize: 18,
color: "#000",
},
forgotPasswordTitle: {
flex: 1,
fontSize: 18,
fontWeight: "600",
color: "#000",
textAlign: "center",
marginRight: 36,
},
forgotPasswordContent: {
padding: 20,
paddingBottom: Platform.OS === "ios" ? 50 : 30,
},
forgotPasswordDescription: {
fontSize: 14,
color: "#333",
marginBottom: 20,
lineHeight: 20,
},
emailInputContainer: {
flexDirection: "row",
alignItems: "center",
borderWidth: 1,
borderColor: "#E1E1E1",
borderRadius: 25,
height: 50,
marginBottom: 20,
position: "relative",
},
emailInput: {
flex: 1,
height: "100%",
paddingHorizontal: 16,
fontSize: 16,
paddingRight: 36,
},
emailClearButton: {
position: "absolute",
right: 12,
top: "50%",
transform: [{ translateY: -12 }],
height: 24,
width: 24,
justifyContent: "center",
alignItems: "center",
},
emailClearButtonText: {
fontSize: 16,
color: "#999",
fontWeight: "500",
textAlign: "center",
},
emailErrorText: {
color: "#FF3B30",
fontSize: 14,
marginTop: -12,
marginBottom: 16,
paddingHorizontal: 5,
},
errorText: {
color: "#FF3B30",
fontSize: 14,
marginTop: -12,
marginBottom: 16,
paddingHorizontal: 5,
},
submitButton: {
height: 50,
backgroundColor: "#0039CB",
borderRadius: 25,
justifyContent: "center",
alignItems: "center",
marginTop: 20,
},
disabledButton: {
backgroundColor: "#CCCCCC",
},
submitButtonText: {
color: "#fff",
fontSize: 16,
fontWeight: "600",
},
});