diff --git a/app/(auth)/calendar/index.tsx b/app/(auth)/calendar/index.tsx index 53fa094..1ced3d2 100644 --- a/app/(auth)/calendar/index.tsx +++ b/app/(auth)/calendar/index.tsx @@ -3,6 +3,7 @@ import { ScrollView, RefreshControl } from "react-native"; import { useSetAtom } from "jotai"; import CalendarPage from "@/components/pages/calendar/CalendarPage"; import { refreshTriggerAtom } from "@/components/pages/calendar/atoms"; +import { colorMap } from "@/constants/colorMap"; export default function Screen() { const [refreshing, setRefreshing] = useState(false); @@ -29,7 +30,19 @@ export default function Screen() { style={{ flex: 1 }} contentContainerStyle={{ flex: 1 }} refreshControl={ - + } bounces={true} showsVerticalScrollIndicator={false} diff --git a/app/(unauth)/cal_sync.tsx b/app/(unauth)/cal_sync.tsx index 0e9f15b..4eb6cba 100644 --- a/app/(unauth)/cal_sync.tsx +++ b/app/(unauth)/cal_sync.tsx @@ -1,17 +1,24 @@ import {SafeAreaView} from "react-native-safe-area-context"; import {Button, Text, View} from "react-native-ui-lib"; -import React from "react"; +import React, { useEffect } from "react"; import {useCalSync} from "@/hooks/useCalSync"; import GoogleIcon from "@/assets/svgs/GoogleIcon"; import AppleIcon from "@/assets/svgs/AppleIcon"; import OutlookIcon from "@/assets/svgs/OutlookIcon"; import {useAuthContext} from "@/contexts/AuthContext"; import {StyleSheet} from "react-native"; +import { useGetHouseholdName } from "@/hooks/firebase/useGetHouseholdName"; export default function Screen() { const {profileData, setRedirectOverride} = useAuthContext() const {handleStartGoogleSignIn, handleAppleSignIn, handleMicrosoftSignIn} = useCalSync() + const {data: householdName, refetch} = useGetHouseholdName(profileData?.familyId); + + useEffect(() => { + refetch(); + }, [profileData?.familyId]) + const hasSomeCalendarsSynced = !!profileData?.appleAccounts || !!profileData?.microsoftAccounts || !!profileData?.googleAccounts @@ -19,6 +26,9 @@ export default function Screen() { + {householdName && + You Joined {householdName} + } Let's get started! diff --git a/components/pages/calendar/CalendarPage.tsx b/components/pages/calendar/CalendarPage.tsx index d6d2d7e..960beee 100644 --- a/components/pages/calendar/CalendarPage.tsx +++ b/components/pages/calendar/CalendarPage.tsx @@ -2,19 +2,38 @@ import React from "react"; import { View } from "react-native-ui-lib"; import HeaderTemplate from "@/components/shared/HeaderTemplate"; import { InnerCalendar } from "@/components/pages/calendar/InnerCalendar"; +import { useSetAtom } from "jotai"; +import { refreshEnabledAtom } from "./atoms"; export default function CalendarPage() { + const setRefreshEnabled = useSetAtom(refreshEnabledAtom); + + const disableRefreshControl = () => setRefreshEnabled(false); + const enableRefreshControl = () => setRefreshEnabled(true); return ( - + { + enableRefreshControl(); + console.log("yeah"); + return true; + }} + onResponderRelease={() => { + disableRefreshControl(); + console.log("sure"); + console.log(refreshEnabledAtom) + }} + > + + ); diff --git a/components/pages/calendar/atoms.ts b/components/pages/calendar/atoms.ts index 3f24905..a2095dc 100644 --- a/components/pages/calendar/atoms.ts +++ b/components/pages/calendar/atoms.ts @@ -12,3 +12,4 @@ export const settingsPageIndex = atom(0); export const userSettingsView = atom(true); export const toDosPageIndex = atom(0); export const refreshTriggerAtom = atom(false); +export const refreshEnabledAtom = atom(true); diff --git a/components/pages/settings/user_settings_views/MyProfile.tsx b/components/pages/settings/user_settings_views/MyProfile.tsx index 8c5be87..e0ecc2e 100644 --- a/components/pages/settings/user_settings_views/MyProfile.tsx +++ b/components/pages/settings/user_settings_views/MyProfile.tsx @@ -1,4 +1,4 @@ -import React, {useCallback, useEffect, useRef, useState} from "react"; +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"; @@ -20,11 +20,17 @@ 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 { AntDesign } from "@expo/vector-icons"; +import { useDeleteUser } from "@/hooks/firebase/useDeleteUser"; +import { useUpdateHouseholdName } from "@/hooks/firebase/useUpdateHouseholdName"; +import { useGetHouseholdName } from "@/hooks/firebase/useGetHouseholdName"; const MyProfile = () => { const { user, profileData } = useAuthContext(); + const { data: hhName, refetch: refetchHHName } = useGetHouseholdName( + profileData.familyId + ); + const [householdName, setHouseholdName] = useState(""); const [timeZone, setTimeZone] = useState( profileData?.timeZone! ?? Localization.getCalendars()[0].timeZone ); @@ -37,10 +43,10 @@ const MyProfile = () => { >(profileData?.pfp || null); const [selectedColor, setSelectedColor] = useState( - profileData?.eventColor ?? colorMap.pink + profileData?.eventColor ?? colorMap.pink ); const [previousSelectedColor, setPreviousSelectedColor] = useState( - profileData?.eventColor ?? colorMap.pink + profileData?.eventColor ?? colorMap.pink ); const [showDeleteDialog, setShowDeleteDialog] = useState(false); @@ -52,15 +58,25 @@ const MyProfile = () => { setShowDeleteDialog(true); }; + const { mutateAsync: updateHouseholdName } = useUpdateHouseholdName(); const { mutateAsync: updateUserData } = useUpdateUserData(); const { mutateAsync: changeProfilePicture } = useChangeProfilePicture(); - const { mutateAsync: deleteAsync } = useDeleteUser() + 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(() => { @@ -71,6 +87,10 @@ const MyProfile = () => { debouncedUserDataUpdate(); }, [timeZone, lastName, firstName]); + useEffect(() => { + handleUpdateHouseholdName(); + }, [householdName]); + useEffect(() => { if (profileData) { setFirstName(profileData.firstName || ""); @@ -81,6 +101,16 @@ const MyProfile = () => { } }, [profileData]); + useEffect(() => { + if (profileData?.familyId) { + refetchHHName(); + } + }, [profileData?.familyId]); + + useEffect(() => { + setHouseholdName(hhName); + }, [hhName]) + const pickImage = async () => { const permissionResult = await ImagePicker.requestMediaLibraryPermissionsAsync(); @@ -119,19 +149,19 @@ const MyProfile = () => { }; 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), - [] + debounce(async (color: string) => { + try { + await updateUserData({ + newUserData: { + eventColor: color, + }, + }); + } catch (error) { + console.error("Failed to update color:", error); + setSelectedColor(previousSelectedColor); + } + }, 500), + [] ); return ( @@ -177,6 +207,18 @@ const MyProfile = () => { )} + + Household + + { + setHouseholdName(value); + }} + /> First name @@ -255,39 +297,35 @@ const MyProfile = () => { handleChangeColor(colorMap.pink)}> {selectedColor == colorMap.pink && ( - + )} - handleChangeColor(colorMap.orange)} - > + handleChangeColor(colorMap.orange)}> {selectedColor == colorMap.orange && ( - + )} handleChangeColor(colorMap.green)}> {selectedColor == colorMap.green && ( - + )} handleChangeColor(colorMap.teal)}> {selectedColor == colorMap.teal && ( - + )} - handleChangeColor(colorMap.purple)} - > + handleChangeColor(colorMap.purple)}> {selectedColor == colorMap.purple && ( - + )} diff --git a/hooks/firebase/useGetHouseholdName.ts b/hooks/firebase/useGetHouseholdName.ts new file mode 100644 index 0000000..bf85b75 --- /dev/null +++ b/hooks/firebase/useGetHouseholdName.ts @@ -0,0 +1,36 @@ +import { useQuery } from "react-query"; +import firestore from "@react-native-firebase/firestore"; + +export const useGetHouseholdName = (familyId: string) => { + return useQuery( + ["getHouseholdName", familyId], // Unique query key + async () => { + console.log(`Fetching household name for familyId: ${familyId}`); + + try { + // Query the Households collection for the given familyId + const snapshot = await firestore() + .collection("Households") + .where("familyId", "==", familyId) + .get(); + + if (!snapshot.empty) { + // Extract the name from the first matching document + const householdData = snapshot.docs[0].data(); + console.log("Household found:", householdData); + return householdData.name || null; // Return the name or null if missing + } else { + console.log("No household found for the given familyId."); + return null; // Return null if no household found + } + } catch (e) { + console.error("Error fetching household name:", e); + throw e; // Ensure error propagates to the query error handling + } + }, + { + enabled: !!familyId, // Only fetch if familyId is provided + staleTime: 5 * 60 * 1000, // Cache the data for 5 minutes + } + ); +}; diff --git a/hooks/firebase/useUpdateHouseholdName.ts b/hooks/firebase/useUpdateHouseholdName.ts new file mode 100644 index 0000000..f601745 --- /dev/null +++ b/hooks/firebase/useUpdateHouseholdName.ts @@ -0,0 +1,50 @@ +import firestore from "@react-native-firebase/firestore"; +import { useMutation, useQueryClient } from "react-query"; + +export const useUpdateHouseholdName = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationKey: ["updateHouseholdName"], + mutationFn: async ({ + familyId, + name, + }: { + familyId: string; + name: string; + }) => { + console.log("Mutation function called with data:", { familyId, name }); + + try { + // Reference to the Households collection + const householdRef = firestore().collection("Households"); + + // Query to check if the household exists + const snapshot = await householdRef.where("familyId", "==", familyId).get(); + + if (!snapshot.empty) { + // If a household with the familyId exists, update the name + const docId = snapshot.docs[0].id; + console.log(`Household found with ID ${docId}, updating name...`); + + await householdRef.doc(docId).update({ name }); + + console.log("Household name updated successfully."); + } else { + // If no household exists, create a new one with familyId and name + console.log("No household found, creating a new one..."); + + await householdRef.add({ familyId, name }); + + console.log("New household created successfully."); + } + } catch (e) { + console.error("Error updating or creating household:", e); + throw e; // Ensure error propagates to the mutation error handling + } + }, + onSuccess: () => { + queryClient.invalidateQueries("households"); // Invalidate the "households" query to refresh data + }, + }); +};