mirror of
https://github.com/mudabbir-ahmad/Media-Aggregation-and-Sorting-System.git
synced 2026-10-07 19:40:20 +00:00
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:
1 parent
37511780b7
commit
c1d7139055
17 files changed
+1431
-10040
No files matched your search
+263
-449
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user