import { useState, useEffect, useRef } from 'react'; import { View, Keyboard, Pressable, Linking, TouchableOpacity, KeyboardAvoidingView, Platform, ScrollView, } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useRouter } from 'expo-router'; import { useTranslation } from 'react-i18next'; import Text from '~/components/atoms/Text'; import Input from '~/components/atoms/Input'; import Button from '~/components/atoms/Button'; import { useAuth } from '~/features/auth'; import { authService } from '~/features/auth/services/authService'; import { NotificationChannel } from '~/features/notifications/types'; import useNotification from '~/features/notifications/useNotification'; import { useTheme } from '~/features/theme/ThemeProvider'; import GoogleSignInButton from './components/GoogleSignInButton'; import AppleSignInButton from './components/AppleSignInButton'; import MemoroLogo from '~/components/atoms/MemoroLogo'; import colors from '~/tailwind.config.js'; import { useResponsive } from '~/hooks/useResponsive'; import Icon from '~/components/atoms/Icon'; import { useLanguage } from '~/features/i18n/LanguageContext'; import LanguageSelector from '~/features/i18n/LanguageSelector'; export type AuthMode = | 'initial' | 'login' | 'register' | 'login-options' | 'register-options' | 'login-email' | 'register-email' | 'forgot-password' | 'password-reset-success'; export default function Auth({ initialMode = 'login' }: { initialMode?: AuthMode }) { // Auth state const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); const [error, setError] = useState(null); const [successMessage, setSuccessMessage] = useState(null); const [mode, setMode] = useState(initialMode); const [keyboardHeight, setKeyboardHeight] = useState(0); const [isLanguageSelectorVisible, setIsLanguageSelectorVisible] = useState(false); const [resetEmail, setResetEmail] = useState(''); // Store email for success message // Hooks const { t } = useTranslation(); const { signIn, signUp, signInWithGoogle, loading, showPasswordResetModal, setShowPasswordResetModal, authModeOverride, setAuthModeOverride, } = useAuth(); const { showNotification } = useNotification(); const router = useRouter(); const { isDark, themeVariant } = useTheme(); const insets = useSafeAreaInsets(); const { isMobile, isTablet } = useResponsive(); const { currentLanguage, languages } = useLanguage(); // Helper functions to simplify complex theme color access const getThemeColor = (colorKey: string) => { const themeColors = isDark ? (colors as any).theme?.extend?.colors?.dark?.[themeVariant] : (colors as any).theme?.extend?.colors?.[themeVariant]; return themeColors?.[colorKey]; }; const getPageBackground = () => getThemeColor('pageBackground'); const getContentBackground = () => getThemeColor('contentBackground'); const getPrimaryColor = () => getThemeColor('primary'); // Helper for keyboard visibility const isKeyboardVisible = keyboardHeight > 0; // Debounce timer ref const keyboardDebounceTimer = useRef(null); // Keyboard height tracking with debouncing for Android useEffect(() => { const keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', (e) => { if (Platform.OS === 'android') { // Debounce keyboard show on Android if (keyboardDebounceTimer.current) { clearTimeout(keyboardDebounceTimer.current); } keyboardDebounceTimer.current = setTimeout(() => { setKeyboardHeight(e.endCoordinates.height); }, 100); } else { setKeyboardHeight(e.endCoordinates.height); } }); const keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', () => { if (Platform.OS === 'android') { // Debounce keyboard hide on Android if (keyboardDebounceTimer.current) { clearTimeout(keyboardDebounceTimer.current); } keyboardDebounceTimer.current = setTimeout(() => { setKeyboardHeight(0); }, 100); } else { setKeyboardHeight(0); } }); return () => { keyboardDidShowListener.remove(); keyboardDidHideListener.remove(); if (keyboardDebounceTimer.current) { clearTimeout(keyboardDebounceTimer.current); } }; }, []); // Apply auth mode override when it changes useEffect(() => { if (authModeOverride) { if (__DEV__) { console.log('Applying auth mode override:', authModeOverride); } setMode(authModeOverride as AuthMode); setAuthModeOverride(null); // Clear the override after applying } }, [authModeOverride, setAuthModeOverride]); // Clear error when input changes const handleEmailChange = (text: string) => { setEmail(text); setError(null); setSuccessMessage(null); }; const handlePasswordChange = (text: string) => { setPassword(text); setError(null); setSuccessMessage(null); }; const handleConfirmPasswordChange = (text: string) => { setConfirmPassword(text); setError(null); setSuccessMessage(null); }; // Reset all form fields const resetForm = () => { setEmail(''); setPassword(''); setConfirmPassword(''); setError(null); setSuccessMessage(null); }; // Switch between auth modes const switchMode = (newMode: AuthMode) => { setMode(newMode); setError(null); setSuccessMessage(null); }; // Handle login async function handleLogin() { // Dismiss keyboard Keyboard.dismiss(); // Clear any previous errors setError(null); // Validate inputs if (!email) { setError(t('auth.error_email_required', 'Bitte gib deine E-Mail-Adresse ein')); return; } if (!password) { setError(t('auth.error_password_required', 'Bitte gib dein Passwort ein')); return; } // Validate email format const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { setError(t('auth.error_invalid_email', 'Bitte gib eine gültige E-Mail-Adresse ein')); return; } // Attempt to sign in and get the result const result = await signIn(email, password); // If not successful, display the error message if (!result.success) { let errorMessage = result.error || t( 'auth.error_invalid_credentials', 'Ungültige Anmeldedaten. Bitte überprüfe deine E-Mail und dein Passwort.' ); // Handle specific error codes with translations if (result.error === 'EMAIL_NOT_VERIFIED') { errorMessage = t( 'auth.error_email_not_verified', 'Bitte bestätige deine E-Mail-Adresse, um dich anzumelden. Überprüfe deinen Posteingang.' ); } else if (result.error === 'INVALID_CREDENTIALS') { errorMessage = t( 'auth.error_invalid_credentials', 'Ungültige Anmeldedaten. Bitte überprüfe deine E-Mail und dein Passwort.' ); } else if (result.error === 'FIREBASE_USER_PASSWORD_RESET_REQUIRED') { errorMessage = t( 'auth.error_firebase_password_reset_required', 'Aufgrund eines Systemupdates musst du dein Passwort zurücksetzen. Bitte nutze die "Passwort vergessen" Funktion.' ); // Don't set error for this case - modal will be shown from AuthContext return; // Early return to prevent setting error below } setError(errorMessage); // Show error notification (except for Firebase password reset which already shows modal) if (result.error !== 'FIREBASE_USER_PASSWORD_RESET_REQUIRED') { showNotification({ title: t('auth.login_failed', 'Anmeldung fehlgeschlagen'), body: errorMessage, channelType: NotificationChannel.FUNCTIONAL, }); } } } // Handle registration async function handleRegister() { // Dismiss keyboard Keyboard.dismiss(); // Clear any previous errors setError(null); // Validate inputs if (!email) { setError(t('auth.error_email_required', 'Bitte gib deine E-Mail-Adresse ein')); return; } if (!password) { setError(t('auth.error_password_required', 'Bitte gib dein Passwort ein')); return; } if (!confirmPassword) { setError(t('auth.error_confirm_password', 'Bitte bestätige dein Passwort')); return; } if (password !== confirmPassword) { setError(t('auth.error_passwords_not_match', 'Passwörter stimmen nicht überein')); return; } // Validate email format const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { setError(t('auth.error_invalid_email', 'Bitte gib eine gültige E-Mail-Adresse ein')); return; } // Check password strength if (password.length < 8) { setError( t('auth.error_password_too_short', 'Das Passwort muss mindestens 8 Zeichen lang sein') ); return; } // Check for lowercase, uppercase, digits and symbols const hasLowercase = /[a-z]/.test(password); const hasUppercase = /[A-Z]/.test(password); const hasDigit = /[0-9]/.test(password); const hasSymbol = /[^a-zA-Z0-9]/.test(password); if (!hasLowercase || !hasUppercase || !hasDigit || !hasSymbol) { setError( t( 'auth.error_password_requirements', 'Das Passwort muss mindestens einen Kleinbuchstaben, einen Großbuchstaben, eine Ziffer und ein Sonderzeichen enthalten' ) ); return; } // Attempt to sign up and get the result const result = await signUp(email, password); // If not successful, display the error message if (!result.success) { const errorMessage = result.error || t( 'auth.error_registration_failed', 'Registrierung fehlgeschlagen. Bitte versuche es erneut.' ); setError(errorMessage); // Show error notification showNotification({ title: t('auth.registration_failed', 'Registrierung fehlgeschlagen'), body: errorMessage, channelType: NotificationChannel.FUNCTIONAL, }); } else if (result.needsVerification) { // Set success message for inline display const confirmMessage = t( 'auth.check_email_confirmation', 'Registrierung erfolgreich! Bitte überprüfe deine E-Mail und klicke auf den Bestätigungslink, um dein Konto zu aktivieren.' ); setSuccessMessage(confirmMessage); // Show success notification for email verification showNotification({ title: t('auth.registration_success', 'Registrierung erfolgreich'), body: confirmMessage, channelType: NotificationChannel.FUNCTIONAL, }); // Clear sensitive fields but keep email to show what was registered setPassword(''); setConfirmPassword(''); // Switch to login mode so user can sign in after confirming email switchMode('login-email'); } else { // Registration completed successfully without verification showNotification({ title: t('auth.registration_success', 'Registrierung erfolgreich'), body: t('auth.welcome_message', 'Willkommen bei Memoro!'), channelType: NotificationChannel.FUNCTIONAL, }); } } // Handle password reset async function handleForgotPassword() { // Dismiss keyboard Keyboard.dismiss(); // Clear any previous errors setError(null); // Validate inputs if (!email) { setError(t('auth.error_email_required', 'Bitte gib deine E-Mail-Adresse ein')); return; } // Validate email format const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { setError(t('auth.error_invalid_email', 'Bitte gib eine gültige E-Mail-Adresse ein')); return; } // Send password reset email const result = await authService.forgotPassword(email); if (result.success) { // Store email for success message and switch to success screen setResetEmail(email); resetForm(); switchMode('password-reset-success'); } else { // Show error with specific handling for rate limiting let errorMessage = result.error || t('auth.passwordResetError', 'Password reset failed'); // Check if it's a rate limit error if (result.error?.includes('rate limit') || result.error?.includes('too many')) { errorMessage = t( 'auth.reset_password_rate_limit', 'Too many password reset attempts. Please wait a few minutes before trying again.' ); } setError(errorMessage); showNotification({ title: t('auth.passwordResetError', 'Password reset failed'), body: errorMessage, channelType: NotificationChannel.FUNCTIONAL, }); } } // Get title based on current mode const getTitle = () => { switch (mode) { case 'initial': return t('auth.welcome', 'Willkommen bei Memoro'); case 'login': return t('auth.welcome_back', 'Willkommen zurück'); case 'register': return t('auth.create_account', 'Konto erstellen'); case 'login-options': return t('auth.login', 'Anmelden'); case 'register-options': return t('auth.create_account', 'Konto erstellen'); case 'login-email': return t('auth.login_with_email', 'Mit E-Mail anmelden'); case 'register-email': return t('auth.register_with_email', 'Mit E-Mail registrieren'); case 'forgot-password': return t('auth.reset_password', 'Passwort zurücksetzen'); case 'password-reset-success': return t('auth.reset_email_sent_title', 'E-Mail wurde versendet!'); default: return 'Memoro'; } }; // Get action button text based on current mode const getActionButtonText = () => { switch (mode) { case 'login-email': return loading ? t('auth.logging_in', 'Anmelden...') : t('auth.login', 'Anmelden'); case 'register-email': return loading ? t('auth.registering', 'Registrieren...') : t('auth.register', 'Registrieren'); case 'forgot-password': return loading ? t('auth.sending', 'Senden...') : t('auth.reset_password', 'Passwort zurücksetzen'); default: return ''; } }; // Get action button icon based on current mode const getActionButtonIcon = () => { switch (mode) { case 'login-email': return 'arrow-right'; case 'register-email': return 'user-plus'; case 'forgot-password': return 'key'; default: return 'arrow-right'; } }; // Handle action button press based on current mode const handleActionButtonPress = () => { switch (mode) { case 'login-email': return handleLogin(); case 'register-email': return handleRegister(); case 'forgot-password': return handleForgotPassword(); default: return null; } }; return ( {/* Language selector button in top right */} setIsLanguageSelectorVisible(true)}> {languages[currentLanguage]?.emoji || '🌐'} {currentLanguage} {/* Language Selector Modal */} setIsLanguageSelectorVisible(false)} /> {/* Logo */} {/* Button-Container */} {getTitle()} {mode === 'initial' ? ( // Initial mode with login and register buttons