Syncing rework

This commit is contained in:
Milan Paunovic
2024-11-26 21:13:54 +01:00
parent 5cfdc84055
commit f2af60111b
14 changed files with 960 additions and 595 deletions
+131 -28
View File
@@ -1,41 +1,144 @@
import {useMutation, useQueryClient} from "react-query";
import {useAuthContext} from "@/contexts/AuthContext";
import {useCreateEventsFromProvider} from "@/hooks/firebase/useCreateEvent";
import {fetchMicrosoftCalendarEvents} from "@/calendar-integration/microsoft-calendar-utils";
import { useMutation, useQueryClient } from "react-query";
import { useAuthContext } from "@/contexts/AuthContext";
import { useSetUserData } from "@/hooks/firebase/useSetUserData";
import functions from '@react-native-firebase/functions';
import * as AuthSession from 'expo-auth-session';
export const useFetchAndSaveOutlookEvents = () => {
const queryClient = useQueryClient()
const {profileData} = useAuthContext();
const {mutateAsync: createEventsFromProvider} = useCreateEventsFromProvider();
interface SyncResponse {
success: boolean;
eventCount: number;
message?: string;
}
return useMutation({
interface SyncError extends Error {
code?: string;
details?: {
requiresReauth?: boolean;
message?: string;
};
}
const microsoftConfig = {
clientId: "13c79071-1066-40a9-9f71-b8c4b138b4af",
scopes: [
"openid",
"profile",
"email",
"offline_access",
"Calendars.ReadWrite",
"User.Read",
],
redirectUri: AuthSession.makeRedirectUri({path: "settings"})
};
export const useFetchAndSaveMicrosoftEvents = () => {
const queryClient = useQueryClient();
const { profileData } = useAuthContext();
const { mutateAsync: setUserData } = useSetUserData();
const handleReauth = async (email: string) => {
try {
const authRequest = new AuthSession.AuthRequest({
clientId: microsoftConfig.clientId,
scopes: microsoftConfig.scopes,
redirectUri: microsoftConfig.redirectUri,
responseType: AuthSession.ResponseType.Code,
usePKCE: true,
});
const result = await authRequest.promptAsync({
authorizationEndpoint: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
});
if (result.type === 'success' && result.params?.code) {
const tokenResponse = await fetch('https://login.microsoftonline.com/common/oauth2/v2.0/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
client_id: microsoftConfig.clientId,
scope: microsoftConfig.scopes.join(' '),
code: result.params.code,
redirect_uri: microsoftConfig.redirectUri,
grant_type: 'authorization_code',
code_verifier: authRequest.codeVerifier || '',
}),
});
const tokens = await tokenResponse.json();
await setUserData({
newUserData: {
microsoftAccounts: {
...profileData?.microsoftAccounts,
[email]: {
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token,
email,
}
}
}
});
return true;
}
return false;
} catch (error) {
console.error('Microsoft reauth error:', error);
return false;
}
};
return useMutation<SyncResponse, SyncError, { email?: string }>({
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));
mutationFn: async ({ email }: { email?: string }) => {
if (!email) {
throw new Error("Email is required");
}
console.log("Token: ", token ?? profileData?.microsoftToken);
if (!profileData?.microsoftAccounts?.[email]) {
throw new Error("No valid Microsoft account found");
}
try {
const response = await fetchMicrosoftCalendarEvents(
token ?? profileData?.microsoftToken,
email ?? profileData?.outlookMail,
profileData?.familyId,
timeMin.toISOString().slice(0, -5) + "Z",
timeMax.toISOString().slice(0, -5) + "Z"
);
const response = await functions()
.httpsCallable('triggerMicrosoftSync')({ email });
return response.data as SyncResponse;
} catch (error: any) {
console.error("Microsoft sync error:", error);
// Check if we need to reauthenticate
if (error.details?.requiresReauth ||
error.code === 'functions/failed-precondition' ||
error.code === 'functions/unauthenticated') {
console.log('Attempting Microsoft reauth...');
const reauthSuccessful = await handleReauth(email);
if (reauthSuccessful) {
// Retry the sync with new tokens
console.log('Retrying sync after reauth...');
const retryResponse = await functions()
.httpsCallable('triggerMicrosoftSync')({ email });
return retryResponse.data as SyncResponse;
}
}
console.log(response);
const items = response ?? [];
await createEventsFromProvider(items);
} catch (error) {
console.error("Error fetching and saving Outlook events: ", error);
throw error;
}
},
onSuccess: () => {
queryClient.invalidateQueries(["events"])
onSuccess: (data) => {
queryClient.invalidateQueries(["events"]);
console.log(`Successfully synced ${data.eventCount} Microsoft events`);
},
onError: (error) => {
console.error('Microsoft sync failed:', {
message: error.message,
code: error.code,
details: error.details
});
}
});
};