refactored code into components (major cleanup for reused code) added email for local acccount creation and changes to how nas and google photo connections work and added themes

This commit is contained in:
bobbert committed 2026-02-21 15:04:58 +00:00
1 parent c1d7139055
commit 6f4ec66e2e
17 files changed
+1761 -1018

No files matched your search

+69 -175
View File
@@ -1,19 +1,16 @@
import React, { useState } from 'react';
import {
StyleSheet,
View,
Text,
TextInput,
TouchableOpacity,
Alert,
ActivityIndicator,
KeyboardAvoidingView,
Platform,
ScrollView,
StyleSheet, View, Text, TextInput, TouchableOpacity, Alert,
ActivityIndicator, KeyboardAvoidingView, Platform, ScrollView, Image,
} from 'react-native';
import { useTheme } from '../services/ThemeContext';
import DatabaseService from '../services/DatabaseService';
// Same icon constant as LoginScreen – edit in one place
const APP_ICON = require('../../assets/icon.png');
const RegisterScreen = ({ navigation }) => {
const { theme } = useTheme();
const [username, setUsername] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
@@ -21,51 +18,23 @@ const RegisterScreen = ({ navigation }) => {
const [loading, setLoading] = useState(false);
const handleRegister = async () => {
if (!username.trim()) {
Alert.alert('Error', 'Please enter a username');
return;
}
if (username.trim().length < 3) {
Alert.alert('Error', 'Username must be at least 3 characters');
return;
}
if (!email.trim()) {
Alert.alert('Error', 'Please enter an email');
return;
}
if (!email.includes('@') || !email.includes('.')) {
Alert.alert('Error', 'Please enter a valid email address');
return;
}
if (!password.trim()) {
Alert.alert('Error', 'Please enter a password');
return;
}
if (password.length < 6) {
Alert.alert('Error', 'Password must be at least 6 characters');
return;
}
if (password !== confirmPassword) {
Alert.alert('Error', 'Passwords do not match');
return;
}
if (!username.trim()) { Alert.alert('Error', 'Please enter a username'); return; }
if (username.trim().length < 3) { Alert.alert('Error', 'Username must be at least 3 characters'); return; }
if (!email.trim()) { Alert.alert('Error', 'Please enter an email'); return; }
if (!email.includes('@') || !email.includes('.')) { Alert.alert('Error', 'Please enter a valid email address'); return; }
if (!password.trim()) { Alert.alert('Error', 'Please enter a password'); return; }
if (password.length < 6) { Alert.alert('Error', 'Password must be at least 6 characters'); return; }
if (password !== confirmPassword) { Alert.alert('Error', 'Passwords do not match'); return; }
setLoading(true);
try {
const exists = await DatabaseService.userExists(username.trim(), email.trim().toLowerCase());
if (exists) {
Alert.alert('Error', 'Username or email already taken');
setLoading(false);
return;
}
if (exists) { Alert.alert('Error', 'Username or email already taken'); setLoading(false); return; }
const userId = await DatabaseService.createUser(username.trim(), email.trim().toLowerCase(), password);
Alert.alert('Success', 'Account created!', [
{
text: 'OK',
onPress: () => navigation.replace('Onboarding', { userId }),
},
{ text: 'OK', onPress: () => navigation.replace('Onboarding', { userId }) },
]);
} catch (error) {
console.error('Registration error:', error);
@@ -78,85 +47,69 @@ const RegisterScreen = ({ navigation }) => {
return (
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={styles.container}
style={[styles.container, { backgroundColor: theme.background }]}
>
<ScrollView contentContainerStyle={styles.scrollContent} showsVerticalScrollIndicator={false}>
<View style={styles.content}>
<View style={styles.headerContainer}>
<Text style={styles.appIcon}>📸</Text>
<Text style={styles.title}>Create Account</Text>
<Text style={styles.subtitle}>Set up your local photo library account</Text>
<Image source={APP_ICON} style={styles.appIcon} resizeMode="contain" />
<Text style={[styles.title, { color: theme.text }]}>Create Account</Text>
<Text style={[styles.subtitle, { color: theme.textSecondary }]}>Set up your local photo library account</Text>
</View>
<View style={styles.inputContainer}>
<Text style={styles.label}>Username</Text>
<Text style={[styles.label, { color: theme.textSecondary }]}>Username</Text>
<TextInput
style={styles.input}
placeholder="Choose a username"
placeholderTextColor="#9aa0a6"
value={username}
onChangeText={setUsername}
editable={!loading}
autoCapitalize="none"
autoCorrect={false}
style={[styles.input, { backgroundColor: theme.inputBg, borderColor: theme.border, color: theme.text }]}
placeholder="Choose a username" placeholderTextColor={theme.textTertiary}
value={username} onChangeText={setUsername} editable={!loading}
autoCapitalize="none" autoCorrect={false}
/>
<Text style={styles.label}>Email</Text>
<Text style={[styles.label, { color: theme.textSecondary }]}>Email</Text>
<TextInput
style={styles.input}
placeholder="Enter your email"
placeholderTextColor="#9aa0a6"
value={email}
onChangeText={setEmail}
editable={!loading}
autoCapitalize="none"
autoCorrect={false}
keyboardType="email-address"
style={[styles.input, { backgroundColor: theme.inputBg, borderColor: theme.border, color: theme.text }]}
placeholder="Enter your email" placeholderTextColor={theme.textTertiary}
value={email} onChangeText={setEmail} editable={!loading}
autoCapitalize="none" autoCorrect={false} keyboardType="email-address"
/>
<Text style={styles.label}>Password</Text>
<Text style={[styles.label, { color: theme.textSecondary }]}>Password</Text>
<TextInput
style={styles.input}
placeholder="Create a password (min 6 characters)"
placeholderTextColor="#9aa0a6"
value={password}
onChangeText={setPassword}
editable={!loading}
secureTextEntry
autoCapitalize="none"
style={[styles.input, { backgroundColor: theme.inputBg, borderColor: theme.border, color: theme.text }]}
placeholder="Create a password (min 6 characters)" placeholderTextColor={theme.textTertiary}
value={password} onChangeText={setPassword} editable={!loading}
secureTextEntry autoCapitalize="none"
/>
<Text style={styles.label}>Confirm Password</Text>
<Text style={[styles.label, { color: theme.textSecondary }]}>Confirm Password</Text>
<TextInput
style={styles.input}
placeholder="Confirm your password"
placeholderTextColor="#9aa0a6"
value={confirmPassword}
onChangeText={setConfirmPassword}
editable={!loading}
secureTextEntry
autoCapitalize="none"
style={[styles.input, { backgroundColor: theme.inputBg, borderColor: theme.border, color: theme.text }]}
placeholder="Confirm your password" placeholderTextColor={theme.textTertiary}
value={confirmPassword} onChangeText={setConfirmPassword} editable={!loading}
secureTextEntry autoCapitalize="none"
/>
</View>
{/* Create Account button – blue */}
<TouchableOpacity
style={[styles.registerButton, loading && styles.buttonDisabled]}
style={[styles.primaryButton, { backgroundColor: theme.primary, borderColor: theme.border }, loading && styles.buttonDisabled]}
onPress={handleRegister}
disabled={loading}
>
{loading ? (
<ActivityIndicator color="#fff" />
) : (
<Text style={styles.buttonText}>Create Account</Text>
{loading ? <ActivityIndicator color="#fff" /> : (
<Text style={styles.primaryButtonText}>Create Account</Text>
)}
</TouchableOpacity>
<View style={styles.loginContainer}>
<Text style={styles.loginText}>Already have an account? </Text>
<TouchableOpacity onPress={() => navigation.navigate('Login')} disabled={loading}>
<Text style={styles.loginLink}>Sign in</Text>
</TouchableOpacity>
</View>
{/* Back to Login button – white/outlined */}
<TouchableOpacity
style={[styles.secondaryButton, { backgroundColor: theme.background, borderColor: theme.border }]}
onPress={() => navigation.navigate('Login')}
disabled={loading}
>
<Text style={[styles.secondaryButtonText, { color: theme.text }]}>Back to Login</Text>
</TouchableOpacity>
</View>
</ScrollView>
</KeyboardAvoidingView>
@@ -164,86 +117,27 @@ const RegisterScreen = ({ navigation }) => {
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
scrollContent: {
flexGrow: 1,
justifyContent: 'center',
},
content: {
paddingHorizontal: 24,
paddingVertical: 40,
},
headerContainer: {
marginBottom: 36,
alignItems: 'center',
},
appIcon: {
fontSize: 56,
marginBottom: 16,
},
title: {
fontSize: 28,
fontWeight: '700',
color: '#202124',
marginBottom: 8,
},
subtitle: {
fontSize: 15,
color: '#5f6368',
textAlign: 'center',
},
inputContainer: {
marginBottom: 24,
},
label: {
fontSize: 13,
fontWeight: '600',
color: '#3c4043',
marginBottom: 6,
marginTop: 16,
},
container: { flex: 1 },
scrollContent: { flexGrow: 1, justifyContent: 'center' },
content: { paddingHorizontal: 24, paddingVertical: 40 },
headerContainer: { marginBottom: 36, alignItems: 'center' },
appIcon: { width: 80, height: 80, marginBottom: 16, borderRadius: 16 },
title: { fontSize: 28, fontWeight: '700', marginBottom: 8 },
subtitle: { fontSize: 15, textAlign: 'center' },
inputContainer: { marginBottom: 24 },
label: { fontSize: 13, fontWeight: '600', marginBottom: 6, marginTop: 16 },
input: {
borderWidth: 1,
borderColor: '#dadce0',
borderRadius: 8,
paddingHorizontal: 14,
paddingVertical: 12,
fontSize: 15,
backgroundColor: '#fff',
color: '#202124',
borderWidth: 1, borderRadius: 8, paddingHorizontal: 14, paddingVertical: 12, fontSize: 15,
},
registerButton: {
backgroundColor: '#1a73e8',
borderRadius: 8,
paddingVertical: 14,
alignItems: 'center',
marginBottom: 20,
primaryButton: {
borderRadius: 8, paddingVertical: 14, alignItems: 'center', marginBottom: 10, borderWidth: 1,
},
buttonDisabled: {
opacity: 0.6,
},
buttonText: {
color: '#fff',
fontSize: 15,
fontWeight: '600',
},
loginContainer: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
},
loginText: {
color: '#5f6368',
fontSize: 14,
},
loginLink: {
color: '#1a73e8',
fontSize: 14,
fontWeight: '600',
primaryButtonText: { color: '#fff', fontSize: 15, fontWeight: '600' },
secondaryButton: {
borderRadius: 8, paddingVertical: 14, alignItems: 'center', borderWidth: 1,
},
secondaryButtonText: { fontSize: 15, fontWeight: '600' },
buttonDisabled: { opacity: 0.6 },
});
export default RegisterScreen;