- Phase 3 PW-Reset: 3 screens (forgot-password → reset-otp → new-password),
verifyOtp({type:'recovery'}), new updatePassword() action
- Custom Brevo-Mail templates (backend/public/templates/) — 5 HTMLs with
go-template i18n (de/en/fr/ar incl. RTL for AR), OTP-only (no link),
ReBreak branding
- signUp metadata.data.locale aus i18n.language → templates resolven Sprache
- Account-Switch-Bug fix: signOut() resettet alle 10 user-spezifischen stores
+ invalidateMe()
134 lines
4.2 KiB
TypeScript
134 lines
4.2 KiB
TypeScript
import { useState } from 'react';
|
|
import {
|
|
View,
|
|
Text,
|
|
TextInput,
|
|
TouchableOpacity,
|
|
ActivityIndicator,
|
|
} from 'react-native';
|
|
import { useRouter } from 'expo-router';
|
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { useAuthStore } from '../../stores/auth';
|
|
import { KeyboardAwareScreen } from '../../components/KeyboardAwareScreen';
|
|
|
|
const INPUT_STYLE = {
|
|
fontSize: 16,
|
|
lineHeight: 22,
|
|
paddingVertical: 14,
|
|
paddingHorizontal: 16,
|
|
color: '#0a0a0a',
|
|
fontFamily: 'Nunito_400Regular',
|
|
} as const;
|
|
|
|
export default function NewPasswordScreen() {
|
|
const router = useRouter();
|
|
const { t } = useTranslation();
|
|
const { updatePassword, signOut } = useAuthStore();
|
|
|
|
const [password, setPassword] = useState('');
|
|
const [confirm, setConfirm] = useState('');
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [saved, setSaved] = useState(false);
|
|
|
|
const onSubmit = async () => {
|
|
if (!password || !confirm) return;
|
|
if (password.length < 8) {
|
|
setError(t('auth.passwordMin8'));
|
|
return;
|
|
}
|
|
if (password !== confirm) {
|
|
setError(t('auth.newPasswordMismatch'));
|
|
return;
|
|
}
|
|
setError(null);
|
|
setLoading(true);
|
|
const res = await updatePassword(password);
|
|
setLoading(false);
|
|
if (res.error) {
|
|
setError(res.error);
|
|
return;
|
|
}
|
|
setSaved(true);
|
|
router.replace('/(app)');
|
|
};
|
|
|
|
const onCancel = async () => {
|
|
await signOut();
|
|
router.replace('/(auth)/signin');
|
|
};
|
|
|
|
return (
|
|
<SafeAreaView className="flex-1 bg-white">
|
|
<KeyboardAwareScreen contentContainerStyle={{ paddingHorizontal: 24, justifyContent: 'center' }}>
|
|
<Text className="text-3xl text-neutral-900 mb-2" style={{ fontFamily: 'Nunito_700Bold' }}>
|
|
{t('auth.newPasswordTitle')}
|
|
</Text>
|
|
<Text className="text-base text-neutral-500 mb-8 leading-6" style={{ fontFamily: 'Nunito_400Regular' }}>
|
|
{t('auth.newPasswordSubtitle')}
|
|
</Text>
|
|
|
|
<TextInput
|
|
className="bg-neutral-100 rounded-xl mb-3"
|
|
style={INPUT_STYLE}
|
|
placeholder={t('auth.newPasswordPlaceholder')}
|
|
placeholderTextColor="#a3a3a3"
|
|
secureTextEntry
|
|
autoComplete="new-password"
|
|
autoCapitalize="none"
|
|
value={password}
|
|
onChangeText={setPassword}
|
|
autoFocus
|
|
/>
|
|
|
|
<TextInput
|
|
className="bg-neutral-100 rounded-xl mb-3"
|
|
style={INPUT_STYLE}
|
|
placeholder={t('auth.newPasswordConfirmPlaceholder')}
|
|
placeholderTextColor="#a3a3a3"
|
|
secureTextEntry
|
|
autoComplete="new-password"
|
|
autoCapitalize="none"
|
|
value={confirm}
|
|
onChangeText={setConfirm}
|
|
/>
|
|
|
|
{error && (
|
|
<View className="bg-red-50 border border-red-200 rounded-xl px-4 py-3 mb-3">
|
|
<Text className="text-red-600 text-sm" style={{ fontFamily: 'Nunito_400Regular' }}>{error}</Text>
|
|
</View>
|
|
)}
|
|
|
|
{saved && (
|
|
<View className="bg-green-50 border border-green-200 rounded-xl px-4 py-3 mb-3">
|
|
<Text className="text-green-700 text-sm" style={{ fontFamily: 'Nunito_400Regular' }}>{t('auth.newPasswordSaved')}</Text>
|
|
</View>
|
|
)}
|
|
|
|
<TouchableOpacity
|
|
onPress={onSubmit}
|
|
disabled={loading || !password || !confirm}
|
|
activeOpacity={0.8}
|
|
className="bg-rebreak-500 rounded-xl items-center mt-1 disabled:opacity-40"
|
|
style={{ paddingVertical: 16 }}
|
|
>
|
|
{loading ? (
|
|
<ActivityIndicator color="white" size="small" />
|
|
) : (
|
|
<Text className="text-white text-base" style={{ fontFamily: 'Nunito_600SemiBold' }}>{t('auth.newPasswordSave')}</Text>
|
|
)}
|
|
</TouchableOpacity>
|
|
|
|
<TouchableOpacity
|
|
onPress={onCancel}
|
|
activeOpacity={0.7}
|
|
className="py-4 items-center mt-2"
|
|
>
|
|
<Text className="text-neutral-500 text-sm" style={{ fontFamily: 'Nunito_400Regular' }}>{t('auth.newPasswordCancelLink')}</Text>
|
|
</TouchableOpacity>
|
|
</KeyboardAwareScreen>
|
|
</SafeAreaView>
|
|
);
|
|
}
|