* Extended functionalities

* Added different UIs
This commit is contained in:
2026-06-24 21:43:40 +02:00
parent 282f4864c4
commit f15bb7a916
348 changed files with 59057 additions and 498 deletions

View File

@@ -0,0 +1,145 @@
import { Link, router } from 'expo-router';
import { useState } from 'react';
import { KeyboardAvoidingView, Platform, ScrollView, StyleSheet, Text, View } from 'react-native';
import { useTranslation } from 'react-i18next';
import { Button } from '@/src/components/Button';
import { Input } from '@/src/components/Input';
import { Screen } from '@/src/components/Screen';
import { useAuth } from '@/src/context/AuthContext';
import { useAppearance } from '@/src/context/AppearanceContext';
import { extractApiError } from '@/src/utils/apiError';
function validatePassword(password: string, t: (key: string) => string): string | null {
if (password.length < 8) return t('validation.passwordMinLength');
if (!/[A-Z]/.test(password)) return t('validation.passwordUppercase');
if (!/[a-z]/.test(password)) return t('validation.passwordLowercase');
if (!/\d/.test(password)) return t('validation.passwordDigit');
return null;
}
export default function RegisterScreen() {
const { t } = useTranslation();
const { register } = useAuth();
const { colors } = useAppearance();
const [email, setEmail] = useState('');
const [userName, setUserName] = useState('');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
async function handleSubmit() {
setError(null);
if (!email.trim()) {
setError(t('validation.emailRequired'));
return;
}
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.trim())) {
setError(t('validation.emailInvalid'));
return;
}
const passwordError = validatePassword(password, t);
if (passwordError) {
setError(passwordError);
return;
}
if (!confirmPassword) {
setError(t('validation.confirmPasswordRequired'));
return;
}
if (password !== confirmPassword) {
setError(t('validation.passwordsMismatch'));
return;
}
setLoading(true);
try {
await register({
email: email.trim(),
password,
userName: userName.trim() || undefined,
});
router.replace('/(main)/(tabs)');
} catch (err) {
setError(extractApiError(err, t('errors.registerFailed')));
} finally {
setLoading(false);
}
}
return (
<Screen title={t('auth.createAccountTitle')} subtitle={t('auth.createAccountSubtitle')}>
<KeyboardAvoidingView
style={styles.flex}
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
>
<ScrollView contentContainerStyle={styles.scroll} keyboardShouldPersistTaps="handled">
<View style={styles.form}>
<Input
label={t('auth.email')}
autoCapitalize="none"
keyboardType="email-address"
placeholder={t('auth.emailPlaceholder')}
value={email}
onChangeText={setEmail}
/>
<Input
label={t('auth.displayName')}
placeholder={t('auth.displayNamePlaceholder')}
value={userName}
onChangeText={setUserName}
/>
<Input
label={t('auth.password')}
secureTextEntry
value={password}
onChangeText={setPassword}
/>
<Text style={[styles.hint, { color: colors.textMuted }]}>{t('auth.passwordHint')}</Text>
<Input
label={t('auth.confirmPassword')}
secureTextEntry
value={confirmPassword}
onChangeText={setConfirmPassword}
/>
{error ? <Text style={[styles.error, { color: colors.danger }]}>{error}</Text> : null}
<Button
title={loading ? t('auth.creatingAccount') : t('auth.createAccount')}
loading={loading}
onPress={handleSubmit}
/>
</View>
<Text style={[styles.footer, { color: colors.textMuted }]}>
{t('auth.hasAccount')}{' '}
<Link href="/(auth)/login" style={{ color: colors.brand, fontWeight: '600' }}>
{t('auth.signIn')}
</Link>
</Text>
</ScrollView>
</KeyboardAvoidingView>
</Screen>
);
}
const styles = StyleSheet.create({
flex: { flex: 1 },
scroll: {
gap: 16,
paddingBottom: 24,
},
form: {
gap: 14,
},
hint: {
fontSize: 13,
marginTop: -6,
},
error: {
fontSize: 14,
},
footer: {
textAlign: 'center',
fontSize: 15,
},
});