Microsoft sync added

This commit is contained in:
Milan Paunovic
2024-10-05 22:22:29 +02:00
parent 2234fac075
commit aed2333404
13 changed files with 865 additions and 1277 deletions

View File

@ -29,6 +29,7 @@
<data android:scheme="myapp"/>
<data android:scheme="com.cally.app"/>
<data android:scheme="exp+cally"/>
<data android:scheme="callyplanner"/>
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" android:exported="false"/>

View File

@ -5,7 +5,7 @@
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/images/icon.png",
"scheme": "myapp",
"scheme": "callyplanner",
"userInterfaceStyle": "light",
"splash": {
"image": "./assets/images/splash.png",

View File

@ -10,6 +10,8 @@ export async function fetchMicrosoftCalendarEvents(token, startDate, endDate) {
const data = await response.json();
console.log(data, startDate, endDate)
const microsoftEvents = [];
data?.value?.forEach((item) => {
const start = item.start;
@ -33,6 +35,8 @@ export async function fetchMicrosoftCalendarEvents(token, startDate, endDate) {
endDate: endDateTime,
allDay: item.isAllDay,
};
microsoftEvents.push(microsoftEvent);
});

View File

@ -16,11 +16,11 @@ import {
} from "react-native-ui-lib";
import { TouchableOpacity } from "react-native";
import { ManuallyAddEventModal } from "@/components/pages/calendar/ManuallyAddEventModal";
import AddChore from "../todos/AddChore";
import AddChoreDialog from "../todos/AddChoreDialog";
import { ToDosContextProvider } from "@/contexts/ToDosContext";
import UploadImageDialog from "./UploadImageDialog";
export const AddEventDialog = () => {
const [show, setShow] = useState(false);
const [showManualInputModal, setShowManualInputModal] = useState(false);

View File

@ -1,321 +1,371 @@
import { AntDesign, Ionicons } from "@expo/vector-icons";
import React, { useState } from "react";
import { Button, Checkbox, Text, View } from "react-native-ui-lib";
import { StyleSheet } from "react-native";
import { colorMap } from "@/contexts/SettingsContext";
import { TouchableOpacity } from "react-native-gesture-handler";
import { fetchGoogleCalendarEvents } from "@/calendar-integration/google-calendar-utils";
import { fetchMicrosoftCalendarEvents } from "@/calendar-integration/microsoft-calendar-utils";
import { useCreateEventFromProvider } from "@/hooks/firebase/useCreateEvent";
import { useAuthContext } from "@/contexts/AuthContext";
import { useUpdateUserData } from "@/hooks/firebase/useUpdateUserData";
import { GoogleSignin } from "@react-native-google-signin/google-signin";
import { authorize } from "react-native-app-auth";
import {AntDesign, Ionicons} from "@expo/vector-icons";
import React, {useState} from "react";
import {Button, Checkbox, Text, View} from "react-native-ui-lib";
import {ScrollView, StyleSheet} from "react-native";
import {colorMap} from "@/contexts/SettingsContext";
import {TouchableOpacity} from "react-native-gesture-handler";
import {fetchGoogleCalendarEvents} from "@/calendar-integration/google-calendar-utils";
import {fetchMicrosoftCalendarEvents} from "@/calendar-integration/microsoft-calendar-utils";
import {useCreateEventFromProvider} from "@/hooks/firebase/useCreateEvent";
import {useAuthContext} from "@/contexts/AuthContext";
import {useUpdateUserData} from "@/hooks/firebase/useUpdateUserData";
import {GoogleSignin} from "@react-native-google-signin/google-signin";
import * as AuthSession from "expo-auth-session";
GoogleSignin.configure({
webClientId:
"406146460310-hjadmfa1gg4ptaouira5rkhu0djlo5ut.apps.googleusercontent.com",
scopes: ["profile", "email"], // Note: add calendar scope
webClientId:
"406146460310-hjadmfa1gg4ptaouira5rkhu0djlo5ut.apps.googleusercontent.com",
scopes: ["profile", "email"], // Note: add calendar scope
});
const GoogleLogin = async () => {
return await GoogleSignin.signIn();
return await GoogleSignin.signIn();
};
const microsoftConfig = {
issuer: "https://login.microsoftonline.com/common",
clientId: "<your-client-id>", // Replace with your microsoft client id
redirectUrl: "<your-redirect-uri>", // replace with your redirect uri added in microsoft portal
scopes: ["openid", "profile", "email"], // Add calendar scope
serviceConfiguration: {
clientId: "13c79071-1066-40a9-9f71-b8c4b138b4af", // Replace with your Microsoft client ID
redirectUri: AuthSession.makeRedirectUri({path: "settings"}), // Generate redirect URI automatically for Expo
scopes: [
"openid",
"profile",
"email",
"offline_access",
"Calendars.ReadWrite", // Scope for reading calendar events
],
authorizationEndpoint:
"https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
tokenEndpoint: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
revocationEndpoint:
"https://login.microsoftonline.com/common/oauth2/v2.0/logout",
},
useNonce: true,
usePKCE: true, //For iOS, we have added the useNonce and usePKCE parameters, which are recommended for security reasons.
additionalParameters: {
prompt: "consent",
},
"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;
setSelectedPage: (page: number) => void;
}) => {
const [selectedColor, setSelectedColor] = useState<string>(colorMap.pink);
const [startDate, setStartDate] = useState<boolean>(true);
const { profileData } = useAuthContext();
const [selectedColor, setSelectedColor] = useState<string>(colorMap.pink);
const [startDate, setStartDate] = useState<boolean>(true);
const {profileData} = useAuthContext();
const { mutateAsync: createEventFromProvider } = useCreateEventFromProvider();
const { mutateAsync: updateUserData } = useUpdateUserData();
const {mutateAsync: createEventFromProvider} = useCreateEventFromProvider();
const {mutateAsync: updateUserData} = useUpdateUserData();
const fetchAndSaveGoogleEvents = () => {
const timeMin = new Date(new Date().setHours(0, 0, 0, 0));
const timeMax = new Date(
new Date(new Date().setHours(0, 0, 0, 0)).setDate(timeMin.getDate() + 30),
);
const fetchAndSaveGoogleEvents = () => {
const timeMin = new Date(new Date().setHours(0, 0, 0, 0));
const timeMax = new Date(
new Date(new Date().setHours(0, 0, 0, 0)).setDate(timeMin.getDate() + 30)
);
fetchGoogleCalendarEvents(
profileData?.googleToken,
timeMin.toISOString().slice(0, -5) + "Z",
timeMax.toISOString().slice(0, -5) + "Z",
).then((response) => {
response?.forEach((item) => saveData(item));
});
};
fetchGoogleCalendarEvents(
profileData?.googleToken,
timeMin.toISOString().slice(0, -5) + "Z",
timeMax.toISOString().slice(0, -5) + "Z",
).then((response) => {
response?.forEach((item) => saveData(item));
});
};
async function saveData(item) {
await createEventFromProvider(item);
}
async function saveData(item: any) {
await createEventFromProvider(item);
}
const fetchAndSaveMicrosoftEvents = () => {
const startDateTime = new Date(new Date().setHours(0, 0, 0, 0));
const endDateTime = new Date(
new Date(new Date().setHours(0, 0, 0, 0)).setDate(
startDateTime.getDate() + 30,
),
);
const fetchAndSaveMicrosoftEvents = () => {
const startDateTime = new Date(new Date().setHours(0, 0, 0, 0));
const endDateTime = new Date(
new Date(new Date().setHours(0, 0, 0, 0)).setDate(
startDateTime.getDate() + 30
)
);
fetchMicrosoftCalendarEvents(
profileData?.microsoftToken,
startDateTime.toISOString().slice(0, -5) + "Z",
endDateTime.toISOString().slice(0, -5) + "Z",
).then((response) => {
response?.forEach((item) => saveData(item));
});
};
const handleGoogleLogin = async () => {
try {
const response = await GoogleLogin();
if (response) {
const googleUserData = response.data;
let idToken = googleUserData?.idToken;
fetchMicrosoftCalendarEvents(
profileData?.microsoftToken,
startDateTime.toISOString().slice(0, -5) + "Z",
endDateTime.toISOString().slice(0, -5) + "Z"
).then((response) => {
console.log(response)
response?.forEach((item) => saveData(item));
});
};
if (idToken) {
await updateUserData({ newUserData: { googleToken: idToken } });
const handleGoogleLogin = async () => {
try {
const response = await GoogleLogin();
if (response) {
const googleUserData = response.data;
let idToken = googleUserData?.idToken;
if (idToken) {
await updateUserData({newUserData: {googleToken: idToken}});
}
}
} catch (apiError) {
console.log(apiError || "Something went wrong");
}
}
} catch (apiError) {
console.log(apiError || "Something went wrong");
}
};
};
const handleMicrosoftSignIn = async () => {
try {
const { idToken } = await authorize(microsoftConfig);
if (idToken) {
await updateUserData({ newUserData: { microsoftToken: idToken } });
}
} catch (error) {
console.log(error);
}
};
const handleMicrosoftSignIn = async () => {
try {
console.log("Starting Microsoft sign-in...");
return (
<View marginH-30>
<TouchableOpacity onPress={() => props.setSelectedPage(0)}>
<View row marginT-20 marginB-35 centerV>
<Ionicons name="chevron-back" size={22} color="#979797" />
<Text text70 color="#979797">
Return to main settings
</Text>
</View>
</TouchableOpacity>
<Text text60R>Calendar settings</Text>
<View style={styles.card}>
<Text text70 marginB-14>
Event Color Preference
</Text>
<View row spread>
<TouchableOpacity onPress={() => setSelectedColor(colorMap.pink)}>
<View style={styles.colorBox} backgroundColor={colorMap.pink}>
{selectedColor == colorMap.pink && (
<AntDesign name="check" size={30} color="white" />
)}
</View>
</TouchableOpacity>
<TouchableOpacity onPress={() => setSelectedColor(colorMap.orange)}>
<View style={styles.colorBox} backgroundColor={colorMap.orange}>
{selectedColor == colorMap.orange && (
<AntDesign name="check" size={30} color="white" />
)}
</View>
</TouchableOpacity>
<TouchableOpacity onPress={() => setSelectedColor(colorMap.green)}>
<View style={styles.colorBox} backgroundColor={colorMap.green}>
{selectedColor == colorMap.green && (
<AntDesign name="check" size={30} color="white" />
)}
</View>
</TouchableOpacity>
<TouchableOpacity onPress={() => setSelectedColor(colorMap.teal)}>
<View style={styles.colorBox} backgroundColor={colorMap.teal}>
{selectedColor == colorMap.teal && (
<AntDesign name="check" size={30} color="white" />
)}
</View>
</TouchableOpacity>
<TouchableOpacity onPress={() => setSelectedColor(colorMap.purple)}>
<View style={styles.colorBox} backgroundColor={colorMap.purple}>
{selectedColor == colorMap.purple && (
<AntDesign name="check" size={30} color="white" />
)}
</View>
</TouchableOpacity>
</View>
</View>
<View style={styles.card}>
<Text text70>Weekly Start Date</Text>
<View row marginV-5 marginT-20>
<Checkbox
value={startDate}
style={styles.checkbox}
color="#ea156d"
onValueChange={() => setStartDate(true)}
/>
<View row marginL-8>
<Text text70>Sundays</Text>
<Text text70 color="gray">
{" "}
(default)
</Text>
</View>
</View>
<View row marginV-5>
<Checkbox
value={!startDate}
style={styles.checkbox}
color="#ea156d"
onValueChange={() => setStartDate(false)}
/>
<Text text70 marginL-8>
Mondays
</Text>
</View>
</View>
<View style={styles.card}>
<Text text70>Add Calendar</Text>
<View style={{ marginTop: 20 }}>
<Button
label={"Connect Google"}
iconSource={() => (
<View
backgroundColor="#ededed"
width={40}
height={40}
style={{ borderRadius: 50 }}
marginR-10
centerV
centerH
>
<Ionicons name="logo-google" size={22} color="#979797" />
</View>
)}
backgroundColor="white"
color="#464039"
borderRadius={15}
onPress={handleGoogleLogin}
/>
<Button
label={"Connect Microsoft"}
iconSource={() => (
<View
backgroundColor="#ededed"
width={40}
height={40}
style={{ borderRadius: 50 }}
marginR-10
centerV
centerH
>
<Ionicons name="logo-microsoft" size={22} color="#979797" />
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")}`,
});
console.log("Token response status:", tokenResponse.status);
if (!tokenResponse.ok) {
console.error("Token exchange failed:", await tokenResponse.text());
return;
}
const tokenData = await tokenResponse.json();
console.log("Token data received:", tokenData);
if (tokenData?.id_token) {
console.log("ID token received, updating user data...");
await updateUserData({newUserData: {microsoftToken: tokenData.access_token}});
console.log("User data updated successfully.");
}
} else {
console.warn("Authentication was not successful:", authResult);
}
} catch (error) {
console.error("Error during Microsoft sign-in:", error);
}
};
return (
<ScrollView>
<View marginH-30>
<TouchableOpacity onPress={() => props.setSelectedPage(0)}>
<View row marginT-20 marginB-35 centerV>
<Ionicons name="chevron-back" size={22} color="#979797"/>
<Text text70 color="#979797">
Return to main settings
</Text>
</View>
</TouchableOpacity>
<Text text60R>Calendar settings</Text>
<View style={styles.card}>
<Text text70 marginB-14>
Event Color Preference
</Text>
<View row spread>
<TouchableOpacity onPress={() => setSelectedColor(colorMap.pink)}>
<View style={styles.colorBox} backgroundColor={colorMap.pink}>
{selectedColor == colorMap.pink && (
<AntDesign name="check" size={30} color="white"/>
)}
</View>
</TouchableOpacity>
<TouchableOpacity onPress={() => setSelectedColor(colorMap.orange)}>
<View style={styles.colorBox} backgroundColor={colorMap.orange}>
{selectedColor == colorMap.orange && (
<AntDesign name="check" size={30} color="white"/>
)}
</View>
</TouchableOpacity>
<TouchableOpacity onPress={() => setSelectedColor(colorMap.green)}>
<View style={styles.colorBox} backgroundColor={colorMap.green}>
{selectedColor == colorMap.green && (
<AntDesign name="check" size={30} color="white"/>
)}
</View>
</TouchableOpacity>
<TouchableOpacity onPress={() => setSelectedColor(colorMap.teal)}>
<View style={styles.colorBox} backgroundColor={colorMap.teal}>
{selectedColor == colorMap.teal && (
<AntDesign name="check" size={30} color="white"/>
)}
</View>
</TouchableOpacity>
<TouchableOpacity onPress={() => setSelectedColor(colorMap.purple)}>
<View style={styles.colorBox} backgroundColor={colorMap.purple}>
{selectedColor == colorMap.purple && (
<AntDesign name="check" size={30} color="white"/>
)}
</View>
</TouchableOpacity>
</View>
</View>
)}
backgroundColor="white"
color="#464039"
borderRadius={15}
onPress={handleMicrosoftSignIn}
/>
</View>
</View>
<View style={styles.card}>
<Text text70>Calendars</Text>
<View style={{ marginTop: 20 }}>
<Button
label={"Sync Outlook"}
iconSource={() => (
<View
backgroundColor="#ededed"
width={40}
height={40}
style={{ borderRadius: 50 }}
marginR-10
centerV
centerH
>
<Ionicons name="logo-microsoft" size={22} color="#979797" />
</View>
)}
backgroundColor="white"
color="#464039"
borderRadius={15}
onPress={fetchAndSaveMicrosoftEvents}
/>
{profileData?.googleToken !== undefined && (
<Button
label={"Sync Google"}
iconSource={() => (
<View
backgroundColor="#ededed"
width={40}
height={40}
style={{ borderRadius: 50 }}
marginR-10
centerV
centerH
>
<Ionicons name="logo-google" size={22} color="#979797" />
<View style={styles.card}>
<Text text70>Weekly Start Date</Text>
<View row marginV-5 marginT-20>
<Checkbox
value={startDate}
style={styles.checkbox}
color="#ea156d"
onValueChange={() => setStartDate(true)}
/>
<View row marginL-8>
<Text text70>Sundays</Text>
<Text text70 color="gray">
{" "}
(default)
</Text>
</View>
</View>
<View row marginV-5>
<Checkbox
value={!startDate}
style={styles.checkbox}
color="#ea156d"
onValueChange={() => setStartDate(false)}
/>
<Text text70 marginL-8>
Mondays
</Text>
</View>
</View>
)}
backgroundColor="white"
color="#464039"
borderRadius={15}
onPress={fetchAndSaveGoogleEvents}
/>
)}
</View>
</View>
</View>
);
<View style={styles.card}>
<Text text70>Add Calendar</Text>
<View style={{marginTop: 20}}>
<Button
label={"Connect Google"}
iconSource={() => (
<View
backgroundColor="#ededed"
width={40}
height={40}
style={{borderRadius: 50}}
marginR-10
centerV
centerH
>
<Ionicons name="logo-google" size={22} color="#979797"/>
</View>
)}
backgroundColor="white"
color="#464039"
borderRadius={15}
onPress={handleGoogleLogin}
/>
<Button
label={"Connect Microsoft"}
iconSource={() => (
<View
backgroundColor="#ededed"
width={40}
height={40}
style={{borderRadius: 50}}
marginR-10
centerV
centerH
>
<Ionicons name="logo-microsoft" size={22} color="#979797"/>
</View>
)}
backgroundColor="white"
color="#464039"
borderRadius={15}
onPress={handleMicrosoftSignIn}
/>
</View>
</View>
<View style={styles.card}>
<Text text70>Calendars</Text>
<View style={{marginTop: 20}}>
<Button
label={"Sync Outlook"}
iconSource={() => (
<View
backgroundColor="#ededed"
width={40}
height={40}
style={{borderRadius: 50}}
marginR-10
centerV
centerH
>
<Ionicons name="logo-microsoft" size={22} color="#979797"/>
</View>
)}
backgroundColor="white"
color="#464039"
borderRadius={15}
onPress={fetchAndSaveMicrosoftEvents}
/>
{profileData?.googleToken !== undefined && (
<Button
label={"Sync Google"}
iconSource={() => (
<View
backgroundColor="#ededed"
width={40}
height={40}
style={{borderRadius: 50}}
marginR-10
centerV
centerH
>
<Ionicons name="logo-google" size={22} color="#979797"/>
</View>
)}
backgroundColor="white"
color="#464039"
borderRadius={15}
onPress={fetchAndSaveGoogleEvents}
/>
)}
</View>
</View>
</View>
</ScrollView>
);
};
const styles = StyleSheet.create({
backBtn: {
backgroundColor: "red",
marginLeft: -2,
justifyContent: "flex-start",
},
card: {
backgroundColor: "white",
width: "100%",
padding: 20,
paddingBottom: 30,
marginTop: 20,
borderRadius: 20,
},
colorBox: {
aspectRatio: 1,
justifyContent: "center",
alignItems: "center",
width: 50,
borderRadius: 12,
},
checkbox: {
borderRadius: 50,
},
backBtn: {
backgroundColor: "red",
marginLeft: -2,
justifyContent: "flex-start",
},
card: {
backgroundColor: "white",
width: "100%",
padding: 20,
paddingBottom: 30,
marginTop: 20,
borderRadius: 20,
},
colorBox: {
aspectRatio: 1,
justifyContent: "center",
alignItems: "center",
width: 50,
borderRadius: 12,
},
checkbox: {
borderRadius: 50,
},
});
export default CalendarSettingsPage;

