fix stuff

This commit is contained in:
Gabriel Brown 2025-03-12 14:13:19 -05:00
parent 7e4978f0e0
commit c503e4fb94
6 changed files with 286 additions and 289 deletions

View File

@ -1,11 +1,114 @@
import { StyleSheet, TouchableOpacity } from 'react-native';
import { useRouter } from 'expo-router';
import React, { useState, useEffect } from 'react';
import {
StyleSheet,
Alert,
ActivityIndicator,
SafeAreaView,
ScrollView,
} from 'react-native';
import { supabase } from '@/lib/supabase';
import { ThemedView, ThemedText, ThemedTextButton, ThemedTextInput } from '@/components/theme';
import ProfileAvatar from '@/components/auth/Profile_Avatar';
import LogoutButton from '@/components/auth/Logout_Button';
import { useFocusEffect } from '@react-navigation/native';
import ParallaxScrollView from '@/components/default/ParallaxScrollView';
import { ThemedText, ThemedTextButton, ThemedView } from '@/components/theme';
import { IconSymbol } from '@/components/ui/IconSymbol';
const SettingsScreen = () => {
const router = useRouter();
const [loading, setLoading] = useState(true);
const [updating, setUpdating] = useState(false);
const [profile, setProfile] = useState({
full_name: '',
email: '',
avatar_url: null,
provider: ''
});
// Fetch profile when screen comes into focus
useFocusEffect(
React.useCallback(() => {
fetchUserProfile();
}, [])
);
const fetchUserProfile = async () => {
setLoading(true);
try {
const { data: { user } } = await supabase.auth.getUser();
if (!user) {
throw new Error('Not authenticated');
}
const { data, error } = await supabase
.from('profiles')
.select('*')
.eq('id', user.id)
.single();
if (error) throw error;
if (data) {
setProfile({
full_name: data.full_name || '',
email: data.email || '',
avatar_url: data.avatar_url,
provider: data.provider || ''
});
}
} catch (error) {
console.error('Error fetching profile:', error);
Alert.alert('Error', 'Failed to load profile information');
} finally {
setLoading(false);
}
};
const updateProfile = async () => {
setUpdating(true);
try {
const { data: { user } } = await supabase.auth.getUser();
if (!user) throw new Error('Not authenticated');
// Validate input
if (!profile.full_name.trim()) {
Alert.alert('Error', 'Please enter your full name');
return;
}
const updates = {
id: user.id,
full_name: profile.full_name.trim(),
updated_at: new Date(),
};
const { error } = await supabase
.from('profiles')
.upsert(updates);
if (error) throw error;
Alert.alert('Success', 'Profile updated successfully!');
} catch (error) {
Alert.alert('Error', error instanceof Error ? error.message : 'Failed to update profile');
} finally {
setUpdating(false);
}
};
const handleAvatarUpload = () => {
// Refresh profile data after avatar upload
fetchUserProfile();
};
if (loading) {
return (
<ThemedView style={styles.loadingContainer}>
<ActivityIndicator size="large" />
</ThemedView>
);
}
return (
<ParallaxScrollView
@ -18,27 +121,57 @@ const SettingsScreen = () => {
</ThemedText>
}
>
<ThemedView style={styles.section}>
<TouchableOpacity
style={styles.settingItem}
onPress={() => router.push('/settings/profile')}
>
<IconSymbol name="person.fill" size={24} color="#007AFF" style={styles.icon} />
<ThemedView style={styles.settingContent}>
<ThemedText style={styles.settingTitle}>Profile Settings</ThemedText>
<ThemedText style={styles.settingSubtitle}>Update profile information or sign out.</ThemedText>
<ThemedView style={styles.container}>
<ProfileAvatar
url={profile.avatar_url}
size={120}
onUpload={handleAvatarUpload}
disabled={updating}
/>
{profile.provider && (
<ThemedText style={styles.providerText}>
Signed in with {profile.provider.charAt(0).toUpperCase() + profile.provider.slice(1)}
</ThemedText>
)}
<SafeAreaView style={styles.formSection}>
<ThemedView style={styles.formSection}>
<ThemedText type='title' style={styles.label}>Name</ThemedText>
<ThemedTextInput
value={profile.full_name}
onChangeText={(text) => setProfile(prev => ({ ...prev, full_name: text }))}
placeholder="Enter your full name"
style={styles.input}
editable={!updating}
autoCapitalize='words'
textContentType='name'
maxLength={50}
onSubmitEditing={updateProfile}
returnKeyType='send'
/>
</ThemedView>
<IconSymbol name="chevron.right" size={20} color="#C7C7CC" />
</TouchableOpacity>
</ThemedView>
<ThemedView style={styles.section}>
</SafeAreaView>
<ThemedTextButton
text={updating ? 'Saving...' : 'Save Changes'}
onPress={updateProfile}
disabled={updating || !profile.full_name.trim()}
fontSize={18}
fontWeight='semibold'
width='90%'
style={styles.saveButton}
/>
<LogoutButton
fontSize={18}
fontWeight='semibold'
width='90%'
style={styles.logoutButton}
/>
</ThemedView>
</ParallaxScrollView>
);
};
export default SettingsScreen;
const styles = StyleSheet.create({
@ -58,32 +191,51 @@ const styles = StyleSheet.create({
lineHeight: 64,
fontWeight: 'bold',
},
section: {
marginVertical: 8,
borderRadius: 10,
overflow: 'hidden',
scrollContainer: {
flexGrow: 1,
},
settingItem: {
flexDirection: 'row',
alignItems: 'center',
padding: 16,
backgroundColor: 'rgba(200, 200, 200, 0.1)',
marginBottom: 1,
},
icon: {
marginRight: 16,
},
settingContent: {
backgroundColor: 'transparent',
container: {
flex: 1,
padding: 16,
alignItems: 'center',
},
settingTitle: {
fontSize: 17,
loadingContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
formSection: {
marginBottom: 20,
},
label: {
marginBottom: 8,
fontSize: 16,
fontWeight: '500',
},
settingSubtitle: {
fontSize: 14,
opacity: 0.6,
marginTop: 4,
input: {
fontSize: 16,
paddingVertical: 12,
paddingHorizontal: 10,
borderRadius: 8,
marginBottom: 20,
width: '100%',
},
disabledInput: {
opacity: 0.7,
},
saveButton: {
borderRadius: 8,
alignItems: 'center',
marginBottom: 10,
},
logoutButton: {
marginTop: 30,
borderRadius: 8,
alignItems: 'center',
},
providerText: {
marginBottom: 20,
fontSize: 14,
opacity: 0.7,
}
});

View File

@ -1,114 +1,11 @@
import React, { useState, useEffect } from 'react';
import {
StyleSheet,
Alert,
ActivityIndicator,
SafeAreaView,
ScrollView,
} from 'react-native';
import { supabase } from '@/lib/supabase';
import { ThemedView, ThemedText, ThemedTextButton, ThemedTextInput } from '@/components/theme';
import ProfileAvatar from '@/components/auth/Profile_Avatar';
import LogoutButton from '@/components/auth/Logout_Button';
import { useFocusEffect } from '@react-navigation/native';
import { StyleSheet, TouchableOpacity } from 'react-native';
import { useRouter } from 'expo-router';
import ParallaxScrollView from '@/components/default/ParallaxScrollView';
import { ThemedText, ThemedTextButton, ThemedView } from '@/components/theme';
import { IconSymbol } from '@/components/ui/IconSymbol';
const SettingsScreen = () => {
const [loading, setLoading] = useState(true);
const [updating, setUpdating] = useState(false);
const [profile, setProfile] = useState({
full_name: '',
email: '',
avatar_url: null,
provider: ''
});
// Fetch profile when screen comes into focus
useFocusEffect(
React.useCallback(() => {
fetchUserProfile();
}, [])
);
const fetchUserProfile = async () => {
setLoading(true);
try {
const { data: { user } } = await supabase.auth.getUser();
if (!user) {
throw new Error('Not authenticated');
}
const { data, error } = await supabase
.from('profiles')
.select('*')
.eq('id', user.id)
.single();
if (error) throw error;
if (data) {
setProfile({
full_name: data.full_name || '',
email: data.email || '',
avatar_url: data.avatar_url,
provider: data.provider || ''
});
}
} catch (error) {
console.error('Error fetching profile:', error);
Alert.alert('Error', 'Failed to load profile information');
} finally {
setLoading(false);
}
};
const updateProfile = async () => {
setUpdating(true);
try {
const { data: { user } } = await supabase.auth.getUser();
if (!user) throw new Error('Not authenticated');
// Validate input
if (!profile.full_name.trim()) {
Alert.alert('Error', 'Please enter your full name');
return;
}
const updates = {
id: user.id,
full_name: profile.full_name.trim(),
updated_at: new Date(),
};
const { error } = await supabase
.from('profiles')
.upsert(updates);
if (error) throw error;
Alert.alert('Success', 'Profile updated successfully!');
} catch (error) {
Alert.alert('Error', error instanceof Error ? error.message : 'Failed to update profile');
} finally {
setUpdating(false);
}
};
const handleAvatarUpload = () => {
// Refresh profile data after avatar upload
fetchUserProfile();
};
if (loading) {
return (
<ThemedView style={styles.loadingContainer}>
<ActivityIndicator size="large" />
</ThemedView>
);
}
const router = useRouter();
return (
<ParallaxScrollView
@ -121,57 +18,27 @@ const SettingsScreen = () => {
</ThemedText>
}
>
<ThemedView style={styles.container}>
<ProfileAvatar
url={profile.avatar_url}
size={120}
onUpload={handleAvatarUpload}
disabled={updating}
/>
{profile.provider && (
<ThemedText style={styles.providerText}>
Signed in with {profile.provider.charAt(0).toUpperCase() + profile.provider.slice(1)}
</ThemedText>
)}
<SafeAreaView style={styles.formSection}>
<ThemedView style={styles.formSection}>
<ThemedText type='title' style={styles.label}>Name</ThemedText>
<ThemedTextInput
value={profile.full_name}
onChangeText={(text) => setProfile(prev => ({ ...prev, full_name: text }))}
placeholder="Enter your full name"
style={styles.input}
editable={!updating}
autoCapitalize='words'
textContentType='name'
maxLength={50}
onSubmitEditing={updateProfile}
returnKeyType='send'
/>
<ThemedView style={styles.section}>
<TouchableOpacity
style={styles.settingItem}
onPress={() => router.push('/settings/profile')}
>
<IconSymbol name="person.fill" size={24} color="#007AFF" style={styles.icon} />
<ThemedView style={styles.settingContent}>
<ThemedText style={styles.settingTitle}>Profile Settings</ThemedText>
<ThemedText style={styles.settingSubtitle}>Update profile information or sign out.</ThemedText>
</ThemedView>
</SafeAreaView>
<ThemedTextButton
text={updating ? 'Saving...' : 'Save Changes'}
onPress={updateProfile}
disabled={updating || !profile.full_name.trim()}
fontSize={18}
fontWeight='semibold'
width='90%'
style={styles.saveButton}
/>
<LogoutButton
fontSize={18}
fontWeight='semibold'
width='90%'
style={styles.logoutButton}
/>
<IconSymbol name="chevron.right" size={20} color="#C7C7CC" />
</TouchableOpacity>
</ThemedView>
<ThemedView style={styles.section}>
</ThemedView>
</ParallaxScrollView>
);
};
export default SettingsScreen;
const styles = StyleSheet.create({
@ -191,51 +58,32 @@ const styles = StyleSheet.create({
lineHeight: 64,
fontWeight: 'bold',
},
scrollContainer: {
flexGrow: 1,
section: {
marginVertical: 8,
borderRadius: 10,
overflow: 'hidden',
},
container: {
flex: 1,
settingItem: {
flexDirection: 'row',
alignItems: 'center',
padding: 16,
alignItems: 'center',
backgroundColor: 'rgba(200, 200, 200, 0.1)',
marginBottom: 1,
},
loadingContainer: {
icon: {
marginRight: 16,
},
settingContent: {
backgroundColor: 'transparent',
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
formSection: {
marginBottom: 20,
},
label: {
marginBottom: 8,
fontSize: 16,
settingTitle: {
fontSize: 17,
fontWeight: '500',
},
input: {
fontSize: 16,
paddingVertical: 12,
paddingHorizontal: 10,
borderRadius: 8,
marginBottom: 20,
width: '100%',
},
disabledInput: {
opacity: 0.7,
},
saveButton: {
borderRadius: 8,
alignItems: 'center',
marginBottom: 10,
},
logoutButton: {
marginTop: 30,
borderRadius: 8,
alignItems: 'center',
},
providerText: {
marginBottom: 20,
settingSubtitle: {
fontSize: 14,
opacity: 0.7,
}
opacity: 0.6,
marginTop: 4,
},
});

View File

@ -1,10 +1,8 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useRef } from 'react';
import {
StyleSheet,
Alert,
ActivityIndicator,
SafeAreaView,
ScrollView,
} from 'react-native';
import { supabase } from '@/lib/supabase';
import { ThemedView, ThemedText, ThemedTextButton, ThemedTextInput } from '@/components/theme';
@ -109,60 +107,53 @@ const ProfileScreen = () => {
}
return (
<ScrollView
contentContainerStyle={styles.scrollContainer}
keyboardShouldPersistTaps='handled'
>
<ThemedView style={styles.container}>
<ThemedView style={styles.avatarContainer}>
<ProfileAvatar
url={profile.avatar_url}
size={120}
onUpload={handleAvatarUpload}
disabled={updating}
/>
</ThemedView>
{profile.provider && (
<ThemedText style={styles.providerText}>
Signed in with {profile.provider.charAt(0).toUpperCase() + profile.provider.slice(1)}
</ThemedText>
)}
<SafeAreaView style={styles.formSection}>
<ThemedView style={styles.formSection}>
<ThemedText type='title' style={styles.label}>Name</ThemedText>
<ThemedTextInput
value={profile.full_name}
onChangeText={(text) => setProfile(prev => ({ ...prev, full_name: text }))}
placeholder="Enter your full name"
style={styles.input}
editable={!updating}
autoCapitalize='words'
textContentType='name'
maxLength={50}
returnKeyType='done'
/>
</ThemedView>
</SafeAreaView>
<ThemedTextButton
text={updating ? 'Saving...' : 'Save Changes'}
onPress={updateProfile}
disabled={updating || !profile.full_name.trim()}
fontSize={18}
fontWeight='semibold'
width='90%'
style={styles.saveButton}
/>
<LogoutButton
fontSize={18}
fontWeight='semibold'
width='90%'
style={styles.logoutButton}
<ThemedView style={styles.container}>
<ThemedView style={styles.avatarContainer}>
<ProfileAvatar
url={profile.avatar_url}
size={120}
onUpload={handleAvatarUpload}
disabled={updating}
/>
</ThemedView>
</ScrollView>
{profile.provider && (
<ThemedText style={styles.providerText}>
Signed in with {profile.provider.charAt(0).toUpperCase() + profile.provider.slice(1)}
</ThemedText>
)}
<ThemedView style={styles.formSection}>
<ThemedText type='title' style={styles.label}>Name</ThemedText>
<ThemedTextInput
value={profile.full_name}
onChangeText={(text) => setProfile(prev => ({ ...prev, full_name: text }))}
placeholder="Enter your full name"
style={styles.input}
editable={!updating}
autoCapitalize='words'
textContentType='name'
maxLength={50}
returnKeyType='done'
/>
</ThemedView>
<ThemedTextButton
text={updating ? 'Saving...' : 'Save Changes'}
onPress={updateProfile}
disabled={updating || !profile.full_name.trim()}
fontSize={18}
fontWeight='semibold'
width='90%'
style={styles.saveButton}
/>
<LogoutButton
fontSize={18}
fontWeight='semibold'
width='90%'
style={styles.logoutButton}
/>
</ThemedView>
);
};
export default ProfileScreen;

View File

@ -81,6 +81,7 @@ const Auth = () => {
<ThemedView style={styles.verticallySpaced}>
<ThemedTextInput
height={60}
fontSize={24}
onChangeText={(text) => setFullName(text)}
value={full_name}
@ -90,6 +91,7 @@ const Auth = () => {
<ThemedView style={[styles.verticallySpaced]}>
<ThemedTextInput
height={60}
fontSize={24}
onChangeText={(text) => setEmail(text)}
value={email}
@ -99,6 +101,7 @@ const Auth = () => {
<ThemedView style={styles.verticallySpaced}>
<ThemedTextInput
height={60}
fontSize={24}
onChangeText={(text) => setPassword(text)}
value={password}

View File

@ -55,6 +55,8 @@ const ParallaxScrollView = ({
scrollEventThrottle={16}
scrollIndicatorInsets={{ bottom }}
contentContainerStyle={{ paddingBottom: bottom }}
keyboardShouldPersistTaps='handled'
keyboardDismissMode='interactive'
>
<Animated.View
style={[

View File

@ -43,6 +43,7 @@ const ThemedTextInput: React.FC<ThemedTextInputProps> = ({
color: Colors[scheme].text,
backgroundColor: Colors[scheme].background,
fontSize,
lineHeight: fontSize * 1.5,
},
style,
]}