mirror of
https://github.com/urosran/cally.git
synced 2025-07-10 15:17:17 +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) {
|
export async function fetchiPhoneCalendarEvents(familyId, email, startDate, endDate) {
|
||||||
try {
|
try {
|
||||||
const {status} = await Calendar.requestCalendarPermissionsAsync();
|
const {granted} = await Calendar.requestCalendarPermissionsAsync();
|
||||||
if (status !== 'granted') {
|
|
||||||
|
if (!granted) {
|
||||||
throw new Error("Calendar permission not granted");
|
throw new Error("Calendar permission not granted");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,7 +23,11 @@ export async function fetchiPhoneCalendarEvents(familyId, email, startDate, endD
|
|||||||
return events.map((event) => {
|
return events.map((event) => {
|
||||||
let isAllDay = event.allDay || false;
|
let isAllDay = event.allDay || false;
|
||||||
const startDateTime = new Date(event.startDate);
|
const startDateTime = new Date(event.startDate);
|
||||||
const endDateTime = new Date(event.endDate);
|
let endDateTime = new Date(event.endDate);
|
||||||
|
|
||||||
|
if (isAllDay) {
|
||||||
|
endDateTime = startDateTime
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: event.id,
|
id: event.id,
|
||||||
|
@ -8,7 +8,9 @@ export async function fetchGoogleCalendarEvents(token, email, familyId, startDat
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
const googleEvents = [];
|
const googleEvents = [];
|
||||||
data.items?.forEach((item) => {
|
data.items?.forEach((item) => {
|
||||||
let isAllDay = false;
|
let isAllDay = false;
|
||||||
@ -49,5 +51,5 @@ export async function fetchGoogleCalendarEvents(token, email, familyId, startDat
|
|||||||
googleEvents.push(googleEvent);
|
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 React from "react";
|
||||||
import {Button, View,} from "react-native-ui-lib";
|
import {Button, View,} from "react-native-ui-lib";
|
||||||
import {useGroceryContext} from "@/contexts/GroceryContext";
|
import {useGroceryContext} from "@/contexts/GroceryContext";
|
||||||
import {FontAwesome6} from "@expo/vector-icons";
|
import {FontAwesome6} from "@expo/vector-icons";
|
||||||
import PlusIcon from "@/assets/svgs/PlusIcon";
|
import PlusIcon from "@/assets/svgs/PlusIcon";
|
||||||
|
|
||||||
|
const { width } = Dimensions.get("screen");
|
||||||
|
|
||||||
const AddGroceryItem = () => {
|
const AddGroceryItem = () => {
|
||||||
const {setIsAddingGrocery} = useGroceryContext();
|
const {setIsAddingGrocery} = useGroceryContext();
|
||||||
|
|
||||||
@ -65,8 +67,14 @@ const styles = StyleSheet.create({
|
|||||||
marginVertical: 10,
|
marginVertical: 10,
|
||||||
},
|
},
|
||||||
btnContainer: {
|
btnContainer: {
|
||||||
width: "100%",
|
position:"absolute",
|
||||||
|
bottom: 30,
|
||||||
|
width: width,
|
||||||
|
padding: 20,
|
||||||
|
paddingBottom: 0,
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
|
alignItems:"center",
|
||||||
|
zIndex: 10,
|
||||||
},
|
},
|
||||||
finishShopBtn: {
|
finishShopBtn: {
|
||||||
width: "100%",
|
width: "100%",
|
||||||
|
@ -1,22 +1,14 @@
|
|||||||
import {
|
import {Button, ButtonSize, Dialog, Text, TextField, TextFieldRef, View,} from "react-native-ui-lib";
|
||||||
Button,
|
import React, {useRef, useState} from "react";
|
||||||
ButtonSize,
|
import {useSignIn} from "@/hooks/firebase/useSignIn";
|
||||||
Dialog,
|
import {StyleSheet} from "react-native";
|
||||||
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 Toast from "react-native-toast-message";
|
||||||
import { useLoginWithQrCode } from "@/hooks/firebase/useLoginWithQrCode";
|
import {useLoginWithQrCode} from "@/hooks/firebase/useLoginWithQrCode";
|
||||||
import { Camera, CameraView } from "expo-camera";
|
import {Camera, CameraView} from "expo-camera";
|
||||||
|
|
||||||
const SignInPage = ({
|
const SignInPage = ({
|
||||||
setTab,
|
setTab,
|
||||||
}: {
|
}: {
|
||||||
setTab: React.Dispatch<
|
setTab: React.Dispatch<
|
||||||
React.SetStateAction<"register" | "login" | "reset-password">
|
React.SetStateAction<"register" | "login" | "reset-password">
|
||||||
>;
|
>;
|
||||||
@ -27,11 +19,11 @@ const SignInPage = ({
|
|||||||
const [showCameraDialog, setShowCameraDialog] = useState<boolean>(false);
|
const [showCameraDialog, setShowCameraDialog] = useState<boolean>(false);
|
||||||
const passwordRef = useRef<TextFieldRef>(null);
|
const passwordRef = useRef<TextFieldRef>(null);
|
||||||
|
|
||||||
const { mutateAsync: signIn, error, isError } = useSignIn();
|
const {mutateAsync: signIn, error, isError} = useSignIn();
|
||||||
const { mutateAsync: signInWithQrCode } = useLoginWithQrCode();
|
const {mutateAsync: signInWithQrCode} = useLoginWithQrCode();
|
||||||
|
|
||||||
const handleSignIn = async () => {
|
const handleSignIn = async () => {
|
||||||
await signIn({ email, password });
|
await signIn({email, password});
|
||||||
if (!isError) {
|
if (!isError) {
|
||||||
Toast.show({
|
Toast.show({
|
||||||
type: "success",
|
type: "success",
|
||||||
@ -46,10 +38,10 @@ const SignInPage = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleQrCodeScanned = async ({ data }: { data: string }) => {
|
const handleQrCodeScanned = async ({data}: { data: string }) => {
|
||||||
setShowCameraDialog(false);
|
setShowCameraDialog(false);
|
||||||
try {
|
try {
|
||||||
await signInWithQrCode({ userId: data });
|
await signInWithQrCode({userId: data});
|
||||||
Toast.show({
|
Toast.show({
|
||||||
type: "success",
|
type: "success",
|
||||||
text1: "Login successful with QR code!",
|
text1: "Login successful with QR code!",
|
||||||
@ -64,7 +56,7 @@ const SignInPage = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getCameraPermissions = async (callback: () => void) => {
|
const getCameraPermissions = async (callback: () => void) => {
|
||||||
const { status } = await Camera.requestCameraPermissionsAsync();
|
const {status} = await Camera.requestCameraPermissionsAsync();
|
||||||
setHasPermission(status === "granted");
|
setHasPermission(status === "granted");
|
||||||
if (status === "granted") {
|
if (status === "granted") {
|
||||||
callback();
|
callback();
|
||||||
@ -75,8 +67,10 @@ const SignInPage = ({
|
|||||||
<View padding-10 centerV height={"100%"}>
|
<View padding-10 centerV height={"100%"}>
|
||||||
<TextField
|
<TextField
|
||||||
placeholder="Email"
|
placeholder="Email"
|
||||||
value={email}
|
keyboardType={"email-address"}
|
||||||
|
returnKeyType={"next"}
|
||||||
|
textContentType={"emailAddress"}
|
||||||
|
defaultValue={email}
|
||||||
onChangeText={setEmail}
|
onChangeText={setEmail}
|
||||||
style={styles.textfield}
|
style={styles.textfield}
|
||||||
onSubmitEditing={() => {
|
onSubmitEditing={() => {
|
||||||
@ -87,10 +81,12 @@ const SignInPage = ({
|
|||||||
<TextField
|
<TextField
|
||||||
ref={passwordRef}
|
ref={passwordRef}
|
||||||
placeholder="Password"
|
placeholder="Password"
|
||||||
|
textContentType={"oneTimeCode"}
|
||||||
value={password}
|
value={password}
|
||||||
onChangeText={setPassword}
|
onChangeText={setPassword}
|
||||||
secureTextEntry
|
secureTextEntry
|
||||||
style={styles.textfield}
|
style={styles.textfield}
|
||||||
|
autoCorrect={false}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
label="Log in"
|
label="Log in"
|
||||||
@ -100,7 +96,7 @@ const SignInPage = ({
|
|||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
}}
|
}}
|
||||||
onPress={handleSignIn}
|
onPress={handleSignIn}
|
||||||
style={{ marginBottom: 20, height: 50 }}
|
style={{marginBottom: 20, height: 50}}
|
||||||
backgroundColor="#fd1775"
|
backgroundColor="#fd1775"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
@ -112,11 +108,11 @@ const SignInPage = ({
|
|||||||
onPress={() => {
|
onPress={() => {
|
||||||
getCameraPermissions(() => setShowCameraDialog(true));
|
getCameraPermissions(() => setShowCameraDialog(true));
|
||||||
}}
|
}}
|
||||||
style={{ marginBottom: 20, height: 50 }}
|
style={{marginBottom: 20, height: 50}}
|
||||||
backgroundColor="#fd1775"
|
backgroundColor="#fd1775"
|
||||||
/>
|
/>
|
||||||
{isError && (
|
{isError && (
|
||||||
<Text center style={{ marginBottom: 20 }}>{`${
|
<Text center style={{marginBottom: 20}}>{`${
|
||||||
error?.toString()?.split("]")?.[1]
|
error?.toString()?.split("]")?.[1]
|
||||||
}`}</Text>
|
}`}</Text>
|
||||||
)}
|
)}
|
||||||
@ -128,7 +124,7 @@ const SignInPage = ({
|
|||||||
label="Sign Up"
|
label="Sign Up"
|
||||||
labelStyle={[
|
labelStyle={[
|
||||||
styles.jakartaMedium,
|
styles.jakartaMedium,
|
||||||
{ textDecorationLine: "none", color: "#fd1575" },
|
{textDecorationLine: "none", color: "#fd1575"},
|
||||||
]}
|
]}
|
||||||
link
|
link
|
||||||
size={ButtonSize.xSmall}
|
size={ButtonSize.xSmall}
|
||||||
@ -147,7 +143,7 @@ const SignInPage = ({
|
|||||||
label="Reset password"
|
label="Reset password"
|
||||||
labelStyle={[
|
labelStyle={[
|
||||||
styles.jakartaMedium,
|
styles.jakartaMedium,
|
||||||
{ textDecorationLine: "none", color: "#fd1575" },
|
{textDecorationLine: "none", color: "#fd1575"},
|
||||||
]}
|
]}
|
||||||
link
|
link
|
||||||
size={ButtonSize.xSmall}
|
size={ButtonSize.xSmall}
|
||||||
@ -167,7 +163,7 @@ const SignInPage = ({
|
|||||||
bottom
|
bottom
|
||||||
width="100%"
|
width="100%"
|
||||||
height="70%"
|
height="70%"
|
||||||
containerStyle={{ padding: 15, backgroundColor:"white" }}
|
containerStyle={{padding: 15, backgroundColor: "white"}}
|
||||||
>
|
>
|
||||||
{hasPermission === null ? (
|
{hasPermission === null ? (
|
||||||
<Text>Requesting camera permissions...</Text>
|
<Text>Requesting camera permissions...</Text>
|
||||||
@ -175,7 +171,7 @@ const SignInPage = ({
|
|||||||
<Text>No access to camera</Text>
|
<Text>No access to camera</Text>
|
||||||
) : (
|
) : (
|
||||||
<CameraView
|
<CameraView
|
||||||
style={{ flex: 1, borderRadius: 15 }}
|
style={{flex: 1, borderRadius: 15}}
|
||||||
onBarcodeScanned={handleQrCodeScanned}
|
onBarcodeScanned={handleQrCodeScanned}
|
||||||
barcodeScannerSettings={{
|
barcodeScannerSettings={{
|
||||||
barcodeTypes: ["qr"],
|
barcodeTypes: ["qr"],
|
||||||
@ -186,7 +182,7 @@ const SignInPage = ({
|
|||||||
label="Cancel"
|
label="Cancel"
|
||||||
onPress={() => setShowCameraDialog(false)}
|
onPress={() => setShowCameraDialog(false)}
|
||||||
backgroundColor="#fd1775"
|
backgroundColor="#fd1775"
|
||||||
style={{ margin: 10, marginBottom: 30 }}
|
style={{margin: 10, marginBottom: 30}}
|
||||||
/>
|
/>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</View>
|
</View>
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { AntDesign, Ionicons } from "@expo/vector-icons";
|
import {AntDesign, Ionicons} from "@expo/vector-icons";
|
||||||
import React, { useCallback, useEffect, useState } from "react";
|
import React, {useCallback, useEffect, useState} from "react";
|
||||||
import { Button, Checkbox, Text, View } from "react-native-ui-lib";
|
import {Button, Checkbox, Text, View} from "react-native-ui-lib";
|
||||||
import { ActivityIndicator, Alert, ScrollView, StyleSheet } from "react-native";
|
import {ActivityIndicator, ScrollView, StyleSheet} from "react-native";
|
||||||
import { TouchableOpacity } from "react-native-gesture-handler";
|
import {TouchableOpacity} from "react-native-gesture-handler";
|
||||||
import { useAuthContext } from "@/contexts/AuthContext";
|
import {useAuthContext} from "@/contexts/AuthContext";
|
||||||
import { useUpdateUserData } from "@/hooks/firebase/useUpdateUserData";
|
import {useUpdateUserData} from "@/hooks/firebase/useUpdateUserData";
|
||||||
import debounce from "debounce";
|
import debounce from "debounce";
|
||||||
import AppleIcon from "@/assets/svgs/AppleIcon";
|
import AppleIcon from "@/assets/svgs/AppleIcon";
|
||||||
import GoogleIcon from "@/assets/svgs/GoogleIcon";
|
import GoogleIcon from "@/assets/svgs/GoogleIcon";
|
||||||
@ -12,16 +12,16 @@ import OutlookIcon from "@/assets/svgs/OutlookIcon";
|
|||||||
import * as AuthSession from "expo-auth-session";
|
import * as AuthSession from "expo-auth-session";
|
||||||
import * as Google from "expo-auth-session/providers/google";
|
import * as Google from "expo-auth-session/providers/google";
|
||||||
import * as WebBrowser from "expo-web-browser";
|
import * as WebBrowser from "expo-web-browser";
|
||||||
import { UserProfile } from "@firebase/auth";
|
import {useFetchAndSaveGoogleEvents} from "@/hooks/useFetchAndSaveGoogleEvents";
|
||||||
import { useFetchAndSaveGoogleEvents } from "@/hooks/useFetchAndSaveGoogleEvents";
|
import {useFetchAndSaveOutlookEvents} from "@/hooks/useFetchAndSaveOutlookEvents";
|
||||||
import { useFetchAndSaveOutlookEvents } from "@/hooks/useFetchAndSaveOutlookEvents";
|
import {useFetchAndSaveAppleEvents} from "@/hooks/useFetchAndSaveAppleEvents";
|
||||||
import { useFetchAndSaveAppleEvents } from "@/hooks/useFetchAndSaveAppleEvents";
|
|
||||||
import * as AppleAuthentication from "expo-apple-authentication";
|
import * as AppleAuthentication from "expo-apple-authentication";
|
||||||
import ExpoLocalization from "expo-localization/src/ExpoLocalization";
|
import ExpoLocalization from "expo-localization/src/ExpoLocalization";
|
||||||
import { colorMap } from "@/constants/colorMap";
|
import {colorMap} from "@/constants/colorMap";
|
||||||
import { useAtom } from "jotai";
|
import {useAtom} from "jotai";
|
||||||
import { settingsPageIndex } from "../calendar/atoms";
|
import {settingsPageIndex} from "../calendar/atoms";
|
||||||
import CalendarSettingsDialog from "./calendar_components/CalendarSettingsDialog";
|
import CalendarSettingsDialog from "./calendar_components/CalendarSettingsDialog";
|
||||||
|
import {useClearTokens} from "@/hooks/firebase/useClearTokens";
|
||||||
|
|
||||||
const googleConfig = {
|
const googleConfig = {
|
||||||
androidClientId:
|
androidClientId:
|
||||||
@ -35,11 +35,14 @@ const googleConfig = {
|
|||||||
"profile",
|
"profile",
|
||||||
"https://www.googleapis.com/auth/calendar.events.owned",
|
"https://www.googleapis.com/auth/calendar.events.owned",
|
||||||
],
|
],
|
||||||
|
extraParams: {
|
||||||
|
access_type: "offline",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const microsoftConfig = {
|
const microsoftConfig = {
|
||||||
clientId: "13c79071-1066-40a9-9f71-b8c4b138b4af",
|
clientId: "13c79071-1066-40a9-9f71-b8c4b138b4af",
|
||||||
redirectUri: AuthSession.makeRedirectUri({ path: "settings" }),
|
redirectUri: AuthSession.makeRedirectUri({path: "settings"}),
|
||||||
scopes: [
|
scopes: [
|
||||||
"openid",
|
"openid",
|
||||||
"profile",
|
"profile",
|
||||||
@ -54,7 +57,7 @@ const microsoftConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const CalendarSettingsPage = () => {
|
const CalendarSettingsPage = () => {
|
||||||
const { profileData } = useAuthContext();
|
const {profileData} = useAuthContext();
|
||||||
const [pageIndex, setPageIndex] = useAtom(settingsPageIndex);
|
const [pageIndex, setPageIndex] = useAtom(settingsPageIndex);
|
||||||
const [firstDayOfWeek, setFirstDayOfWeek] = useState<string>(
|
const [firstDayOfWeek, setFirstDayOfWeek] = useState<string>(
|
||||||
profileData?.firstDayOfWeek ??
|
profileData?.firstDayOfWeek ??
|
||||||
@ -93,14 +96,15 @@ const CalendarSettingsPage = () => {
|
|||||||
profileData?.eventColor ?? colorMap.pink
|
profileData?.eventColor ?? colorMap.pink
|
||||||
);
|
);
|
||||||
|
|
||||||
const { mutateAsync: updateUserData } = useUpdateUserData();
|
const {mutateAsync: updateUserData} = useUpdateUserData();
|
||||||
const { mutateAsync: fetchAndSaveGoogleEvents, isLoading: isSyncingGoogle } =
|
const {mutateAsync: clearToken} = useClearTokens();
|
||||||
|
const {mutateAsync: fetchAndSaveGoogleEvents, isLoading: isSyncingGoogle} =
|
||||||
useFetchAndSaveGoogleEvents();
|
useFetchAndSaveGoogleEvents();
|
||||||
const {
|
const {
|
||||||
mutateAsync: fetchAndSaveOutlookEvents,
|
mutateAsync: fetchAndSaveOutlookEvents,
|
||||||
isLoading: isSyncingOutlook,
|
isLoading: isSyncingOutlook,
|
||||||
} = useFetchAndSaveOutlookEvents();
|
} = useFetchAndSaveOutlookEvents();
|
||||||
const { mutateAsync: fetchAndSaveAppleEvents, isLoading: isSyncingApple } =
|
const {mutateAsync: fetchAndSaveAppleEvents, isLoading: isSyncingApple} =
|
||||||
useFetchAndSaveAppleEvents();
|
useFetchAndSaveAppleEvents();
|
||||||
|
|
||||||
WebBrowser.maybeCompleteAuthSession();
|
WebBrowser.maybeCompleteAuthSession();
|
||||||
@ -113,7 +117,7 @@ const CalendarSettingsPage = () => {
|
|||||||
const signInWithGoogle = async () => {
|
const signInWithGoogle = async () => {
|
||||||
try {
|
try {
|
||||||
if (response?.type === "success") {
|
if (response?.type === "success") {
|
||||||
const accessToken = response.authentication?.accessToken;
|
const { accessToken, refreshToken } = response?.authentication!;
|
||||||
|
|
||||||
const userInfoResponse = await fetch(
|
const userInfoResponse = await fetch(
|
||||||
"https://www.googleapis.com/oauth2/v3/userinfo",
|
"https://www.googleapis.com/oauth2/v3/userinfo",
|
||||||
@ -125,10 +129,11 @@ const CalendarSettingsPage = () => {
|
|||||||
const userInfo = await userInfoResponse.json();
|
const userInfo = await userInfoResponse.json();
|
||||||
const googleMail = userInfo.email;
|
const googleMail = userInfo.email;
|
||||||
|
|
||||||
let googleAccounts = profileData?.googleAccounts;
|
let googleAccounts = profileData?.googleAccounts || {};
|
||||||
const updatedGoogleAccounts = googleAccounts
|
const updatedGoogleAccounts = {
|
||||||
? { ...googleAccounts, [googleMail]: accessToken }
|
...googleAccounts,
|
||||||
: { [googleMail]: accessToken };
|
[googleMail]: { accessToken, refreshToken },
|
||||||
|
};
|
||||||
|
|
||||||
await updateUserData({
|
await updateUserData({
|
||||||
newUserData: { googleAccounts: updatedGoogleAccounts },
|
newUserData: { googleAccounts: updatedGoogleAccounts },
|
||||||
@ -219,12 +224,11 @@ const CalendarSettingsPage = () => {
|
|||||||
|
|
||||||
let microsoftAccounts = profileData?.microsoftAccounts;
|
let microsoftAccounts = profileData?.microsoftAccounts;
|
||||||
const updatedMicrosoftAccounts = microsoftAccounts
|
const updatedMicrosoftAccounts = microsoftAccounts
|
||||||
? { ...microsoftAccounts, [outlookMail]: tokenData.access_token }
|
? {...microsoftAccounts, [outlookMail]: tokenData.access_token}
|
||||||
: { [outlookMail]: tokenData.access_token };
|
: {[outlookMail]: tokenData.access_token};
|
||||||
|
|
||||||
// Update user data with Microsoft token and email
|
|
||||||
await updateUserData({
|
await updateUserData({
|
||||||
newUserData: { microsoftAccounts: updatedMicrosoftAccounts },
|
newUserData: {microsoftAccounts: updatedMicrosoftAccounts},
|
||||||
});
|
});
|
||||||
|
|
||||||
await fetchAndSaveOutlookEvents(
|
await fetchAndSaveOutlookEvents(
|
||||||
@ -255,18 +259,26 @@ const CalendarSettingsPage = () => {
|
|||||||
|
|
||||||
console.log("Apple sign-in result:", credential);
|
console.log("Apple sign-in result:", credential);
|
||||||
|
|
||||||
|
alert(JSON.stringify(credential))
|
||||||
|
|
||||||
const appleToken = credential.identityToken;
|
const appleToken = credential.identityToken;
|
||||||
const appleMail = credential.email;
|
const appleMail = credential.email!;
|
||||||
|
|
||||||
|
|
||||||
if (appleToken) {
|
if (appleToken) {
|
||||||
console.log("Apple ID token received. Fetch user info if needed...");
|
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({
|
await updateUserData({
|
||||||
newUserData: { appleToken, appleMail },
|
newUserData: {appleAccounts: updatedAppleAccounts},
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("User data updated with Apple ID token.");
|
console.log("User data updated with Apple ID token.");
|
||||||
await fetchAndSaveAppleEvents({ token: appleToken, email: appleMail! });
|
await fetchAndSaveAppleEvents({token: appleToken, email: appleMail!});
|
||||||
} else {
|
} else {
|
||||||
console.warn(
|
console.warn(
|
||||||
"Apple authentication was not successful or email was hidden."
|
"Apple authentication was not successful or email was hidden."
|
||||||
@ -321,33 +333,6 @@ const CalendarSettingsPage = () => {
|
|||||||
debouncedUpdateUserData(color);
|
debouncedUpdateUserData(color);
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearToken = async (
|
|
||||||
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 });
|
|
||||||
};
|
|
||||||
|
|
||||||
let isConnectedToGoogle = false;
|
let isConnectedToGoogle = false;
|
||||||
if (profileData?.googleAccounts) {
|
if (profileData?.googleAccounts) {
|
||||||
Object.values(profileData?.googleAccounts).forEach((item) => {
|
Object.values(profileData?.googleAccounts).forEach((item) => {
|
||||||
@ -387,10 +372,10 @@ const CalendarSettingsPage = () => {
|
|||||||
name="chevron-back"
|
name="chevron-back"
|
||||||
size={14}
|
size={14}
|
||||||
color="#979797"
|
color="#979797"
|
||||||
style={{ paddingBottom: 3 }}
|
style={{paddingBottom: 3}}
|
||||||
/>
|
/>
|
||||||
<Text
|
<Text
|
||||||
style={{ fontFamily: "Poppins_400Regular", fontSize: 14.71 }}
|
style={{fontFamily: "Poppins_400Regular", fontSize: 14.71}}
|
||||||
color="#979797"
|
color="#979797"
|
||||||
>
|
>
|
||||||
Return to main settings
|
Return to main settings
|
||||||
@ -407,7 +392,7 @@ const CalendarSettingsPage = () => {
|
|||||||
<TouchableOpacity onPress={() => handleChangeColor(colorMap.pink)}>
|
<TouchableOpacity onPress={() => handleChangeColor(colorMap.pink)}>
|
||||||
<View style={styles.colorBox} backgroundColor={colorMap.pink}>
|
<View style={styles.colorBox} backgroundColor={colorMap.pink}>
|
||||||
{selectedColor == colorMap.pink && (
|
{selectedColor == colorMap.pink && (
|
||||||
<AntDesign name="check" size={30} color="white" />
|
<AntDesign name="check" size={30} color="white"/>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
@ -416,21 +401,21 @@ const CalendarSettingsPage = () => {
|
|||||||
>
|
>
|
||||||
<View style={styles.colorBox} backgroundColor={colorMap.orange}>
|
<View style={styles.colorBox} backgroundColor={colorMap.orange}>
|
||||||
{selectedColor == colorMap.orange && (
|
{selectedColor == colorMap.orange && (
|
||||||
<AntDesign name="check" size={30} color="white" />
|
<AntDesign name="check" size={30} color="white"/>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<TouchableOpacity onPress={() => handleChangeColor(colorMap.green)}>
|
<TouchableOpacity onPress={() => handleChangeColor(colorMap.green)}>
|
||||||
<View style={styles.colorBox} backgroundColor={colorMap.green}>
|
<View style={styles.colorBox} backgroundColor={colorMap.green}>
|
||||||
{selectedColor == colorMap.green && (
|
{selectedColor == colorMap.green && (
|
||||||
<AntDesign name="check" size={30} color="white" />
|
<AntDesign name="check" size={30} color="white"/>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<TouchableOpacity onPress={() => handleChangeColor(colorMap.teal)}>
|
<TouchableOpacity onPress={() => handleChangeColor(colorMap.teal)}>
|
||||||
<View style={styles.colorBox} backgroundColor={colorMap.teal}>
|
<View style={styles.colorBox} backgroundColor={colorMap.teal}>
|
||||||
{selectedColor == colorMap.teal && (
|
{selectedColor == colorMap.teal && (
|
||||||
<AntDesign name="check" size={30} color="white" />
|
<AntDesign name="check" size={30} color="white"/>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
@ -439,7 +424,7 @@ const CalendarSettingsPage = () => {
|
|||||||
>
|
>
|
||||||
<View style={styles.colorBox} backgroundColor={colorMap.purple}>
|
<View style={styles.colorBox} backgroundColor={colorMap.purple}>
|
||||||
{selectedColor == colorMap.purple && (
|
{selectedColor == colorMap.purple && (
|
||||||
<AntDesign name="check" size={30} color="white" />
|
<AntDesign name="check" size={30} color="white"/>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
@ -480,14 +465,14 @@ const CalendarSettingsPage = () => {
|
|||||||
|
|
||||||
<Button
|
<Button
|
||||||
onPress={() => promptAsync()}
|
onPress={() => promptAsync()}
|
||||||
label={"Connect Google"}
|
label={profileData?.googleAccounts ? "Connect another Google account" : "Connect Google account"}
|
||||||
labelStyle={styles.addCalLbl}
|
labelStyle={styles.addCalLbl}
|
||||||
labelProps={{
|
labelProps={{
|
||||||
numberOfLines: 2,
|
numberOfLines: 2,
|
||||||
}}
|
}}
|
||||||
iconSource={() => (
|
iconSource={() => (
|
||||||
<View marginR-15>
|
<View marginR-15>
|
||||||
<GoogleIcon />
|
<GoogleIcon/>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
style={styles.addCalBtn}
|
style={styles.addCalBtn}
|
||||||
@ -496,7 +481,7 @@ const CalendarSettingsPage = () => {
|
|||||||
/>
|
/>
|
||||||
{profileData?.googleAccounts
|
{profileData?.googleAccounts
|
||||||
? Object.keys(profileData?.googleAccounts)?.map((googleMail) => {
|
? Object.keys(profileData?.googleAccounts)?.map((googleMail) => {
|
||||||
const googleToken = profileData?.googleAccounts?.[googleMail];
|
const googleToken = profileData?.googleAccounts?.[googleMail]?.accessToken;
|
||||||
return (
|
return (
|
||||||
googleToken && (
|
googleToken && (
|
||||||
<Button
|
<Button
|
||||||
@ -511,7 +496,7 @@ const CalendarSettingsPage = () => {
|
|||||||
}}
|
}}
|
||||||
iconSource={() => (
|
iconSource={() => (
|
||||||
<View marginR-15>
|
<View marginR-15>
|
||||||
<GoogleIcon />
|
<GoogleIcon/>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
style={styles.addCalBtn}
|
style={styles.addCalBtn}
|
||||||
@ -523,6 +508,7 @@ const CalendarSettingsPage = () => {
|
|||||||
})
|
})
|
||||||
: null}
|
: null}
|
||||||
|
|
||||||
|
{!profileData?.appleAccounts && (
|
||||||
<Button
|
<Button
|
||||||
onPress={() => handleAppleSignIn()}
|
onPress={() => handleAppleSignIn()}
|
||||||
label={"Connect Apple"}
|
label={"Connect Apple"}
|
||||||
@ -532,29 +518,31 @@ const CalendarSettingsPage = () => {
|
|||||||
}}
|
}}
|
||||||
iconSource={() => (
|
iconSource={() => (
|
||||||
<View marginR-15>
|
<View marginR-15>
|
||||||
<AppleIcon />
|
<AppleIcon/>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
style={styles.addCalBtn}
|
style={styles.addCalBtn}
|
||||||
color="black"
|
color="black"
|
||||||
text70BL
|
text70BL
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{profileData?.appleAccounts
|
{profileData?.appleAccounts
|
||||||
? Object.keys(profileData?.appleAccounts)?.map((appleEmail) => {
|
? Object.keys(profileData?.appleAccounts)?.map((appleEmail) => {
|
||||||
const appleToken = profileData?.appleAccounts?.[appleEmail];
|
const appleToken = profileData?.appleAccounts?.[appleEmail!];
|
||||||
return (
|
return (
|
||||||
appleToken && (
|
appleToken && (
|
||||||
<Button
|
<Button
|
||||||
key={appleEmail}
|
key={appleEmail}
|
||||||
onPress={() => showConfirmationDialog("apple", appleEmail)}
|
onPress={() => showConfirmationDialog("apple", appleEmail)}
|
||||||
label={`Disconnect ${appleEmail}`}
|
label={`Disconnect Apple`}
|
||||||
labelStyle={styles.addCalLbl}
|
labelStyle={styles.addCalLbl}
|
||||||
labelProps={{
|
labelProps={{
|
||||||
numberOfLines: 2,
|
numberOfLines: 2,
|
||||||
}}
|
}}
|
||||||
iconSource={() => (
|
iconSource={() => (
|
||||||
<View marginR-15>
|
<View marginR-15>
|
||||||
<AppleIcon />
|
<AppleIcon/>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
style={styles.addCalBtn}
|
style={styles.addCalBtn}
|
||||||
@ -568,14 +556,14 @@ const CalendarSettingsPage = () => {
|
|||||||
|
|
||||||
<Button
|
<Button
|
||||||
onPress={() => handleMicrosoftSignIn()}
|
onPress={() => handleMicrosoftSignIn()}
|
||||||
label={"Connect Outlook"}
|
label={profileData?.microsoftAccounts ? "Connect another Outlook account" : "Connect Outlook"}
|
||||||
labelStyle={styles.addCalLbl}
|
labelStyle={styles.addCalLbl}
|
||||||
labelProps={{
|
labelProps={{
|
||||||
numberOfLines: 2,
|
numberOfLines: 2,
|
||||||
}}
|
}}
|
||||||
iconSource={() => (
|
iconSource={() => (
|
||||||
<View marginR-15>
|
<View marginR-15>
|
||||||
<OutlookIcon />
|
<OutlookIcon/>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
style={styles.addCalBtn}
|
style={styles.addCalBtn}
|
||||||
@ -601,7 +589,7 @@ const CalendarSettingsPage = () => {
|
|||||||
}}
|
}}
|
||||||
iconSource={() => (
|
iconSource={() => (
|
||||||
<View marginR-15>
|
<View marginR-15>
|
||||||
<OutlookIcon />
|
<OutlookIcon/>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
style={styles.addCalBtn}
|
style={styles.addCalBtn}
|
||||||
@ -623,7 +611,7 @@ const CalendarSettingsPage = () => {
|
|||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<View style={styles.noPaddingCard}>
|
<View style={styles.noPaddingCard}>
|
||||||
<View style={{ marginTop: 20 }}>
|
<View style={{marginTop: 20}}>
|
||||||
{profileData?.googleAccounts &&
|
{profileData?.googleAccounts &&
|
||||||
Object.keys(profileData?.googleAccounts)?.map(
|
Object.keys(profileData?.googleAccounts)?.map(
|
||||||
(googleEmail) => {
|
(googleEmail) => {
|
||||||
@ -650,10 +638,10 @@ const CalendarSettingsPage = () => {
|
|||||||
}
|
}
|
||||||
label={`Sync ${googleEmail}`}
|
label={`Sync ${googleEmail}`}
|
||||||
labelStyle={styles.addCalLbl}
|
labelStyle={styles.addCalLbl}
|
||||||
labelProps={{ numberOfLines: 3 }}
|
labelProps={{numberOfLines: 3}}
|
||||||
iconSource={() => (
|
iconSource={() => (
|
||||||
<View marginR-15>
|
<View marginR-15>
|
||||||
<GoogleIcon />
|
<GoogleIcon/>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
style={styles.addCalBtn}
|
style={styles.addCalBtn}
|
||||||
@ -662,7 +650,7 @@ const CalendarSettingsPage = () => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{isSyncingGoogle ? (
|
{isSyncingGoogle ? (
|
||||||
<ActivityIndicator />
|
<ActivityIndicator/>
|
||||||
) : (
|
) : (
|
||||||
<Ionicons
|
<Ionicons
|
||||||
name={"refresh"}
|
name={"refresh"}
|
||||||
@ -679,6 +667,8 @@ const CalendarSettingsPage = () => {
|
|||||||
|
|
||||||
{profileData?.appleAccounts &&
|
{profileData?.appleAccounts &&
|
||||||
Object.keys(profileData?.appleAccounts)?.map((appleEmail) => {
|
Object.keys(profileData?.appleAccounts)?.map((appleEmail) => {
|
||||||
|
console.log(profileData?.appleAccounts)
|
||||||
|
|
||||||
const appleToken = profileData?.appleAccounts?.[appleEmail];
|
const appleToken = profileData?.appleAccounts?.[appleEmail];
|
||||||
return (
|
return (
|
||||||
appleToken && (
|
appleToken && (
|
||||||
@ -699,12 +689,12 @@ const CalendarSettingsPage = () => {
|
|||||||
token: appleToken,
|
token: appleToken,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
label={`Sync ${appleEmail}`}
|
label={isSyncingApple ? "Syncing Events from the Apple calendar..." : `Sync Apple`}
|
||||||
labelStyle={styles.addCalLbl}
|
labelStyle={styles.addCalLbl}
|
||||||
labelProps={{ numberOfLines: 3 }}
|
labelProps={{numberOfLines: 3}}
|
||||||
iconSource={() => (
|
iconSource={() => (
|
||||||
<View marginR-15>
|
<View marginR-15>
|
||||||
<AppleIcon />
|
<AppleIcon/>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
style={styles.addCalBtn}
|
style={styles.addCalBtn}
|
||||||
@ -712,7 +702,7 @@ const CalendarSettingsPage = () => {
|
|||||||
text70BL
|
text70BL
|
||||||
/>
|
/>
|
||||||
{isSyncingApple ? (
|
{isSyncingApple ? (
|
||||||
<ActivityIndicator />
|
<ActivityIndicator/>
|
||||||
) : (
|
) : (
|
||||||
<Ionicons
|
<Ionicons
|
||||||
name={"refresh"}
|
name={"refresh"}
|
||||||
@ -752,10 +742,10 @@ const CalendarSettingsPage = () => {
|
|||||||
}
|
}
|
||||||
label={`Sync ${microsoftEmail}`}
|
label={`Sync ${microsoftEmail}`}
|
||||||
labelStyle={styles.addCalLbl}
|
labelStyle={styles.addCalLbl}
|
||||||
labelProps={{ numberOfLines: 3 }}
|
labelProps={{numberOfLines: 3}}
|
||||||
iconSource={() => (
|
iconSource={() => (
|
||||||
<View marginR-15>
|
<View marginR-15>
|
||||||
<OutlookIcon />
|
<OutlookIcon/>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
style={styles.addCalBtn}
|
style={styles.addCalBtn}
|
||||||
@ -763,7 +753,7 @@ const CalendarSettingsPage = () => {
|
|||||||
text70BL
|
text70BL
|
||||||
/>
|
/>
|
||||||
{isSyncingOutlook ? (
|
{isSyncingOutlook ? (
|
||||||
<ActivityIndicator />
|
<ActivityIndicator/>
|
||||||
) : (
|
) : (
|
||||||
<Ionicons
|
<Ionicons
|
||||||
name={"refresh"}
|
name={"refresh"}
|
||||||
|
@ -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...');
|
console.log('Running token refresh job...');
|
||||||
|
|
||||||
const profilesSnapshot = await db.collection('Profiles').get();
|
const profilesSnapshot = await db.collection('Profiles').get();
|
||||||
@ -192,7 +192,7 @@ exports.refreshTokens = functions.pubsub.schedule('every 12 hours').onRun(async
|
|||||||
if (profileData.googleAccounts) {
|
if (profileData.googleAccounts) {
|
||||||
try {
|
try {
|
||||||
for (const googleEmail of Object.keys(profileData?.googleAccounts)) {
|
for (const googleEmail of Object.keys(profileData?.googleAccounts)) {
|
||||||
const googleToken = profileData?.googleAccounts?.[googleEmail];
|
const googleToken = profileData?.googleAccounts?.[googleEmail]?.refreshToken;
|
||||||
if (googleToken) {
|
if (googleToken) {
|
||||||
const refreshedGoogleToken = await refreshGoogleToken(googleToken);
|
const refreshedGoogleToken = await refreshGoogleToken(googleToken);
|
||||||
const updatedGoogleAccounts = {...profileData.googleAccounts, [googleEmail]: refreshedGoogleToken};
|
const updatedGoogleAccounts = {...profileData.googleAccounts, [googleEmail]: refreshedGoogleToken};
|
||||||
@ -239,29 +239,35 @@ exports.refreshTokens = functions.pubsub.schedule('every 12 hours').onRun(async
|
|||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Function to refresh Google token
|
async function refreshGoogleToken(refreshToken) {
|
||||||
async function refreshGoogleToken(token) {
|
try {
|
||||||
// Assuming you use OAuth2 token refresh flow
|
|
||||||
const response = await axios.post('https://oauth2.googleapis.com/token', {
|
const response = await axios.post('https://oauth2.googleapis.com/token', {
|
||||||
grant_type: 'refresh_token',
|
grant_type: 'refresh_token',
|
||||||
refresh_token: token, // Add refresh token stored previously
|
refresh_token: refreshToken,
|
||||||
client_id: 'YOUR_GOOGLE_CLIENT_ID',
|
client_id: "406146460310-2u67ab2nbhu23trp8auho1fq4om29fc0.apps.googleusercontent.com", // Web client ID from googleConfig
|
||||||
client_secret: 'YOUR_GOOGLE_CLIENT_SECRET',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
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) {
|
async function refreshMicrosoftToken(refreshToken) {
|
||||||
|
try {
|
||||||
const response = await axios.post('https://login.microsoftonline.com/common/oauth2/v2.0/token', {
|
const response = await axios.post('https://login.microsoftonline.com/common/oauth2/v2.0/token', {
|
||||||
grant_type: 'refresh_token',
|
grant_type: 'refresh_token',
|
||||||
refresh_token: token, // Add refresh token stored previously
|
refresh_token: refreshToken,
|
||||||
client_id: 'YOUR_MICROSOFT_CLIENT_ID',
|
client_id: "13c79071-1066-40a9-9f71-b8c4b138b4af", // Client ID from microsoftConfig
|
||||||
client_secret: 'YOUR_MICROSOFT_CLIENT_SECRET',
|
scope: "openid profile email offline_access Calendars.ReadWrite User.Read", // Scope from microsoftConfig
|
||||||
scope: 'https://graph.microsoft.com/Calendars.ReadWrite offline_access',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
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() {
|
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 = () => {
|
export const useCreateEventsFromProvider = () => {
|
||||||
const {user: currentUser} = useAuthContext();
|
const { user: currentUser } = useAuthContext();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationKey: ["createEventsFromProvider"],
|
mutationKey: ["createEventsFromProvider"],
|
||||||
mutationFn: async (eventDataArray: Partial<EventData>[]) => {
|
mutationFn: async (eventDataArray: Partial<EventData>[]) => {
|
||||||
try {
|
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);
|
console.log("Processing EventData: ", eventData);
|
||||||
|
|
||||||
|
// Check if the event already exists
|
||||||
const snapshot = await firestore()
|
const snapshot = await firestore()
|
||||||
.collection("Events")
|
.collection("Events")
|
||||||
.where("id", "==", eventData.id)
|
.where("id", "==", eventData.id)
|
||||||
.get();
|
.get();
|
||||||
|
|
||||||
if (snapshot.empty) {
|
if (snapshot.empty) {
|
||||||
await firestore()
|
// Event doesn't exist, so add it
|
||||||
|
return firestore()
|
||||||
.collection("Events")
|
.collection("Events")
|
||||||
.add({...eventData, creatorId: currentUser?.uid});
|
.add({ ...eventData, creatorId: currentUser?.uid });
|
||||||
} else {
|
} else {
|
||||||
console.log("Event already exists, updating...");
|
// Event exists, update it
|
||||||
const docId = snapshot.docs[0].id;
|
const docId = snapshot.docs[0].id;
|
||||||
await firestore()
|
return firestore()
|
||||||
.collection("Events")
|
.collection("Events")
|
||||||
.doc(docId)
|
.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) {
|
} catch (e) {
|
||||||
console.error("Error creating/updating events: ", 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 {fetchGoogleCalendarEvents} from "@/calendar-integration/google-calendar-utils";
|
||||||
import {useAuthContext} from "@/contexts/AuthContext";
|
import {useAuthContext} from "@/contexts/AuthContext";
|
||||||
import {useCreateEventsFromProvider} from "@/hooks/firebase/useCreateEvent";
|
import {useCreateEventsFromProvider} from "@/hooks/firebase/useCreateEvent";
|
||||||
|
import {useClearTokens} from "@/hooks/firebase/useClearTokens";
|
||||||
|
|
||||||
export const useFetchAndSaveGoogleEvents = () => {
|
export const useFetchAndSaveGoogleEvents = () => {
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const {profileData} = useAuthContext();
|
const {profileData} = useAuthContext();
|
||||||
const {mutateAsync: createEventsFromProvider} = useCreateEventsFromProvider();
|
const {mutateAsync: createEventsFromProvider} = useCreateEventsFromProvider();
|
||||||
|
const {mutateAsync: clearToken} = useClearTokens();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationKey: ["fetchAndSaveGoogleEvents"],
|
mutationKey: ["fetchAndSaveGoogleEvents"],
|
||||||
@ -26,9 +28,14 @@ export const useFetchAndSaveGoogleEvents = () => {
|
|||||||
timeMax.toISOString().slice(0, -5) + "Z"
|
timeMax.toISOString().slice(0, -5) + "Z"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if(!response.success) {
|
||||||
|
await clearToken({email: email!, provider: "google"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
console.log("Google Calendar events fetched:", response);
|
console.log("Google Calendar events fetched:", response);
|
||||||
|
|
||||||
const items = response?.map((item) => {
|
const items = response?.googleEvents?.map((item) => {
|
||||||
if (item.allDay) {
|
if (item.allDay) {
|
||||||
item.startDate = new Date(new Date(item.startDate).setHours(0, 0, 0, 0));
|
item.startDate = new Date(new Date(item.startDate).setHours(0, 0, 0, 0));
|
||||||
item.endDate = item.startDate;
|
item.endDate = item.startDate;
|
||||||
|
Reference in New Issue
Block a user