mirror of
https://github.com/urosran/cally.git
synced 2025-07-10 15:17:17 +00:00
152 lines
5.7 KiB
TypeScript
152 lines
5.7 KiB
TypeScript
import { useState, useEffect, useCallback } from "react";
|
|
import { useAuthContext } from "@/contexts/AuthContext";
|
|
import { useAtomValue } from "jotai";
|
|
import { useFetchAndSaveGoogleEvents } from "./useFetchAndSaveGoogleEvents";
|
|
import { useFetchAndSaveAppleEvents } from "./useFetchAndSaveAppleEvents";
|
|
import { useFetchAndSaveMicrosoftEvents } from "./useFetchAndSaveOutlookEvents";
|
|
import { selectedDateAtom } from "@/components/pages/calendar/atoms";
|
|
import { addDays, subDays, isBefore, isAfter, format } from "date-fns";
|
|
|
|
interface SyncResponse {
|
|
success: boolean;
|
|
eventCount?: number;
|
|
error?: string;
|
|
}
|
|
|
|
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<Error | null>(null);
|
|
const [syncStats, setSyncStats] = useState<{
|
|
total: number;
|
|
success: number;
|
|
failed: number;
|
|
events: number;
|
|
}>({ total: 0, success: 0, failed: 0, events: 0 });
|
|
|
|
const syncedRanges = useState<Set<string>>(new Set())[0];
|
|
|
|
const { mutateAsync: fetchAndSaveGoogleEvents } = useFetchAndSaveGoogleEvents();
|
|
const { mutateAsync: fetchAndSaveOutlookEvents } = useFetchAndSaveMicrosoftEvents();
|
|
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)) {
|
|
const results: SyncResponse[] = [];
|
|
const stats = { total: 0, success: 0, failed: 0, events: 0 };
|
|
|
|
try {
|
|
if (profileData?.googleAccounts) {
|
|
for (const [email] of Object.entries(profileData.googleAccounts)) {
|
|
try {
|
|
stats.total++;
|
|
const result = await fetchAndSaveGoogleEvents({ email }) as SyncResponse;
|
|
if (result.success) {
|
|
stats.success++;
|
|
stats.events += result.eventCount || 0;
|
|
} else {
|
|
stats.failed++;
|
|
}
|
|
results.push(result);
|
|
} catch (err) {
|
|
stats.failed++;
|
|
console.error(`Failed to sync Google calendar for ${email}:`, err);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (profileData?.microsoftAccounts) {
|
|
for (const [email] of Object.entries(profileData.microsoftAccounts)) {
|
|
try {
|
|
stats.total++;
|
|
const result = await fetchAndSaveOutlookEvents({ email });
|
|
if (result.success) {
|
|
stats.success++;
|
|
stats.events += result.eventCount || 0;
|
|
} else {
|
|
stats.failed++;
|
|
}
|
|
results.push(result);
|
|
} catch (err) {
|
|
stats.failed++;
|
|
console.error(`Failed to sync Microsoft calendar for ${email}:`, err);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (profileData?.appleAccounts) {
|
|
for (const [email] of Object.entries(profileData.appleAccounts)) {
|
|
try {
|
|
stats.total++;
|
|
const result = await fetchAndSaveAppleEvents({ email });
|
|
} catch (err) {
|
|
stats.failed++;
|
|
console.error(`Failed to sync Apple calendar for ${email}:`, err);
|
|
}
|
|
}
|
|
}
|
|
|
|
setSyncStats(stats);
|
|
setLastSyncDate(selectedDate);
|
|
setLowerBoundDate(newLowerBound);
|
|
setUpperBoundDate(newUpperBound);
|
|
syncedRanges.add(rangeKey);
|
|
|
|
if (stats.failed > 0) {
|
|
throw new Error(`Failed to sync ${stats.failed} calendars`);
|
|
}
|
|
|
|
} catch (err: any) {
|
|
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,
|
|
syncStats,
|
|
};
|
|
}; |