slight refactor of all the code, made it so that login and onboarding works, need to add system to validate connections to google photos + NAS before just blindly accepting

Added library home page and navigation bar at the bottom, using emojis as icon placeholders, album page also works.

elipses for settings and icon also work.
This commit is contained in:
bobbert committed 2026-02-21 13:52:28 +00:00
1 parent 37511780b7
commit c1d7139055
17 files changed
+1431 -10040

No files matched your search

+263 -449
View File
@@ -12,7 +12,6 @@ import {
} from 'react-native';
import DatabaseService from '../services/DatabaseService';
import GoogleAUTH from '../AUTH/GoogleAUTH';
import IOSAUTH from '../AUTH/IOSAUTH';
import NASAUTH from '../AUTH/NASAUTH';
const OnboardingScreen = ({ navigation, route }) => {
@@ -20,41 +19,37 @@ const OnboardingScreen = ({ navigation, route }) => {
const [currentStep, setCurrentStep] = useState(0);
const [loading, setLoading] = useState(false);
// Service selections
// Service toggles
const [googleEnabled, setGoogleEnabled] = useState(false);
const [iosEnabled, setIosEnabled] = useState(false);
const [nasEnabled, setNasEnabled] = useState(false);
// NAS configuration
// Service connection status
const [googleConnected, setGoogleConnected] = useState(false);
const [iosConnected, setIosConnected] = useState(false);
const [nasConnected, setNasConnected] = useState(false);
// NAS config
const [nasHost, setNasHost] = useState('');
const [nasPort, setNasPort] = useState('22');
const [nasUsername, setNasUsername] = useState('');
const [nasPassword, setNasPassword] = useState('');
const [nasPhotoFolder, setNasPhotoFolder] = useState('/home/user/photos');
// Default service selection
// Default service
const [defaultService, setDefaultService] = useState(null);
const steps = [
{
title: 'Select Services',
description: 'Choose which services to enable (you can enable 1-3)',
},
{
title: 'Configure Services',
description: 'Set up the enabled services',
},
{
title: 'Choose Default Service',
description: 'Select which service to use for uploads by default',
},
{ title: 'Choose Services', description: 'Select which photo services to connect (1-3)' },
{ title: 'Connect Services', description: 'Set up each selected service' },
{ title: 'Default Upload', description: 'Choose where new photos upload by default' },
];
const getEnabledServices = () => {
const services = [];
if (googleEnabled) services.push('Google Photos');
if (iosEnabled) services.push('iCloud Photos');
if (nasEnabled) services.push('NAS');
if (googleEnabled) services.push({ name: 'Google Photos', type: 'GOOGLE_PHOTOS' });
if (iosEnabled) services.push({ name: 'iCloud Photos', type: 'ICLOUD_PHOTOS' });
if (nasEnabled) services.push({ name: 'NAS Storage', type: 'NAS' });
return services;
};
@@ -62,21 +57,18 @@ const OnboardingScreen = ({ navigation, route }) => {
try {
setLoading(true);
const googleAuth = new GoogleAUTH();
// In a real app, this would trigger OAuth flow
const result = await googleAuth.authenticate('auth_code');
const result = await googleAuth.authenticate();
await DatabaseService.linkAccount(
userId,
'GOOGLE_PHOTOS',
{
accessToken: result.accessToken,
email: 'user@gmail.com',
},
defaultService === 'GOOGLE_PHOTOS'
);
await DatabaseService.linkAccount(userId, 'GOOGLE_PHOTOS', {
accessToken: result.accessToken,
refreshToken: result.refreshToken || null,
email: result.email || 'google-user',
});
setGoogleConnected(true);
Alert.alert('Success', 'Google Photos connected!');
} catch (error) {
console.error('Google auth error:', error);
Alert.alert('Error', 'Failed to connect Google Photos: ' + error.message);
} finally {
setLoading(false);
@@ -86,57 +78,50 @@ const OnboardingScreen = ({ navigation, route }) => {
const handleIosAuth = async () => {
try {
setLoading(true);
const iosAuth = new IOSAUTH();
const result = await iosAuth.authenticate('icloud_user', 'icloud_password');
// Placeholder - iCloud API not yet available
await DatabaseService.linkAccount(userId, 'ICLOUD_PHOTOS', {
status: 'placeholder',
note: 'iCloud integration pending Apple API access',
});
await DatabaseService.linkAccount(
userId,
'ICLOUD_PHOTOS',
{
accessToken: result.accessToken,
email: 'user@icloud.com',
},
defaultService === 'ICLOUD_PHOTOS'
);
Alert.alert('Success', 'iCloud Photos connected!');
setIosConnected(true);
Alert.alert('Info', 'iCloud Photos registered. Full integration coming soon.');
} catch (error) {
Alert.alert('Error', 'Failed to connect iCloud Photos: ' + error.message);
console.error('iCloud auth error:', error);
Alert.alert('Error', 'Failed to set up iCloud: ' + error.message);
} finally {
setLoading(false);
}
};
const handleNasAuth = async () => {
if (!nasHost || !nasUsername || !nasPassword) {
Alert.alert('Error', 'Please fill in all NAS connection details');
if (!nasHost.trim() || !nasUsername.trim() || !nasPassword.trim()) {
Alert.alert('Error', 'Please fill in all NAS connection fields');
return;
}
try {
setLoading(true);
const nasAuth = new NASAUTH();
const result = await nasAuth.authenticate(
nasHost,
await nasAuth.authenticate(
nasHost.trim(),
parseInt(nasPort) || 22,
nasUsername,
nasPassword
nasUsername.trim(),
nasPassword,
nasPhotoFolder.trim()
);
await DatabaseService.linkAccount(
userId,
'NAS',
{
host: nasHost,
port: nasPort,
username: nasUsername,
photoFolder: nasPhotoFolder,
},
defaultService === 'NAS'
);
await DatabaseService.linkAccount(userId, 'NAS', {
host: nasHost.trim(),
port: parseInt(nasPort) || 22,
username: nasUsername.trim(),
photoFolder: nasPhotoFolder.trim(),
});
setNasConnected(true);
Alert.alert('Success', 'NAS connected!');
} catch (error) {
console.error('NAS auth error:', error);
Alert.alert('Error', 'Failed to connect NAS: ' + error.message);
} finally {
setLoading(false);
@@ -145,49 +130,37 @@ const OnboardingScreen = ({ navigation, route }) => {
const handleNext = async () => {
if (currentStep === 0) {
const enabledCount = getEnabledServices().length;
if (enabledCount === 0) {
if (getEnabledServices().length === 0) {
Alert.alert('Error', 'Please select at least one service');
return;
}
if (enabledCount > 3) {
Alert.alert('Error', 'You can enable a maximum of 3 services');
return;
}
setCurrentStep(1);
} else if (currentStep === 1) {
// Configure services
try {
setLoading(true);
if (googleEnabled) await handleGoogleAuth();
if (iosEnabled) await handleIosAuth();
if (nasEnabled) await handleNasAuth();
setCurrentStep(2);
} catch (error) {
console.error('Configuration error:', error);
} finally {
setLoading(false);
// Check at least one service is connected
const anyConnected =
(googleEnabled && googleConnected) ||
(iosEnabled && iosConnected) ||
(nasEnabled && nasConnected);
if (!anyConnected) {
Alert.alert('Error', 'Please connect at least one service before continuing');
return;
}
setCurrentStep(2);
} else if (currentStep === 2) {
// Complete onboarding
if (!defaultService) {
Alert.alert('Error', 'Please select a default service');
Alert.alert('Error', 'Please select a default upload service');
return;
}
try {
setLoading(true);
// Set default service
const accounts = await DatabaseService.getLinkedAccounts(userId);
const defaultAccount = accounts.find(
(acc) => acc.service_type === defaultService
);
const defaultAccount = accounts.find((a) => a.service_type === defaultService);
if (defaultAccount) {
await DatabaseService.setDefaultService(userId, defaultAccount.id);
}
// Navigate to home screen
navigation.replace('Home', { userId });
navigation.replace('MainTabs', { userId });
} catch (error) {
Alert.alert('Error', 'Failed to complete setup: ' + error.message);
} finally {
@@ -196,192 +169,184 @@ const OnboardingScreen = ({ navigation, route }) => {
}
};
const handleBack = () => {
if (currentStep > 0) {
setCurrentStep(currentStep - 1);
}
};
return (
<View style={styles.container}>
{/* Progress bar */}
<View style={styles.progressBar}>
{steps.map((_, i) => (
<View key={i} style={[styles.progressDot, i <= currentStep && styles.progressDotActive]} />
))}
</View>
<ScrollView style={styles.content} showsVerticalScrollIndicator={false}>
<View style={styles.header}>
<Text style={styles.stepIndicator}>
Step {currentStep + 1} of {steps.length}
</Text>
<Text style={styles.stepLabel}>Step {currentStep + 1} of {steps.length}</Text>
<Text style={styles.title}>{steps[currentStep].title}</Text>
<Text style={styles.description}>
{steps[currentStep].description}
</Text>
<Text style={styles.description}>{steps[currentStep].description}</Text>
</View>
{/* Step 0: Select services */}
{currentStep === 0 && (
<View style={styles.servicesContainer}>
<ServiceCard
<View style={styles.cardsContainer}>
<ServiceToggleCard
icon="📷"
name="Google Photos"
description="Access your Google Photos library"
enabled={googleEnabled}
onToggle={() => setGoogleEnabled(!googleEnabled)}
icon="📷"
description="Connect to your Google Photos account"
/>
<ServiceCard
<ServiceToggleCard
icon="🍎"
name="iCloud Photos"
description="Access your iCloud photo library (placeholder)"
enabled={iosEnabled}
onToggle={() => setIosEnabled(!iosEnabled)}
icon="🍎"
description="Connect to your iCloud Photos account"
/>
<ServiceCard
name="Network Storage (NAS)"
<ServiceToggleCard
icon="💾"
name="NAS Storage"
description="Connect to your Ubuntu server via SFTP"
enabled={nasEnabled}
onToggle={() => setNasEnabled(!nasEnabled)}
icon="💾"
description="Connect to your Ubuntu NAS via SFTP"
/>
</View>
)}
{/* Step 1: Configure each service */}
{currentStep === 1 && (
<View style={styles.configContainer}>
<View style={styles.cardsContainer}>
{googleEnabled && (
<ServiceConfigCard
title="Google Photos"
onPress={handleGoogleAuth}
loading={loading}
>
<Text style={styles.configText}>
Tap to authenticate with your Google account
</Text>
</ServiceConfigCard>
<View style={styles.configCard}>
<View style={styles.configHeader}>
<Text style={styles.configIcon}>📷</Text>
<Text style={styles.configTitle}>Google Photos</Text>
{googleConnected && <Text style={styles.connectedBadge}>✓ Connected</Text>}
</View>
{!googleConnected && (
<TouchableOpacity
style={[styles.authButton, loading && styles.buttonDisabled]}
onPress={handleGoogleAuth}
disabled={loading}
>
{loading ? <ActivityIndicator color="#fff" /> : (
<Text style={styles.authButtonText}>Sign in with Google</Text>
)}
</TouchableOpacity>
)}
</View>
)}
{iosEnabled && (
<ServiceConfigCard
title="iCloud Photos"
onPress={handleIosAuth}
loading={loading}
>
<Text style={styles.configText}>
Tap to authenticate with your Apple account
</Text>
</ServiceConfigCard>
<View style={styles.configCard}>
<View style={styles.configHeader}>
<Text style={styles.configIcon}>🍎</Text>
<Text style={styles.configTitle}>iCloud Photos</Text>
{iosConnected && <Text style={styles.connectedBadge}>✓ Connected</Text>}
</View>
{!iosConnected && (
<TouchableOpacity
style={[styles.authButton, { backgroundColor: '#333' }, loading && styles.buttonDisabled]}
onPress={handleIosAuth}
disabled={loading}
>
{loading ? <ActivityIndicator color="#fff" /> : (
<Text style={styles.authButtonText}>Connect iCloud</Text>
)}
</TouchableOpacity>
)}
</View>
)}
{nasEnabled && (
<ServiceConfigCard title="Network Storage (NAS)" loading={loading}>
<Text style={styles.configLabel}>Server Address</Text>
<TextInput
style={styles.configInput}
placeholder="192.168.1.100 or nas.example.com"
value={nasHost}
onChangeText={setNasHost}
editable={!loading}
placeholderTextColor="#999"
/>
<View style={styles.configCard}>
<View style={styles.configHeader}>
<Text style={styles.configIcon}>💾</Text>
<Text style={styles.configTitle}>NAS Storage</Text>
{nasConnected && <Text style={styles.connectedBadge}>✓ Connected</Text>}
</View>
{!nasConnected && (
<>
<Text style={styles.fieldLabel}>Server Address</Text>
<TextInput style={styles.fieldInput} placeholder="192.168.1.100" value={nasHost}
onChangeText={setNasHost} editable={!loading} placeholderTextColor="#9aa0a6" />
<Text style={styles.configLabel}>Port</Text>
<TextInput
style={styles.configInput}
placeholder="22"
value={nasPort}
onChangeText={setNasPort}
editable={!loading}
keyboardType="numeric"
placeholderTextColor="#999"
/>
<Text style={styles.fieldLabel}>Port</Text>
<TextInput style={styles.fieldInput} placeholder="22" value={nasPort}
onChangeText={setNasPort} editable={!loading} keyboardType="numeric" placeholderTextColor="#9aa0a6" />
<Text style={styles.configLabel}>Username</Text>
<TextInput
style={styles.configInput}
placeholder="SSH username"
value={nasUsername}
onChangeText={setNasUsername}
editable={!loading}
autoCapitalize="none"
placeholderTextColor="#999"
/>
<Text style={styles.fieldLabel}>Username</Text>
<TextInput style={styles.fieldInput} placeholder="SSH username" value={nasUsername}
onChangeText={setNasUsername} editable={!loading} autoCapitalize="none" placeholderTextColor="#9aa0a6" />
<Text style={styles.configLabel}>Password</Text>
<TextInput
style={styles.configInput}
placeholder="SSH password"
value={nasPassword}
onChangeText={setNasPassword}
editable={!loading}
secureTextEntry
autoCapitalize="none"
placeholderTextColor="#999"
/>
<Text style={styles.fieldLabel}>Password</Text>
<TextInput style={styles.fieldInput} placeholder="SSH password" value={nasPassword}
onChangeText={setNasPassword} editable={!loading} secureTextEntry autoCapitalize="none" placeholderTextColor="#9aa0a6" />
<Text style={styles.configLabel}>Photos Folder Path</Text>
<TextInput
style={styles.configInput}
placeholder="/home/user/photos"
value={nasPhotoFolder}
onChangeText={setNasPhotoFolder}
editable={!loading}
placeholderTextColor="#999"
/>
<Text style={styles.fieldLabel}>Photos Folder</Text>
<TextInput style={styles.fieldInput} placeholder="/home/user/photos" value={nasPhotoFolder}
onChangeText={setNasPhotoFolder} editable={!loading} placeholderTextColor="#9aa0a6" />
<TouchableOpacity
style={[styles.testButton, loading && styles.buttonDisabled]}
onPress={handleNasAuth}
disabled={loading}
>
{loading ? (
<ActivityIndicator color="#fff" />
) : (
<Text style={styles.buttonText}>Test Connection</Text>
)}
</TouchableOpacity>
</ServiceConfigCard>
<TouchableOpacity
style={[styles.authButton, { backgroundColor: '#34a853' }, loading && styles.buttonDisabled]}
onPress={handleNasAuth}
disabled={loading}
>
{loading ? <ActivityIndicator color="#fff" /> : (
<Text style={styles.authButtonText}>Test & Connect</Text>
)}
</TouchableOpacity>
</>
)}
</View>
)}
</View>
)}
{/* Step 2: Default service */}
{currentStep === 2 && (
<View style={styles.defaultServiceContainer}>
<Text style={styles.defaultServiceTitle}>
Select your default upload service:
</Text>
{getEnabledServices().map((service) => (
<ServiceSelectCard
key={service}
service={service}
selected={defaultService === service}
onSelect={() => {
const serviceTypeMap = {
'Google Photos': 'GOOGLE_PHOTOS',
'iCloud Photos': 'ICLOUD_PHOTOS',
NAS: 'NAS',
};
setDefaultService(serviceTypeMap[service]);
}}
/>
))}
<View style={styles.cardsContainer}>
{getEnabledServices().map((svc) => {
const connected =
(svc.type === 'GOOGLE_PHOTOS' && googleConnected) ||
(svc.type === 'ICLOUD_PHOTOS' && iosConnected) ||
(svc.type === 'NAS' && nasConnected);
if (!connected) return null;
return (
<TouchableOpacity
key={svc.type}
style={[styles.selectCard, defaultService === svc.type && styles.selectCardActive]}
onPress={() => setDefaultService(svc.type)}
>
<View style={[styles.radio, defaultService === svc.type && styles.radioActive]} />
<Text style={[styles.selectText, defaultService === svc.type && styles.selectTextActive]}>
{svc.name}
</Text>
</TouchableOpacity>
);
})}
</View>
)}
</ScrollView>
{/* Footer */}
<View style={styles.footer}>
{currentStep > 0 ? (
<TouchableOpacity style={styles.backButton} onPress={() => setCurrentStep(currentStep - 1)} disabled={loading}>
<Text style={styles.backButtonText}>Back</Text>
</TouchableOpacity>
) : (
<View style={{ flex: 1 }} />
)}
<TouchableOpacity
style={[styles.footerButton, currentStep === 0 && styles.hidden]}
onPress={handleBack}
disabled={loading}
>
<Text style={styles.footerButtonText}>Back</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.footerButton, styles.nextButton, loading && styles.buttonDisabled]}
style={[styles.nextButton, loading && styles.buttonDisabled]}
onPress={handleNext}
disabled={loading}
>
{loading ? (
<ActivityIndicator color="#fff" />
) : (
<Text style={styles.footerButtonText}>
{currentStep === steps.length - 1 ? 'Complete' : 'Next'}
{loading ? <ActivityIndicator color="#fff" /> : (
<Text style={styles.nextButtonText}>
{currentStep === steps.length - 1 ? 'Get Started' : 'Next'}
</Text>
)}
</TouchableOpacity>
@@ -390,252 +355,101 @@ const OnboardingScreen = ({ navigation, route }) => {
);
};
const ServiceCard = ({ name, enabled, onToggle, icon, description }) => (
<View style={styles.serviceCard}>
<View style={styles.serviceCardContent}>
<Text style={styles.serviceIcon}>{icon}</Text>
<View style={styles.serviceInfo}>
<Text style={styles.serviceName}>{name}</Text>
<Text style={styles.serviceDescription}>{description}</Text>
const ServiceToggleCard = ({ icon, name, description, enabled, onToggle }) => (
<View style={[styles.toggleCard, enabled && styles.toggleCardActive]}>
<View style={styles.toggleCardContent}>
<Text style={styles.toggleIcon}>{icon}</Text>
<View style={styles.toggleInfo}>
<Text style={styles.toggleName}>{name}</Text>
<Text style={styles.toggleDescription}>{description}</Text>
</View>
</View>
<Switch value={enabled} onValueChange={onToggle} />
<Switch
value={enabled}
onValueChange={onToggle}
trackColor={{ false: '#dadce0', true: '#a8dab5' }}
thumbColor={enabled ? '#34a853' : '#f4f3f4'}
/>
</View>
);
const ServiceConfigCard = ({ title, onPress, loading, children }) => (
<View style={styles.configCard}>
<Text style={styles.configCardTitle}>{title}</Text>
{onPress && (
<TouchableOpacity
style={[styles.configButton, loading && styles.buttonDisabled]}
onPress={onPress}
disabled={loading}
>
{loading ? (
<ActivityIndicator color="#fff" />
) : (
<Text style={styles.buttonText}>Authenticate</Text>
)}
</TouchableOpacity>
)}
{children}
</View>
);
const ServiceSelectCard = ({ service, selected, onSelect }) => (
<TouchableOpacity
style={[styles.selectCard, selected && styles.selectedCard]}
onPress={onSelect}
>
<View style={[styles.radioButton, selected && styles.radioSelected]} />
<Text style={[styles.selectText, selected && styles.selectedText]}>
{service}
</Text>
</TouchableOpacity>
);
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
container: { flex: 1, backgroundColor: '#fff' },
progressBar: {
flexDirection: 'row', justifyContent: 'center', gap: 8,
paddingTop: 60, paddingBottom: 8,
},
content: {
flex: 1,
paddingHorizontal: 20,
paddingTop: 20,
progressDot: {
width: 32, height: 4, borderRadius: 2, backgroundColor: '#dadce0',
},
header: {
marginBottom: 30,
},
stepIndicator: {
fontSize: 12,
color: '#007AFF',
fontWeight: '600',
marginBottom: 8,
},
title: {
fontSize: 28,
fontWeight: 'bold',
color: '#333',
marginBottom: 8,
},
description: {
fontSize: 16,
color: '#666',
},
servicesContainer: {
marginBottom: 20,
},
serviceCard: {
backgroundColor: '#fff',
borderRadius: 12,
padding: 16,
marginBottom: 12,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
borderWidth: 1,
borderColor: '#e0e0e0',
},
serviceCardContent: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
},
serviceIcon: {
fontSize: 32,
marginRight: 16,
},
serviceInfo: {
flex: 1,
},
serviceName: {
fontSize: 16,
fontWeight: '600',
color: '#333',
marginBottom: 4,
},
serviceDescription: {
fontSize: 13,
color: '#999',
},
configContainer: {
marginBottom: 20,
progressDotActive: { backgroundColor: '#1a73e8' },
content: { flex: 1, paddingHorizontal: 20 },
header: { marginBottom: 24 },
stepLabel: { fontSize: 12, color: '#1a73e8', fontWeight: '600', marginBottom: 6 },
title: { fontSize: 26, fontWeight: '700', color: '#202124', marginBottom: 6 },
description: { fontSize: 14, color: '#5f6368' },
cardsContainer: { gap: 12 },
// Toggle cards (Step 0)
toggleCard: {
backgroundColor: '#f8f9fa', borderRadius: 12, padding: 16,
flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center',
borderWidth: 1.5, borderColor: '#e8eaed',
},
toggleCardActive: { borderColor: '#1a73e8', backgroundColor: '#e8f0fe' },
toggleCardContent: { flex: 1, flexDirection: 'row', alignItems: 'center' },
toggleIcon: { fontSize: 28, marginRight: 14 },
toggleInfo: { flex: 1 },
toggleName: { fontSize: 15, fontWeight: '600', color: '#202124', marginBottom: 2 },
toggleDescription: { fontSize: 12, color: '#5f6368' },
// Config cards (Step 1)
configCard: {
backgroundColor: '#fff',
borderRadius: 12,
padding: 16,
marginBottom: 16,
borderWidth: 1,
borderColor: '#e0e0e0',
backgroundColor: '#f8f9fa', borderRadius: 12, padding: 16,
borderWidth: 1, borderColor: '#e8eaed',
},
configCardTitle: {
fontSize: 16,
fontWeight: '600',
color: '#333',
marginBottom: 12,
configHeader: { flexDirection: 'row', alignItems: 'center', marginBottom: 12 },
configIcon: { fontSize: 24, marginRight: 10 },
configTitle: { fontSize: 16, fontWeight: '600', color: '#202124', flex: 1 },
connectedBadge: { fontSize: 13, color: '#34a853', fontWeight: '600' },
authButton: {
backgroundColor: '#1a73e8', borderRadius: 8, paddingVertical: 12, alignItems: 'center',
},
configLabel: {
fontSize: 14,
fontWeight: '600',
color: '#333',
marginTop: 12,
marginBottom: 6,
},
configInput: {
borderWidth: 1,
borderColor: '#ddd',
borderRadius: 8,
paddingHorizontal: 12,
paddingVertical: 10,
fontSize: 14,
backgroundColor: '#f9f9f9',
color: '#333',
},
configText: {
fontSize: 14,
color: '#666',
marginTop: 12,
},
configButton: {
backgroundColor: '#007AFF',
borderRadius: 8,
paddingVertical: 10,
paddingHorizontal: 16,
alignItems: 'center',
},
testButton: {
backgroundColor: '#34C759',
borderRadius: 8,
paddingVertical: 10,
paddingHorizontal: 16,
alignItems: 'center',
marginTop: 16,
},
buttonDisabled: {
opacity: 0.6,
},
buttonText: {
color: '#fff',
fontSize: 14,
fontWeight: '600',
},
defaultServiceContainer: {
marginBottom: 20,
},
defaultServiceTitle: {
fontSize: 16,
fontWeight: '600',
color: '#333',
marginBottom: 12,
authButtonText: { color: '#fff', fontSize: 14, fontWeight: '600' },
fieldLabel: { fontSize: 12, fontWeight: '600', color: '#3c4043', marginTop: 10, marginBottom: 4 },
fieldInput: {
borderWidth: 1, borderColor: '#dadce0', borderRadius: 8,
paddingHorizontal: 12, paddingVertical: 10, fontSize: 14,
backgroundColor: '#fff', color: '#202124',
},
// Select cards (Step 2)
selectCard: {
backgroundColor: '#fff',
borderRadius: 12,
padding: 16,
marginBottom: 12,
flexDirection: 'row',
alignItems: 'center',
borderWidth: 2,
borderColor: '#ddd',
backgroundColor: '#f8f9fa', borderRadius: 12, padding: 16,
flexDirection: 'row', alignItems: 'center',
borderWidth: 2, borderColor: '#e8eaed',
},
selectedCard: {
borderColor: '#007AFF',
backgroundColor: '#f0f7ff',
},
radioButton: {
width: 20,
height: 20,
borderRadius: 10,
borderWidth: 2,
borderColor: '#ddd',
marginRight: 12,
},
radioSelected: {
borderColor: '#007AFF',
backgroundColor: '#007AFF',
},
selectText: {
fontSize: 16,
color: '#333',
},
selectedText: {
color: '#007AFF',
fontWeight: '600',
selectCardActive: { borderColor: '#1a73e8', backgroundColor: '#e8f0fe' },
radio: {
width: 20, height: 20, borderRadius: 10, borderWidth: 2,
borderColor: '#dadce0', marginRight: 12,
},
radioActive: { borderColor: '#1a73e8', backgroundColor: '#1a73e8' },
selectText: { fontSize: 15, color: '#202124', fontWeight: '500' },
selectTextActive: { color: '#1a73e8', fontWeight: '600' },
// Footer
footer: {
flexDirection: 'row',
paddingHorizontal: 20,
paddingVertical: 16,
borderTopWidth: 1,
borderTopColor: '#e0e0e0',
gap: 12,
flexDirection: 'row', paddingHorizontal: 20, paddingVertical: 14,
borderTopWidth: 1, borderTopColor: '#e8eaed', gap: 12,
},
footerButton: {
flex: 1,
paddingVertical: 12,
paddingHorizontal: 16,
borderRadius: 8,
alignItems: 'center',
backgroundColor: '#fff',
borderWidth: 1,
borderColor: '#ddd',
backButton: {
flex: 1, paddingVertical: 12, borderRadius: 8, alignItems: 'center',
backgroundColor: '#f8f9fa', borderWidth: 1, borderColor: '#dadce0',
},
backButtonText: { fontSize: 15, fontWeight: '600', color: '#5f6368' },
nextButton: {
backgroundColor: '#007AFF',
borderColor: '#007AFF',
},
footerButtonText: {
fontSize: 16,
fontWeight: '600',
color: '#333',
},
hidden: {
opacity: 0,
pointerEvents: 'none',
flex: 1, paddingVertical: 12, borderRadius: 8, alignItems: 'center',
backgroundColor: '#1a73e8',
},
nextButtonText: { fontSize: 15, fontWeight: '600', color: '#fff' },
buttonDisabled: { opacity: 0.6 },
});
export default OnboardingScreen;