BaaS_Driver_Android_App/app/user/profile.tsx

296 lines
7.8 KiB
TypeScript

import React from "react";
import {
View,
Text,
StyleSheet,
Image,
TouchableOpacity,
ScrollView,
} from "react-native";
import { MaterialIcons } from "@expo/vector-icons";
import LanguageModal from "@/components/Profile/LangaugeModal";
import { useSelector } from "react-redux";
import { AppDispatch, RootState } from "@/store";
import EditIcon from "../../assets/icons/edit.svg";
import { router } from "expo-router";
import { useDispatch } from "react-redux";
import { logout } from "@/store/authSlice";
import * as ImagePicker from "expo-image-picker";
import { useSnackbar } from "@/contexts/Snackbar";
import { AWS, BASE_URL, USER_PROFILE } from "@/constants/config";
import { bytesToMB, handleUpload, updateUserProfile } from "@/utils/User";
import api from "@/services/axiosClient";
import { PresignedUrlDataItem } from "@/utils/User";
import { setUserData } from "@/store/userSlice";
import { Overlay } from "@/components/common/Overlay";
interface PresignedUrlResponse {
message: string;
data?: PresignedUrlDataItem[];
}
export default function ProfileScreen() {
const [isLangaugeModalVisible, setLanguageModalVisible] =
React.useState(false);
const { showSnackbar } = useSnackbar();
const toggleLanguageModal = () => {
setLanguageModalVisible(!isLangaugeModalVisible);
};
const [isUploading, setIsUploading] = React.useState(false);
const { data } = useSelector((state: RootState) => state.user);
const userName = data?.name || "User";
const mobileNumber = data?.mobile || "Not provided";
const userImageUrl = data?.profile_url;
const dispatch = useDispatch<AppDispatch>();
const handleLogout = () => {
dispatch(logout());
router.replace("/auth/login");
};
const uploadImage = async (fileName: string, uri: string) => {
try {
setIsUploading(true);
const getPresignedUrl = `${BASE_URL}/api/v1/generate-presigned-urls`;
const response = await api.post<PresignedUrlResponse>(getPresignedUrl, {
files: [fileName],
});
const uploadData = response?.data?.data?.[0];
if (!uploadData) throw new Error("Presigned URL not received");
const { url, fields, originalFileName } = uploadData;
const presignedUrl: PresignedUrlDataItem = {
url,
fields,
originalFileName,
};
await handleUpload(uri, presignedUrl);
const region = AWS.REGION;
const bucketName = AWS.BUCKET_NAME;
const objectKey = presignedUrl.fields.key;
const uploadedImageUrl = `https://s3.${region}.amazonaws.com/${bucketName}/${encodeURIComponent(
objectKey
)}`;
console.log("Uploaded image URL:", uploadedImageUrl);
await updateUserProfile({
profile_url: uploadedImageUrl,
mobile: mobileNumber,
});
dispatch(setUserData({ profile_url: uri }));
showSnackbar("Image uploaded successfully", "success");
} catch (error) {
console.error("Error uploading image:", error);
showSnackbar("Error uploading image", "error");
} finally {
setIsUploading(false);
}
};
const handlePickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
quality: 1,
allowsEditing: true,
aspect: [1, 1],
});
if (!result.canceled) {
const { uri, fileSize } = result.assets[0];
console.log(uri, "File size:", fileSize);
if (!fileSize) {
showSnackbar("Image size is not available", "error");
return;
}
const size = bytesToMB(fileSize);
if (size > USER_PROFILE.MAX_IMAGE_SIZE_IN_MB) {
showSnackbar(
`Image size exceeds ${USER_PROFILE.MAX_IMAGE_SIZE_IN_MB}MB limit`,
"error"
);
throw new Error(
`Image size exceeds ${USER_PROFILE.MAX_IMAGE_SIZE_IN_MB}MB limit`
);
}
const fileName = uri.split("/").pop() || "image.jpg";
await uploadImage(fileName, uri);
}
};
return (
<>
<ScrollView contentContainerStyle={styles.scrollContent}>
<View style={styles.avatarContainer}>
<Image
source={
userImageUrl
? { uri: userImageUrl }
: require("@/assets/images/user_image.jpg")
}
style={styles.avatar}
/>
<TouchableOpacity
style={styles.editAvatar}
onPress={() => handlePickImage()}
>
<EditIcon />
</TouchableOpacity>
</View>
<View style={styles.card}>
<View style={styles.row}>
<View style={styles.textGroup}>
<Text style={styles.label}>Name</Text>
<Text style={styles.value}>{userName}</Text>
</View>
<TouchableOpacity
onPress={() => {
router.push("/user/edit_name");
}}
style={styles.edit_button}
>
<MaterialIcons name="edit" size={20} color="#555C70" />
</TouchableOpacity>
</View>
<View style={styles.divider} />
<View style={styles.row}>
<View style={styles.textGroup}>
<Text style={styles.label}>Mobile Number</Text>
<Text style={styles.value}>{mobileNumber}</Text>
</View>
</View>
</View>
<View style={styles.card}>
{menuItem("My Vehicle")}
<View style={styles.divider} />
{menuItem("Language", () => toggleLanguageModal())}
</View>
<View style={styles.card}>
{menuItem("About App")}
<View style={styles.divider} />
{menuItem("Logout", handleLogout)}
</View>
</ScrollView>
<LanguageModal
onClose={() => setLanguageModalVisible(false)}
visible={isLangaugeModalVisible}
/>
<Overlay isUploading={isUploading} />
</>
);
}
const menuItem = (title: string, onPress?: () => void) => (
<TouchableOpacity style={styles.menuRow} onPress={onPress}>
<Text style={styles.menuText}>{title}</Text>
<MaterialIcons name="chevron-right" size={20} color="#555C70" />
</TouchableOpacity>
);
const styles = StyleSheet.create({
edit_button: {
position: "absolute",
bottom: 0,
right: 0,
width: 40,
height: 40,
},
container: {
borderWidth: 1,
flex: 1,
backgroundColor: "#F3F5F8",
},
topBar: {
flexDirection: "row",
alignItems: "center",
paddingHorizontal: 8,
paddingVertical: 12,
backgroundColor: "#F3F5F8",
},
backButton: {
padding: 8,
},
headerText: {
fontSize: 16,
fontWeight: "600",
marginLeft: 8,
color: "#252A34",
},
scrollContent: {
paddingHorizontal: 16,
paddingBottom: 32,
},
avatarContainer: {
alignItems: "center",
marginVertical: 24,
},
avatar: {
width: 120,
height: 120,
borderRadius: 60,
},
editAvatar: {
position: "absolute",
bottom: 0,
right: "35%",
width: 40,
height: 40,
backgroundColor: "#008866",
borderRadius: 20,
justifyContent: "center",
alignItems: "center",
},
card: {
backgroundColor: "#FCFCFC",
borderRadius: 8,
marginBottom: 16,
paddingHorizontal: 16,
paddingVertical: 8,
},
row: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingVertical: 12,
},
textGroup: {
flexDirection: "column",
},
label: {
fontSize: 14,
color: "#252A34",
},
value: {
fontSize: 14,
fontWeight: "600",
color: "#252A34",
marginTop: 2,
},
divider: {
height: 1,
backgroundColor: "#E5E9F0",
marginVertical: 4,
},
menuRow: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingVertical: 16,
},
menuText: {
fontSize: 14,
color: "#252A34",
},
});