← back to Costa Rica App
app/login.tsx
82 lines
import { useState } from 'react';
import { View, Text, TextInput, TouchableOpacity, StyleSheet, Alert, Platform } from 'react-native';
import * as AppleAuthentication from 'expo-apple-authentication';
import { router } from 'expo-router';
import { api, setToken } from '../src/api';
export default function Login() {
const [mode, setMode] = useState<'login' | 'register'>('login');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [name, setName] = useState('');
const [phone, setPhone] = useState('');
const [busy, setBusy] = useState(false);
async function submit() {
setBusy(true);
try {
const r = mode === 'login'
? await api.login(email, password)
: await api.register({ email, password, full_name: name, phone });
await setToken(r.token);
router.back();
} catch (e: any) { Alert.alert('Error', e.message); } finally { setBusy(false); }
}
async function appleSignIn() {
try {
const cred = await AppleAuthentication.signInAsync({
requestedScopes: [
AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
AppleAuthentication.AppleAuthenticationScope.EMAIL,
],
});
const fullName = [cred.fullName?.givenName, cred.fullName?.familyName].filter(Boolean).join(' ');
const r = await api.apple(cred.identityToken!, fullName || undefined);
await setToken(r.token);
router.back();
} catch (e: any) {
if (e.code === 'ERR_REQUEST_CANCELED') return;
Alert.alert('Apple sign-in failed', e.message);
}
}
return (
<View style={s.wrap}>
<Text style={s.h}>{mode === 'login' ? 'Welcome back' : 'Create account'}</Text>
{Platform.OS === 'ios' && (
<>
<AppleAuthentication.AppleAuthenticationButton
buttonType={AppleAuthentication.AppleAuthenticationButtonType.SIGN_IN}
buttonStyle={AppleAuthentication.AppleAuthenticationButtonStyle.BLACK}
cornerRadius={10}
style={{ height: 48, marginBottom: 14 }}
onPress={appleSignIn}
/>
<View style={s.orRow}><View style={s.line} /><Text style={s.or}>or</Text><View style={s.line} /></View>
</>
)}
{mode === 'register' && <TextInput style={s.i} placeholder="Full name" value={name} onChangeText={setName} />}
<TextInput style={s.i} placeholder="Email" autoCapitalize="none" keyboardType="email-address" value={email} onChangeText={setEmail} />
{mode === 'register' && <TextInput style={s.i} placeholder="Phone (+506…)" keyboardType="phone-pad" value={phone} onChangeText={setPhone} />}
<TextInput style={s.i} placeholder="Password" secureTextEntry value={password} onChangeText={setPassword} />
<TouchableOpacity style={s.cta} onPress={submit} disabled={busy}>
<Text style={s.ctaTxt}>{busy ? '…' : mode === 'login' ? 'Sign in' : 'Sign up'}</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => setMode(mode === 'login' ? 'register' : 'login')}>
<Text style={s.switch}>{mode === 'login' ? 'New here? Create an account' : 'Have an account? Sign in'}</Text>
</TouchableOpacity>
</View>
);
}
const s = StyleSheet.create({
wrap: { flex: 1, padding: 20, backgroundColor: '#f6f7f5' },
h: { fontSize: 24, fontWeight: '800', color: '#1a2b22', marginBottom: 16 },
i: { backgroundColor: '#fff', borderRadius: 10, height: 48, paddingHorizontal: 14, marginBottom: 12, borderWidth: 1, borderColor: '#e2e5e0' },
cta: { backgroundColor: '#0a7d55', height: 52, borderRadius: 12, alignItems: 'center', justifyContent: 'center', marginTop: 6 },
ctaTxt: { color: '#fff', fontSize: 16, fontWeight: '700' },
switch: { color: '#0a7d55', textAlign: 'center', marginTop: 18 },
orRow: { flexDirection: 'row', alignItems: 'center', marginBottom: 14 },
line: { flex: 1, height: 1, backgroundColor: '#d8ddd6' },
or: { color: '#6b756e', marginHorizontal: 10, fontSize: 13 },
});