feat: add expo mobile application source code
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
View, Text, StyleSheet, Platform,
|
||||
TouchableOpacity, TextInput, ActivityIndicator,
|
||||
} from 'react-native';
|
||||
import { useRouter } from 'expo-router';
|
||||
import { Feather } from '@expo/vector-icons';
|
||||
import { useAuth } from '../../context/AuthContext';
|
||||
import { useToast } from '../../context/ToastContext';
|
||||
import { useForm } from '../../hooks/useForm';
|
||||
import { useAppTheme } from '../../context/ThemeContext';
|
||||
import { useAppConfig } from '../../context/ConfigContext';
|
||||
import { AppScreen } from '../../components/AppScreen';
|
||||
import { AIButton, AIInput } from '../../components/UI';
|
||||
import { Image } from 'expo-image';
|
||||
import { useTranslation } from '../../context/LanguageContext';
|
||||
|
||||
export default function RegisterScreen() {
|
||||
const router = useRouter();
|
||||
const { signUp, isLoading } = useAuth();
|
||||
const { showToast } = useToast();
|
||||
const { colors, isDark } = useAppTheme();
|
||||
const { config } = useAppConfig();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { values, handleChange } = useForm({
|
||||
name: '',
|
||||
email: '',
|
||||
password: '',
|
||||
password_confirmation: '',
|
||||
});
|
||||
|
||||
const handleRegister = async () => {
|
||||
if (!values.name || !values.email || !values.password) {
|
||||
showToast(t('fillAll'), 'error');
|
||||
return;
|
||||
}
|
||||
if (values.password !== values.password_confirmation) {
|
||||
showToast(t('passMismatch'), 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await signUp(values.name, values.email, values.password);
|
||||
showToast(t('accountCreated'), 'success');
|
||||
router.replace('/(tabs)');
|
||||
} catch (error: any) {
|
||||
showToast(error.message || t('regFailed'), 'error');
|
||||
}
|
||||
};
|
||||
|
||||
const cardBg = isDark ? '#1A1A1A' : '#FFFFFF';
|
||||
const inputBg = isDark ? '#222222' : '#F5F5F5';
|
||||
const border = isDark ? '#2A2A2A' : '#EEEEEE';
|
||||
|
||||
return (
|
||||
<AppScreen scrollable={true}>
|
||||
<View style={styles.scroll}>
|
||||
{/* Header */}
|
||||
<View style={styles.header}>
|
||||
<View style={[styles.logoBox, { backgroundColor: isDark ? '#1A1A1A' : '#FFFFFF' }]}>
|
||||
{config?.branding?.logo_url ? (
|
||||
<Image source={{ uri: config.branding.logo_url }} style={{ width: 52, height: 52 }} contentFit="contain" />
|
||||
) : (
|
||||
<Text style={[styles.logoLetter, { color: colors.primary }]}>B</Text>
|
||||
)}
|
||||
</View>
|
||||
<Text style={[styles.brandName, { color: colors.text }]}>{t('createAccount')}</Text>
|
||||
<Text style={[styles.tagline, { color: colors.textSecondary }]}>
|
||||
{t('join')} {config?.branding?.app_name || 'biiproject'} ecosystem
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{/* Form card */}
|
||||
<View style={[styles.card, { backgroundColor: cardBg, borderColor: border }]}>
|
||||
<AIInput
|
||||
label={t('fullName')}
|
||||
icon="user"
|
||||
placeholder={t('namePlaceholder')}
|
||||
value={values.name}
|
||||
onChangeText={v => handleChange('name', v)}
|
||||
/>
|
||||
|
||||
<AIInput
|
||||
label={t('email')}
|
||||
icon="mail"
|
||||
placeholder={t('emailPlaceholder')}
|
||||
value={values.email}
|
||||
onChangeText={v => handleChange('email', v)}
|
||||
autoCapitalize="none"
|
||||
keyboardType="email-address"
|
||||
containerStyle={{ marginTop: 14 }}
|
||||
/>
|
||||
|
||||
<AIInput
|
||||
label={t('password')}
|
||||
icon="lock"
|
||||
placeholder={t('passwordPlaceholder')}
|
||||
value={values.password}
|
||||
onChangeText={v => handleChange('password', v)}
|
||||
isPassword
|
||||
containerStyle={{ marginTop: 14 }}
|
||||
/>
|
||||
|
||||
<AIInput
|
||||
label={t('confirmPassword')}
|
||||
icon="check-circle"
|
||||
placeholder={t('passwordPlaceholder')}
|
||||
value={values.password_confirmation}
|
||||
onChangeText={v => handleChange('password_confirmation', v)}
|
||||
isPassword
|
||||
containerStyle={{ marginTop: 14 }}
|
||||
/>
|
||||
|
||||
<AIButton
|
||||
title={t('signUp')}
|
||||
onPress={handleRegister}
|
||||
loading={isLoading}
|
||||
style={{ marginTop: 24 }}
|
||||
/>
|
||||
|
||||
<View style={styles.footer}>
|
||||
<Text style={[styles.footerText, { color: colors.textSecondary }]}>{t('haveAccount')}</Text>
|
||||
<TouchableOpacity onPress={() => router.push('/(auth)/login')}>
|
||||
<Text style={[styles.linkText, { color: colors.primary }]}>{t('signIn')}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
<View style={{ height: 40 }} />
|
||||
</View>
|
||||
</AppScreen>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
scroll: { paddingHorizontal: 24, paddingTop: 40 },
|
||||
header: { alignItems: 'center', marginBottom: 28 },
|
||||
logoBox: {
|
||||
width: 72, height: 72, borderRadius: 24,
|
||||
alignItems: 'center', justifyContent: 'center',
|
||||
elevation: 8, shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.1, shadowRadius: 12,
|
||||
},
|
||||
logoLetter: { fontSize: 40, fontFamily: 'Outfit_800ExtraBold' },
|
||||
brandName: { fontSize: 28, fontFamily: 'Outfit_800ExtraBold', marginTop: 14 },
|
||||
tagline: { fontSize: 13, fontFamily: 'Outfit_400Regular', marginTop: 4 },
|
||||
|
||||
card: { borderRadius: 28, padding: 28, borderWidth: 1 },
|
||||
inputGroup: {},
|
||||
label: { fontSize: 10, fontFamily: 'Outfit_700Bold', letterSpacing: 1, marginBottom: 8, textTransform: 'uppercase' },
|
||||
inputRow: {
|
||||
flexDirection: 'row', alignItems: 'center',
|
||||
height: 54, borderRadius: 14, borderWidth: 1, paddingHorizontal: 14,
|
||||
},
|
||||
inputIcon: { marginRight: 10 },
|
||||
input: { flex: 1, fontSize: 15, fontFamily: 'Outfit_500Medium' },
|
||||
mainBtn: { height: 56, borderRadius: 16, alignItems: 'center', justifyContent: 'center' },
|
||||
btnText: { fontSize: 16, fontFamily: 'Outfit_700Bold' },
|
||||
|
||||
footer: { flexDirection: 'row', justifyContent: 'center', marginTop: 24 },
|
||||
footerText: { fontSize: 14, fontFamily: 'Outfit_400Regular' },
|
||||
linkText: { fontSize: 14, fontFamily: 'Outfit_700Bold' },
|
||||
});
|
||||
Reference in New Issue
Block a user