mirror of
https://github.com/urosran/cally.git
synced 2025-07-10 07:07:16 +00:00
Small fixes, calendar token refresh for google
This commit is contained in:
@ -2,8 +2,9 @@ import * as Calendar from 'expo-calendar';
|
||||
|
||||
export async function fetchiPhoneCalendarEvents(familyId, email, startDate, endDate) {
|
||||
try {
|
||||
const {status} = await Calendar.requestCalendarPermissionsAsync();
|
||||
if (status !== 'granted') {
|
||||
const {granted} = await Calendar.requestCalendarPermissionsAsync();
|
||||
|
||||
if (!granted) {
|
||||
throw new Error("Calendar permission not granted");
|
||||
}
|
||||
|
||||
@ -22,7 +23,11 @@ export async function fetchiPhoneCalendarEvents(familyId, email, startDate, endD
|
||||
return events.map((event) => {
|
||||
let isAllDay = event.allDay || false;
|
||||
const startDateTime = new Date(event.startDate);
|
||||
const endDateTime = new Date(event.endDate);
|
||||
let endDateTime = new Date(event.endDate);
|
||||
|
||||
if (isAllDay) {
|
||||
endDateTime = startDateTime
|
||||
}
|
||||
|
||||
return {
|
||||
id: event.id,
|
||||
|
@ -8,7 +8,9 @@ export async function fetchGoogleCalendarEvents(token, email, familyId, startDat
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
const googleEvents = [];
|
||||
data.items?.forEach((item) => {
|
||||
let isAllDay = false;
|
||||
@ -49,5 +51,5 @@ export async function fetchGoogleCalendarEvents(token, email, familyId, startDat
|
||||
googleEvents.push(googleEvent);
|
||||
});
|
||||
|
||||
return googleEvents;
|
||||
return {googleEvents, success: response.ok};
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
import {StyleSheet} from "react-native";
|
||||
import {Dimensions, StyleSheet} from "react-native";
|
||||
import React from "react";
|
||||
import {Button, View,} from "react-native-ui-lib";
|
||||
import {useGroceryContext} from "@/contexts/GroceryContext";
|
||||
import {FontAwesome6} from "@expo/vector-icons";
|
||||
import PlusIcon from "@/assets/svgs/PlusIcon";
|
||||
|
||||
const { width } = Dimensions.get("screen");
|
||||
|
||||
const AddGroceryItem = () => {
|
||||
const {setIsAddingGrocery} = useGroceryContext();
|
||||
|
||||
@ -65,8 +67,14 @@ const styles = StyleSheet.create({
|
||||
marginVertical: 10,
|
||||
},
|
||||
btnContainer: {
|
||||
width: "100%",
|
||||
position:"absolute",
|
||||
bottom: 30,
|
||||
width: width,
|
||||
padding: 20,
|
||||
paddingBottom: 0,
|
||||
justifyContent: "center",
|
||||
alignItems:"center",
|
||||
zIndex: 10,
|
||||
},
|
||||
finishShopBtn: {
|
||||
width: "100%",
|
||||
|
@ -1,218 +1,214 @@
|
||||
import {
|
||||
Button,
|
||||
ButtonSize,
|
||||
Dialog,
|
||||
Text,
|
||||
TextField,
|
||||
TextFieldRef,
|
||||
View,
|
||||
} from "react-native-ui-lib";
|
||||
import React, { useRef, useState } from "react";
|
||||
import { useSignIn } from "@/hooks/firebase/useSignIn";
|
||||
import { StyleSheet } from "react-native";
|
||||
import {Button, ButtonSize, Dialog, Text, TextField, TextFieldRef, View,} from "react-native-ui-lib";
|
||||
import React, {useRef, useState} from "react";
|
||||
import {useSignIn} from "@/hooks/firebase/useSignIn";
|
||||
import {StyleSheet} from "react-native";
|
||||
import Toast from "react-native-toast-message";
|
||||
import { useLoginWithQrCode } from "@/hooks/firebase/useLoginWithQrCode";
|
||||
import { Camera, CameraView } from "expo-camera";
|
||||
import {useLoginWithQrCode} from "@/hooks/firebase/useLoginWithQrCode";
|
||||
import {Camera, CameraView} from "expo-camera";
|
||||
|
||||
const SignInPage = ({
|
||||
setTab,
|
||||
}: {
|
||||
setTab: React.Dispatch<
|
||||
React.SetStateAction<"register" | "login" | "reset-password">
|
||||
>;
|
||||
setTab,
|
||||
}: {
|
||||
setTab: React.Dispatch<
|
||||
React.SetStateAction<"register" | "login" | "reset-password">
|
||||
>;
|
||||
}) => {
|
||||
const [email, setEmail] = useState<string>("");
|
||||
const [password, setPassword] = useState<string>("");
|
||||
const [hasPermission, setHasPermission] = useState<boolean | null>(null);
|
||||
const [showCameraDialog, setShowCameraDialog] = useState<boolean>(false);
|
||||
const passwordRef = useRef<TextFieldRef>(null);
|
||||
const [email, setEmail] = useState<string>("");
|
||||
const [password, setPassword] = useState<string>("");
|
||||
const [hasPermission, setHasPermission] = useState<boolean | null>(null);
|
||||
const [showCameraDialog, setShowCameraDialog] = useState<boolean>(false);
|
||||
const passwordRef = useRef<TextFieldRef>(null);
|
||||
|
||||
const { mutateAsync: signIn, error, isError } = useSignIn();
|
||||
const { mutateAsync: signInWithQrCode } = useLoginWithQrCode();
|
||||
const {mutateAsync: signIn, error, isError} = useSignIn();
|
||||
const {mutateAsync: signInWithQrCode} = useLoginWithQrCode();
|
||||
|
||||
const handleSignIn = async () => {
|
||||
await signIn({ email, password });
|
||||
if (!isError) {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Login successful!",
|
||||
});
|
||||
} else {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Error logging in",
|
||||
text2: `${error}`,
|
||||
});
|
||||
}
|
||||
};
|
||||
const handleSignIn = async () => {
|
||||
await signIn({email, password});
|
||||
if (!isError) {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Login successful!",
|
||||
});
|
||||
} else {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Error logging in",
|
||||
text2: `${error}`,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleQrCodeScanned = async ({ data }: { data: string }) => {
|
||||
setShowCameraDialog(false);
|
||||
try {
|
||||
await signInWithQrCode({ userId: data });
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Login successful with QR code!",
|
||||
});
|
||||
} catch (err) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Error logging in with QR code",
|
||||
text2: `${err}`,
|
||||
});
|
||||
}
|
||||
};
|
||||
const handleQrCodeScanned = async ({data}: { data: string }) => {
|
||||
setShowCameraDialog(false);
|
||||
try {
|
||||
await signInWithQrCode({userId: data});
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Login successful with QR code!",
|
||||
});
|
||||
} catch (err) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Error logging in with QR code",
|
||||
text2: `${err}`,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const getCameraPermissions = async (callback: () => void) => {
|
||||
const { status } = await Camera.requestCameraPermissionsAsync();
|
||||
setHasPermission(status === "granted");
|
||||
if (status === "granted") {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
const getCameraPermissions = async (callback: () => void) => {
|
||||
const {status} = await Camera.requestCameraPermissionsAsync();
|
||||
setHasPermission(status === "granted");
|
||||
if (status === "granted") {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View padding-10 centerV height={"100%"}>
|
||||
<TextField
|
||||
placeholder="Email"
|
||||
value={email}
|
||||
|
||||
onChangeText={setEmail}
|
||||
style={styles.textfield}
|
||||
onSubmitEditing={() => {
|
||||
// Move focus to the description field
|
||||
passwordRef.current?.focus();
|
||||
}}
|
||||
/>
|
||||
<TextField
|
||||
ref={passwordRef}
|
||||
placeholder="Password"
|
||||
value={password}
|
||||
onChangeText={setPassword}
|
||||
secureTextEntry
|
||||
style={styles.textfield}
|
||||
/>
|
||||
<Button
|
||||
label="Log in"
|
||||
marginT-50
|
||||
labelStyle={{
|
||||
fontFamily: "PlusJakartaSans_600SemiBold",
|
||||
fontSize: 16,
|
||||
}}
|
||||
onPress={handleSignIn}
|
||||
style={{ marginBottom: 20, height: 50 }}
|
||||
backgroundColor="#fd1775"
|
||||
/>
|
||||
<Button
|
||||
label="Log in with a QR Code"
|
||||
labelStyle={{
|
||||
fontFamily: "PlusJakartaSans_600SemiBold",
|
||||
fontSize: 16,
|
||||
}}
|
||||
onPress={() => {
|
||||
getCameraPermissions(() => setShowCameraDialog(true));
|
||||
}}
|
||||
style={{ marginBottom: 20, height: 50 }}
|
||||
backgroundColor="#fd1775"
|
||||
/>
|
||||
{isError && (
|
||||
<Text center style={{ marginBottom: 20 }}>{`${
|
||||
error?.toString()?.split("]")?.[1]
|
||||
}`}</Text>
|
||||
)}
|
||||
return (
|
||||
<View padding-10 centerV height={"100%"}>
|
||||
<TextField
|
||||
placeholder="Email"
|
||||
keyboardType={"email-address"}
|
||||
returnKeyType={"next"}
|
||||
textContentType={"emailAddress"}
|
||||
defaultValue={email}
|
||||
onChangeText={setEmail}
|
||||
style={styles.textfield}
|
||||
onSubmitEditing={() => {
|
||||
// Move focus to the description field
|
||||
passwordRef.current?.focus();
|
||||
}}
|
||||
/>
|
||||
<TextField
|
||||
ref={passwordRef}
|
||||
placeholder="Password"
|
||||
textContentType={"oneTimeCode"}
|
||||
value={password}
|
||||
onChangeText={setPassword}
|
||||
secureTextEntry
|
||||
style={styles.textfield}
|
||||
autoCorrect={false}
|
||||
/>
|
||||
<Button
|
||||
label="Log in"
|
||||
marginT-50
|
||||
labelStyle={{
|
||||
fontFamily: "PlusJakartaSans_600SemiBold",
|
||||
fontSize: 16,
|
||||
}}
|
||||
onPress={handleSignIn}
|
||||
style={{marginBottom: 20, height: 50}}
|
||||
backgroundColor="#fd1775"
|
||||
/>
|
||||
<Button
|
||||
label="Log in with a QR Code"
|
||||
labelStyle={{
|
||||
fontFamily: "PlusJakartaSans_600SemiBold",
|
||||
fontSize: 16,
|
||||
}}
|
||||
onPress={() => {
|
||||
getCameraPermissions(() => setShowCameraDialog(true));
|
||||
}}
|
||||
style={{marginBottom: 20, height: 50}}
|
||||
backgroundColor="#fd1775"
|
||||
/>
|
||||
{isError && (
|
||||
<Text center style={{marginBottom: 20}}>{`${
|
||||
error?.toString()?.split("]")?.[1]
|
||||
}`}</Text>
|
||||
)}
|
||||
|
||||
<View row centerH marginB-5 gap-5>
|
||||
<Text style={styles.jakartaLight}>Don't have an account?</Text>
|
||||
<Button
|
||||
onPress={() => setTab("register")}
|
||||
label="Sign Up"
|
||||
labelStyle={[
|
||||
styles.jakartaMedium,
|
||||
{ textDecorationLine: "none", color: "#fd1575" },
|
||||
]}
|
||||
link
|
||||
size={ButtonSize.xSmall}
|
||||
padding-0
|
||||
margin-0
|
||||
text70
|
||||
left
|
||||
color="#fd1775"
|
||||
/>
|
||||
</View>
|
||||
<View row centerH marginB-5 gap-5>
|
||||
<Text style={styles.jakartaLight}>Don't have an account?</Text>
|
||||
<Button
|
||||
onPress={() => setTab("register")}
|
||||
label="Sign Up"
|
||||
labelStyle={[
|
||||
styles.jakartaMedium,
|
||||
{textDecorationLine: "none", color: "#fd1575"},
|
||||
]}
|
||||
link
|
||||
size={ButtonSize.xSmall}
|
||||
padding-0
|
||||
margin-0
|
||||
text70
|
||||
left
|
||||
color="#fd1775"
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View row centerH marginB-5 gap-5>
|
||||
<Text text70>Forgot your password?</Text>
|
||||
<Button
|
||||
onPress={() => setTab("reset-password")}
|
||||
label="Reset password"
|
||||
labelStyle={[
|
||||
styles.jakartaMedium,
|
||||
{ textDecorationLine: "none", color: "#fd1575" },
|
||||
]}
|
||||
link
|
||||
size={ButtonSize.xSmall}
|
||||
padding-0
|
||||
margin-0
|
||||
text70
|
||||
left
|
||||
avoidInnerPadding
|
||||
color="#fd1775"
|
||||
/>
|
||||
</View>
|
||||
<View row centerH marginB-5 gap-5>
|
||||
<Text text70>Forgot your password?</Text>
|
||||
<Button
|
||||
onPress={() => setTab("reset-password")}
|
||||
label="Reset password"
|
||||
labelStyle={[
|
||||
styles.jakartaMedium,
|
||||
{textDecorationLine: "none", color: "#fd1575"},
|
||||
]}
|
||||
link
|
||||
size={ButtonSize.xSmall}
|
||||
padding-0
|
||||
margin-0
|
||||
text70
|
||||
left
|
||||
avoidInnerPadding
|
||||
color="#fd1775"
|
||||
/>
|
||||
</View>
|
||||
|
||||
{/* Camera Dialog */}
|
||||
<Dialog
|
||||
visible={showCameraDialog}
|
||||
onDismiss={() => setShowCameraDialog(false)}
|
||||
bottom
|
||||
width="100%"
|
||||
height="70%"
|
||||
containerStyle={{ padding: 15, backgroundColor:"white" }}
|
||||
>
|
||||
{hasPermission === null ? (
|
||||
<Text>Requesting camera permissions...</Text>
|
||||
) : !hasPermission ? (
|
||||
<Text>No access to camera</Text>
|
||||
) : (
|
||||
<CameraView
|
||||
style={{ flex: 1, borderRadius: 15 }}
|
||||
onBarcodeScanned={handleQrCodeScanned}
|
||||
barcodeScannerSettings={{
|
||||
barcodeTypes: ["qr"],
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
label="Cancel"
|
||||
onPress={() => setShowCameraDialog(false)}
|
||||
backgroundColor="#fd1775"
|
||||
style={{ margin: 10, marginBottom: 30 }}
|
||||
/>
|
||||
</Dialog>
|
||||
</View>
|
||||
);
|
||||
{/* Camera Dialog */}
|
||||
<Dialog
|
||||
visible={showCameraDialog}
|
||||
onDismiss={() => setShowCameraDialog(false)}
|
||||
bottom
|
||||
width="100%"
|
||||
height="70%"
|
||||
containerStyle={{padding: 15, backgroundColor: "white"}}
|
||||
>
|
||||
{hasPermission === null ? (
|
||||
<Text>Requesting camera permissions...</Text>
|
||||
) : !hasPermission ? (
|
||||
<Text>No access to camera</Text>
|
||||
) : (
|
||||
<CameraView
|
||||
style={{flex: 1, borderRadius: 15}}
|
||||
onBarcodeScanned={handleQrCodeScanned}
|
||||
barcodeScannerSettings={{
|
||||
barcodeTypes: ["qr"],
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
label="Cancel"
|
||||
onPress={() => setShowCameraDialog(false)}
|
||||
backgroundColor="#fd1775"
|
||||
style={{margin: 10, marginBottom: 30}}
|
||||
/>
|
||||
</Dialog>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
textfield: {
|
||||
backgroundColor: "white",
|
||||
marginVertical: 10,
|
||||
padding: 30,
|
||||
height: 45,
|
||||
borderRadius: 50,
|
||||
fontFamily: "PlusJakartaSans_300Light",
|
||||
},
|
||||
jakartaLight: {
|
||||
fontFamily: "PlusJakartaSans_300Light",
|
||||
fontSize: 16,
|
||||
color: "#484848",
|
||||
},
|
||||
jakartaMedium: {
|
||||
fontFamily: "PlusJakartaSans_500Medium",
|
||||
fontSize: 16,
|
||||
color: "#919191",
|
||||
textDecorationLine: "underline",
|
||||
},
|
||||
textfield: {
|
||||
backgroundColor: "white",
|
||||
marginVertical: 10,
|
||||
padding: 30,
|
||||
height: 45,
|
||||
borderRadius: 50,
|
||||
fontFamily: "PlusJakartaSans_300Light",
|
||||
},
|
||||
jakartaLight: {
|
||||
fontFamily: "PlusJakartaSans_300Light",
|
||||
fontSize: 16,
|
||||
color: "#484848",
|
||||
},
|
||||
jakartaMedium: {
|
||||
fontFamily: "PlusJakartaSans_500Medium",
|
||||
fontSize: 16,
|
||||
color: "#919191",
|
||||
textDecorationLine: "underline",
|
||||
},
|
||||
});
|
||||
|
||||
export default SignInPage;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -181,7 +181,7 @@ exports.generateCustomToken = onRequest(async (request, response) => {
|
||||
}
|
||||
});
|
||||
|
||||
exports.refreshTokens = functions.pubsub.schedule('every 12 hours').onRun(async (context) => {
|
||||
exports.refreshTokens = functions.pubsub.schedule('every 1 hours').onRun(async (context) => {
|
||||
console.log('Running token refresh job...');
|
||||
|
||||
const profilesSnapshot = await db.collection('Profiles').get();
|
||||
@ -192,7 +192,7 @@ exports.refreshTokens = functions.pubsub.schedule('every 12 hours').onRun(async
|
||||
if (profileData.googleAccounts) {
|
||||
try {
|
||||
for (const googleEmail of Object.keys(profileData?.googleAccounts)) {
|
||||
const googleToken = profileData?.googleAccounts?.[googleEmail];
|
||||
const googleToken = profileData?.googleAccounts?.[googleEmail]?.refreshToken;
|
||||
if (googleToken) {
|
||||
const refreshedGoogleToken = await refreshGoogleToken(googleToken);
|
||||
const updatedGoogleAccounts = {...profileData.googleAccounts, [googleEmail]: refreshedGoogleToken};
|
||||
@ -239,29 +239,35 @@ exports.refreshTokens = functions.pubsub.schedule('every 12 hours').onRun(async
|
||||
return null;
|
||||
});
|
||||
|
||||
// Function to refresh Google token
|
||||
async function refreshGoogleToken(token) {
|
||||
// Assuming you use OAuth2 token refresh flow
|
||||
const response = await axios.post('https://oauth2.googleapis.com/token', {
|
||||
grant_type: 'refresh_token',
|
||||
refresh_token: token, // Add refresh token stored previously
|
||||
client_id: 'YOUR_GOOGLE_CLIENT_ID',
|
||||
client_secret: 'YOUR_GOOGLE_CLIENT_SECRET',
|
||||
});
|
||||
async function refreshGoogleToken(refreshToken) {
|
||||
try {
|
||||
const response = await axios.post('https://oauth2.googleapis.com/token', {
|
||||
grant_type: 'refresh_token',
|
||||
refresh_token: refreshToken,
|
||||
client_id: "406146460310-2u67ab2nbhu23trp8auho1fq4om29fc0.apps.googleusercontent.com", // Web client ID from googleConfig
|
||||
});
|
||||
|
||||
return response.data.access_token; // Return new access token
|
||||
return response.data.access_token; // Return the new access token
|
||||
} catch (error) {
|
||||
console.error("Error refreshing Google token:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshMicrosoftToken(token) {
|
||||
const response = await axios.post('https://login.microsoftonline.com/common/oauth2/v2.0/token', {
|
||||
grant_type: 'refresh_token',
|
||||
refresh_token: token, // Add refresh token stored previously
|
||||
client_id: 'YOUR_MICROSOFT_CLIENT_ID',
|
||||
client_secret: 'YOUR_MICROSOFT_CLIENT_SECRET',
|
||||
scope: 'https://graph.microsoft.com/Calendars.ReadWrite offline_access',
|
||||
});
|
||||
async function refreshMicrosoftToken(refreshToken) {
|
||||
try {
|
||||
const response = await axios.post('https://login.microsoftonline.com/common/oauth2/v2.0/token', {
|
||||
grant_type: 'refresh_token',
|
||||
refresh_token: refreshToken,
|
||||
client_id: "13c79071-1066-40a9-9f71-b8c4b138b4af", // Client ID from microsoftConfig
|
||||
scope: "openid profile email offline_access Calendars.ReadWrite User.Read", // Scope from microsoftConfig
|
||||
});
|
||||
|
||||
return response.data.access_token; // Return new access token
|
||||
return response.data.access_token; // Return the new access token
|
||||
} catch (error) {
|
||||
console.error("Error refreshing Microsoft token:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function getPushTokensForEvent() {
|
||||
|
39
hooks/firebase/useClearTokens.ts
Normal file
39
hooks/firebase/useClearTokens.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import {useMutation} from "react-query";
|
||||
import {UserProfile} from "@firebase/auth";
|
||||
import {useAuthContext} from "@/contexts/AuthContext";
|
||||
import {useUpdateUserData} from "@/hooks/firebase/useUpdateUserData";
|
||||
|
||||
export const useClearTokens = () => {
|
||||
const {profileData} = useAuthContext();
|
||||
const {mutateAsync: updateUserData} = useUpdateUserData();
|
||||
|
||||
return useMutation({
|
||||
mutationKey: ["clearTokens"],
|
||||
mutationFn: async ({provider, email}: {
|
||||
provider: "google" | "outlook" | "apple",
|
||||
email: string
|
||||
}) => {
|
||||
const newUserData: Partial<UserProfile> = {};
|
||||
if (provider === "google") {
|
||||
let googleAccounts = profileData?.googleAccounts;
|
||||
if (googleAccounts) {
|
||||
googleAccounts[email] = null;
|
||||
newUserData.googleAccounts = googleAccounts;
|
||||
}
|
||||
} else if (provider === "outlook") {
|
||||
let microsoftAccounts = profileData?.microsoftAccounts;
|
||||
if (microsoftAccounts) {
|
||||
microsoftAccounts[email] = null;
|
||||
newUserData.microsoftAccounts = microsoftAccounts;
|
||||
}
|
||||
} else if (provider === "apple") {
|
||||
let appleAccounts = profileData?.appleAccounts;
|
||||
if (appleAccounts) {
|
||||
appleAccounts[email] = null;
|
||||
newUserData.appleAccounts = appleAccounts;
|
||||
}
|
||||
}
|
||||
await updateUserData({newUserData});
|
||||
},
|
||||
})
|
||||
}
|
@ -45,34 +45,41 @@ export const useCreateEvent = () => {
|
||||
}
|
||||
|
||||
export const useCreateEventsFromProvider = () => {
|
||||
const {user: currentUser} = useAuthContext();
|
||||
const { user: currentUser } = useAuthContext();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationKey: ["createEventsFromProvider"],
|
||||
mutationFn: async (eventDataArray: Partial<EventData>[]) => {
|
||||
try {
|
||||
for (const eventData of eventDataArray) {
|
||||
// Create an array of promises for each event's Firestore read/write operation
|
||||
const promises = eventDataArray.map(async (eventData) => {
|
||||
console.log("Processing EventData: ", eventData);
|
||||
|
||||
// Check if the event already exists
|
||||
const snapshot = await firestore()
|
||||
.collection("Events")
|
||||
.where("id", "==", eventData.id)
|
||||
.get();
|
||||
|
||||
if (snapshot.empty) {
|
||||
await firestore()
|
||||
// Event doesn't exist, so add it
|
||||
return firestore()
|
||||
.collection("Events")
|
||||
.add({...eventData, creatorId: currentUser?.uid});
|
||||
.add({ ...eventData, creatorId: currentUser?.uid });
|
||||
} else {
|
||||
console.log("Event already exists, updating...");
|
||||
// Event exists, update it
|
||||
const docId = snapshot.docs[0].id;
|
||||
await firestore()
|
||||
return firestore()
|
||||
.collection("Events")
|
||||
.doc(docId)
|
||||
.set({...eventData, creatorId: currentUser?.uid}, {merge: true});
|
||||
.set({ ...eventData, creatorId: currentUser?.uid }, { merge: true });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Execute all promises in parallel
|
||||
await Promise.all(promises);
|
||||
|
||||
} catch (e) {
|
||||
console.error("Error creating/updating events: ", e);
|
||||
}
|
||||
|
@ -2,11 +2,13 @@ import {useMutation, useQueryClient} from "react-query";
|
||||
import {fetchGoogleCalendarEvents} from "@/calendar-integration/google-calendar-utils";
|
||||
import {useAuthContext} from "@/contexts/AuthContext";
|
||||
import {useCreateEventsFromProvider} from "@/hooks/firebase/useCreateEvent";
|
||||
import {useClearTokens} from "@/hooks/firebase/useClearTokens";
|
||||
|
||||
export const useFetchAndSaveGoogleEvents = () => {
|
||||
const queryClient = useQueryClient()
|
||||
const {profileData} = useAuthContext();
|
||||
const {mutateAsync: createEventsFromProvider} = useCreateEventsFromProvider();
|
||||
const {mutateAsync: clearToken} = useClearTokens();
|
||||
|
||||
return useMutation({
|
||||
mutationKey: ["fetchAndSaveGoogleEvents"],
|
||||
@ -26,9 +28,14 @@ export const useFetchAndSaveGoogleEvents = () => {
|
||||
timeMax.toISOString().slice(0, -5) + "Z"
|
||||
);
|
||||
|
||||
if(!response.success) {
|
||||
await clearToken({email: email!, provider: "google"})
|
||||
return
|
||||
}
|
||||
|
||||
console.log("Google Calendar events fetched:", response);
|
||||
|
||||
const items = response?.map((item) => {
|
||||
const items = response?.googleEvents?.map((item) => {
|
||||
if (item.allDay) {
|
||||
item.startDate = new Date(new Date(item.startDate).setHours(0, 0, 0, 0));
|
||||
item.endDate = item.startDate;
|
||||
|
Reference in New Issue
Block a user