import React, { useCallback, useEffect, useRef, useState } from "react"; import { StyleSheet, TouchableOpacity } from "react-native"; import { ScrollView } from "react-native-gesture-handler"; import * as ImagePicker from "expo-image-picker"; import { Button, Colors, DateTimePicker, Image, Picker, Text, TextField, View, } from "react-native-ui-lib"; import Ionicons from "@expo/vector-icons/Ionicons"; import * as tz from "tzdata"; import * as Localization from "expo-localization"; import debounce from "debounce"; import { ProfileType, useAuthContext } from "@/contexts/AuthContext"; import { useUpdateUserData } from "@/hooks/firebase/useUpdateUserData"; import { useChangeProfilePicture } from "@/hooks/firebase/useChangeProfilePicture"; import { colorMap } from "@/constants/colorMap"; import DeleteProfileDialogs from "../user_components/DeleteProfileDialogs"; import { AntDesign } from "@expo/vector-icons"; import { useDeleteUser } from "@/hooks/firebase/useDeleteUser"; import { useUpdateHouseholdName } from "@/hooks/firebase/useUpdateHouseholdName"; import { useGetHouseholdName } from "@/hooks/firebase/useGetHouseholdName"; import { useGetFamilyMembers } from "@/hooks/firebase/useGetFamilyMembers"; import { useDeleteFamily } from "@/hooks/firebase/useDeleteFamily"; const MyProfile = () => { const { user, profileData } = useAuthContext(); const { data: familyMembers } = useGetFamilyMembers(); const [takenColors, setTakenColors] = useState([]); const { mutate: deleteFamily, isLoading } = useDeleteFamily(); const { data: hhName, refetch: refetchHHName } = useGetHouseholdName( profileData.familyId ); const [householdName, setHouseholdName] = useState(""); const [timeZone, setTimeZone] = useState( profileData?.timeZone! ?? Localization.getCalendars()[0].timeZone ); const [lastName, setLastName] = useState(profileData?.lastName || ""); const [firstName, setFirstName] = useState( profileData?.firstName || "" ); const [profileImage, setProfileImage] = useState< string | ImagePicker.ImagePickerAsset | null >(profileData?.pfp || null); const [selectedColor, setSelectedColor] = useState( profileData?.eventColor ?? colorMap.pink ); const [previousSelectedColor, setPreviousSelectedColor] = useState( profileData?.eventColor ?? colorMap.pink ); const [birthday, setBirthday] = useState(() => { if (profileData?.birthday) { if (profileData.birthday.toDate) { return profileData.birthday.toDate(); } const date = new Date(profileData.birthday); return isNaN(date.getTime()) ? new Date() : date; } return new Date(); }); const [showDeleteDialog, setShowDeleteDialog] = useState(false); const [isDeleteFamily, setIsDeleteFamily] = useState(false); const handleHideDeleteDialog = () => { setShowDeleteDialog(false); }; const handleShowDeleteDialog = (isFamily: boolean) => { setIsDeleteFamily(isFamily); setShowDeleteDialog(true); }; const { mutateAsync: updateHouseholdName } = useUpdateHouseholdName(); const { mutateAsync: updateUserData } = useUpdateUserData(); const { mutateAsync: changeProfilePicture } = useChangeProfilePicture(); const { mutateAsync: deleteAsync } = useDeleteUser(); const isFirstRender = useRef(true); const handleUpdateUserData = async () => { await updateUserData({ newUserData: { firstName, lastName, timeZone } }); }; const handleUpdateHouseholdName = async () => { if (profileData?.familyId) { await updateHouseholdName({ familyId: profileData.familyId, name: householdName, }); } }; const debouncedUserDataUpdate = debounce(handleUpdateUserData, 500); useEffect(() => { if (isFirstRender.current) { isFirstRender.current = false; return; } debouncedUserDataUpdate(); }, [timeZone, lastName, firstName]); useEffect(() => { if (familyMembers) { const colors = familyMembers .filter((member) => member?.eventColor && member.uid !== user?.uid) .map((member) => member.eventColor!); setTakenColors(colors); } }, [familyMembers]); useEffect(() => { if (profileData) { setFirstName(profileData.firstName || ""); setLastName(profileData.lastName || ""); setTimeZone( profileData.timeZone || Localization.getCalendars()[0].timeZone! ); if (profileData?.birthday) { if (profileData.birthday.toDate) { setBirthday(profileData.birthday.toDate()); } else { const date = new Date(profileData.birthday); if (!isNaN(date.getTime())) { setBirthday(date); } } } } }, [profileData]); useEffect(() => { if (profileData?.familyId) { refetchHHName(); } }, [profileData?.familyId]); useEffect(() => { setHouseholdName(hhName ?? ""); }, [hhName]); const pickImage = async () => { const permissionResult = await ImagePicker.requestMediaLibraryPermissionsAsync(); if (!permissionResult.granted) { alert("Permission to access camera roll is required!"); return; } const result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images, allowsEditing: true, aspect: [1, 1], quality: 1, }); if (!result.canceled) { setProfileImage(result.assets[0].uri); await changeProfilePicture(result.assets[0]); } }; const handleClearImage = async () => { await updateUserData({ newUserData: { pfp: null } }); setProfileImage(null); }; const pfpUri = profileImage && typeof profileImage === "object" && "uri" in profileImage ? profileImage.uri : profileImage; const handleChangeColor = (color: string) => { setPreviousSelectedColor(selectedColor); setSelectedColor(color); debouncedUpdateUserData(color); }; const handleDeleteFamily = () => { if(profileData?.familyId){ deleteFamily({familyId: profileData?.familyId}, { onSuccess: () => { }, onError: (error) => { console.log("from delete fam:\n" + error); } }) } } const debouncedUpdateUserData = useCallback( debounce(async (color: string) => { try { await updateUserData({ newUserData: { eventColor: color, }, }); } catch (error) { console.error("Failed to update color:", error); setSelectedColor(previousSelectedColor); } }, 500), [] ); return ( Your Profile {pfpUri ? ( ) : ( {profileData?.firstName?.at(0)} {profileData?.lastName?.at(0)} )} {profileData?.pfp ? "Change" : "Add"} Photo {profileData?.pfp && ( Remove Photo )} {profileData?.userType == ProfileType.PARENT && ( <> Household { setHouseholdName(value); }} onEndEditing={() => handleUpdateHouseholdName()} /> )} First name { setFirstName(value); }} /> Last name { setLastName(value); }} /> Birthday { if (date) { const validDate = new Date(date); if (!isNaN(validDate.getTime())) { setBirthday(validDate); updateUserData({ newUserData: { birthday: validDate, }, }); } } }} /> Email address Color Preference handleChangeColor(colorMap.pink)} disabled={takenColors.includes(colorMap.pink)} > {selectedColor == colorMap.pink && ( )} handleChangeColor(colorMap.lightPink)} disabled={takenColors.includes(colorMap.lightPink)} > {selectedColor == colorMap.lightPink && ( )} handleChangeColor(colorMap.orange)} disabled={takenColors.includes(colorMap.orange)} > {selectedColor == colorMap.orange && ( )} handleChangeColor(colorMap.lightOrange)} disabled={takenColors.includes(colorMap.lightOrange)} > {selectedColor == colorMap.lightOrange && ( )} handleChangeColor(colorMap.green)} disabled={takenColors.includes(colorMap.green)} > {selectedColor == colorMap.green && ( )} handleChangeColor(colorMap.lightGreen)} disabled={takenColors.includes(colorMap.lightGreen)} > {selectedColor == colorMap.lightGreen && ( )} handleChangeColor(colorMap.teal)} disabled={takenColors.includes(colorMap.teal)} > {selectedColor == colorMap.teal && ( )} handleChangeColor(colorMap.lightTeal)} disabled={takenColors.includes(colorMap.lightTeal)} > {selectedColor == colorMap.lightTeal && ( )} handleChangeColor(colorMap.purple)} disabled={takenColors.includes(colorMap.purple)} > {selectedColor == colorMap.purple && ( )} handleChangeColor(colorMap.lightPurple)} disabled={takenColors.includes(colorMap.lightPurple)} > {selectedColor == colorMap.lightPurple && ( )} Settings Time Zone setTimeZone(item as string)} showSearch floatingPlaceholder style={styles.inViewPicker} trailingAccessory={ } > {timeZoneItems} handleShowDeleteDialog(false)} style={{ marginTop: 10, alignSelf: "center" }} > Delete Profile {profileData?.userType === ProfileType.PARENT && ( handleShowDeleteDialog(true)} style={{ marginTop: 20, alignSelf: "center" }} > Delete Family )} { setShowDeleteDialog(false); }} visible={showDeleteDialog} onDismiss={handleHideDeleteDialog} onConfirm={() => { if(isDeleteFamily) //deletes family handleDeleteFamily(); else { //deletes profile deleteAsync({}); } }} isDeleteFamily={isDeleteFamily} householdName={householdName} /> ); }; const timeZoneItems = Object.keys(tz.zones) .sort() .map((zone) => ( )); const styles = StyleSheet.create({ cardTitle: { fontFamily: "Manrope_500Medium", fontSize: 15, }, colorBox: { aspectRatio: 1, justifyContent: "center", alignItems: "center", width: 51, borderRadius: 12, }, card: { marginVertical: 15, backgroundColor: "white", width: "100%", borderRadius: 12, paddingHorizontal: 20, paddingVertical: 21, }, pfpTxt: { fontFamily: "Manrope_500Medium", fontSize: 30, color: "white", }, pfp: { aspectRatio: 1, width: 65.54, backgroundColor: "gray", borderRadius: 20, }, txtBox: { backgroundColor: "#fafafa", borderRadius: 50, borderWidth: 2, borderColor: "#cecece", padding: 15, height: 45, fontFamily: "PlusJakartaSans_500Medium", fontSize: 13, }, subTit: { fontFamily: "Manrope_500Medium", fontSize: 15, }, label: { fontFamily: "PlusJakartaSans_500Medium", fontSize: 12, color: "#a1a1a1", }, photoSet: { fontFamily: "PlusJakartaSans_500Medium", fontSize: 13.07, }, jakarta12: { paddingVertical: 10, fontFamily: "PlusJakartaSans_500Medium", fontSize: 12, color: "#a1a1a1", }, viewPicker: { borderRadius: 50, backgroundColor: Colors.grey80, marginBottom: 16, borderColor: Colors.grey50, borderWidth: 1, marginTop: 0, height: 40, zIndex: 10, }, inViewPicker: { borderRadius: 50, paddingVertical: 12, paddingHorizontal: 16, marginBottom: 16, marginTop: -20, height: 40, zIndex: 10, }, }); export default MyProfile;