import {AntDesign, Ionicons} from "@expo/vector-icons"; import React, {useCallback, useEffect, useState} from "react"; import {Button, Checkbox, Text, View} from "react-native-ui-lib"; import {ActivityIndicator, ScrollView, StyleSheet} from "react-native"; import {TouchableOpacity} from "react-native-gesture-handler"; import {useAuthContext} from "@/contexts/AuthContext"; import {useUpdateUserData} from "@/hooks/firebase/useUpdateUserData"; import debounce from "debounce"; import AppleIcon from "@/assets/svgs/AppleIcon"; import GoogleIcon from "@/assets/svgs/GoogleIcon"; import OutlookIcon from "@/assets/svgs/OutlookIcon"; import * as AuthSession from "expo-auth-session"; import * as Google from "expo-auth-session/providers/google"; import * as WebBrowser from "expo-web-browser"; import {UserProfile} from "@firebase/auth"; import {useFetchAndSaveGoogleEvents} from "@/hooks/useFetchAndSaveGoogleEvents"; import {useFetchAndSaveOutlookEvents} from "@/hooks/useFetchAndSaveOutlookEvents"; import {useFetchAndSaveAppleEvents} from "@/hooks/useFetchAndSaveAppleEvents"; import * as AppleAuthentication from 'expo-apple-authentication'; import ExpoLocalization from "expo-localization/src/ExpoLocalization"; import {colorMap} from "@/constants/colorMap"; 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", ], }; 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", }; const CalendarSettingsPage = (props: { setSelectedPage: (page: number) => void; }) => { const {profileData} = useAuthContext(); const [firstDayOfWeek, setFirstDayOfWeek] = useState(profileData?.firstDayOfWeek ?? ExpoLocalization.getCalendars()[0].firstWeekday === 1 ? "Mondays" : "Sundays"); const [selectedColor, setSelectedColor] = useState( profileData?.eventColor ?? colorMap.pink ); const [previousSelectedColor, setPreviousSelectedColor] = useState( profileData?.eventColor ?? colorMap.pink ); 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 = response.authentication?.accessToken; const userInfoResponse = await fetch( "https://www.googleapis.com/oauth2/v3/userinfo", { headers: {Authorization: `Bearer ${accessToken}`}, } ); const userInfo = await userInfoResponse.json(); const googleMail = userInfo.email; await updateUserData({ newUserData: {googleToken: accessToken, googleMail: googleMail}, }); 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; // Update user data with Microsoft token and email await updateUserData({ newUserData: {microsoftToken: tokenData.access_token, outlookMail: outlookMail}, }); 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); const appleToken = credential.identityToken; const appleMail = credential.email; if (appleToken) { console.log("Apple ID token received. Fetch user info if needed..."); await updateUserData({ newUserData: {appleToken, appleMail}, }); 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); } }; const debouncedUpdateUserData = useCallback( debounce(async (color: string) => { try { await updateUserData({ newUserData: { eventColor: color, }, }); } catch (error) { console.error("Failed to update color:", error); setSelectedColor(previousSelectedColor); } }, 500), [] ); const debouncedUpdateFirstDayOfWeek = useCallback( debounce(async (firstDayOfWeek: string) => { try { await updateUserData({ newUserData: { firstDayOfWeek, }, }); } catch (error) { console.error("Failed to update first day of week:", error); } }, 500), [] ); const handleChangeFirstDayOfWeek = (firstDayOfWeek: string) => { setFirstDayOfWeek(firstDayOfWeek === "Sundays" ? "Mondays" : "Sundays"); debouncedUpdateFirstDayOfWeek(firstDayOfWeek === "Sundays" ? "Mondays" : "Sundays"); } const handleChangeColor = (color: string) => { setPreviousSelectedColor(selectedColor); setSelectedColor(color); debouncedUpdateUserData(color); }; const clearToken = async (provider: "google" | "outlook" | "apple") => { const newUserData: Partial = {}; if (provider === "google") { newUserData.googleToken = null; newUserData.googleMail = null; } else if (provider === "outlook") { newUserData.microsoftToken = null; newUserData.outlookMail = null; } else if (provider === "apple") { newUserData.appleToken = null; newUserData.appleMail = null; } await updateUserData({newUserData}); }; return ( props.setSelectedPage(0)}> Return to main settings Calendar settings Event Color Preference handleChangeColor(colorMap.pink)}> {selectedColor == colorMap.pink && ( )} handleChangeColor(colorMap.orange)} > {selectedColor == colorMap.orange && ( )} handleChangeColor(colorMap.green)}> {selectedColor == colorMap.green && ( )} handleChangeColor(colorMap.teal)}> {selectedColor == colorMap.teal && ( )} handleChangeColor(colorMap.purple)} > {selectedColor == colorMap.purple && ( )} Weekly Start Date handleChangeFirstDayOfWeek("Sundays")} /> Sundays {" "} (default) handleChangeFirstDayOfWeek("Mondays")} /> Mondays Add Calendar