learning_ai_notes/mobile/src/app/auth.tsx

158 lines
4.7 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 { 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
autoCapitalize="words"
onChangeText={(value: string) => {
setErrorMessage(null);
setDisplayName(value);
}}
placeholder="Display name"
placeholderTextColor={colors.textTertiary}
style={styles.input}
value={displayName}
/>
) : null}
<TextInput
autoCapitalize="none"
keyboardType="email-address"
onChangeText={(value: string) => {
setErrorMessage(null);
setEmail(value);
}}
placeholder="Email"
placeholderTextColor={colors.textTertiary}
style={styles.input}
value={email}
/>
<TextInput
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
accessibilityLabel={mode === 'signin' ? 'Sign in' : 'Register account'}
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}
>
<Text style={styles.buttonText}>
{isLoading ? (mode === 'signin' ? 'Signing in…' : 'Creating account…') : mode === 'signin' ? 'Sign in' : 'Register'}
</Text>
</Pressable>
<Pressable
accessibilityLabel={mode === 'signin' ? 'Switch to register mode' : 'Switch to sign in mode'}
onPress={() => {
setErrorMessage(null);
setMode((current) => (current === 'signin' ? 'register' : 'signin'));
}}
style={styles.modeSwitch}
>
<Text 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',
},
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',
},
});