mirror of
https://github.com/urosran/cally.git
synced 2025-11-26 16:34:54 +00:00
New onboarding flow, calendar sync logic refactor
This commit is contained in:
288
hooks/useCalSync.ts
Normal file
288
hooks/useCalSync.ts
Normal file
@ -0,0 +1,288 @@
|
||||
import {useAuthContext} from "@/contexts/AuthContext";
|
||||
import {useEffect} from "react";
|
||||
import {useUpdateUserData} from "@/hooks/firebase/useUpdateUserData";
|
||||
import {useFetchAndSaveGoogleEvents} from "@/hooks/useFetchAndSaveGoogleEvents";
|
||||
import {useFetchAndSaveOutlookEvents} from "@/hooks/useFetchAndSaveOutlookEvents";
|
||||
import {useFetchAndSaveAppleEvents} from "@/hooks/useFetchAndSaveAppleEvents";
|
||||
import * as WebBrowser from "expo-web-browser";
|
||||
import * as Google from "expo-auth-session/providers/google";
|
||||
import * as AuthSession from "expo-auth-session";
|
||||
import * as AppleAuthentication from "expo-apple-authentication";
|
||||
|
||||
const googleConfig = {
|
||||
androidClientId:
|
||||
"406146460310-2u67ab2nbhu23trp8auho1fq4om29fc0.apps.googleusercontent.com",
|
||||
iosClientId:
|
||||
"406146460310-2u67ab2nbhu23trp8auho1fq4om29fc0.apps.googleusercontent.com",
|
||||
webClientId:
|
||||
"406146460310-2u67ab2nbhu23trp8auho1fq4om29fc0.apps.googleusercontent.com",
|
||||
scopes: [
|
||||
"email",
|
||||
"profile",
|
||||
"https://www.googleapis.com/auth/calendar.events.owned",
|
||||
],
|
||||
extraParams: {
|
||||
access_type: "offline",
|
||||
},
|
||||
};
|
||||
|
||||
const microsoftConfig = {
|
||||
clientId: "13c79071-1066-40a9-9f71-b8c4b138b4af",
|
||||
redirectUri: AuthSession.makeRedirectUri({path: "settings"}),
|
||||
scopes: [
|
||||
"openid",
|
||||
"profile",
|
||||
"email",
|
||||
"offline_access",
|
||||
"Calendars.ReadWrite",
|
||||
"User.Read",
|
||||
],
|
||||
authorizationEndpoint:
|
||||
"https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
|
||||
tokenEndpoint: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
WebBrowser.maybeCompleteAuthSession();
|
||||
const [_, response, promptAsync] = Google.useAuthRequest(googleConfig);
|
||||
|
||||
useEffect(() => {
|
||||
signInWithGoogle();
|
||||
}, [response]);
|
||||
|
||||
const signInWithGoogle = async () => {
|
||||
try {
|
||||
if (response?.type === "success") {
|
||||
const {accessToken, refreshToken} = response?.authentication!;
|
||||
|
||||
const userInfoResponse = await fetch(
|
||||
"https://www.googleapis.com/oauth2/v3/userinfo",
|
||||
{
|
||||
headers: {Authorization: `Bearer ${accessToken}`},
|
||||
}
|
||||
);
|
||||
|
||||
const userInfo = await userInfoResponse.json();
|
||||
const googleMail = userInfo.email;
|
||||
|
||||
let googleAccounts = profileData?.googleAccounts || {};
|
||||
const updatedGoogleAccounts = {
|
||||
...googleAccounts,
|
||||
[googleMail]: {accessToken, refreshToken},
|
||||
};
|
||||
|
||||
await updateUserData({
|
||||
newUserData: {googleAccounts: updatedGoogleAccounts},
|
||||
});
|
||||
|
||||
await fetchAndSaveGoogleEvents({
|
||||
token: accessToken,
|
||||
email: googleMail,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error during Google sign-in:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMicrosoftSignIn = async () => {
|
||||
try {
|
||||
console.log("Starting Microsoft sign-in...");
|
||||
|
||||
const authRequest = new AuthSession.AuthRequest({
|
||||
clientId: microsoftConfig.clientId,
|
||||
scopes: microsoftConfig.scopes,
|
||||
redirectUri: microsoftConfig.redirectUri,
|
||||
responseType: AuthSession.ResponseType.Code,
|
||||
usePKCE: true, // Enable PKCE
|
||||
});
|
||||
|
||||
console.log("Auth request created:", authRequest);
|
||||
|
||||
const authResult = await authRequest.promptAsync({
|
||||
authorizationEndpoint: microsoftConfig.authorizationEndpoint,
|
||||
});
|
||||
|
||||
console.log("Auth result:", authResult);
|
||||
|
||||
if (authResult.type === "success" && authResult.params?.code) {
|
||||
const code = authResult.params.code;
|
||||
console.log("Authorization code received:", code);
|
||||
|
||||
// Exchange authorization code for tokens
|
||||
const tokenResponse = await fetch(microsoftConfig.tokenEndpoint, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
body: `client_id=${
|
||||
microsoftConfig.clientId
|
||||
}&redirect_uri=${encodeURIComponent(
|
||||
microsoftConfig.redirectUri
|
||||
)}&grant_type=authorization_code&code=${code}&code_verifier=${
|
||||
authRequest.codeVerifier
|
||||
}&scope=${encodeURIComponent(
|
||||
"https://graph.microsoft.com/Calendars.ReadWrite offline_access User.Read"
|
||||
)}`,
|
||||
});
|
||||
|
||||
console.log("Token response status:", tokenResponse.status);
|
||||
|
||||
if (!tokenResponse.ok) {
|
||||
const errorText = await tokenResponse.text();
|
||||
console.error("Token exchange failed:", errorText);
|
||||
return;
|
||||
}
|
||||
|
||||
const tokenData = await tokenResponse.json();
|
||||
console.log("Token data received:", tokenData);
|
||||
|
||||
if (tokenData?.access_token) {
|
||||
console.log("Access token received, fetching user info...");
|
||||
|
||||
// Fetch user info from Microsoft Graph API to get the email
|
||||
const userInfoResponse = await fetch(
|
||||
"https://graph.microsoft.com/v1.0/me",
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokenData.access_token}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const userInfo = await userInfoResponse.json();
|
||||
console.log("User info received:", userInfo);
|
||||
|
||||
if (userInfo.error) {
|
||||
console.error("Error fetching user info:", userInfo.error);
|
||||
} else {
|
||||
const outlookMail = userInfo.mail || userInfo.userPrincipalName;
|
||||
|
||||
let microsoftAccounts = profileData?.microsoftAccounts;
|
||||
const updatedMicrosoftAccounts = microsoftAccounts
|
||||
? {...microsoftAccounts, [outlookMail]: tokenData.access_token}
|
||||
: {[outlookMail]: tokenData.access_token};
|
||||
|
||||
await updateUserData({
|
||||
newUserData: {microsoftAccounts: updatedMicrosoftAccounts},
|
||||
});
|
||||
|
||||
await fetchAndSaveOutlookEvents(
|
||||
tokenData.access_token,
|
||||
outlookMail
|
||||
);
|
||||
console.log("User data updated successfully.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.warn("Authentication was not successful:", authResult);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error during Microsoft sign-in:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleAppleSignIn = async () => {
|
||||
try {
|
||||
console.log("Starting Apple Sign-in...");
|
||||
|
||||
const credential = await AppleAuthentication.signInAsync({
|
||||
requestedScopes: [
|
||||
AppleAuthentication.AppleAuthenticationScope.EMAIL,
|
||||
AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
|
||||
],
|
||||
});
|
||||
|
||||
console.log("Apple sign-in result:", credential);
|
||||
|
||||
alert(JSON.stringify(credential))
|
||||
|
||||
const appleToken = credential.identityToken;
|
||||
const appleMail = credential.email!;
|
||||
|
||||
|
||||
if (appleToken) {
|
||||
console.log("Apple ID token received. Fetch user info if needed...");
|
||||
|
||||
let appleAcounts = profileData?.appleAccounts;
|
||||
const updatedAppleAccounts = appleAcounts
|
||||
? {...appleAcounts, [appleMail]: appleToken}
|
||||
: {[appleMail]: appleToken};
|
||||
|
||||
await updateUserData({
|
||||
newUserData: {appleAccounts: updatedAppleAccounts},
|
||||
});
|
||||
|
||||
console.log("User data updated with Apple ID token.");
|
||||
await fetchAndSaveAppleEvents({token: appleToken, email: appleMail!});
|
||||
} else {
|
||||
console.warn(
|
||||
"Apple authentication was not successful or email was hidden."
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error during Apple Sign-in:", error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
let isConnectedToGoogle = false;
|
||||
if (profileData?.googleAccounts) {
|
||||
Object.values(profileData?.googleAccounts).forEach((item) => {
|
||||
if (item !== null) {
|
||||
isConnectedToGoogle = true;
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let isConnectedToMicrosoft = false;
|
||||
const microsoftAccounts = profileData?.microsoftAccounts;
|
||||
if (microsoftAccounts) {
|
||||
Object.values(profileData?.microsoftAccounts).forEach((item) => {
|
||||
if (item !== null) {
|
||||
isConnectedToMicrosoft = true;
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let isConnectedToApple = false;
|
||||
if (profileData?.appleAccounts) {
|
||||
Object.values(profileData?.appleAccounts).forEach((item) => {
|
||||
if (item !== null) {
|
||||
isConnectedToApple = true;
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
handleAppleSignIn,
|
||||
handleMicrosoftSignIn,
|
||||
handleGoogleSignIn: signInWithGoogle,
|
||||
handleStartGoogleSignIn: promptAsync,
|
||||
fetchAndSaveOutlookEvents,
|
||||
fetchAndSaveAppleEvents,
|
||||
fetchAndSaveGoogleEvents,
|
||||
isConnectedToApple,
|
||||
isConnectedToMicrosoft,
|
||||
isConnectedToGoogle,
|
||||
isSyncingOutlook,
|
||||
isSyncingGoogle,
|
||||
isSyncingApple
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user