109 lines
3.5 KiB
TypeScript
109 lines
3.5 KiB
TypeScript
import { useState } from 'react';
|
|
import { Alert, Text } from 'react-native';
|
|
import { Stack } from 'expo-router';
|
|
import { useAction, useMutation, useQuery } from 'convex/react';
|
|
|
|
import { api } from '@spoon/backend/convex/_generated/api.js';
|
|
|
|
import { AppScreen } from '~/components/ui/app-screen';
|
|
import { Button } from '~/components/ui/button';
|
|
import { Card } from '~/components/ui/card';
|
|
import { Field } from '~/components/ui/field';
|
|
import { titleize } from '~/utils/format';
|
|
|
|
const ProfileRoute = () => {
|
|
const user = useQuery(api.auth.getUser, {});
|
|
const provider = useQuery(api.auth.getUserProvider, {});
|
|
const updateUser = useMutation(api.auth.updateUser);
|
|
const updatePassword = useAction(api.auth.updateUserPassword);
|
|
const [name, setName] = useState(user?.name ?? '');
|
|
const [email, setEmail] = useState(user?.email ?? '');
|
|
const [currentPassword, setCurrentPassword] = useState('');
|
|
const [newPassword, setNewPassword] = useState('');
|
|
const [savingProfile, setSavingProfile] = useState(false);
|
|
const [savingPassword, setSavingPassword] = useState(false);
|
|
|
|
const saveProfile = async () => {
|
|
setSavingProfile(true);
|
|
try {
|
|
await updateUser({ name, email });
|
|
Alert.alert('Saved', 'Profile updated.');
|
|
} catch (error) {
|
|
console.error(error);
|
|
Alert.alert('Could not save profile.');
|
|
} finally {
|
|
setSavingProfile(false);
|
|
}
|
|
};
|
|
|
|
const savePassword = async () => {
|
|
setSavingPassword(true);
|
|
try {
|
|
await updatePassword({ currentPassword, newPassword });
|
|
setCurrentPassword('');
|
|
setNewPassword('');
|
|
Alert.alert('Saved', 'Password updated.');
|
|
} catch (error) {
|
|
console.error(error);
|
|
Alert.alert('Could not update password.');
|
|
} finally {
|
|
setSavingPassword(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<AppScreen>
|
|
<Stack.Screen options={{ title: 'Profile' }} />
|
|
<Text className='text-foreground text-3xl font-bold'>Profile</Text>
|
|
<Card className='gap-4'>
|
|
<Text className='text-muted-foreground text-sm'>
|
|
Email is currently managed by {titleize(provider ?? 'your provider')}.
|
|
</Text>
|
|
<Field label='Name' value={name} onChangeText={setName} />
|
|
<Field
|
|
keyboardType='email-address'
|
|
label='Email'
|
|
value={email}
|
|
onChangeText={setEmail}
|
|
/>
|
|
<Button disabled={savingProfile} onPress={() => void saveProfile()}>
|
|
{savingProfile ? 'Saving...' : 'Save profile'}
|
|
</Button>
|
|
</Card>
|
|
{provider === 'password' ? (
|
|
<Card className='gap-4'>
|
|
<Text className='text-foreground font-semibold'>Password</Text>
|
|
<Field
|
|
label='Current password'
|
|
secureTextEntry
|
|
value={currentPassword}
|
|
onChangeText={setCurrentPassword}
|
|
/>
|
|
<Field
|
|
label='New password'
|
|
secureTextEntry
|
|
value={newPassword}
|
|
onChangeText={setNewPassword}
|
|
/>
|
|
<Button
|
|
disabled={savingPassword}
|
|
variant='outline'
|
|
onPress={() => void savePassword()}
|
|
>
|
|
{savingPassword ? 'Updating...' : 'Update password'}
|
|
</Button>
|
|
</Card>
|
|
) : (
|
|
<Card>
|
|
<Text className='text-muted-foreground text-sm leading-5'>
|
|
Password changes are hidden because this account is currently using{' '}
|
|
{titleize(provider ?? 'an OAuth provider')}.
|
|
</Text>
|
|
</Card>
|
|
)}
|
|
</AppScreen>
|
|
);
|
|
};
|
|
|
|
export default ProfileRoute;
|