Skip to content

Commit 899985e

Browse files
author
Guillermo Machado
committed
feat: add update password screen
1 parent ea620d2 commit 899985e

File tree

5 files changed

+169
-0
lines changed

5 files changed

+169
-0
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { createMutation } from 'react-query-kit';
2+
3+
import { client } from '../common';
4+
5+
type Variables = {
6+
password: string;
7+
passwordConfirmation: string;
8+
};
9+
10+
type ResponseData = {
11+
email: string;
12+
provider: string;
13+
uid: string;
14+
id: number;
15+
allow_password_change: boolean;
16+
name: string;
17+
nickname: string;
18+
image: string | null;
19+
created_at: string;
20+
updated_at: string;
21+
birthday: string | null;
22+
};
23+
24+
type Response = {
25+
success: boolean;
26+
message: string;
27+
data?: ResponseData;
28+
};
29+
30+
const updatePasswordRequest = async (variables: Variables) => {
31+
const { data } = await client({
32+
url: '/v1/users/password',
33+
method: 'PUT',
34+
data: { ...variables },
35+
headers: {
36+
'Content-Type': 'application/json',
37+
},
38+
});
39+
return data;
40+
};
41+
42+
export const useUpdatePassword = createMutation<Response, Variables>({
43+
mutationFn: (variables) => updatePasswordRequest(variables),
44+
});

src/app/(app)/settings.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ export default function Settings() {
5050
text={'settings.account.email'}
5151
value={userData?.email ?? ''}
5252
/>
53+
<Link
54+
asChild
55+
href={{
56+
pathname: '/update-password',
57+
}}
58+
>
59+
<Item text="settings.account.password" />
60+
</Link>
5361
</ItemsContainer>
5462
<ItemsContainer title="settings.generale">
5563
<LanguageItem />

src/app/_layout.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
55
import { ThemeProvider } from '@react-navigation/native';
66
import { SplashScreen, Stack } from 'expo-router';
77
import React from 'react';
8+
import { useTranslation } from 'react-i18next';
89
import { StyleSheet } from 'react-native';
910
import FlashMessage from 'react-native-flash-message';
1011
import { GestureHandlerRootView } from 'react-native-gesture-handler';
@@ -29,12 +30,19 @@ interceptors();
2930
SplashScreen.preventAutoHideAsync();
3031

3132
export default function RootLayout() {
33+
const { t } = useTranslation();
3234
return (
3335
<Providers>
3436
<Stack>
3537
<Stack.Screen name="(app)" options={{ headerShown: false }} />
3638
<Stack.Screen name="onboarding" options={{ headerShown: false }} />
3739
<Stack.Screen name="forgot-password" />
40+
<Stack.Screen
41+
name="update-password"
42+
options={{
43+
title: t('updatePassword.title'),
44+
}}
45+
/>
3846
<Stack.Screen name="sign-in" options={{ headerShown: false }} />
3947
<Stack.Screen
4048
name="sign-up"

src/app/update-password.tsx

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { zodResolver } from '@hookform/resolvers/zod';
2+
import { useNavigation } from 'expo-router';
3+
import React from 'react';
4+
import { useForm } from 'react-hook-form';
5+
import { useTranslation } from 'react-i18next';
6+
import { showMessage } from 'react-native-flash-message';
7+
import { KeyboardAvoidingView } from 'react-native-keyboard-controller';
8+
import { z } from 'zod';
9+
10+
import { useUpdatePassword } from '@/api/auth/use-update-password';
11+
import { Button, ControlledInput, FocusAwareStatusBar, Text, View } from '@/ui';
12+
13+
type FormValues = { password: string; passwordConfirmation: string };
14+
const SIX = 6;
15+
16+
const schema = z
17+
.object({
18+
password: z
19+
.string()
20+
.min(SIX, 'Password must be at least 6 characters long'),
21+
passwordConfirmation: z.string(),
22+
})
23+
.refine((data) => data.password === data.passwordConfirmation, {
24+
message: 'Passwords must match',
25+
path: ['confirmPassword'],
26+
});
27+
28+
export default function UpdatePassword() {
29+
const { t } = useTranslation();
30+
const navigate = useNavigation();
31+
32+
const { mutateAsync: updatePassword } = useUpdatePassword({
33+
onSuccess: () => {
34+
showMessage({
35+
message: t('updatePassword.successMessage'),
36+
type: 'success',
37+
});
38+
navigate.goBack();
39+
},
40+
onError: () => {
41+
showMessage({
42+
message: t('updatePassword.errorMessage'),
43+
type: 'danger',
44+
});
45+
},
46+
});
47+
48+
const { handleSubmit, control } = useForm<FormValues>({
49+
resolver: zodResolver(schema),
50+
});
51+
const onSubmit = async (data: FormValues) => {
52+
await updatePassword(data);
53+
};
54+
55+
return (
56+
<>
57+
<FocusAwareStatusBar />
58+
<KeyboardAvoidingView>
59+
<View className="gap-8 p-4">
60+
<View className="gap-2">
61+
<Text className="text-center text-2xl">
62+
{t('updatePassword.title')}
63+
</Text>
64+
<Text className="text-center text-gray-600">
65+
{t('updatePassword.description')}
66+
</Text>
67+
</View>
68+
<View className="gap-2">
69+
<ControlledInput
70+
testID="password-input"
71+
autoCapitalize="none"
72+
secureTextEntry
73+
control={control}
74+
name="password"
75+
label={t('updatePassword.passwordLabel')}
76+
placeholder={t('updatePassword.passwordPlaceholder')}
77+
/>
78+
<ControlledInput
79+
testID="confirm-password-input"
80+
autoCapitalize="none"
81+
secureTextEntry
82+
control={control}
83+
name="passwordConfirmation"
84+
label={t('updatePassword.confirmPasswordLabel')}
85+
placeholder={t('updatePassword.confirmPasswordPlaceholder')}
86+
/>
87+
<Button
88+
testID="update-password-button"
89+
label={t('updatePassword.buttonLabel')}
90+
onPress={handleSubmit(onSubmit)}
91+
/>
92+
</View>
93+
</View>
94+
</KeyboardAvoidingView>
95+
</>
96+
);
97+
}

src/translations/en.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
"account": {
6969
"email": "Email",
7070
"name": "Name",
71+
"password": "Password",
7172
"title": "Account"
7273
},
7374
"app_name": "App Name",
@@ -102,6 +103,17 @@
102103
"version": "Version",
103104
"website": "Website"
104105
},
106+
"updatePassword": {
107+
"buttonLabel": "Update Password",
108+
"confirmPasswordLabel": "Confirm Password",
109+
"confirmPasswordPlaceholder": "Re-enter new password",
110+
"description": "Enter a new password for your account.",
111+
"errorMessage": "An error occurred while updating your password. Please try again.",
112+
"passwordLabel": "New Password",
113+
"passwordPlaceholder": "Enter new password",
114+
"successMessage": "Your password has been successfully updated.",
115+
"title": "Update Password"
116+
},
105117
"welcome": "Welcome to rootstrap app site",
106118
"www": {
107119
"invalidUrl": "Invalid Url"

0 commit comments

Comments
 (0)