mirror of
https://github.com/urosran/cally.git
synced 2026-10-23 07:27:55 +00:00
All day event package
This commit is contained in:
@@ -20,6 +20,7 @@ export const useGetEvents = () => {
|
||||
|
||||
// If family view is active, include family, creator, and attendee events
|
||||
if (isFamilyView) {
|
||||
|
||||
const familyQuery = db.collection("Events").where("familyID", "==", familyId);
|
||||
const creatorQuery = db.collection("Events").where("creatorId", "==", userId);
|
||||
const attendeeQuery = db.collection("Events").where("attendees", "array-contains", userId);
|
||||
@@ -83,7 +84,7 @@ export const useGetEvents = () => {
|
||||
const eventColor = profileData?.eventColor || colorMap.pink;
|
||||
|
||||
return {
|
||||
id: event.id || Math.random().toString(36).substr(2, 9), // Generate temp ID if missing
|
||||
id: event.id || Math.random().toString(36).slice(2, 9), // Generate temp ID if missing
|
||||
title: event.title,
|
||||
start: new Date(event.startDate.seconds * 1000),
|
||||
end: new Date(event.endDate.seconds * 1000),
|
||||
@@ -96,5 +97,6 @@ export const useGetEvents = () => {
|
||||
},
|
||||
staleTime: Infinity,
|
||||
cacheTime: Infinity,
|
||||
keepPreviousData: true,
|
||||
});
|
||||
};
|
||||
+7
-9
@@ -46,14 +46,9 @@ export const useCalSync = () => {
|
||||
const {profileData} = useAuthContext();
|
||||
|
||||
const {mutateAsync: updateUserData} = useUpdateUserData();
|
||||
const {mutateAsync: fetchAndSaveGoogleEvents, isLoading: isSyncingGoogle} =
|
||||
useFetchAndSaveGoogleEvents();
|
||||
const {
|
||||
mutateAsync: fetchAndSaveOutlookEvents,
|
||||
isLoading: isSyncingOutlook,
|
||||
} = useFetchAndSaveOutlookEvents();
|
||||
const {mutateAsync: fetchAndSaveAppleEvents, isLoading: isSyncingApple} =
|
||||
useFetchAndSaveAppleEvents();
|
||||
const {mutateAsync: fetchAndSaveGoogleEvents, isLoading: isSyncingGoogle} = useFetchAndSaveGoogleEvents();
|
||||
const {mutateAsync: fetchAndSaveOutlookEvents, isLoading: isSyncingOutlook} = useFetchAndSaveOutlookEvents();
|
||||
const {mutateAsync: fetchAndSaveAppleEvents, isLoading: isSyncingApple} = useFetchAndSaveAppleEvents();
|
||||
|
||||
WebBrowser.maybeCompleteAuthSession();
|
||||
const [_, response, promptAsync] = Google.useAuthRequest(googleConfig);
|
||||
@@ -74,6 +69,7 @@ export const useCalSync = () => {
|
||||
}
|
||||
);
|
||||
|
||||
console.log(response)
|
||||
const userInfo = await userInfoResponse.json();
|
||||
const googleMail = userInfo.email;
|
||||
|
||||
@@ -89,6 +85,7 @@ export const useCalSync = () => {
|
||||
|
||||
await fetchAndSaveGoogleEvents({
|
||||
token: accessToken,
|
||||
refreshToken: refreshToken,
|
||||
email: googleMail,
|
||||
});
|
||||
}
|
||||
@@ -283,6 +280,7 @@ export const useCalSync = () => {
|
||||
isConnectedToGoogle,
|
||||
isSyncingOutlook,
|
||||
isSyncingGoogle,
|
||||
isSyncingApple
|
||||
isSyncingApple,
|
||||
isSyncing: isSyncingApple || isSyncingOutlook || isSyncingGoogle
|
||||
}
|
||||
}
|
||||
@@ -9,11 +9,12 @@ export const useFetchAndSaveAppleEvents = () => {
|
||||
const {mutateAsync: createEventsFromProvider} = useCreateEventsFromProvider();
|
||||
|
||||
return useMutation({
|
||||
mutationKey: ["fetchAndSaveAppleEvents"],
|
||||
mutationFn: async ({token, email}: { token?: string, email?: string }) => {
|
||||
console.log("CALLL")
|
||||
const timeMin = new Date(new Date().setFullYear(new Date().getFullYear() - 1));
|
||||
const timeMax = new Date(new Date().setFullYear(new Date().getFullYear() + 5));
|
||||
mutationKey: ["fetchAndSaveAppleEvents", "sync"],
|
||||
mutationFn: async ({token, email, date}: { token?: string; email?: string, date?: Date }) => {
|
||||
const baseDate = date || new Date();
|
||||
const timeMin = new Date(new Date(baseDate).setFullYear(new Date(baseDate).getMonth() - 1));
|
||||
const timeMax = new Date(new Date(baseDate).setFullYear(new Date(baseDate).getMonth() + 1));
|
||||
|
||||
try {
|
||||
const response = await fetchiPhoneCalendarEvents(
|
||||
profileData?.familyId!,
|
||||
|
||||
@@ -1,56 +1,107 @@
|
||||
import {useMutation, useQueryClient} from "react-query";
|
||||
import {fetchGoogleCalendarEvents} from "@/calendar-integration/google-calendar-utils";
|
||||
import {useAuthContext} from "@/contexts/AuthContext";
|
||||
import {useCreateEventsFromProvider} from "@/hooks/firebase/useCreateEvent";
|
||||
import {useClearTokens} from "@/hooks/firebase/useClearTokens";
|
||||
import { useMutation, useQueryClient } from "react-query";
|
||||
import { fetchGoogleCalendarEvents } from "@/calendar-integration/google-calendar-utils";
|
||||
import { useAuthContext } from "@/contexts/AuthContext";
|
||||
import { useCreateEventsFromProvider } from "@/hooks/firebase/useCreateEvent";
|
||||
import { useClearTokens } from "@/hooks/firebase/useClearTokens";
|
||||
import { useUpdateUserData } from "@/hooks/firebase/useUpdateUserData";
|
||||
|
||||
export const useFetchAndSaveGoogleEvents = () => {
|
||||
const queryClient = useQueryClient()
|
||||
const {profileData} = useAuthContext();
|
||||
const {mutateAsync: createEventsFromProvider} = useCreateEventsFromProvider();
|
||||
const {mutateAsync: clearToken} = useClearTokens();
|
||||
const queryClient = useQueryClient();
|
||||
const { profileData } = useAuthContext();
|
||||
const { mutateAsync: createEventsFromProvider } = useCreateEventsFromProvider();
|
||||
const { mutateAsync: clearToken } = useClearTokens();
|
||||
const { mutateAsync: updateUserData } = useUpdateUserData();
|
||||
|
||||
return useMutation({
|
||||
mutationKey: ["fetchAndSaveGoogleEvents"],
|
||||
mutationFn: async ({token, email}: { token?: string; email?: string }) => {
|
||||
console.log("Fetching Google Calendar events...");
|
||||
const timeMin = new Date(new Date().setMonth(new Date().getMonth() - 2));
|
||||
const timeMax = new Date(new Date().setMonth(new Date().getMonth() + 2));
|
||||
mutationKey: ["fetchAndSaveGoogleEvents", "sync"],
|
||||
mutationFn: async ({ token, email, date, refreshToken }: { token?: string; refreshToken?: string; email?: string; date?: Date }) => {
|
||||
const baseDate = date || new Date();
|
||||
const timeMin = new Date(baseDate.setMonth(baseDate.getMonth() - 1)).toISOString().slice(0, -5) + "Z";
|
||||
const timeMax = new Date(baseDate.setMonth(baseDate.getMonth() + 2)).toISOString().slice(0, -5) + "Z";
|
||||
|
||||
console.log("Token: ", token);
|
||||
|
||||
try {
|
||||
const response = await fetchGoogleCalendarEvents(
|
||||
token,
|
||||
email,
|
||||
profileData?.familyId,
|
||||
timeMin.toISOString().slice(0, -5) + "Z",
|
||||
timeMax.toISOString().slice(0, -5) + "Z"
|
||||
);
|
||||
const tryFetchEvents = async (isRetry = false) => {
|
||||
try {
|
||||
const response = await fetchGoogleCalendarEvents(
|
||||
token,
|
||||
email,
|
||||
profileData?.familyId,
|
||||
timeMin,
|
||||
timeMax
|
||||
);
|
||||
|
||||
if(!response.success) {
|
||||
await clearToken({email: email!, provider: "google"})
|
||||
return
|
||||
}
|
||||
|
||||
console.log("Google Calendar events fetched:", response);
|
||||
|
||||
const items = response?.googleEvents?.map((item) => {
|
||||
if (item.allDay) {
|
||||
item.startDate = new Date(new Date(item.startDate).setHours(0, 0, 0, 0));
|
||||
item.endDate = item.startDate;
|
||||
if (!response.success) {
|
||||
await clearToken({ email: email!, provider: "google" });
|
||||
return; // Stop refetching if clearing the token
|
||||
}
|
||||
return item;
|
||||
}) || [];
|
||||
|
||||
await createEventsFromProvider(items);
|
||||
} catch (error) {
|
||||
console.error("Error fetching Google Calendar events:", error);
|
||||
throw error; // Ensure errors are propagated to the mutation
|
||||
}
|
||||
console.log("Google Calendar events fetched:", response);
|
||||
|
||||
const items = response?.googleEvents?.map((item) => {
|
||||
if (item.allDay) {
|
||||
item.startDate = new Date(item.startDate.setHours(0, 0, 0, 0));
|
||||
item.endDate = item.startDate;
|
||||
}
|
||||
return item;
|
||||
}) || [];
|
||||
|
||||
await createEventsFromProvider(items);
|
||||
} catch (error) {
|
||||
console.error("Error fetching Google Calendar events:", error);
|
||||
|
||||
if (!isRetry) {
|
||||
const refreshedToken = await handleRefreshToken(email, refreshToken);
|
||||
if (refreshedToken) {
|
||||
await updateUserData({
|
||||
newUserData: {
|
||||
googleAccounts: {
|
||||
...profileData.googleAccounts,
|
||||
[email!]: { ...profileData.googleAccounts[email!], accessToken: refreshedToken },
|
||||
},
|
||||
},
|
||||
});
|
||||
return tryFetchEvents(true); // Retry once after refreshing
|
||||
} else {
|
||||
await clearToken({ email: email!, provider: "google" });
|
||||
console.error(`Token refresh failed; token cleared for ${email}`);
|
||||
throw error;
|
||||
}
|
||||
} else {
|
||||
console.error(`Retry failed after refreshing token for user ${profileData?.email}:`, error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return tryFetchEvents();
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries(["events"])
|
||||
queryClient.invalidateQueries(["events"]);
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
async function handleRefreshToken(email: string, refreshToken: string) {
|
||||
if (!refreshToken) return null;
|
||||
|
||||
try {
|
||||
const response = await fetch('https://oauth2.googleapis.com/token', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
grant_type: 'refresh_token',
|
||||
refresh_token: refreshToken,
|
||||
client_id: "406146460310-2u67ab2nbhu23trp8auho1fq4om29fc0.apps.googleusercontent.com",
|
||||
}),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
return data.access_token;
|
||||
} catch (error) {
|
||||
console.error("Error refreshing Google token:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -9,10 +9,11 @@ export const useFetchAndSaveOutlookEvents = () => {
|
||||
const {mutateAsync: createEventsFromProvider} = useCreateEventsFromProvider();
|
||||
|
||||
return useMutation({
|
||||
mutationKey: ["fetchAndSaveOutlookEvents"],
|
||||
mutationFn: async ({token, email}: { token?: string; email?: string }) => {
|
||||
const timeMin = new Date(new Date().setFullYear(new Date().getFullYear() - 1));
|
||||
const timeMax = new Date(new Date().setFullYear(new Date().getFullYear() + 3));
|
||||
mutationKey: ["fetchAndSaveOutlookEvents", "sync"],
|
||||
mutationFn: async ({token, email, date}: { token?: string; email?: string, date?: Date }) => {
|
||||
const baseDate = date || new Date();
|
||||
const timeMin = new Date(new Date(baseDate).setFullYear(new Date(baseDate).getMonth() - 1));
|
||||
const timeMax = new Date(new Date(baseDate).setFullYear(new Date(baseDate).getMonth() + 1));
|
||||
|
||||
console.log("Token: ", token ?? profileData?.microsoftToken);
|
||||
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { useAuthContext } from "@/contexts/AuthContext";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { useFetchAndSaveGoogleEvents } from "./useFetchAndSaveGoogleEvents";
|
||||
import { useFetchAndSaveAppleEvents } from "./useFetchAndSaveAppleEvents";
|
||||
import { useFetchAndSaveOutlookEvents } from "./useFetchAndSaveOutlookEvents";
|
||||
import { selectedDateAtom } from "@/components/pages/calendar/atoms";
|
||||
import { addDays, subDays, isBefore, isAfter, format } from "date-fns";
|
||||
|
||||
export const useSyncEvents = () => {
|
||||
const { profileData } = useAuthContext();
|
||||
const selectedDate = useAtomValue(selectedDateAtom);
|
||||
|
||||
const [lastSyncDate, setLastSyncDate] = useState<Date>(selectedDate);
|
||||
const [lowerBoundDate, setLowerBoundDate] = useState<Date>(subDays(selectedDate, 6 * 30));
|
||||
const [upperBoundDate, setUpperBoundDate] = useState<Date>(addDays(selectedDate, 6 * 30));
|
||||
const [isSyncing, setIsSyncing] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
const syncedRanges = useState<Set<string>>(new Set())[0];
|
||||
|
||||
const { mutateAsync: fetchAndSaveGoogleEvents } = useFetchAndSaveGoogleEvents();
|
||||
const { mutateAsync: fetchAndSaveOutlookEvents } = useFetchAndSaveOutlookEvents();
|
||||
const { mutateAsync: fetchAndSaveAppleEvents } = useFetchAndSaveAppleEvents();
|
||||
|
||||
const generateRangeKey = (startDate: Date, endDate: Date) => {
|
||||
return `${format(startDate, "yyyy-MM-dd")}_${format(endDate, "yyyy-MM-dd")}`;
|
||||
};
|
||||
|
||||
const syncEvents = useCallback(async () => {
|
||||
setIsSyncing(true);
|
||||
setError(null);
|
||||
|
||||
const newLowerBound = subDays(selectedDate, 6 * 30);
|
||||
const newUpperBound = addDays(selectedDate, 6 * 30);
|
||||
const rangeKey = generateRangeKey(newLowerBound, newUpperBound);
|
||||
|
||||
if (syncedRanges.has(rangeKey)) {
|
||||
setIsSyncing(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isBefore(selectedDate, lowerBoundDate) || isAfter(selectedDate, upperBoundDate)) {
|
||||
try {
|
||||
const googleEvents = Object.entries(profileData?.googleAccounts || {}).map(([email, { accessToken }]) =>
|
||||
fetchAndSaveGoogleEvents({ token: accessToken, email, date: selectedDate })
|
||||
);
|
||||
|
||||
const outlookEvents = Object.entries(profileData?.microsoftAccounts || {}).map(([email, token]) =>
|
||||
fetchAndSaveOutlookEvents({ token, email, date: selectedDate })
|
||||
);
|
||||
|
||||
const appleEvents = Object.entries(profileData?.appleAccounts || {}).map(([email, token]) =>
|
||||
fetchAndSaveAppleEvents({ token, email, date: selectedDate })
|
||||
);
|
||||
|
||||
await Promise.all([...googleEvents, ...outlookEvents, ...appleEvents]);
|
||||
|
||||
setLastSyncDate(selectedDate);
|
||||
setLowerBoundDate(newLowerBound);
|
||||
setUpperBoundDate(newUpperBound);
|
||||
syncedRanges.add(rangeKey);
|
||||
} catch (err) {
|
||||
console.error("Error syncing events:", err);
|
||||
setError(err);
|
||||
} finally {
|
||||
setIsSyncing(false);
|
||||
}
|
||||
} else {
|
||||
setIsSyncing(false);
|
||||
}
|
||||
}, [selectedDate, lowerBoundDate, upperBoundDate, profileData, fetchAndSaveGoogleEvents, fetchAndSaveOutlookEvents, fetchAndSaveAppleEvents, syncedRanges]);
|
||||
|
||||
useEffect(() => {
|
||||
syncEvents();
|
||||
}, [selectedDate, syncEvents]);
|
||||
|
||||
return {
|
||||
isSyncing,
|
||||
error,
|
||||
lastSyncDate,
|
||||
lowerBoundDate,
|
||||
upperBoundDate,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user