mirror of
https://github.com/urosran/cally.git
synced 2025-07-10 15:17:17 +00:00
All day event package
This commit is contained in:
@ -1,15 +1,25 @@
|
|||||||
export async function fetchGoogleCalendarEvents(token, email, familyId, startDate, endDate) {
|
export async function fetchGoogleCalendarEvents(token, email, familyId, startDate, endDate) {
|
||||||
const response = await fetch(
|
const googleEvents = [];
|
||||||
`https://www.googleapis.com/calendar/v3/calendars/primary/events?single_events=true&time_min=${startDate}&time_max=${endDate}`,
|
let pageToken = null;
|
||||||
{
|
|
||||||
|
do {
|
||||||
|
const url = new URL(`https://www.googleapis.com/calendar/v3/calendars/primary/events`);
|
||||||
|
url.searchParams.set('singleEvents', 'true');
|
||||||
|
url.searchParams.set('timeMin', startDate);
|
||||||
|
url.searchParams.set('timeMax', endDate);
|
||||||
|
if (pageToken) url.searchParams.set('pageToken', pageToken);
|
||||||
|
|
||||||
|
const response = await fetch(url.toString(), {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${token}`,
|
Authorization: `Bearer ${token}`,
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
const googleEvents = [];
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Error fetching events: ${data.error?.message || response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
data.items?.forEach((item) => {
|
data.items?.forEach((item) => {
|
||||||
let isAllDay = false;
|
let isAllDay = false;
|
||||||
@ -46,5 +56,9 @@ export async function fetchGoogleCalendarEvents(token, email, familyId, startDat
|
|||||||
googleEvents.push(googleEvent);
|
googleEvents.push(googleEvent);
|
||||||
});
|
});
|
||||||
|
|
||||||
return {googleEvents, success: response.ok};
|
// Prepare for the next page if it exists
|
||||||
|
pageToken = data.nextPageToken;
|
||||||
|
} while (pageToken);
|
||||||
|
|
||||||
|
return { googleEvents, success: true };
|
||||||
}
|
}
|
@ -14,6 +14,7 @@ import { useAtom } from "jotai";
|
|||||||
import { modeAtom, selectedDateAtom } from "@/components/pages/calendar/atoms";
|
import { modeAtom, selectedDateAtom } from "@/components/pages/calendar/atoms";
|
||||||
import { format, isSameDay } from "date-fns";
|
import { format, isSameDay } from "date-fns";
|
||||||
import { useAuthContext } from "@/contexts/AuthContext";
|
import { useAuthContext } from "@/contexts/AuthContext";
|
||||||
|
import {useIsMutating} from "react-query";
|
||||||
|
|
||||||
export const CalendarHeader = memo(() => {
|
export const CalendarHeader = memo(() => {
|
||||||
const [selectedDate, setSelectedDate] = useAtom(selectedDateAtom);
|
const [selectedDate, setSelectedDate] = useAtom(selectedDateAtom);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, {useCallback, useEffect, useMemo, useState} from "react";
|
import React, {useCallback, useEffect, useMemo, useState} from "react";
|
||||||
import {Calendar} from "react-native-big-calendar";
|
import {Calendar} from "react-native-big-calendar";
|
||||||
import {ActivityIndicator, StyleSheet, View, ViewStyle} from "react-native";
|
import {ActivityIndicator, ScrollView, StyleSheet, View, ViewStyle} from "react-native";
|
||||||
import {useGetEvents} from "@/hooks/firebase/useGetEvents";
|
import {useGetEvents} from "@/hooks/firebase/useGetEvents";
|
||||||
import {useAtom, useSetAtom} from "jotai";
|
import {useAtom, useSetAtom} from "jotai";
|
||||||
import {
|
import {
|
||||||
@ -14,6 +14,9 @@ import {useAuthContext} from "@/contexts/AuthContext";
|
|||||||
import {CalendarEvent} from "@/components/pages/calendar/interfaces";
|
import {CalendarEvent} from "@/components/pages/calendar/interfaces";
|
||||||
import {Text} from "react-native-ui-lib";
|
import {Text} from "react-native-ui-lib";
|
||||||
import {addDays, compareAsc, isWithinInterval, subDays} from "date-fns";
|
import {addDays, compareAsc, isWithinInterval, subDays} from "date-fns";
|
||||||
|
import {useCalSync} from "@/hooks/useCalSync";
|
||||||
|
import { useIsMutating } from "react-query";
|
||||||
|
import {useSyncEvents} from "@/hooks/useSyncOnScroll";
|
||||||
|
|
||||||
interface EventCalendarProps {
|
interface EventCalendarProps {
|
||||||
calendarHeight: number;
|
calendarHeight: number;
|
||||||
@ -37,6 +40,7 @@ export const EventCalendar: React.FC<EventCalendarProps> = React.memo(
|
|||||||
const setEventForEdit = useSetAtom(eventForEditAtom);
|
const setEventForEdit = useSetAtom(eventForEditAtom);
|
||||||
const setSelectedNewEndDate = useSetAtom(selectedNewEventDateAtom);
|
const setSelectedNewEndDate = useSetAtom(selectedNewEventDateAtom);
|
||||||
|
|
||||||
|
const {isSyncing} = useSyncEvents()
|
||||||
const [offsetMinutes, setOffsetMinutes] = useState(getTotalMinutes());
|
const [offsetMinutes, setOffsetMinutes] = useState(getTotalMinutes());
|
||||||
|
|
||||||
const todaysDate = new Date();
|
const todaysDate = new Date();
|
||||||
@ -75,7 +79,7 @@ export const EventCalendar: React.FC<EventCalendarProps> = React.memo(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const memoizedEventCellStyle = useCallback(
|
const memoizedEventCellStyle = useCallback(
|
||||||
(event: CalendarEvent) => ({backgroundColor: event.eventColor}),
|
(event: CalendarEvent) => ({backgroundColor: event.eventColor, fontSize: 14}),
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -211,6 +215,7 @@ export const EventCalendar: React.FC<EventCalendarProps> = React.memo(
|
|||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<View style={styles.loadingContainer}>
|
<View style={styles.loadingContainer}>
|
||||||
|
{isSyncing && <Text>Syncing...</Text>}
|
||||||
<ActivityIndicator size="large" color="#0000ff"/>
|
<ActivityIndicator size="large" color="#0000ff"/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
@ -219,6 +224,13 @@ export const EventCalendar: React.FC<EventCalendarProps> = React.memo(
|
|||||||
// console.log(enrichedEvents, filteredEvents)
|
// console.log(enrichedEvents, filteredEvents)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
{isSyncing && (
|
||||||
|
<View style={styles.loadingContainer}>
|
||||||
|
{isSyncing && <Text>Syncing...</Text>}
|
||||||
|
<ActivityIndicator size="large" color="#0000ff"/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
<Calendar
|
<Calendar
|
||||||
bodyContainerStyle={styles.calHeader}
|
bodyContainerStyle={styles.calHeader}
|
||||||
swipeEnabled
|
swipeEnabled
|
||||||
@ -227,7 +239,7 @@ export const EventCalendar: React.FC<EventCalendarProps> = React.memo(
|
|||||||
sortedMonthView
|
sortedMonthView
|
||||||
// enrichedEventsByDate={enrichedEvents}
|
// enrichedEventsByDate={enrichedEvents}
|
||||||
events={filteredEvents}
|
events={filteredEvents}
|
||||||
// eventCellStyle={memoizedEventCellStyle}
|
eventCellStyle={memoizedEventCellStyle}
|
||||||
onPressEvent={handlePressEvent}
|
onPressEvent={handlePressEvent}
|
||||||
weekStartsOn={memoizedWeekStartsOn}
|
weekStartsOn={memoizedWeekStartsOn}
|
||||||
height={calendarHeight}
|
height={calendarHeight}
|
||||||
@ -249,10 +261,10 @@ export const EventCalendar: React.FC<EventCalendarProps> = React.memo(
|
|||||||
},
|
},
|
||||||
typography: {
|
typography: {
|
||||||
fontFamily: "PlusJakartaSans_500Medium",
|
fontFamily: "PlusJakartaSans_500Medium",
|
||||||
sm: {fontFamily: "Manrope_600SemiBold", fontSize: 15},
|
sm: {fontFamily: "Manrope_600SemiBold", fontSize: 8},
|
||||||
xl: {
|
xl: {
|
||||||
fontFamily: "PlusJakartaSans_500Medium",
|
fontFamily: "PlusJakartaSans_500Medium",
|
||||||
fontSize: 16,
|
fontSize: 14,
|
||||||
},
|
},
|
||||||
moreLabel: {},
|
moreLabel: {},
|
||||||
xs: {fontSize: 10},
|
xs: {fontSize: 10},
|
||||||
@ -261,10 +273,16 @@ export const EventCalendar: React.FC<EventCalendarProps> = React.memo(
|
|||||||
dayHeaderStyle={dateStyle}
|
dayHeaderStyle={dateStyle}
|
||||||
dayHeaderHighlightColor={"white"}
|
dayHeaderHighlightColor={"white"}
|
||||||
showAdjacentMonths
|
showAdjacentMonths
|
||||||
|
// headerContainerStyle={mode !== "month" ? {
|
||||||
|
// overflow:"hidden",
|
||||||
|
// height: 12,
|
||||||
|
// } : {}}
|
||||||
hourStyle={styles.hourStyle}
|
hourStyle={styles.hourStyle}
|
||||||
ampm
|
ampm
|
||||||
// renderCustomDateForMonth={renderCustomDateForMonth}
|
// renderCustomDateForMonth={renderCustomDateForMonth}
|
||||||
/>
|
/>
|
||||||
|
</>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -291,6 +309,11 @@ const styles = StyleSheet.create({
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
|
position: "absolute",
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
zIndex: 100,
|
||||||
|
backgroundColor: "rgba(255, 255, 255, 0.9)",
|
||||||
},
|
},
|
||||||
dayHeader: {
|
dayHeader: {
|
||||||
backgroundColor: "#4184f2",
|
backgroundColor: "#4184f2",
|
||||||
|
@ -181,7 +181,7 @@ exports.generateCustomToken = onRequest(async (request, response) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
exports.refreshTokens = functions.pubsub.schedule('every 1 hours').onRun(async (context) => {
|
exports.refreshTokens = functions.pubsub.schedule('every 45 minutes').onRun(async (context) => {
|
||||||
console.log('Running token refresh job...');
|
console.log('Running token refresh job...');
|
||||||
|
|
||||||
const profilesSnapshot = await db.collection('Profiles').get();
|
const profilesSnapshot = await db.collection('Profiles').get();
|
||||||
|
@ -20,6 +20,7 @@ export const useGetEvents = () => {
|
|||||||
|
|
||||||
// If family view is active, include family, creator, and attendee events
|
// If family view is active, include family, creator, and attendee events
|
||||||
if (isFamilyView) {
|
if (isFamilyView) {
|
||||||
|
|
||||||
const familyQuery = db.collection("Events").where("familyID", "==", familyId);
|
const familyQuery = db.collection("Events").where("familyID", "==", familyId);
|
||||||
const creatorQuery = db.collection("Events").where("creatorId", "==", userId);
|
const creatorQuery = db.collection("Events").where("creatorId", "==", userId);
|
||||||
const attendeeQuery = db.collection("Events").where("attendees", "array-contains", userId);
|
const attendeeQuery = db.collection("Events").where("attendees", "array-contains", userId);
|
||||||
@ -83,7 +84,7 @@ export const useGetEvents = () => {
|
|||||||
const eventColor = profileData?.eventColor || colorMap.pink;
|
const eventColor = profileData?.eventColor || colorMap.pink;
|
||||||
|
|
||||||
return {
|
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,
|
title: event.title,
|
||||||
start: new Date(event.startDate.seconds * 1000),
|
start: new Date(event.startDate.seconds * 1000),
|
||||||
end: new Date(event.endDate.seconds * 1000),
|
end: new Date(event.endDate.seconds * 1000),
|
||||||
@ -96,5 +97,6 @@ export const useGetEvents = () => {
|
|||||||
},
|
},
|
||||||
staleTime: Infinity,
|
staleTime: Infinity,
|
||||||
cacheTime: Infinity,
|
cacheTime: Infinity,
|
||||||
|
keepPreviousData: true,
|
||||||
});
|
});
|
||||||
};
|
};
|
@ -46,14 +46,9 @@ export const useCalSync = () => {
|
|||||||
const {profileData} = useAuthContext();
|
const {profileData} = useAuthContext();
|
||||||
|
|
||||||
const {mutateAsync: updateUserData} = useUpdateUserData();
|
const {mutateAsync: updateUserData} = useUpdateUserData();
|
||||||
const {mutateAsync: fetchAndSaveGoogleEvents, isLoading: isSyncingGoogle} =
|
const {mutateAsync: fetchAndSaveGoogleEvents, isLoading: isSyncingGoogle} = useFetchAndSaveGoogleEvents();
|
||||||
useFetchAndSaveGoogleEvents();
|
const {mutateAsync: fetchAndSaveOutlookEvents, isLoading: isSyncingOutlook} = useFetchAndSaveOutlookEvents();
|
||||||
const {
|
const {mutateAsync: fetchAndSaveAppleEvents, isLoading: isSyncingApple} = useFetchAndSaveAppleEvents();
|
||||||
mutateAsync: fetchAndSaveOutlookEvents,
|
|
||||||
isLoading: isSyncingOutlook,
|
|
||||||
} = useFetchAndSaveOutlookEvents();
|
|
||||||
const {mutateAsync: fetchAndSaveAppleEvents, isLoading: isSyncingApple} =
|
|
||||||
useFetchAndSaveAppleEvents();
|
|
||||||
|
|
||||||
WebBrowser.maybeCompleteAuthSession();
|
WebBrowser.maybeCompleteAuthSession();
|
||||||
const [_, response, promptAsync] = Google.useAuthRequest(googleConfig);
|
const [_, response, promptAsync] = Google.useAuthRequest(googleConfig);
|
||||||
@ -74,6 +69,7 @@ export const useCalSync = () => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
console.log(response)
|
||||||
const userInfo = await userInfoResponse.json();
|
const userInfo = await userInfoResponse.json();
|
||||||
const googleMail = userInfo.email;
|
const googleMail = userInfo.email;
|
||||||
|
|
||||||
@ -89,6 +85,7 @@ export const useCalSync = () => {
|
|||||||
|
|
||||||
await fetchAndSaveGoogleEvents({
|
await fetchAndSaveGoogleEvents({
|
||||||
token: accessToken,
|
token: accessToken,
|
||||||
|
refreshToken: refreshToken,
|
||||||
email: googleMail,
|
email: googleMail,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -283,6 +280,7 @@ export const useCalSync = () => {
|
|||||||
isConnectedToGoogle,
|
isConnectedToGoogle,
|
||||||
isSyncingOutlook,
|
isSyncingOutlook,
|
||||||
isSyncingGoogle,
|
isSyncingGoogle,
|
||||||
isSyncingApple
|
isSyncingApple,
|
||||||
|
isSyncing: isSyncingApple || isSyncingOutlook || isSyncingGoogle
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,11 +9,12 @@ export const useFetchAndSaveAppleEvents = () => {
|
|||||||
const {mutateAsync: createEventsFromProvider} = useCreateEventsFromProvider();
|
const {mutateAsync: createEventsFromProvider} = useCreateEventsFromProvider();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationKey: ["fetchAndSaveAppleEvents"],
|
mutationKey: ["fetchAndSaveAppleEvents", "sync"],
|
||||||
mutationFn: async ({token, email}: { token?: string, email?: string }) => {
|
mutationFn: async ({token, email, date}: { token?: string; email?: string, date?: Date }) => {
|
||||||
console.log("CALLL")
|
const baseDate = date || new Date();
|
||||||
const timeMin = new Date(new Date().setFullYear(new Date().getFullYear() - 1));
|
const timeMin = new Date(new Date(baseDate).setFullYear(new Date(baseDate).getMonth() - 1));
|
||||||
const timeMax = new Date(new Date().setFullYear(new Date().getFullYear() + 5));
|
const timeMax = new Date(new Date(baseDate).setFullYear(new Date(baseDate).getMonth() + 1));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetchiPhoneCalendarEvents(
|
const response = await fetchiPhoneCalendarEvents(
|
||||||
profileData?.familyId!,
|
profileData?.familyId!,
|
||||||
|
@ -3,41 +3,44 @@ import {fetchGoogleCalendarEvents} from "@/calendar-integration/google-calendar-
|
|||||||
import { useAuthContext } from "@/contexts/AuthContext";
|
import { useAuthContext } from "@/contexts/AuthContext";
|
||||||
import { useCreateEventsFromProvider } from "@/hooks/firebase/useCreateEvent";
|
import { useCreateEventsFromProvider } from "@/hooks/firebase/useCreateEvent";
|
||||||
import { useClearTokens } from "@/hooks/firebase/useClearTokens";
|
import { useClearTokens } from "@/hooks/firebase/useClearTokens";
|
||||||
|
import { useUpdateUserData } from "@/hooks/firebase/useUpdateUserData";
|
||||||
|
|
||||||
export const useFetchAndSaveGoogleEvents = () => {
|
export const useFetchAndSaveGoogleEvents = () => {
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient();
|
||||||
const { profileData } = useAuthContext();
|
const { profileData } = useAuthContext();
|
||||||
const { mutateAsync: createEventsFromProvider } = useCreateEventsFromProvider();
|
const { mutateAsync: createEventsFromProvider } = useCreateEventsFromProvider();
|
||||||
const { mutateAsync: clearToken } = useClearTokens();
|
const { mutateAsync: clearToken } = useClearTokens();
|
||||||
|
const { mutateAsync: updateUserData } = useUpdateUserData();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationKey: ["fetchAndSaveGoogleEvents"],
|
mutationKey: ["fetchAndSaveGoogleEvents", "sync"],
|
||||||
mutationFn: async ({token, email}: { token?: string; email?: string }) => {
|
mutationFn: async ({ token, email, date, refreshToken }: { token?: string; refreshToken?: string; email?: string; date?: Date }) => {
|
||||||
console.log("Fetching Google Calendar events...");
|
const baseDate = date || new Date();
|
||||||
const timeMin = new Date(new Date().setMonth(new Date().getMonth() - 2));
|
const timeMin = new Date(baseDate.setMonth(baseDate.getMonth() - 1)).toISOString().slice(0, -5) + "Z";
|
||||||
const timeMax = new Date(new Date().setMonth(new Date().getMonth() + 2));
|
const timeMax = new Date(baseDate.setMonth(baseDate.getMonth() + 2)).toISOString().slice(0, -5) + "Z";
|
||||||
|
|
||||||
console.log("Token: ", token);
|
console.log("Token: ", token);
|
||||||
|
|
||||||
|
const tryFetchEvents = async (isRetry = false) => {
|
||||||
try {
|
try {
|
||||||
const response = await fetchGoogleCalendarEvents(
|
const response = await fetchGoogleCalendarEvents(
|
||||||
token,
|
token,
|
||||||
email,
|
email,
|
||||||
profileData?.familyId,
|
profileData?.familyId,
|
||||||
timeMin.toISOString().slice(0, -5) + "Z",
|
timeMin,
|
||||||
timeMax.toISOString().slice(0, -5) + "Z"
|
timeMax
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
await clearToken({email: email!, provider: "google"})
|
await clearToken({ email: email!, provider: "google" });
|
||||||
return
|
return; // Stop refetching if clearing the token
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Google Calendar events fetched:", response);
|
console.log("Google Calendar events fetched:", response);
|
||||||
|
|
||||||
const items = response?.googleEvents?.map((item) => {
|
const items = response?.googleEvents?.map((item) => {
|
||||||
if (item.allDay) {
|
if (item.allDay) {
|
||||||
item.startDate = new Date(new Date(item.startDate).setHours(0, 0, 0, 0));
|
item.startDate = new Date(item.startDate.setHours(0, 0, 0, 0));
|
||||||
item.endDate = item.startDate;
|
item.endDate = item.startDate;
|
||||||
}
|
}
|
||||||
return item;
|
return item;
|
||||||
@ -46,11 +49,59 @@ export const useFetchAndSaveGoogleEvents = () => {
|
|||||||
await createEventsFromProvider(items);
|
await createEventsFromProvider(items);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching Google Calendar events:", error);
|
console.error("Error fetching Google Calendar events:", error);
|
||||||
throw error; // Ensure errors are propagated to the mutation
|
|
||||||
|
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: () => {
|
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();
|
const {mutateAsync: createEventsFromProvider} = useCreateEventsFromProvider();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationKey: ["fetchAndSaveOutlookEvents"],
|
mutationKey: ["fetchAndSaveOutlookEvents", "sync"],
|
||||||
mutationFn: async ({token, email}: { token?: string; email?: string }) => {
|
mutationFn: async ({token, email, date}: { token?: string; email?: string, date?: Date }) => {
|
||||||
const timeMin = new Date(new Date().setFullYear(new Date().getFullYear() - 1));
|
const baseDate = date || new Date();
|
||||||
const timeMax = new Date(new Date().setFullYear(new Date().getFullYear() + 3));
|
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);
|
console.log("Token: ", token ?? profileData?.microsoftToken);
|
||||||
|
|
||||||
|
85
hooks/useSyncOnScroll.ts
Normal file
85
hooks/useSyncOnScroll.ts
Normal file
@ -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,
|
||||||
|
};
|
||||||
|
};
|
@ -1,5 +1,5 @@
|
|||||||
diff --git a/node_modules/react-native-big-calendar/build/index.js b/node_modules/react-native-big-calendar/build/index.js
|
diff --git a/node_modules/react-native-big-calendar/build/index.js b/node_modules/react-native-big-calendar/build/index.js
|
||||||
index 848ceba..57fbaed 100644
|
index 848ceba..add1b1c 100644
|
||||||
--- a/node_modules/react-native-big-calendar/build/index.js
|
--- a/node_modules/react-native-big-calendar/build/index.js
|
||||||
+++ b/node_modules/react-native-big-calendar/build/index.js
|
+++ b/node_modules/react-native-big-calendar/build/index.js
|
||||||
@@ -9,6 +9,17 @@ var isoWeek = require('dayjs/plugin/isoWeek');
|
@@ -9,6 +9,17 @@ var isoWeek = require('dayjs/plugin/isoWeek');
|
||||||
|
Reference in New Issue
Block a user