View File

@ -14,8 +14,9 @@ export enum ProfileType {
interface IAuthContext {
user: FirebaseAuthTypes.User | null,
profileType?: ProfileType,
profileData?: UserProfile
setProfileData: (profileData: UserProfile) => void
profileData?: UserProfile,
setProfileData: (profileData: UserProfile) => void,
refreshProfileData: () => Promise<void>
}
const AuthContext = createContext<IAuthContext>(undefined!)
@ -32,8 +33,14 @@ export const AuthContextProvider: FC<{ children: ReactNode }> = ({children}) =>
const onAuthStateChanged = async (user: FirebaseAuthTypes.User | null) => {
setUser(user);
console.log(user)
if (user) {
await refreshProfileData();
}
if (initializing) setInitializing(false);
}
const refreshProfileData = async () => {
if (user) {
try {
const documentSnapshot = await firestore()
@ -42,17 +49,15 @@ export const AuthContextProvider: FC<{ children: ReactNode }> = ({children}) =>
.get();
if (documentSnapshot.exists) {
setProfileType(documentSnapshot.data()?.userType);
setProfileData(documentSnapshot.data() as UserProfile)
setProfileData(documentSnapshot.data() as UserProfile);
}
} catch (error) {
console.error("Error fetching user profile type:", error);
console.error("Error fetching user profile data:", error);
setProfileType(undefined);
setProfileData(undefined);
}
}
if (initializing) setInitializing(false);
}
};
useEffect(() => {
const subscriber = auth().onAuthStateChanged(onAuthStateChanged);
@ -78,10 +83,10 @@ export const AuthContextProvider: FC<{ children: ReactNode }> = ({children}) =>
}
return (
<AuthContext.Provider value={{user, profileType, profileData, setProfileData}}>
<AuthContext.Provider value={{user, profileType, profileData, setProfileData, refreshProfileData}}>
{children}
</AuthContext.Provider>
)
}
export const useAuthContext = () => useContext(AuthContext)!;
export const useAuthContext = () => useContext(AuthContext)!;

