166 lines
5.1 KiB
TypeScript
166 lines
5.1 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { Pressable, StyleSheet, Text, TextInput, View } from 'react-native';
|
|
import { router } from 'expo-router';
|
|
import { useAuthStore, type AuthState } from '../store/auth-store';
|
|
import { buttonA11y, textInputA11y, dynamicType } from '../lib/accessibility';
|
|
import { colors } from '../theme';
|
|
|
|
export default function AuthScreen() {
|
|
const [mode, setMode] = useState<'signin' | 'register'>('signin');
|
|
const [displayName, setDisplayName] = useState('');
|
|
const [email, setEmail] = useState('');
|
|
const [password, setPassword] = useState('');
|
|
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
|
const isAuthenticated = useAuthStore((state: AuthState) => state.isAuthenticated);
|
|
const signIn = useAuthStore((state: AuthState) => state.signIn);
|
|
const register = useAuthStore((state: AuthState) => state.register);
|
|
const isLoading = useAuthStore((state: AuthState) => state.isLoading);
|
|
|
|
useEffect(() => {
|
|
if (isAuthenticated) {
|
|
router.replace('/(tabs)');
|
|
}
|
|
}, [isAuthenticated]);
|
|
|
|
return (
|
|
<View style={styles.container}>
|
|
<Text style={styles.title}>NoteLett</Text>
|
|
<Text style={styles.subtitle}>{mode === 'signin' ? 'Sign in to continue' : 'Create your account'}</Text>
|
|
{mode === 'register' ? (
|
|
<TextInput
|
|
{...textInputA11y('Display name')}
|
|
autoCapitalize="words"
|
|
onChangeText={(value: string) => {
|
|
setErrorMessage(null);
|
|
setDisplayName(value);
|
|
}}
|
|
placeholder="Display name"
|
|
placeholderTextColor={colors.textTertiary}
|
|
style={styles.input}
|
|
value={displayName}
|
|
/>
|
|
) : null}
|
|
<TextInput
|
|
{...textInputA11y('Email address')}
|
|
autoCapitalize="none"
|
|
keyboardType="email-address"
|
|
onChangeText={(value: string) => {
|
|
setErrorMessage(null);
|
|
setEmail(value);
|
|
}}
|
|
placeholder="Email"
|
|
placeholderTextColor={colors.textTertiary}
|
|
style={styles.input}
|
|
value={email}
|
|
/>
|
|
<TextInput
|
|
{...textInputA11y('Password')}
|
|
onChangeText={(value: string) => {
|
|
setErrorMessage(null);
|
|
setPassword(value);
|
|
}}
|
|
placeholder="Password"
|
|
placeholderTextColor={colors.textTertiary}
|
|
secureTextEntry
|
|
style={styles.input}
|
|
value={password}
|
|
/>
|
|
{errorMessage ? <Text style={styles.errorText}>{errorMessage}</Text> : null}
|
|
<Pressable
|
|
{...buttonA11y(mode === 'signin' ? 'Sign in' : 'Register account', { disabled: isLoading })}
|
|
disabled={isLoading}
|
|
onPress={async () => {
|
|
const didAuthenticate =
|
|
mode === 'signin'
|
|
? await signIn(email.trim(), password)
|
|
: await register(email.trim(), password, displayName.trim());
|
|
|
|
if (didAuthenticate) {
|
|
router.replace('/(tabs)');
|
|
return;
|
|
}
|
|
|
|
setErrorMessage(
|
|
mode === 'signin'
|
|
? 'Sign-in failed. Check your credentials or connection and try again.'
|
|
: 'Registration failed. Check your details or connection and try again.',
|
|
);
|
|
}}
|
|
style={[styles.button, isLoading ? styles.buttonDisabled : null]}
|
|
>
|
|
<Text maxFontSizeMultiplier={dynamicType.control} style={styles.buttonText}>
|
|
{isLoading ? (mode === 'signin' ? 'Signing in…' : 'Creating account…') : mode === 'signin' ? 'Sign in' : 'Register'}
|
|
</Text>
|
|
</Pressable>
|
|
<Pressable
|
|
{...buttonA11y(mode === 'signin' ? 'Switch to register mode' : 'Switch to sign in mode')}
|
|
onPress={() => {
|
|
setErrorMessage(null);
|
|
setMode((current) => (current === 'signin' ? 'register' : 'signin'));
|
|
}}
|
|
style={styles.modeSwitch}
|
|
>
|
|
<Text maxFontSizeMultiplier={dynamicType.control} style={styles.modeSwitchText}>
|
|
{mode === 'signin' ? 'Need an account? Register' : 'Already have an account? Sign in'}
|
|
</Text>
|
|
</Pressable>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
justifyContent: 'center',
|
|
padding: 24,
|
|
backgroundColor: colors.bgCanvas,
|
|
gap: 12,
|
|
},
|
|
title: {
|
|
fontSize: 28,
|
|
fontWeight: '700',
|
|
color: colors.textPrimary,
|
|
},
|
|
subtitle: {
|
|
fontSize: 16,
|
|
color: colors.textSecondary,
|
|
marginBottom: 12,
|
|
},
|
|
input: {
|
|
borderWidth: 1,
|
|
borderColor: colors.borderDefault,
|
|
borderRadius: 12,
|
|
paddingHorizontal: 14,
|
|
paddingVertical: 12,
|
|
color: colors.textPrimary,
|
|
backgroundColor: colors.surfaceCard,
|
|
},
|
|
button: {
|
|
marginTop: 8,
|
|
backgroundColor: colors.accentPrimary,
|
|
borderRadius: 12,
|
|
paddingVertical: 14,
|
|
alignItems: 'center',
|
|
},
|
|
buttonDisabled: {
|
|
opacity: 0.55,
|
|
},
|
|
buttonText: {
|
|
color: colors.textPrimary,
|
|
fontWeight: '700',
|
|
},
|
|
errorText: {
|
|
color: colors.danger,
|
|
fontSize: 14,
|
|
},
|
|
modeSwitch: {
|
|
alignItems: 'center',
|
|
paddingVertical: 4,
|
|
},
|
|
modeSwitchText: {
|
|
color: colors.accentSecondary,
|
|
fontSize: 14,
|
|
fontWeight: '600',
|
|
},
|
|
});
|