created a localised login system

This commit is contained in:
bobbert committed 2026-02-21 13:14:01 +00:00
1 parent 59ac1fc926
commit 37511780b7
16 files changed
+3721 -20

No files matched your search

+642
View File
@@ -0,0 +1,642 @@
import React, { useState } from 'react';
import {
StyleSheet,
View,
Text,
ScrollView,
TouchableOpacity,
Alert,
ActivityIndicator,
Switch,
TextInput,
} 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 }) => {
const { userId } = route.params;
const [currentStep, setCurrentStep] = useState(0);
const [loading, setLoading] = useState(false);
// Service selections
const [googleEnabled, setGoogleEnabled] = useState(false);
const [iosEnabled, setIosEnabled] = useState(false);
const [nasEnabled, setNasEnabled] = useState(false);
// NAS configuration
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
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',
},
];
const getEnabledServices = () => {
const services = [];
if (googleEnabled) services.push('Google Photos');
if (iosEnabled) services.push('iCloud Photos');
if (nasEnabled) services.push('NAS');
return services;
};
const handleGoogleAuth = async () => {
try {
setLoading(true);
const googleAuth = new GoogleAUTH();
// In a real app, this would trigger OAuth flow
const result = await googleAuth.authenticate('auth_code');
await DatabaseService.linkAccount(
userId,
'GOOGLE_PHOTOS',
{
accessToken: result.accessToken,
email: 'user@gmail.com',
},
defaultService === 'GOOGLE_PHOTOS'
);
Alert.alert('Success', 'Google Photos connected!');
} catch (error) {
Alert.alert('Error', 'Failed to connect Google Photos: ' + error.message);
} finally {
setLoading(false);
}
};
const handleIosAuth = async () => {
try {
setLoading(true);
const iosAuth = new IOSAUTH();
const result = await iosAuth.authenticate('icloud_user', 'icloud_password');
await DatabaseService.linkAccount(
userId,
'ICLOUD_PHOTOS',
{
accessToken: result.accessToken,
email: 'user@icloud.com',
},
defaultService === 'ICLOUD_PHOTOS'
);
Alert.alert('Success', 'iCloud Photos connected!');
} catch (error) {
Alert.alert('Error', 'Failed to connect iCloud Photos: ' + error.message);
} finally {
setLoading(false);
}
};
const handleNasAuth = async () => {
if (!nasHost || !nasUsername || !nasPassword) {
Alert.alert('Error', 'Please fill in all NAS connection details');
return;
}
try {
setLoading(true);
const nasAuth = new NASAUTH();
const result = await nasAuth.authenticate(
nasHost,
parseInt(nasPort) || 22,
nasUsername,
nasPassword
);
await DatabaseService.linkAccount(
userId,
'NAS',
{
host: nasHost,
port: nasPort,
username: nasUsername,
photoFolder: nasPhotoFolder,
},
defaultService === 'NAS'
);
Alert.alert('Success', 'NAS connected!');
} catch (error) {
Alert.alert('Error', 'Failed to connect NAS: ' + error.message);
} finally {
setLoading(false);
}
};
const handleNext = async () => {
if (currentStep === 0) {
const enabledCount = getEnabledServices().length;
if (enabledCount === 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);
}
} else if (currentStep === 2) {
// Complete onboarding
if (!defaultService) {
Alert.alert('Error', 'Please select a default service');
return;
}
try {
setLoading(true);
// Set default service
const accounts = await DatabaseService.getLinkedAccounts(userId);
const defaultAccount = accounts.find(
(acc) => acc.service_type === defaultService
);
if (defaultAccount) {
await DatabaseService.setDefaultService(userId, defaultAccount.id);
}
// Navigate to home screen
navigation.replace('Home', { userId });
} catch (error) {
Alert.alert('Error', 'Failed to complete setup: ' + error.message);
} finally {
setLoading(false);
}
}
};
const handleBack = () => {
if (currentStep > 0) {
setCurrentStep(currentStep - 1);
}
};
return (
<View style={styles.container}>
<ScrollView style={styles.content} showsVerticalScrollIndicator={false}>
<View style={styles.header}>
<Text style={styles.stepIndicator}>
Step {currentStep + 1} of {steps.length}
</Text>
<Text style={styles.title}>{steps[currentStep].title}</Text>
<Text style={styles.description}>
{steps[currentStep].description}
</Text>
</View>
{currentStep === 0 && (
<View style={styles.servicesContainer}>
<ServiceCard
name="Google Photos"
enabled={googleEnabled}
onToggle={() => setGoogleEnabled(!googleEnabled)}
icon="📷"
description="Connect to your Google Photos account"
/>
<ServiceCard
name="iCloud Photos"
enabled={iosEnabled}
onToggle={() => setIosEnabled(!iosEnabled)}
icon="🍎"
description="Connect to your iCloud Photos account"
/>
<ServiceCard
name="Network Storage (NAS)"
enabled={nasEnabled}
onToggle={() => setNasEnabled(!nasEnabled)}
icon="💾"
description="Connect to your Ubuntu NAS via SFTP"
/>
</View>
)}
{currentStep === 1 && (
<View style={styles.configContainer}>
{googleEnabled && (
<ServiceConfigCard
title="Google Photos"
onPress={handleGoogleAuth}
loading={loading}
>
<Text style={styles.configText}>
Tap to authenticate with your Google account
</Text>
</ServiceConfigCard>
)}
{iosEnabled && (
<ServiceConfigCard
title="iCloud Photos"
onPress={handleIosAuth}
loading={loading}
>
<Text style={styles.configText}>
Tap to authenticate with your Apple account
</Text>
</ServiceConfigCard>
)}
{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"
/>
<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.configLabel}>Username</Text>
<TextInput
style={styles.configInput}
placeholder="SSH username"
value={nasUsername}
onChangeText={setNasUsername}
editable={!loading}
autoCapitalize="none"
placeholderTextColor="#999"
/>
<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.configLabel}>Photos Folder Path</Text>
<TextInput
style={styles.configInput}
placeholder="/home/user/photos"
value={nasPhotoFolder}
onChangeText={setNasPhotoFolder}
editable={!loading}
placeholderTextColor="#999"
/>
<TouchableOpacity
style={[styles.testButton, loading && styles.buttonDisabled]}
onPress={handleNasAuth}
disabled={loading}
>
{loading ? (
<ActivityIndicator color="#fff" />
) : (
<Text style={styles.buttonText}>Test Connection</Text>
)}
</TouchableOpacity>
</ServiceConfigCard>
)}
</View>
)}
{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>
)}
</ScrollView>
<View style={styles.footer}>
<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]}
onPress={handleNext}
disabled={loading}
>
{loading ? (
<ActivityIndicator color="#fff" />
) : (
<Text style={styles.footerButtonText}>
{currentStep === steps.length - 1 ? 'Complete' : 'Next'}
</Text>
)}
</TouchableOpacity>
</View>
</View>
);
};
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>
</View>
</View>
<Switch value={enabled} onValueChange={onToggle} />
</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',
},
content: {
flex: 1,
paddingHorizontal: 20,
paddingTop: 20,
},
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,
},
configCard: {
backgroundColor: '#fff',
borderRadius: 12,
padding: 16,
marginBottom: 16,
borderWidth: 1,
borderColor: '#e0e0e0',
},
configCardTitle: {
fontSize: 16,
fontWeight: '600',
color: '#333',
marginBottom: 12,
},
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,
},
selectCard: {
backgroundColor: '#fff',
borderRadius: 12,
padding: 16,
marginBottom: 12,
flexDirection: 'row',
alignItems: 'center',
borderWidth: 2,
borderColor: '#ddd',
},
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',
},
footer: {
flexDirection: 'row',
paddingHorizontal: 20,
paddingVertical: 16,
borderTopWidth: 1,
borderTopColor: '#e0e0e0',
gap: 12,
},
footerButton: {
flex: 1,
paddingVertical: 12,
paddingHorizontal: 16,
borderRadius: 8,
alignItems: 'center',
backgroundColor: '#fff',
borderWidth: 1,
borderColor: '#ddd',
},
nextButton: {
backgroundColor: '#007AFF',
borderColor: '#007AFF',
},
footerButtonText: {
fontSize: 16,
fontWeight: '600',
color: '#333',
},
hidden: {
opacity: 0,
pointerEvents: 'none',
},
});
export default OnboardingScreen;