View File

@ -5,26 +5,36 @@ import {UserProfile} from "@/hooks/firebase/types/profileTypes";
import {FirebaseAuthTypes} from "@react-native-firebase/auth";
export const useUpdateUserData = () => {
const {user: currentUser, setProfileData} = useAuthContext()
const {user: currentUser, setProfileData, refreshProfileData} = useAuthContext();
return useMutation({
mutationKey: ["updateUserData"],
mutationFn: async ({newUserData, customUser}: {newUserData: Partial<UserProfile>, customUser?: FirebaseAuthTypes.User }) => {
const user = currentUser ?? customUser
mutationFn: async ({newUserData, customUser}: {newUserData: Partial<UserProfile>, customUser?: FirebaseAuthTypes.User }) => {
console.log("Mutation function called with data:", { newUserData, customUser });
const user = currentUser ?? customUser;
if (user) {
console.log("Updating user data for UID:", user.uid);
try {
console.log("New user data:", newUserData);
await firestore()
.collection("Profiles")
.doc(user.uid)
.update(newUserData);
const profileData = await firestore().collection("Profiles").doc(user?.uid!).get()
setProfileData(profileData.data() as UserProfile)
console.log("User data updated successfully, fetching updated profile...");
await refreshProfileData()
console.log("Profile data updated in context.");
} catch (e) {
console.error(e)
console.error("Error updating user data:", e);
}
} else {
console.warn("No user found: currentUser and customUser are both undefined.");
}
}
})
}
});
};

