diff --git a/app/(tabs)/settings/_index.tsx b/app/(tabs)/settings/_index.tsx
index d12e21a..91fada9 100644
--- a/app/(tabs)/settings/_index.tsx
+++ b/app/(tabs)/settings/_index.tsx
@@ -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 (
+
+
+
+ );
+ }
return (
{
}
>
-
- router.push('/settings/profile')}
- >
-
-
- Profile Settings
- Update profile information or sign out.
+
+
+
+ {profile.provider && (
+
+ Signed in with {profile.provider.charAt(0).toUpperCase() + profile.provider.slice(1)}
+
+ )}
+
+
+ Name
+ 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'
+ />
-
-
-
-
-
-
+
+
+
+
+
);
};
-
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,
+ }
});
diff --git a/app/(tabs)/settings/index.tsx b/app/(tabs)/settings/index.tsx
index 91fada9..d12e21a 100644
--- a/app/(tabs)/settings/index.tsx
+++ b/app/(tabs)/settings/index.tsx
@@ -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 (
-
-
-
- );
- }
+ const router = useRouter();
return (
{
}
>
-
-
-
- {profile.provider && (
-
- Signed in with {profile.provider.charAt(0).toUpperCase() + profile.provider.slice(1)}
-
- )}
-
-
- Name
- 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'
- />
+
+ router.push('/settings/profile')}
+ >
+
+
+ Profile Settings
+ Update profile information or sign out.
-
-
-
-
-
+
+
+
+
+
+
);
};
+
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,
+ },
});
diff --git a/app/(tabs)/settings/profile.tsx b/app/(tabs)/settings/profile.tsx
index 1e1ccab..f4e080f 100644
--- a/app/(tabs)/settings/profile.tsx
+++ b/app/(tabs)/settings/profile.tsx
@@ -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 (
-
-
-
-
-
-
- {profile.provider && (
-
- Signed in with {profile.provider.charAt(0).toUpperCase() + profile.provider.slice(1)}
-
- )}
-
-
- Name
- setProfile(prev => ({ ...prev, full_name: text }))}
- placeholder="Enter your full name"
- style={styles.input}
- editable={!updating}
- autoCapitalize='words'
- textContentType='name'
- maxLength={50}
- returnKeyType='done'
- />
-
-
-
-
-
-
+
+
-
+
+ {profile.provider && (
+
+ Signed in with {profile.provider.charAt(0).toUpperCase() + profile.provider.slice(1)}
+
+ )}
+
+ Name
+ setProfile(prev => ({ ...prev, full_name: text }))}
+ placeholder="Enter your full name"
+ style={styles.input}
+ editable={!updating}
+ autoCapitalize='words'
+ textContentType='name'
+ maxLength={50}
+ returnKeyType='done'
+ />
+
+
+
+
+
+
);
};
export default ProfileScreen;
diff --git a/components/auth/Auth.tsx b/components/auth/Auth.tsx
index f198911..f581e72 100644
--- a/components/auth/Auth.tsx
+++ b/components/auth/Auth.tsx
@@ -81,6 +81,7 @@ const Auth = () => {
setFullName(text)}
value={full_name}
@@ -90,6 +91,7 @@ const Auth = () => {
setEmail(text)}
value={email}
@@ -99,6 +101,7 @@ const Auth = () => {
setPassword(text)}
value={password}
diff --git a/components/default/ParallaxScrollView.tsx b/components/default/ParallaxScrollView.tsx
index 8667e7e..2ee0f4f 100644
--- a/components/default/ParallaxScrollView.tsx
+++ b/components/default/ParallaxScrollView.tsx
@@ -55,6 +55,8 @@ const ParallaxScrollView = ({
scrollEventThrottle={16}
scrollIndicatorInsets={{ bottom }}
contentContainerStyle={{ paddingBottom: bottom }}
+ keyboardShouldPersistTaps='handled'
+ keyboardDismissMode='interactive'
>
= ({
color: Colors[scheme].text,
backgroundColor: Colors[scheme].background,
fontSize,
+ lineHeight: fontSize * 1.5,
},
style,
]}