Browse Source

fine-tune UI functionalities

Ian Fung 1 year ago
parent
commit
ee96c6b103

+ 13 - 0
app/(auth)/(tabs)/(account)/changePasswordPage.tsx

@@ -0,0 +1,13 @@
+import { View, Text } from 'react-native';
+import React from 'react';
+import ChangePasswordPageComponent from '../../../../component/accountPages/changePasswordPageComponent';
+
+const ChangePasswordPage = () => {
+    return (
+        <View className="flex-1">
+       <ChangePasswordPageComponent />
+        </View>
+    );
+};
+
+export default ChangePasswordPage;

+ 1 - 1
component/accountPages/accountSettingPageComponent.tsx

@@ -54,7 +54,7 @@ const AccountSettingPageComponent = () => {
                         <View className="h-0.5  bg-[#f4f4f4] " />
                     </View>
                     <View className="flex-col ">
-                        <Pressable onPress={() => console.log('abc')}>
+                        <Pressable onPress={() => router.push('changePasswordPage')}>
                             <View className="flex-row items-center justify-between">
                                 <View className="flex-col py-4">
                                     <Text className="text-lg pb-1">

+ 57 - 25
component/accountPages/changeGenderPageComponent.tsx

@@ -13,24 +13,55 @@ const ChangeGenderPageComponent = () => {
     const { user, setUser } = useContext(AuthContext);
     const [token, setToken] = useState<string | null>(null);
     const [gender, setGender] = useState<string | null>(null);
-    useEffect(() => {
-        const fetchToken = async () => {
-            try {
-                const storedToken = await SecureStore.getItemAsync(
-                    'accessToken'
-                );
-                setToken(storedToken);
-            } catch (error) {
-                console.error('Failed to load access token', error);
-            }
-        };
+    const [isLoading, setIsLoading] = useState(false);
+    const [error, setError] = useState<string | null>(null);
 
-        fetchToken();
-    }, []);
     const dropdownOption = [
         { label: '男', value: 'man' },
         { label: '女', value: 'woman' }
     ];
+
+    useEffect(() => {
+        const getToken = async () => {
+            const storedToken = await SecureStore.getItemAsync('accessToken');
+            setToken(storedToken);
+        };
+        getToken();
+    }, []);
+    const handleChangeGender = async () => {
+        if (!gender) {
+            setError('請選擇性別');
+            return;
+        }
+        if (!token) {
+            setError('未找到有效的登錄令牌,請重新登錄');
+            return;
+        }
+        setError(null);
+        setIsLoading(true);
+        try {
+            const success = await authenticationService.changeGender(
+                gender,
+                token
+            );
+            if (success) {
+                if (user) {
+                    setUser({
+                        ...user,
+                        gender: gender
+                    });
+                }
+                router.replace('accountMainPage');
+            } else {
+                setError('更新性別失敗,請稍後再試');
+            }
+        } catch (error) {
+            console.error('Error changing gender:', error);
+            setError('發生錯誤,請稍後再試');
+        } finally {
+            setIsLoading(false);
+        }
+    };
     return (
         <SafeAreaView
             className="flex-1 bg-white"
@@ -52,7 +83,7 @@ const ChangeGenderPageComponent = () => {
                     <Text style={{ fontSize: 45, marginVertical: 25 }}>
                         更改性別
                     </Text>
-                    <Text className="text-xl ">請輸入新性別</Text>
+                    <Text className="text-xl ">請選擇新性別</Text>
                     <View className="py-2">
                         <View className="flex-1 ">
                             <DropdownSelect
@@ -66,18 +97,19 @@ const ChangeGenderPageComponent = () => {
                         </View>
                     </View>
                     <NormalButton
-                        title={<Text className="text-white text-lg">確認</Text>}
-                        onPress={() => {
-                            authenticationService.changeGender(gender, token);
-                            if (user && gender) {
-                                setUser({
-                                    ...user,
-                                    gender: gender
-                                });
-                            }
-                            router.replace('accountMainPage');
-                        }}
+                        title={
+                            <Text className="text-white text-lg">
+                                {isLoading ? '更新中...' : '確認'}
+                            </Text>
+                        }
+                        onPress={handleChangeGender}
+                        disabled={isLoading}
                     />
+                    {error && (
+                        <Text style={{ color: 'red', marginTop: 10 }}>
+                            {error}
+                        </Text>
+                    )}
                 </View>
             </ScrollView>
         </SafeAreaView>

+ 48 - 25
component/accountPages/changeNamePageComponent.tsx

@@ -6,28 +6,54 @@ import { CrossLogoSvg } from '../global/SVG';
 import { AuthContext } from '../../context/AuthProvider';
 import NormalInput from '../global/normal_input';
 import NormalButton from '../global/normal_button';
-import * as SecureStore from 'expo-secure-store';
 import { authenticationService } from '../../service/authService';
-
+import * as SecureStore from 'expo-secure-store';
 const ChangeNamePageComponent = () => {
     const { user, setUser } = useContext(AuthContext);
     const [token, setToken] = useState<string | null>(null);
     const [name, setName] = useState<string | null>(null);
+    const [isLoading, setIsLoading] = useState(false);
+    const [error, setError] = useState<string | null>(null);
     useEffect(() => {
-        const fetchToken = async () => {
-            try {
-                const storedToken = await SecureStore.getItemAsync(
-                    'accessToken'
-                );
-                setToken(storedToken);
-            } catch (error) {
-                console.error('Failed to load access token', error);
-            }
+        const getToken = async () => {
+            const storedToken = await SecureStore.getItemAsync('accessToken');
+            setToken(storedToken);
         };
-
-        fetchToken();
+        getToken();
     }, []);
 
+    const handleChangeName = async () => {
+        if (!name) {
+            setError('請輸入新的暱稱');
+            return;
+        }
+        if (!token) {
+            setError('未找到有效的登錄令牌,請重新登錄');
+            return;
+        }
+        setError(null);
+        setIsLoading(true);
+        try {
+            const success = await authenticationService.changeName(name, token);
+            if (success) {
+                if (user) {
+                    setUser({
+                        ...user,
+                        nickname: name
+                    });
+                }
+                router.replace('accountMainPage');
+            } else {
+                setError('更新暱稱失敗,請稍後再試');
+            }
+        } catch (error) {
+            console.error('Error changing name:', error);
+            setError('發生錯誤,請稍後再試');
+        } finally {
+            setIsLoading(false);
+        }
+    };
+
     return (
         <SafeAreaView
             className="flex-1 bg-white"
@@ -50,24 +76,21 @@ const ChangeNamePageComponent = () => {
                         更改暱稱
                     </Text>
                     <Text className="text-xl ">請輸入新名稱</Text>
-                    <View className='py-2'>
+                    <View className="py-2">
                         <NormalInput
                             placeholder={user?.nickname}
                             onChangeText={(t) => setName(t)}
+                            editable={!isLoading}
                         />
                     </View>
                     <NormalButton
-                        title={<Text className="text-white text-lg">確認</Text>}
-                        onPress={() => {
-                            authenticationService.changeName(name, token);
-                            if (user && name) {
-                                setUser({
-                                    ...user,
-                                    nickname: name
-                                });
-                            }
-                            router.replace('accountMainPage');
-                        }}
+                        title={
+                            <Text className="text-white">
+                                {isLoading ? '更新中...' : '確認'}
+                            </Text>
+                        }
+                        disabled={isLoading}
+                        onPress={handleChangeName}
                     />
                 </View>
             </ScrollView>

+ 245 - 0
component/accountPages/changePasswordPageComponent.tsx

@@ -0,0 +1,245 @@
+import { View, Text, ScrollView, StyleSheet, Pressable } from 'react-native';
+import React, { useContext, useEffect, useState } from 'react';
+import { SafeAreaView } from 'react-native-safe-area-context';
+import { router } from 'expo-router';
+import { CrossLogoSvg } from '../global/SVG';
+import { AuthContext } from '../../context/AuthProvider';
+import NormalInput from '../global/normal_input';
+import NormalButton from '../global/normal_button';
+import * as SecureStore from 'expo-secure-store';
+import { authenticationService } from '../../service/authService';
+
+const changePasswordPageComponent = () => {
+    const { user, setUser } = useContext(AuthContext);
+    const [token, setToken] = useState<string | null>(null);
+    const [currentPassword, setCurrentPassword] = useState<string | null>(null);
+    const [newPassword, setNewPassword] = useState<string | null>(null);
+    const [newConfirmPassword, setNewConfirmPassword] = useState<string | null>(
+        null
+    );
+    const [isPasswordVerified, setIsPasswordVerified] =
+        useState<boolean>(false);
+    const [error, setError] = useState<string | null>(null);
+    const [isLoading, setIsLoading] = useState(false);
+    const [isLoading2, setIsLoading2] = useState(false);
+
+    useEffect(() => {
+        const getToken = async () => {
+            const storedToken = await SecureStore.getItemAsync('accessToken');
+            setToken(storedToken);
+        };
+        getToken();
+    }, []);
+
+    const handleVerifyPassword = async (currentPassword: string) => {
+        try {
+            setIsLoading(true);
+            if (!currentPassword) {
+                setError('請輸入密碼');
+                return;
+            }
+            if (!user || !user.email) {
+                setError('無法驗證用戶,請重新登錄');
+                return;
+            }
+
+            const success = await authenticationService.login(
+                user.email,
+                currentPassword
+            );
+            if (success) {
+                setIsPasswordVerified(true);
+                setError('');
+                return true;
+            } else {
+                setError('密碼錯誤,請稍後再試');
+            }
+        } catch (error) {
+            console.error('Error verifying password:', error);
+            setError('密碼錯誤,請稍後再試');
+            return false;
+        } finally {
+            setIsLoading(false);
+        }
+    };
+
+    const handlePasswordChange = async () => {
+        try {
+            setIsLoading2(true);
+            if (newPassword !== newConfirmPassword) {
+                setError('新密碼不相符');
+                return;
+            }
+
+            if (!newConfirmPassword || !token) {
+                setError('請輸入新密碼');
+                return;
+            }
+
+            const success = await authenticationService.changePassword(
+                newConfirmPassword,
+                token
+            );
+
+            if (success) {
+                setError('');
+                console.log('change password success');
+                router.replace('accountMainPage');
+            } else {
+                setError('密碼更改失敗,請稍後再試');
+            }
+        } catch (error) {
+            console.error('Error changing password:', error);
+            setError('發生錯誤,請稍後再試');
+        } finally {
+            setIsLoading2(false);
+        }
+    };
+
+    return (
+        <SafeAreaView
+            className="flex-1 bg-white"
+            edges={['top', 'right', 'left']}
+        >
+            <ScrollView className="flex-1 mx-[5%]">
+                <View style={{ marginTop: 25 }}>
+                    <Pressable
+                        onPress={() => {
+                            if (router.canGoBack()) {
+                                router.back();
+                            } else {
+                                router.replace('/accountMainPage');
+                            }
+                        }}
+                    >
+                        <CrossLogoSvg />
+                    </Pressable>
+                    <Text style={{ fontSize: 45, marginVertical: 25 }}>
+                        更改密碼
+                    </Text>
+                    <Text className="text-xl ">請輸入您現時的帳戶密碼</Text>
+                    <View className="py-2">
+                        <NormalInput
+                            placeholder="帳戶密碼"
+                            onChangeText={(t) => setCurrentPassword(t)}
+                            secureTextEntry={true}
+                            editable={!isPasswordVerified}
+                        />
+                    </View>
+
+                    <NormalButton
+                        title={
+                            <Text style={{ color: '#fff' }}>
+                                {isLoading
+                                    ? '驗證中...'
+                                    : isPasswordVerified
+                                    ? '已驗證'
+                                    : '下一步'}
+                            </Text>
+                        }
+                        onPress={() => {
+                            if (currentPassword) {
+                                handleVerifyPassword(currentPassword);
+                            }
+                        }}
+                        disabled={isPasswordVerified}
+                        extendedStyle={
+                            isPasswordVerified == true
+                                ? { backgroundColor: '#70787C' }
+                                : {}
+                        }
+                    />
+                </View>
+
+                {isPasswordVerified && (
+                    <View
+                        style={[
+                            styles.hiddenPasswordFields,
+                            isPasswordVerified
+                                ? styles.opacityFull
+                                : styles.opacityZero
+                        ]}
+                    >
+                        <NormalInput
+                            placeholder="新密碼"
+                            onChangeText={(t) => setNewPassword(t)}
+                            secureTextEntry={true}
+                            textContentType={'oneTimeCode'}
+                        />
+                        <NormalInput
+                            placeholder="確認密碼"
+                            onChangeText={(t) => setNewConfirmPassword(t)}
+                            secureTextEntry={true}
+                            textContentType={'oneTimeCode'}
+                        />
+                        <NormalButton
+                            title={
+                                <Text style={{ color: '#fff' }}>
+                                    {isLoading2 ? '驗證中...' : '確認'}
+                                </Text>
+                            }
+                            onPress={handlePasswordChange}
+                        />
+                    </View>
+                )}
+
+                {error && <Text style={styles.errorMessage}>{error}</Text>}
+            </ScrollView>
+        </SafeAreaView>
+    );
+};
+
+const styles = StyleSheet.create({
+    container: {
+        flex: 1,
+        marginHorizontal: 20
+    },
+
+    titleText: {
+        fontSize: 24,
+        fontWeight: '300'
+    },
+    bottomContainer: {
+        flex: 3,
+        paddingBottom: 100
+    },
+    breakline: {
+        width: 24,
+        height: 1,
+        backgroundColor: '#000000',
+        marginVertical: 17
+    },
+    text: {
+        fontSize: 18,
+        paddingBottom: 10
+    },
+    hiddenPasswordFields: {
+        gap: 10,
+        paddingTop: 10
+    },
+    opacityZero: {
+        opacity: 0
+    },
+    opacityFull: {
+        opacity: 1
+    },
+    errorMessage: {
+        fontSize: 14,
+        color: '#ff0033',
+        fontWeight: '400',
+        marginLeft: 10,
+        marginTop: 10
+    },
+    footer: { color: '#02677D', fontSize: 16, paddingVertical: 10 }
+});
+
+export default changePasswordPageComponent;
+
+// <NormalButton
+// title={<Text className="text-white text-lg">確認</Text>}
+// onPress={() => {
+//     // poSSIBLY ADD ERROR HANDLING AND LOADING
+//     // authenticationService.changePassword(password, token);
+//     router.replace('accountMainPage');
+// }}
+// />

+ 100 - 25
component/accountPages/changePhonePageComponent.tsx

@@ -1,4 +1,4 @@
-import { View, Text, ScrollView, Pressable } from 'react-native';
+import { View, Text, ScrollView, Pressable, StyleSheet } from 'react-native';
 import React, { useContext, useEffect, useState } from 'react';
 import { SafeAreaView } from 'react-native-safe-area-context';
 import { router } from 'expo-router';
@@ -13,21 +13,51 @@ const ChangePhonePageComponent = () => {
     const { user, setUser } = useContext(AuthContext);
     const [token, setToken] = useState<string | null>(null);
     const [phone, setPhone] = useState<string | null>(null);
+    const [isLoading, setIsLoading] = useState(false);
+    const [error, setError] = useState<string | null>(null);
     useEffect(() => {
-        const fetchToken = async () => {
-            try {
-                const storedToken = await SecureStore.getItemAsync(
-                    'accessToken'
-                );
-                setToken(storedToken);
-            } catch (error) {
-                console.error('Failed to load access token', error);
-            }
+        const getToken = async () => {
+            const storedToken = await SecureStore.getItemAsync('accessToken');
+            setToken(storedToken);
         };
-
-        fetchToken();
+        getToken();
     }, []);
 
+    const handleChangePhone = async () => {
+        if (!phone) {
+            setError('請輸入新的電話號碼');
+            return;
+        }
+        if (!token) {
+            setError('未找到有效的登錄令牌,請重新登錄');
+            return;
+        }
+        setError(null);
+        setIsLoading(true);
+        try {
+            const success = await authenticationService.changePhone(
+                phone,
+                token
+            );
+            if (success) {
+                if (user) {
+                    setUser({
+                        ...user,
+                        phone: phone
+                    });
+                }
+                router.replace('accountMainPage');
+            } else {
+                setError('更新電話號碼失敗,請稍後再試');
+            }
+        } catch (error) {
+            console.error('Error changing phone:', error);
+            setError('發生錯誤,請稍後再試');
+        } finally {
+            setIsLoading(false);
+        }
+    };
+
     return (
         <SafeAreaView
             className="flex-1 bg-white"
@@ -47,7 +77,7 @@ const ChangePhonePageComponent = () => {
                         <CrossLogoSvg />
                     </Pressable>
                     <Text style={{ fontSize: 45, marginVertical: 25 }}>
-                        更改暱稱
+                        更改電話號碼
                     </Text>
                     <Text className="text-xl ">請輸入新的電話號碼</Text>
                     <View className="py-2">
@@ -55,23 +85,24 @@ const ChangePhonePageComponent = () => {
                             placeholder="請輸入新的電話號碼"
                             onChangeText={(t) => {
                                 setPhone(t);
-                                console.log(phone);
                             }}
+                            editable={!isLoading}
                         />
                     </View>
                     <NormalButton
-                        title={<Text className="text-white text-lg">確認</Text>}
-                        onPress={() => {
-                            authenticationService.changePhone(phone,token)
-                            if (user && phone) {
-                                setUser({
-                                    ...user,
-                                    phone: phone
-                                });
-                            }
-                            router.replace('accountMainPage');
-                        }}
+                        title={
+                            <Text className="text-white">
+                                {isLoading ? '更新中...' : '確認'}
+                            </Text>
+                        }
+                        onPress={handleChangePhone}
+                        disabled={isLoading}
                     />
+                    {error && (
+                        <Text style={{ color: 'red', marginTop: 10 }}>
+                            {error}
+                        </Text>
+                    )}
                 </View>
             </ScrollView>
         </SafeAreaView>
@@ -79,3 +110,47 @@ const ChangePhonePageComponent = () => {
 };
 
 export default ChangePhonePageComponent;
+
+const styles = StyleSheet.create({
+    container: {
+        flex: 1,
+        marginHorizontal: 20
+    },
+
+    titleText: {
+        fontSize: 24,
+        fontWeight: '300'
+    },
+    bottomContainer: {
+        flex: 3,
+        paddingBottom: 100
+    },
+    breakline: {
+        width: 24,
+        height: 1,
+        backgroundColor: '#000000',
+        marginVertical: 17
+    },
+    text: {
+        fontSize: 18,
+        paddingBottom: 10
+    },
+    hiddenPasswordFields: {
+        gap: 10,
+        paddingTop: 10
+    },
+    opacityZero: {
+        opacity: 0
+    },
+    opacityFull: {
+        opacity: 1
+    },
+    errorMessage: {
+        fontSize: 14,
+        color: '#ff0033',
+        fontWeight: '400',
+        marginLeft: 10,
+        marginTop: 10
+    },
+    footer: { color: '#02677D', fontSize: 16, paddingVertical: 10 }
+});

+ 1 - 1
component/forgetPasswordMultiStepForm/formComponent/formPages/forgetPasswordPage.tsx

@@ -87,7 +87,7 @@ const ForgetPasswordPage: React.FC<ForgetPasswordPageProps> = ({
             setError('');
             try {
                 const success =
-                    await authenticationService.changePasswordAfterOtpIsVerified(
+                    await authenticationService.changePassword(
                         forgetPasswordFormData.confirmedNewPassword,
                         data
                     );

+ 1 - 1
service/authService.tsx

@@ -254,7 +254,7 @@ class AuthenticationService {
         }
     }
 
-    async changePasswordAfterOtpIsVerified(
+    async changePassword(
         confirmedNewPassword: string,
         data: string
     ) {