View File

@ -946,6 +946,12 @@ PODS:
- abseil/meta/type_traits
- abseil/xcprivacy
- abseil/xcprivacy (1.20240116.2)
- AppAuth (1.7.5):
- AppAuth/Core (= 1.7.5)
- AppAuth/ExternalUserAgent (= 1.7.5)
- AppAuth/Core (1.7.5)
- AppAuth/ExternalUserAgent (1.7.5):
- AppAuth/Core
- boost (1.83.0)
- BoringSSL-GRPC (0.0.32):
- BoringSSL-GRPC/Implementation (= 0.0.32)
@ -956,6 +962,8 @@ PODS:
- BVLinearGradient (2.8.3):
- React-Core
- DoubleConversion (1.1.6)
- EXApplication (5.9.1):
- ExpoModulesCore
- EXBarCodeScanner (13.0.1):
- EXImageLoader
- ExpoModulesCore
@ -1178,18 +1186,26 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- ExpoAdapterGoogleSignIn (13.1.0):
- ExpoModulesCore
- GoogleSignIn (~> 7.1)
- React-Core
- ExpoAsset (10.0.10):
- ExpoModulesCore
- ExpoCamera (15.0.16):
- ExpoModulesCore
- ZXingObjC/OneD
- ZXingObjC/PDF417
- ExpoCrypto (13.0.2):
- ExpoModulesCore
- ExpoFileSystem (17.0.1):
- ExpoModulesCore
- ExpoFont (12.0.10):
- ExpoModulesCore
- ExpoHead (3.5.23):
- ExpoModulesCore
- ExpoImagePicker (15.0.7):
- ExpoModulesCore
- ExpoKeepAwake (13.0.2):
- ExpoModulesCore
- ExpoModulesCore (1.12.24):
@ -1335,6 +1351,10 @@ PODS:
- GoogleUtilities/Environment (~> 7.7)
- nanopb (< 2.30911.0, >= 2.30908.0)
- PromisesObjC (< 3.0, >= 1.2)
- GoogleSignIn (7.1.0):
- AppAuth (< 2.0, >= 1.7.3)
- GTMAppAuth (< 5.0, >= 4.1.1)
- GTMSessionFetcher/Core (~> 3.3)
- GoogleUtilities/AppDelegateSwizzler (7.13.3):
- GoogleUtilities/Environment
- GoogleUtilities/Logger
@ -1438,6 +1458,9 @@ PODS:
- gRPC-Core/Privacy (= 1.62.5)
- gRPC-Core/Interface (1.62.5)
- gRPC-Core/Privacy (1.62.5)
- GTMAppAuth (4.1.1):
- AppAuth/Core (~> 1.7)
- GTMSessionFetcher/Core (< 4.0, >= 3.3)
- GTMSessionFetcher/Core (3.5.0)
- hermes-engine (0.74.3):
- hermes-engine/Pre-built (= 0.74.3)
@ -2379,6 +2402,9 @@ PODS:
- React-Mapbuffer (0.74.3):
- glog
- React-debug
- react-native-app-auth (8.0.0):
- AppAuth (>= 1.7.3)
- React-Core
- react-native-blur (4.4.0):
- DoubleConversion
- glog
@ -2678,6 +2704,9 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- RNGoogleSignin (13.1.0):
- GoogleSignIn (~> 7.1)
- React-Core
- RNReanimated (3.10.1):
- DoubleConversion
- glog
@ -2735,6 +2764,7 @@ DEPENDENCIES:
- boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
- BVLinearGradient (from `../node_modules/react-native-linear-gradient`)
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- EXApplication (from `../node_modules/expo-application/ios`)
- EXBarCodeScanner (from `../node_modules/expo-barcode-scanner/ios`)
- EXConstants (from `../node_modules/expo-constants/ios`)
- EXImageLoader (from `../node_modules/expo-image-loader/ios`)
@ -2745,11 +2775,14 @@ DEPENDENCIES:
- expo-dev-launcher (from `../node_modules/expo-dev-launcher`)
- expo-dev-menu (from `../node_modules/expo-dev-menu`)
- expo-dev-menu-interface (from `../node_modules/expo-dev-menu-interface/ios`)
- "ExpoAdapterGoogleSignIn (from `../node_modules/@react-native-google-signin/google-signin/expo/ios`)"
- ExpoAsset (from `../node_modules/expo-asset/ios`)
- ExpoCamera (from `../node_modules/expo-camera/ios`)
- ExpoCrypto (from `../node_modules/expo-crypto/ios`)
- ExpoFileSystem (from `../node_modules/expo-file-system/ios`)
- ExpoFont (from `../node_modules/expo-font/ios`)
- ExpoHead (from `../node_modules/expo-router/ios`)
- ExpoImagePicker (from `../node_modules/expo-image-picker/ios`)
- ExpoKeepAwake (from `../node_modules/expo-keep-awake/ios`)
- ExpoModulesCore (from `../node_modules/expo-modules-core`)
- ExpoSystemUI (from `../node_modules/expo-system-ui/ios`)
@ -2786,6 +2819,7 @@ DEPENDENCIES:
- React-jsitracing (from `../node_modules/react-native/ReactCommon/hermes/executor/`)
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
- React-Mapbuffer (from `../node_modules/react-native/ReactCommon`)
- react-native-app-auth (from `../node_modules/react-native-app-auth`)
- "react-native-blur (from `../node_modules/@react-native-community/blur`)"
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
- React-nativeconfig (from `../node_modules/react-native/ReactCommon`)
@ -2819,6 +2853,7 @@ DEPENDENCIES:
- "RNFBFirestore (from `../node_modules/@react-native-firebase/firestore`)"
- "RNFBFunctions (from `../node_modules/@react-native-firebase/functions`)"
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
- "RNGoogleSignin (from `../node_modules/@react-native-google-signin/google-signin`)"
- RNReanimated (from `../node_modules/react-native-reanimated`)
- RNScreens (from `../node_modules/react-native-screens`)
- RNSVG (from `../node_modules/react-native-svg`)
@ -2827,6 +2862,7 @@ DEPENDENCIES:
SPEC REPOS:
trunk:
- abseil
- AppAuth
- BoringSSL-GRPC
- Firebase
- FirebaseAppCheckInterop
@ -2845,9 +2881,11 @@ SPEC REPOS:
- FirebaseSessions
- FirebaseSharedSwift
- GoogleDataTransport
- GoogleSignIn
- GoogleUtilities
- "gRPC-C++"
- gRPC-Core
- GTMAppAuth
- GTMSessionFetcher
- leveldb-library
- nanopb
@ -2864,6 +2902,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-linear-gradient"
DoubleConversion:
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
EXApplication:
:path: "../node_modules/expo-application/ios"
EXBarCodeScanner:
:path: "../node_modules/expo-barcode-scanner/ios"
EXConstants:
@ -2884,16 +2924,22 @@ EXTERNAL SOURCES:
:path: "../node_modules/expo-dev-menu"
expo-dev-menu-interface:
:path: "../node_modules/expo-dev-menu-interface/ios"
ExpoAdapterGoogleSignIn:
:path: "../node_modules/@react-native-google-signin/google-signin/expo/ios"
ExpoAsset:
:path: "../node_modules/expo-asset/ios"
ExpoCamera:
:path: "../node_modules/expo-camera/ios"
ExpoCrypto:
:path: "../node_modules/expo-crypto/ios"
ExpoFileSystem:
:path: "../node_modules/expo-file-system/ios"
ExpoFont:
:path: "../node_modules/expo-font/ios"
ExpoHead:
:path: "../node_modules/expo-router/ios"
ExpoImagePicker:
:path: "../node_modules/expo-image-picker/ios"
ExpoKeepAwake:
:path: "../node_modules/expo-keep-awake/ios"
ExpoModulesCore:
@ -2963,6 +3009,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/logger"
React-Mapbuffer:
:path: "../node_modules/react-native/ReactCommon"
react-native-app-auth:
:path: "../node_modules/react-native-app-auth"
react-native-blur:
:path: "../node_modules/@react-native-community/blur"
react-native-safe-area-context:
@ -3029,6 +3077,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/@react-native-firebase/functions"
RNGestureHandler:
:path: "../node_modules/react-native-gesture-handler"
RNGoogleSignin:
:path: "../node_modules/@react-native-google-signin/google-signin"
RNReanimated:
:path: "../node_modules/react-native-reanimated"
RNScreens:
@ -3040,10 +3090,12 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
abseil: d121da9ef7e2ff4cab7666e76c5a3e0915ae08c3
AppAuth: 501c04eda8a8d11f179dbe8637b7a91bb7e5d2fa
boost: d3f49c53809116a5d38da093a8aa78bf551aed09
BoringSSL-GRPC: 1e2348957acdbcad360b80a264a90799984b2ba6
BVLinearGradient: 880f91a7854faff2df62518f0281afb1c60d49a3
DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5
EXApplication: c08200c34daca7af7fd76ac4b9d606077410e8ad
EXBarCodeScanner: e2dd9b42c1b522a2adc9202b1dfbc64cb34456d1
EXConstants: 409690fbfd5afea964e5e9d6c4eb2c2b59222c59
EXImageLoader: ab589d67d6c5f2c33572afea9917304418566334
@ -3054,11 +3106,14 @@ SPEC CHECKSUMS:
expo-dev-launcher: fe4f2c0a0aa627449eeaec5f9f11e04090f97c40
expo-dev-menu: 5b14897ecce3a8cf9e9cf9109344c2c192a3766a
expo-dev-menu-interface: be32c09f1e03833050f0ee290dcc86b3ad0e73e4
ExpoAdapterGoogleSignIn: da10ae7e7c1d73a10c2facebcdfe5ebea8e073ce
ExpoAsset: 323700f291684f110fb55f0d4022a3362ea9f875
ExpoCamera: 929be541d1c1319fcf32f9f5d9df8b97804346b5
ExpoCrypto: 156078f266bf28f80ecf5e2a9c3a0d6ffce07a1c
ExpoFileSystem: 80bfe850b1f9922c16905822ecbf97acd711dc51
ExpoFont: 00756e6c796d8f7ee8d211e29c8b619e75cbf238
ExpoHead: fcb28a68ed4ba28f177394d2dfb8a0a8824cd103
ExpoImagePicker: 12a420923383ae38dccb069847218f27a3b87816
ExpoKeepAwake: 3b8815d9dd1d419ee474df004021c69fdd316d08
ExpoModulesCore: db3e31e694684f08223d713e89f7648c6d3e04d0
ExpoSystemUI: d4f065a016cae6721b324eb659cdee4d4cf0cb26
@ -3085,9 +3140,11 @@ SPEC CHECKSUMS:
fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120
glog: fdfdfe5479092de0c4bdbebedd9056951f092c4f
GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a
GoogleSignIn: d4281ab6cf21542b1cfaff85c191f230b399d2db
GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15
"gRPC-C++": e725ef63c4475d7cdb7e2cf16eb0fde84bd9ee51
gRPC-Core: eee4be35df218649fe66d721a05a7f27a28f069b
GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
hermes-engine: 1f547997900dd0752dc0cc0ae6dd16173c49e09b
leveldb-library: e8eadf9008a61f9e1dde3978c086d2b6d9b9dc28
@ -3118,6 +3175,7 @@ SPEC CHECKSUMS:
React-jsitracing: 1aa5681c353b41573b03e0e480a5adf5fa1c56d8
React-logger: fa92ba4d3a5d39ac450f59be2a3cec7b099f0304
React-Mapbuffer: 70da5955150a58732e9336a0c7e71cd49e909f25
react-native-app-auth: c4a3b4edc11100070d8d7bc48d55fb66668cead9
react-native-blur: b58f3155f534d975b7647593d93a2e907513addd
react-native-safe-area-context: a240ad4b683349e48b1d51fed1611138d1bdad97
React-nativeconfig: 84806b820491db30175afbf027e99e8415dc63f0
@ -3152,6 +3210,7 @@ SPEC CHECKSUMS:
RNFBFirestore: e47cdde04ea3d9e73e58e037e1aa1d0b1141c316
RNFBFunctions: 738cc9e2177d060d29b5d143ef2f9ed0eda4bb1f
RNGestureHandler: 20a4307fd21cbff339abfcfa68192f3f0a6a518b
RNGoogleSignin: 9e68b9bcc3888219357924e32ee563624745647d
RNReanimated: d51431fd3597a8f8320319dce8e42cee82a5445f
RNScreens: 30249f9331c3b00ae7cb7922e11f58b3ed369c07
RNSVG: 4590aa95758149fa27c5c83e54a6a466349a1688
@ -3161,4 +3220,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 50f618790da7cbbfd5c5e988b7f9370bd45d34a6
COCOAPODS: 1.14.3
COCOAPODS: 1.15.2

View File

@ -293,7 +293,9 @@
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-cally/Pods-cally-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/AppAuth/AppAuthCore_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/BoringSSL-GRPC/openssl_grpc.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXApplication/ExpoApplication_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/ExpoConstants_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle",
@ -306,8 +308,10 @@
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestore/FirebaseFirestore_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestoreInternal/FirebaseFirestoreInternal_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstallations/FirebaseInstallations_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/GTMAppAuth/GTMAppAuth_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher/GTMSessionFetcher_Core_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/GoogleDataTransport/GoogleDataTransport_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/GoogleSignIn/GoogleSignIn.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC/FBLPromises_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/PromisesSwift/Promises_Privacy.bundle",
@ -322,7 +326,9 @@
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AppAuthCore_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/openssl_grpc.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoApplication_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoConstants_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle",
@ -335,8 +341,10 @@
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseFirestore_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseFirestoreInternal_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseInstallations_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GTMAppAuth_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GTMSessionFetcher_Core_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleDataTransport_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleSignIn.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleUtilities_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FBLPromises_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Promises_Privacy.bundle",
@ -429,7 +437,7 @@
);
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
PRODUCT_BUNDLE_IDENTIFIER = com.cally.app;
PRODUCT_NAME = "KaliFamilyPlanner";
PRODUCT_NAME = KaliFamilyPlanner;
SWIFT_OBJC_BRIDGING_HEADER = "cally/cally-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@ -460,7 +468,7 @@
);
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.cally.app;
PRODUCT_NAME = "KaliFamilyPlanner";
PRODUCT_NAME = KaliFamilyPlanner;
SWIFT_OBJC_BRIDGING_HEADER = "cally/cally-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";

View File

@ -4,6 +4,7 @@
#import <React/RCTBundleURLProvider.h>
#import <React/RCTLinkingManager.h>
#import "RNAppAuthAuthorizationFlowManager.h"
@implementation AppDelegate

View File

@ -27,7 +27,7 @@
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
<string>callyplanner</string>
<string>com.cally.app</string>
</array>
</dict>
@ -75,6 +75,8 @@
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
</array>
<key>UILaunchStoryboardName</key>
<string>SplashScreen</string>

View File

@ -40,6 +40,7 @@
"@react-navigation/native": "^6.0.2",
"date-fns": "^3.6.0",
"expo": "~51.0.24",
"expo-auth-session": "^5.5.2",
"expo-barcode-scanner": "~13.0.1",
"expo-build-properties": "~0.12.4",
"expo-camera": "~15.0.16",

1373
yarn.lock

File diff suppressed because it is too large Load Diff