mirror of
https://github.com/urosran/cally.git
synced 2025-07-16 01:56:16 +00:00
Apple calendar sync, timezone, first day of week additions
This commit is contained in:
@ -1,10 +1,12 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
|
||||||
<uses-permission android:name="android.permission.CAMERA"/>
|
<uses-permission android:name="android.permission.CAMERA"/>
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<uses-permission android:name="android.permission.READ_CALENDAR"/>
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||||
|
<uses-permission android:name="android.permission.WRITE_CALENDAR"/>
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
<queries>
|
<queries>
|
||||||
<intent>
|
<intent>
|
||||||
@ -22,7 +24,7 @@
|
|||||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
|
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
|
||||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>
|
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>
|
||||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATE_URL" android:value="https://u.expo.dev/bdb8c57b-25bb-4d36-b3b8-5b09c5092f52"/>
|
<meta-data android:name="expo.modules.updates.EXPO_UPDATE_URL" android:value="https://u.expo.dev/bdb8c57b-25bb-4d36-b3b8-5b09c5092f52"/>
|
||||||
<activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true" android:screenOrientation="portrait">
|
<activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode|locale|layoutDirection" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true" android:screenOrientation="portrait">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Cally - Family Planner</string>
|
<string name="app_name">Cally.</string>
|
||||||
<string name="expo_splash_screen_resize_mode" translatable="false">contain</string>
|
<string name="expo_splash_screen_resize_mode" translatable="false">contain</string>
|
||||||
<string name="expo_splash_screen_status_bar_translucent" translatable="false">false</string>
|
<string name="expo_splash_screen_status_bar_translucent" translatable="false">false</string>
|
||||||
<string name="expo_system_ui_user_interface_style" translatable="false">light</string>
|
<string name="expo_system_ui_user_interface_style" translatable="false">light</string>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
rootProject.name = 'Cally - Family Planner'
|
rootProject.name = 'Cally.'
|
||||||
|
|
||||||
dependencyResolutionManagement {
|
dependencyResolutionManagement {
|
||||||
versionCatalogs {
|
versionCatalogs {
|
||||||
|
17
app.json
17
app.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"expo": {
|
"expo": {
|
||||||
"name": "Cally - Family Planner",
|
"name": "Cally.",
|
||||||
"slug": "cally",
|
"slug": "cally",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"orientation": "portrait",
|
"orientation": "portrait",
|
||||||
@ -16,7 +16,8 @@
|
|||||||
"supportsTablet": true,
|
"supportsTablet": true,
|
||||||
"bundleIdentifier": "com.cally.app",
|
"bundleIdentifier": "com.cally.app",
|
||||||
"googleServicesFile": "./ios/GoogleService-Info.plist",
|
"googleServicesFile": "./ios/GoogleService-Info.plist",
|
||||||
"buildNumber": "23"
|
"buildNumber": "23",
|
||||||
|
"usesAppleSignIn": true
|
||||||
},
|
},
|
||||||
"android": {
|
"android": {
|
||||||
"adaptiveIcon": {
|
"adaptiveIcon": {
|
||||||
@ -63,7 +64,17 @@
|
|||||||
"defaultChannel": "default"
|
"defaultChannel": "default"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"expo-font"
|
[
|
||||||
|
"expo-calendar",
|
||||||
|
{
|
||||||
|
"calendarPermission": "The app needs to access your calendar."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"expo-apple-authentication"
|
||||||
|
],
|
||||||
|
"expo-font",
|
||||||
|
"expo-localization"
|
||||||
],
|
],
|
||||||
"experiments": {
|
"experiments": {
|
||||||
"typedRoutes": true
|
"typedRoutes": true
|
||||||
|
41
calendar-integration/apple-calendar-utils.js
Normal file
41
calendar-integration/apple-calendar-utils.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import * as Calendar from 'expo-calendar';
|
||||||
|
|
||||||
|
export async function fetchiPhoneCalendarEvents(familyId, email, startDate, endDate) {
|
||||||
|
try {
|
||||||
|
const {status} = await Calendar.requestCalendarPermissionsAsync();
|
||||||
|
if (status !== 'granted') {
|
||||||
|
throw new Error("Calendar permission not granted");
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultCalendarSource = await Calendar.getDefaultCalendarAsync();
|
||||||
|
|
||||||
|
if (!defaultCalendarSource) {
|
||||||
|
throw new Error("No calendar found");
|
||||||
|
}
|
||||||
|
|
||||||
|
const events = await Calendar.getEventsAsync(
|
||||||
|
[defaultCalendarSource.id],
|
||||||
|
startDate,
|
||||||
|
endDate
|
||||||
|
);
|
||||||
|
|
||||||
|
return events.map((event) => {
|
||||||
|
let isAllDay = event.allDay || false;
|
||||||
|
const startDateTime = new Date(event.startDate);
|
||||||
|
const endDateTime = new Date(event.endDate);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: event.id,
|
||||||
|
title: event.title,
|
||||||
|
startDate: startDateTime,
|
||||||
|
endDate: endDateTime,
|
||||||
|
allDay: isAllDay,
|
||||||
|
familyId,
|
||||||
|
email
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching iPhone Calendar events: ", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
export async function fetchGoogleCalendarEvents(token, email, startDate, endDate) {
|
export async function fetchGoogleCalendarEvents(token, email, familyId, startDate, endDate) {
|
||||||
console.log(token);
|
console.log(token);
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`https://www.googleapis.com/calendar/v3/calendars/primary/events?single_events=true&time_min=${startDate}&time_max=${endDate}`,
|
`https://www.googleapis.com/calendar/v3/calendars/primary/events?single_events=true&time_min=${startDate}&time_max=${endDate}`,
|
||||||
@ -45,6 +45,7 @@ export async function fetchGoogleCalendarEvents(token, email, startDate, endDate
|
|||||||
startDate: startDateTime,
|
startDate: startDateTime,
|
||||||
endDate: endDateTime,
|
endDate: endDateTime,
|
||||||
allDay: isAllDay,
|
allDay: isAllDay,
|
||||||
|
familyId,
|
||||||
email
|
email
|
||||||
};
|
};
|
||||||
googleEvents.push(googleEvent);
|
googleEvents.push(googleEvent);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
export async function fetchMicrosoftCalendarEvents(token, email, startDate, endDate) {
|
export async function fetchMicrosoftCalendarEvents(token, email, familyId, startDate, endDate) {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`https://graph.microsoft.com/v1.0/me/calendar/calendarView?startDateTime=${startDate}&endDateTime=${endDate}`,
|
`https://graph.microsoft.com/v1.0/me/calendar/calendarView?startDateTime=${startDate}&endDateTime=${endDate}`,
|
||||||
{
|
{
|
||||||
@ -34,6 +34,7 @@ export async function fetchMicrosoftCalendarEvents(token, email, startDate, endD
|
|||||||
startDate: startDateTime,
|
startDate: startDateTime,
|
||||||
endDate: endDateTime,
|
endDate: endDateTime,
|
||||||
allDay: item.isAllDay,
|
allDay: item.isAllDay,
|
||||||
|
familyId,
|
||||||
email
|
email
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
selectedDateAtom,
|
selectedDateAtom,
|
||||||
selectedNewEventDateAtom
|
selectedNewEventDateAtom
|
||||||
} from "@/components/pages/calendar/atoms";
|
} from "@/components/pages/calendar/atoms";
|
||||||
|
import {useAuthContext} from "@/contexts/AuthContext";
|
||||||
|
|
||||||
interface EventCalendarProps {
|
interface EventCalendarProps {
|
||||||
calendarHeight: number;
|
calendarHeight: number;
|
||||||
@ -17,6 +18,7 @@ interface EventCalendarProps {
|
|||||||
|
|
||||||
export const EventCalendar: React.FC<EventCalendarProps> = memo(({calendarHeight}) => {
|
export const EventCalendar: React.FC<EventCalendarProps> = memo(({calendarHeight}) => {
|
||||||
const {data: events} = useGetEvents();
|
const {data: events} = useGetEvents();
|
||||||
|
const {profileData} = useAuthContext()
|
||||||
const [selectedDate, setSelectedDate] = useAtom(selectedDateAtom)
|
const [selectedDate, setSelectedDate] = useAtom(selectedDateAtom)
|
||||||
const mode = useAtomValue(modeAtom)
|
const mode = useAtomValue(modeAtom)
|
||||||
const setEditVisible = useSetAtom(editVisibleAtom)
|
const setEditVisible = useSetAtom(editVisibleAtom)
|
||||||
@ -35,6 +37,7 @@ export const EventCalendar: React.FC<EventCalendarProps> = memo(({calendarHeight
|
|||||||
setEditVisible(true);
|
setEditVisible(true);
|
||||||
setEventForEdit(event);
|
setEventForEdit(event);
|
||||||
}}
|
}}
|
||||||
|
weekStartsOn={profileData?.firstDayOfWeek === "Mondays" ? 1 : 0}
|
||||||
height={calendarHeight}
|
height={calendarHeight}
|
||||||
activeDate={selectedDate}
|
activeDate={selectedDate}
|
||||||
date={selectedDate}
|
date={selectedDate}
|
||||||
|
@ -20,7 +20,7 @@ export const InnerCalendar = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<View
|
<View
|
||||||
style={{flex: 1, backgroundColor: "#fff", borderRadius: 30}}
|
style={{flex: 1, backgroundColor: "#fff", borderRadius: 30, marginBottom: 60}}
|
||||||
ref={calendarContainerRef}
|
ref={calendarContainerRef}
|
||||||
onLayout={onLayout}
|
onLayout={onLayout}
|
||||||
>
|
>
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
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 {ScrollView, StyleSheet} from "react-native";
|
import {ActivityIndicator, ScrollView, StyleSheet} from "react-native";
|
||||||
import {colorMap} from "@/contexts/SettingsContext";
|
import {colorMap} from "@/contexts/SettingsContext";
|
||||||
import {TouchableOpacity} from "react-native-gesture-handler";
|
import {TouchableOpacity} from "react-native-gesture-handler";
|
||||||
import {fetchGoogleCalendarEvents} from "@/calendar-integration/google-calendar-utils";
|
|
||||||
import {fetchMicrosoftCalendarEvents} from "@/calendar-integration/microsoft-calendar-utils";
|
|
||||||
import {useCreateEventsFromProvider} from "@/hooks/firebase/useCreateEvent";
|
|
||||||
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";
|
||||||
@ -17,6 +14,11 @@ 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 {UserProfile} from "@firebase/auth";
|
||||||
|
import {useFetchAndSaveGoogleEvents} from "@/hooks/useFetchAndSaveGoogleEvents";
|
||||||
|
import {useFetchAndSaveOutlookEvents} from "@/hooks/useFetchAndSaveOutlookEvents";
|
||||||
|
import {useFetchAndSaveAppleEvents} from "@/hooks/useFetchAndSaveAppleEvents";
|
||||||
|
import * as AppleAuthentication from 'expo-apple-authentication';
|
||||||
|
import ExpoLocalization from "expo-localization/src/ExpoLocalization";
|
||||||
|
|
||||||
const googleConfig = {
|
const googleConfig = {
|
||||||
androidClientId:
|
androidClientId:
|
||||||
@ -50,8 +52,8 @@ const microsoftConfig = {
|
|||||||
const CalendarSettingsPage = (props: {
|
const CalendarSettingsPage = (props: {
|
||||||
setSelectedPage: (page: number) => void;
|
setSelectedPage: (page: number) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const [startDate, setStartDate] = useState<boolean>(false);
|
|
||||||
const {profileData} = useAuthContext();
|
const {profileData} = useAuthContext();
|
||||||
|
const [firstDayOfWeek, setFirstDayOfWeek] = useState<string>(profileData?.firstDayOfWeek ?? ExpoLocalization.getCalendars()[0].firstWeekday === 1 ? "Mondays" : "Sundays");
|
||||||
|
|
||||||
const [selectedColor, setSelectedColor] = useState<string>(
|
const [selectedColor, setSelectedColor] = useState<string>(
|
||||||
profileData?.eventColor ?? colorMap.pink
|
profileData?.eventColor ?? colorMap.pink
|
||||||
@ -60,8 +62,11 @@ const CalendarSettingsPage = (props: {
|
|||||||
profileData?.eventColor ?? colorMap.pink
|
profileData?.eventColor ?? colorMap.pink
|
||||||
);
|
);
|
||||||
|
|
||||||
const {mutateAsync: createEventsFromProvider} = useCreateEventsFromProvider();
|
|
||||||
const {mutateAsync: updateUserData} = useUpdateUserData();
|
const {mutateAsync: updateUserData} = useUpdateUserData();
|
||||||
|
const {mutateAsync: fetchAndSaveGoogleEvents, isLoading: isSyncingGoogle} = useFetchAndSaveGoogleEvents();
|
||||||
|
const {mutateAsync: fetchAndSaveOutlookEvents, isLoading: isSyncingOutlook} = useFetchAndSaveOutlookEvents();
|
||||||
|
const {mutateAsync: fetchAndSaveAppleEvents, isLoading: isSyncingApple} = useFetchAndSaveAppleEvents();
|
||||||
|
|
||||||
|
|
||||||
WebBrowser.maybeCompleteAuthSession();
|
WebBrowser.maybeCompleteAuthSession();
|
||||||
const [_, response, promptAsync] = Google.useAuthRequest(googleConfig);
|
const [_, response, promptAsync] = Google.useAuthRequest(googleConfig);
|
||||||
@ -70,49 +75,6 @@ const CalendarSettingsPage = (props: {
|
|||||||
signInWithGoogle();
|
signInWithGoogle();
|
||||||
}, [response]);
|
}, [response]);
|
||||||
|
|
||||||
const fetchAndSaveGoogleEvents = async (token?: string, email?: string) => {
|
|
||||||
console.log("Fetching Google Calendar events...");
|
|
||||||
const timeMin = new Date(new Date().setFullYear(new Date().getFullYear() - 1));
|
|
||||||
const timeMax = new Date(new Date().setFullYear(new Date().getFullYear() + 5));
|
|
||||||
|
|
||||||
console.log("Token: ", token ?? profileData?.googleToken)
|
|
||||||
fetchGoogleCalendarEvents(
|
|
||||||
token ?? profileData?.googleToken,
|
|
||||||
email ?? profileData?.googleMail,
|
|
||||||
timeMin.toISOString().slice(0, -5) + "Z",
|
|
||||||
timeMax.toISOString().slice(0, -5) + "Z"
|
|
||||||
).then(async (response) => {
|
|
||||||
console.log("Google Calendar events fetched:", response);
|
|
||||||
const items = response?.map((item) => {
|
|
||||||
if (item.allDay) {
|
|
||||||
item.startDate = new Date(new Date(item.startDate).setHours(0, 0, 0, 0))
|
|
||||||
item.endDate = item.startDate
|
|
||||||
}
|
|
||||||
return item;
|
|
||||||
}) || [];
|
|
||||||
await createEventsFromProvider(items);
|
|
||||||
}).catch((error) => {
|
|
||||||
console.error("Error fetching Google Calendar events:", error);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchAndSaveMicrosoftEvents = async (token?: string, email?: string) => {
|
|
||||||
const timeMin = new Date(new Date().setFullYear(new Date().getFullYear() - 1));
|
|
||||||
const timeMax = new Date(new Date().setFullYear(new Date().getFullYear() + 3));
|
|
||||||
|
|
||||||
console.log("Token: ", token ?? profileData?.microsoftToken)
|
|
||||||
fetchMicrosoftCalendarEvents(
|
|
||||||
token ?? profileData?.microsoftToken,
|
|
||||||
email ?? profileData?.outlookMail,
|
|
||||||
timeMin.toISOString().slice(0, -5) + "Z",
|
|
||||||
timeMax.toISOString().slice(0, -5) + "Z"
|
|
||||||
).then(async (response) => {
|
|
||||||
console.log(response);
|
|
||||||
const items = response ?? [];
|
|
||||||
await createEventsFromProvider(items);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const signInWithGoogle = async () => {
|
const signInWithGoogle = async () => {
|
||||||
try {
|
try {
|
||||||
if (response?.type === "success") {
|
if (response?.type === "success") {
|
||||||
@ -214,7 +176,7 @@ const CalendarSettingsPage = (props: {
|
|||||||
newUserData: {microsoftToken: tokenData.access_token, outlookMail: outlookMail},
|
newUserData: {microsoftToken: tokenData.access_token, outlookMail: outlookMail},
|
||||||
});
|
});
|
||||||
|
|
||||||
await fetchAndSaveMicrosoftEvents(tokenData.access_token, outlookMail)
|
await fetchAndSaveOutlookEvents(tokenData.access_token, outlookMail)
|
||||||
console.log("User data updated successfully.");
|
console.log("User data updated successfully.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -226,6 +188,39 @@ const CalendarSettingsPage = (props: {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleAppleSignIn = async () => {
|
||||||
|
try {
|
||||||
|
console.log("Starting Apple Sign-in...");
|
||||||
|
|
||||||
|
const credential = await AppleAuthentication.signInAsync({
|
||||||
|
requestedScopes: [
|
||||||
|
AppleAuthentication.AppleAuthenticationScope.EMAIL,
|
||||||
|
AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("Apple sign-in result:", credential);
|
||||||
|
|
||||||
|
const appleToken = credential.identityToken;
|
||||||
|
const appleMail = credential.email;
|
||||||
|
|
||||||
|
if (appleToken) {
|
||||||
|
console.log("Apple ID token received. Fetch user info if needed...");
|
||||||
|
|
||||||
|
// Example: Store user token and email
|
||||||
|
await updateUserData({
|
||||||
|
newUserData: {appleToken, appleMail},
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("User data updated with Apple ID token.");
|
||||||
|
} else {
|
||||||
|
console.warn("Apple authentication was not successful or email was hidden.");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error during Apple Sign-in:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const debouncedUpdateUserData = useCallback(
|
const debouncedUpdateUserData = useCallback(
|
||||||
debounce(async (color: string) => {
|
debounce(async (color: string) => {
|
||||||
try {
|
try {
|
||||||
@ -242,6 +237,26 @@ const CalendarSettingsPage = (props: {
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const debouncedUpdateFirstDayOfWeek = useCallback(
|
||||||
|
debounce(async (firstDayOfWeek: string) => {
|
||||||
|
try {
|
||||||
|
await updateUserData({
|
||||||
|
newUserData: {
|
||||||
|
firstDayOfWeek,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to update first day of week:", error);
|
||||||
|
}
|
||||||
|
}, 500),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleChangeFirstDayOfWeek = () => {
|
||||||
|
setFirstDayOfWeek(firstDayOfWeek === "Sundays" ? "Mondays" : "Sundays");
|
||||||
|
debouncedUpdateFirstDayOfWeek(firstDayOfWeek === "Sundays" ? "Mondays" : "Sundays");
|
||||||
|
}
|
||||||
|
|
||||||
const handleChangeColor = (color: string) => {
|
const handleChangeColor = (color: string) => {
|
||||||
setPreviousSelectedColor(selectedColor);
|
setPreviousSelectedColor(selectedColor);
|
||||||
setSelectedColor(color);
|
setSelectedColor(color);
|
||||||
@ -333,10 +348,10 @@ const CalendarSettingsPage = (props: {
|
|||||||
<Text style={styles.cardTitle}>Weekly Start Date</Text>
|
<Text style={styles.cardTitle}>Weekly Start Date</Text>
|
||||||
<View row marginV-5 marginT-20>
|
<View row marginV-5 marginT-20>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
value={startDate}
|
value={firstDayOfWeek === "Sundays"}
|
||||||
style={styles.checkbox}
|
style={styles.checkbox}
|
||||||
color="#ea156d"
|
color="#ea156d"
|
||||||
onValueChange={() => setStartDate(true)}
|
onValueChange={() => handleChangeFirstDayOfWeek("Sundays")}
|
||||||
/>
|
/>
|
||||||
<View row marginL-8>
|
<View row marginL-8>
|
||||||
<Text text70>Sundays</Text>
|
<Text text70>Sundays</Text>
|
||||||
@ -348,10 +363,10 @@ const CalendarSettingsPage = (props: {
|
|||||||
</View>
|
</View>
|
||||||
<View row marginV-5>
|
<View row marginV-5>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
value={!startDate}
|
value={firstDayOfWeek === "Mondays"}
|
||||||
style={styles.checkbox}
|
style={styles.checkbox}
|
||||||
color="#ea156d"
|
color="#ea156d"
|
||||||
onValueChange={() => setStartDate(false)}
|
onValueChange={() => handleChangeFirstDayOfWeek("Mondays")}
|
||||||
/>
|
/>
|
||||||
<Text text70 marginL-8>
|
<Text text70 marginL-8>
|
||||||
Mondays
|
Mondays
|
||||||
@ -379,7 +394,8 @@ const CalendarSettingsPage = (props: {
|
|||||||
text70BL
|
text70BL
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
label="Connect Apple"
|
onPress={() => !profileData?.appleToken ? handleAppleSignIn() : clearToken("google")}
|
||||||
|
label={profileData?.appleToken ? `Disconnect ${profileData.appleMail}` : "Connect Apple"}
|
||||||
labelStyle={styles.addCalLbl}
|
labelStyle={styles.addCalLbl}
|
||||||
labelProps={{
|
labelProps={{
|
||||||
numberOfLines: 2
|
numberOfLines: 2
|
||||||
@ -416,40 +432,93 @@ const CalendarSettingsPage = (props: {
|
|||||||
Connected Calendars
|
Connected Calendars
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<View style={styles.card}>
|
<View style={styles.noPaddingCard}>
|
||||||
<View style={{marginTop: 20}}>
|
<View style={{marginTop: 20}}>
|
||||||
{!!profileData?.googleMail && (
|
{!!profileData?.googleMail && (
|
||||||
<Button
|
<TouchableOpacity
|
||||||
onPress={() => fetchAndSaveGoogleEvents()}
|
onPress={() => fetchAndSaveGoogleEvents(undefined, undefined)}
|
||||||
label={`Sync ${profileData?.googleMail}`}
|
>
|
||||||
labelStyle={styles.addCalLbl}
|
<View row paddingR-20 center>
|
||||||
labelProps={{numberOfLines: 2}}
|
<Button
|
||||||
iconSource={() => (
|
disabled={isSyncingGoogle}
|
||||||
<View marginR-15>
|
onPress={() => fetchAndSaveGoogleEvents(undefined, undefined)}
|
||||||
<GoogleIcon/>
|
label={`Sync ${profileData?.googleMail}`}
|
||||||
</View>
|
labelStyle={styles.addCalLbl}
|
||||||
)}
|
labelProps={{numberOfLines: 3}}
|
||||||
style={styles.addCalBtn}
|
iconSource={() => (
|
||||||
color="black"
|
<View marginR-15>
|
||||||
text70BL
|
<GoogleIcon/>
|
||||||
/>
|
</View>
|
||||||
|
)}
|
||||||
|
style={styles.addCalBtn}
|
||||||
|
color="black"
|
||||||
|
text70BL
|
||||||
|
/>
|
||||||
|
|
||||||
|
{isSyncingGoogle ? (
|
||||||
|
<ActivityIndicator/>
|
||||||
|
) : (
|
||||||
|
<Ionicons name={"refresh"} size={20} color={"#000000"}/>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
{!!profileData?.appleMail && (
|
||||||
|
<TouchableOpacity>
|
||||||
|
<View row paddingR-20 center>
|
||||||
|
<Button
|
||||||
|
disabled={isSyncingApple}
|
||||||
|
onPress={() => fetchAndSaveAppleEvents(undefined, undefined)}
|
||||||
|
label={`Sync ${profileData?.appleMail}`}
|
||||||
|
labelStyle={styles.addCalLbl}
|
||||||
|
labelProps={{numberOfLines: 3}}
|
||||||
|
iconSource={() => (
|
||||||
|
<View marginR-15>
|
||||||
|
<AppleIcon/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
style={styles.addCalBtn}
|
||||||
|
color="black"
|
||||||
|
text70BL
|
||||||
|
/>
|
||||||
|
{isSyncingApple ? (
|
||||||
|
<ActivityIndicator/>
|
||||||
|
) : (
|
||||||
|
<Ionicons name={"refresh"} size={20} color={"#000000"}/>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!!profileData?.outlookMail && (
|
{!!profileData?.outlookMail && (
|
||||||
<Button
|
<TouchableOpacity
|
||||||
onPress={() => fetchAndSaveMicrosoftEvents()}
|
onPress={() => fetchAndSaveOutlookEvents(undefined, undefined)}
|
||||||
label={`Sync ${profileData?.outlookMail}`}
|
>
|
||||||
labelStyle={styles.addCalLbl}
|
<View row paddingR-20 center>
|
||||||
labelProps={{numberOfLines: 2}}
|
<Button
|
||||||
iconSource={() => (
|
disabled={isSyncingOutlook}
|
||||||
<View marginR-15>
|
onPress={() => fetchAndSaveOutlookEvents(undefined, undefined)}
|
||||||
<OutlookIcon/>
|
label={`Sync ${profileData?.outlookMail}`}
|
||||||
</View>
|
labelStyle={styles.addCalLbl}
|
||||||
)}
|
labelProps={{numberOfLines: 3}}
|
||||||
style={styles.addCalBtn}
|
iconSource={() => (
|
||||||
color="black"
|
<View marginR-15>
|
||||||
text70BL
|
<OutlookIcon/>
|
||||||
/>
|
</View>
|
||||||
|
)}
|
||||||
|
style={styles.addCalBtn}
|
||||||
|
color="black"
|
||||||
|
text70BL
|
||||||
|
/>
|
||||||
|
{isSyncingOutlook ? (
|
||||||
|
<ActivityIndicator/>
|
||||||
|
) : (
|
||||||
|
<Ionicons name={"refresh"} size={20} color={"#000000"}/>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@ -480,6 +549,12 @@ const styles = StyleSheet.create({
|
|||||||
marginTop: 20,
|
marginTop: 20,
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
},
|
},
|
||||||
|
noPaddingCard: {
|
||||||
|
backgroundColor: "white",
|
||||||
|
width: "100%",
|
||||||
|
marginTop: 20,
|
||||||
|
borderRadius: 12,
|
||||||
|
},
|
||||||
colorBox: {
|
colorBox: {
|
||||||
aspectRatio: 1,
|
aspectRatio: 1,
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,122 +1,207 @@
|
|||||||
import { Text, TextField, View } from "react-native-ui-lib";
|
import {Colors, Picker, Text, TextField, View} from "react-native-ui-lib";
|
||||||
import React, { useState } from "react";
|
import React, {useEffect, useRef, useState} from "react";
|
||||||
import { ImageBackground, StyleSheet } from "react-native";
|
import {ImageBackground, StyleSheet} from "react-native";
|
||||||
import { ScrollView } from "react-native-gesture-handler";
|
import {ScrollView} 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 Ionicons from "@expo/vector-icons/Ionicons";
|
||||||
|
import * as tz from 'tzdata';
|
||||||
|
import * as Localization from 'expo-localization';
|
||||||
|
import debounce from "debounce";
|
||||||
|
|
||||||
const MyProfile = () => {
|
const MyProfile = () => {
|
||||||
const { user, profileData } = useAuthContext();
|
const {user, profileData} = useAuthContext();
|
||||||
|
|
||||||
const [lastName, setLastName] = useState<string>(profileData?.lastName || "");
|
const [timeZone, setTimeZone] = useState<string>(profileData?.timeZone! ?? Localization.getCalendars()[0].timeZone);
|
||||||
const [firstName, setFirstName] = useState<string>(
|
const [lastName, setLastName] = useState<string>(profileData?.lastName || "");
|
||||||
profileData?.firstName || ""
|
const [firstName, setFirstName] = useState<string>(
|
||||||
);
|
profileData?.firstName || ""
|
||||||
|
);
|
||||||
|
|
||||||
const { mutateAsync: updateUserData } = useUpdateUserData();
|
const {mutateAsync: updateUserData} = useUpdateUserData();
|
||||||
return (
|
const isFirstRender = useRef(true);
|
||||||
<ScrollView style={{ paddingBottom: 100, flex: 1 }}>
|
|
||||||
<View style={styles.card}>
|
|
||||||
<Text style={styles.subTit}>Your Profile</Text>
|
|
||||||
<View row spread paddingH-15 centerV marginV-15>
|
|
||||||
<ImageBackground
|
|
||||||
style={styles.pfp}
|
|
||||||
source={require("../../../../assets/images/profile-picture.png")}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Text style={styles.photoSet} color="#50be0c">
|
|
||||||
Change Photo
|
|
||||||
</Text>
|
|
||||||
<Text style={styles.photoSet}>Remove Photo</Text>
|
|
||||||
</View>
|
|
||||||
<View paddingH-15>
|
|
||||||
<Text text80 marginT-10 marginB-7 style={styles.label}>
|
|
||||||
First name
|
|
||||||
</Text>
|
|
||||||
<TextField
|
|
||||||
text70
|
|
||||||
placeholder="First name"
|
|
||||||
style={styles.txtBox}
|
|
||||||
value={firstName}
|
|
||||||
onChangeText={async (value) => {
|
|
||||||
setFirstName(value);
|
|
||||||
await updateUserData({ newUserData: { firstName: value } });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Text text80 marginT-10 marginB-7 style={styles.label}>
|
|
||||||
Last name
|
|
||||||
</Text>
|
|
||||||
<TextField
|
|
||||||
text70
|
|
||||||
placeholder="Last name"
|
|
||||||
style={styles.txtBox}
|
|
||||||
value={lastName}
|
|
||||||
onChangeText={async (value) => {
|
|
||||||
setLastName(value);
|
|
||||||
await updateUserData({ newUserData: { lastName: value } });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Text text80 marginT-10 marginB-7 style={styles.label}>
|
|
||||||
Email address
|
|
||||||
</Text>
|
|
||||||
<TextField
|
|
||||||
text70
|
|
||||||
placeholder="Email address"
|
|
||||||
value={user?.email?.toString()}
|
|
||||||
style={styles.txtBox}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View style={styles.card}>
|
const handleUpdateUserData = async () => {
|
||||||
<Text style={styles.subTit}>Settings</Text>
|
await updateUserData({newUserData: {firstName, lastName, timeZone}});
|
||||||
<Text text80 marginT-20 marginB-7 style={styles.label}>
|
}
|
||||||
Time Zone
|
|
||||||
</Text>
|
const debouncedUserDataUpdate = debounce(handleUpdateUserData, 500);
|
||||||
<TextField text70 placeholder="Time Zone" style={styles.txtBox} />
|
|
||||||
</View>
|
useEffect(() => {
|
||||||
</ScrollView>
|
if (isFirstRender.current) {
|
||||||
);
|
isFirstRender.current = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
debouncedUserDataUpdate();
|
||||||
|
}, [timeZone, lastName, firstName]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ScrollView style={{paddingBottom: 100, flex: 1}}>
|
||||||
|
<View style={styles.card}>
|
||||||
|
<Text style={styles.subTit}>Your Profile</Text>
|
||||||
|
<View row spread paddingH-15 centerV marginV-15>
|
||||||
|
<ImageBackground
|
||||||
|
style={styles.pfp}
|
||||||
|
source={require("../../../../assets/images/profile-picture.png")}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Text style={styles.photoSet} color="#50be0c">
|
||||||
|
Change Photo
|
||||||
|
</Text>
|
||||||
|
<Text style={styles.photoSet}>Remove Photo</Text>
|
||||||
|
</View>
|
||||||
|
<View paddingH-15>
|
||||||
|
<Text text80 marginT-10 marginB-7 style={styles.label}>
|
||||||
|
First name
|
||||||
|
</Text>
|
||||||
|
<TextField
|
||||||
|
text70
|
||||||
|
placeholder="First name"
|
||||||
|
style={styles.txtBox}
|
||||||
|
value={firstName}
|
||||||
|
onChangeText={async (value) => {
|
||||||
|
setFirstName(value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Text text80 marginT-10 marginB-7 style={styles.label}>
|
||||||
|
Last name
|
||||||
|
</Text>
|
||||||
|
<TextField
|
||||||
|
text70
|
||||||
|
placeholder="Last name"
|
||||||
|
style={styles.txtBox}
|
||||||
|
value={lastName}
|
||||||
|
onChangeText={async (value) => {
|
||||||
|
setLastName(value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Text text80 marginT-10 marginB-7 style={styles.label}>
|
||||||
|
Email address
|
||||||
|
</Text>
|
||||||
|
<TextField
|
||||||
|
editable={false}
|
||||||
|
text70
|
||||||
|
placeholder="Email address"
|
||||||
|
value={user?.email?.toString()}
|
||||||
|
style={styles.txtBox}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.card}>
|
||||||
|
<Text style={styles.subTit}>Settings</Text>
|
||||||
|
<Text style={styles.jakarta12}>Time Zone</Text>
|
||||||
|
<View style={styles.viewPicker}>
|
||||||
|
<Picker
|
||||||
|
// editable={!isLoading}
|
||||||
|
value={timeZone}
|
||||||
|
onChange={(item) => {
|
||||||
|
setTimeZone(item as string)
|
||||||
|
}}
|
||||||
|
showSearch
|
||||||
|
floatingPlaceholder
|
||||||
|
style={styles.inViewPicker}
|
||||||
|
trailingAccessory={
|
||||||
|
<View style={{
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
height: "100%",
|
||||||
|
marginTop: -38,
|
||||||
|
paddingRight: 15
|
||||||
|
}}>
|
||||||
|
<Ionicons name={"chevron-down"} style={{alignSelf: "center"}} size={20}
|
||||||
|
color={"#000000"}/>
|
||||||
|
</View>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{timeZoneItems}
|
||||||
|
</Picker>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const timeZoneItems = Object.keys(tz.zones).sort().map((zone) => (
|
||||||
|
<Picker.Item key={zone} label={zone.replace("/", " / ").replace("_", " ")} value={zone}/>
|
||||||
|
));
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
card: {
|
card: {
|
||||||
marginVertical: 15,
|
marginVertical: 15,
|
||||||
backgroundColor: "white",
|
backgroundColor: "white",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
paddingHorizontal: 20,
|
paddingHorizontal: 20,
|
||||||
paddingVertical: 21,
|
paddingVertical: 21,
|
||||||
},
|
},
|
||||||
pfp: {
|
pfp: {
|
||||||
aspectRatio: 1,
|
aspectRatio: 1,
|
||||||
width: 65.54,
|
width: 65.54,
|
||||||
backgroundColor: "green",
|
backgroundColor: "green",
|
||||||
borderRadius: 20,
|
borderRadius: 20,
|
||||||
},
|
},
|
||||||
txtBox: {
|
txtBox: {
|
||||||
backgroundColor: "#fafafa",
|
backgroundColor: "#fafafa",
|
||||||
borderRadius: 50,
|
borderRadius: 50,
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
borderColor: "#cecece",
|
borderColor: "#cecece",
|
||||||
padding: 15,
|
padding: 15,
|
||||||
height: 45,
|
height: 45,
|
||||||
fontFamily: "PlusJakartaSans_500Medium",
|
fontFamily: "PlusJakartaSans_500Medium",
|
||||||
fontSize: 13
|
fontSize: 13
|
||||||
},
|
},
|
||||||
subTit: {
|
subTit: {
|
||||||
fontFamily: "Manrope_500Medium",
|
fontFamily: "Manrope_500Medium",
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
fontFamily: "PlusJakartaSans_500Medium",
|
fontFamily: "PlusJakartaSans_500Medium",
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: "#a1a1a1"
|
color: "#a1a1a1"
|
||||||
},
|
},
|
||||||
photoSet:{
|
photoSet: {
|
||||||
fontFamily: "PlusJakartaSans_500Medium",
|
fontFamily: "PlusJakartaSans_500Medium",
|
||||||
fontSize: 13.07
|
fontSize: 13.07
|
||||||
}
|
},
|
||||||
|
jakarta12: {
|
||||||
|
paddingVertical: 10,
|
||||||
|
fontFamily: "PlusJakartaSans_500Medium",
|
||||||
|
fontSize: 12,
|
||||||
|
color: "#a1a1a1",
|
||||||
|
},
|
||||||
|
picker: {
|
||||||
|
borderRadius: 50,
|
||||||
|
paddingVertical: 12,
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
backgroundColor: Colors.grey80,
|
||||||
|
marginBottom: 16,
|
||||||
|
borderColor: Colors.grey50,
|
||||||
|
borderWidth: 1,
|
||||||
|
marginTop: -20,
|
||||||
|
height: 40,
|
||||||
|
zIndex: 10,
|
||||||
|
},
|
||||||
|
viewPicker: {
|
||||||
|
borderRadius: 50,
|
||||||
|
backgroundColor: Colors.grey80,
|
||||||
|
marginBottom: 16,
|
||||||
|
borderColor: Colors.grey50,
|
||||||
|
borderWidth: 1,
|
||||||
|
marginTop: 0,
|
||||||
|
height: 40,
|
||||||
|
zIndex: 10,
|
||||||
|
},
|
||||||
|
inViewPicker: {
|
||||||
|
borderRadius: 50,
|
||||||
|
paddingVertical: 12,
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
marginBottom: 16,
|
||||||
|
marginTop: -20,
|
||||||
|
height: 40,
|
||||||
|
zIndex: 10,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default MyProfile;
|
export default MyProfile;
|
||||||
|
@ -24,6 +24,8 @@ export interface UserProfile {
|
|||||||
googleMail?: string | null;
|
googleMail?: string | null;
|
||||||
outlookMail?: string | null;
|
outlookMail?: string | null;
|
||||||
appleMail?: string | null;
|
appleMail?: string | null;
|
||||||
|
timeZone?: string | null;
|
||||||
|
firstDayOfWeek?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ParentProfile extends UserProfile {
|
export interface ParentProfile extends UserProfile {
|
||||||
|
@ -6,7 +6,7 @@ import {useAtomValue} from "jotai";
|
|||||||
import {isFamilyViewAtom} from "@/components/pages/calendar/atoms";
|
import {isFamilyViewAtom} from "@/components/pages/calendar/atoms";
|
||||||
|
|
||||||
export const useGetEvents = () => {
|
export const useGetEvents = () => {
|
||||||
const { user, profileData } = useAuthContext();
|
const {user, profileData} = useAuthContext();
|
||||||
const isFamilyView = useAtomValue(isFamilyViewAtom)
|
const isFamilyView = useAtomValue(isFamilyViewAtom)
|
||||||
|
|
||||||
return useQuery({
|
return useQuery({
|
||||||
@ -43,5 +43,7 @@ export const useGetEvents = () => {
|
|||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
staleTime: Infinity,
|
||||||
|
cacheTime: Infinity
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -3,6 +3,7 @@ import auth from "@react-native-firebase/auth";
|
|||||||
import { ProfileType } from "@/contexts/AuthContext";
|
import { ProfileType } from "@/contexts/AuthContext";
|
||||||
import { useSetUserData } from "./useSetUserData";
|
import { useSetUserData } from "./useSetUserData";
|
||||||
import {uuidv4} from "@firebase/util";
|
import {uuidv4} from "@firebase/util";
|
||||||
|
import * as Localization from "expo-localization";
|
||||||
|
|
||||||
export const useSignUp = () => {
|
export const useSignUp = () => {
|
||||||
const { mutateAsync: setUserData } = useSetUserData();
|
const { mutateAsync: setUserData } = useSetUserData();
|
||||||
@ -30,6 +31,7 @@ export const useSignUp = () => {
|
|||||||
firstName: firstName,
|
firstName: firstName,
|
||||||
lastName: lastName,
|
lastName: lastName,
|
||||||
familyId: uuidv4(),
|
familyId: uuidv4(),
|
||||||
|
timeZone: Localization.getCalendars()[0].timeZone,
|
||||||
},
|
},
|
||||||
customUser: res.user,
|
customUser: res.user,
|
||||||
});
|
});
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import firestore from "@react-native-firebase/firestore";
|
import firestore from "@react-native-firebase/firestore";
|
||||||
import { FirebaseAuthTypes } from "@react-native-firebase/auth";
|
import {FirebaseAuthTypes} from "@react-native-firebase/auth";
|
||||||
import { useMutation, useQueryClient } from "react-query";
|
import {useMutation, useQueryClient} from "react-query";
|
||||||
import { useAuthContext } from "@/contexts/AuthContext";
|
import {useAuthContext} from "@/contexts/AuthContext";
|
||||||
import { UserProfile } from "@/hooks/firebase/types/profileTypes";
|
import {UserProfile} from "@/hooks/firebase/types/profileTypes";
|
||||||
|
|
||||||
export const useUpdateUserData = () => {
|
export const useUpdateUserData = () => {
|
||||||
const { user: currentUser, refreshProfileData } = useAuthContext();
|
const {user: currentUser, refreshProfileData} = useAuthContext();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
@ -17,7 +17,7 @@ export const useUpdateUserData = () => {
|
|||||||
newUserData: Partial<UserProfile>;
|
newUserData: Partial<UserProfile>;
|
||||||
customUser?: FirebaseAuthTypes.User;
|
customUser?: FirebaseAuthTypes.User;
|
||||||
}) => {
|
}) => {
|
||||||
console.log("Mutation function called with data:", { newUserData, customUser });
|
console.log("Mutation function called with data:", {newUserData, customUser});
|
||||||
|
|
||||||
const user = currentUser ?? customUser;
|
const user = currentUser ?? customUser;
|
||||||
|
|
||||||
|
32
hooks/useFetchAndSaveAppleEvents.ts
Normal file
32
hooks/useFetchAndSaveAppleEvents.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import {useMutation} from "react-query";
|
||||||
|
import {useAuthContext} from "@/contexts/AuthContext";
|
||||||
|
import {useCreateEventsFromProvider} from "@/hooks/firebase/useCreateEvent";
|
||||||
|
import {fetchiPhoneCalendarEvents} from "@/calendar-integration/apple-calendar-utils";
|
||||||
|
|
||||||
|
export const useFetchAndSaveAppleEvents = () => {
|
||||||
|
const {profileData} = useAuthContext();
|
||||||
|
const {mutateAsync: createEventsFromProvider} = useCreateEventsFromProvider();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationKey: ["fetchAndSaveAppleEvents"],
|
||||||
|
mutationFn: async (token?: string, email?: string) => {
|
||||||
|
const timeMin = new Date(new Date().setFullYear(new Date().getFullYear() - 1));
|
||||||
|
const timeMax = new Date(new Date().setFullYear(new Date().getFullYear() + 5));
|
||||||
|
try {
|
||||||
|
const response = await fetchiPhoneCalendarEvents(
|
||||||
|
profileData?.familyId!,
|
||||||
|
email,
|
||||||
|
timeMin,
|
||||||
|
timeMax
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(response);
|
||||||
|
const items = response ?? [];
|
||||||
|
await createEventsFromProvider(items);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching and saving Apple Calendar events: ", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
45
hooks/useFetchAndSaveGoogleEvents.ts
Normal file
45
hooks/useFetchAndSaveGoogleEvents.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import {useMutation} from "react-query";
|
||||||
|
import {fetchGoogleCalendarEvents} from "@/calendar-integration/google-calendar-utils";
|
||||||
|
import {useAuthContext} from "@/contexts/AuthContext";
|
||||||
|
import {useCreateEventsFromProvider} from "@/hooks/firebase/useCreateEvent";
|
||||||
|
|
||||||
|
export const useFetchAndSaveGoogleEvents = () => {
|
||||||
|
const {profileData} = useAuthContext();
|
||||||
|
const {mutateAsync: createEventsFromProvider} = useCreateEventsFromProvider();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationKey: ["fetchAndSaveGoogleEvents"],
|
||||||
|
mutationFn: async (token?: string, email?: string) => {
|
||||||
|
console.log("Fetching Google Calendar events...");
|
||||||
|
const timeMin = new Date(new Date().setFullYear(new Date().getFullYear() - 1));
|
||||||
|
const timeMax = new Date(new Date().setFullYear(new Date().getFullYear() + 5));
|
||||||
|
|
||||||
|
console.log("Token: ", token ?? profileData?.googleToken);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetchGoogleCalendarEvents(
|
||||||
|
token ?? profileData?.googleToken,
|
||||||
|
email ?? profileData?.googleMail,
|
||||||
|
profileData?.familyId,
|
||||||
|
timeMin.toISOString().slice(0, -5) + "Z",
|
||||||
|
timeMax.toISOString().slice(0, -5) + "Z"
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("Google Calendar events fetched:", response);
|
||||||
|
|
||||||
|
const items = response?.map((item) => {
|
||||||
|
if (item.allDay) {
|
||||||
|
item.startDate = new Date(new Date(item.startDate).setHours(0, 0, 0, 0));
|
||||||
|
item.endDate = item.startDate;
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}) || [];
|
||||||
|
|
||||||
|
await createEventsFromProvider(items);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching Google Calendar events:", error);
|
||||||
|
throw error; // Ensure errors are propagated to the mutation
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
36
hooks/useFetchAndSaveOutlookEvents.ts
Normal file
36
hooks/useFetchAndSaveOutlookEvents.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { useMutation } from "react-query";
|
||||||
|
import { useAuthContext } from "@/contexts/AuthContext";
|
||||||
|
import { useCreateEventsFromProvider } from "@/hooks/firebase/useCreateEvent";
|
||||||
|
import { fetchMicrosoftCalendarEvents } from "@/calendar-integration/microsoft-calendar-utils";
|
||||||
|
|
||||||
|
export const useFetchAndSaveOutlookEvents = () => {
|
||||||
|
const { profileData } = useAuthContext();
|
||||||
|
const { mutateAsync: createEventsFromProvider } = useCreateEventsFromProvider();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationKey: ["fetchAndSaveOutlookEvents"],
|
||||||
|
mutationFn: async (token?: string, email?: string) => {
|
||||||
|
const timeMin = new Date(new Date().setFullYear(new Date().getFullYear() - 1));
|
||||||
|
const timeMax = new Date(new Date().setFullYear(new Date().getFullYear() + 3));
|
||||||
|
|
||||||
|
console.log("Token: ", token ?? profileData?.microsoftToken);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetchMicrosoftCalendarEvents(
|
||||||
|
token ?? profileData?.microsoftToken,
|
||||||
|
email ?? profileData?.outlookMail,
|
||||||
|
profileData?.familyId,
|
||||||
|
timeMin.toISOString().slice(0, -5) + "Z",
|
||||||
|
timeMax.toISOString().slice(0, -5) + "Z"
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(response);
|
||||||
|
const items = response ?? [];
|
||||||
|
await createEventsFromProvider(items);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching and saving Outlook events: ", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
@ -1190,8 +1190,12 @@ PODS:
|
|||||||
- ReactCommon/turbomodule/bridging
|
- ReactCommon/turbomodule/bridging
|
||||||
- ReactCommon/turbomodule/core
|
- ReactCommon/turbomodule/core
|
||||||
- Yoga
|
- Yoga
|
||||||
|
- ExpoAppleAuthentication (6.4.2):
|
||||||
|
- ExpoModulesCore
|
||||||
- ExpoAsset (10.0.10):
|
- ExpoAsset (10.0.10):
|
||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
|
- ExpoCalendar (13.0.5):
|
||||||
|
- ExpoModulesCore
|
||||||
- ExpoCamera (15.0.16):
|
- ExpoCamera (15.0.16):
|
||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
- ZXingObjC/OneD
|
- ZXingObjC/OneD
|
||||||
@ -1210,6 +1214,8 @@ PODS:
|
|||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
- ExpoKeepAwake (13.0.2):
|
- ExpoKeepAwake (13.0.2):
|
||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
|
- ExpoLocalization (15.0.3):
|
||||||
|
- ExpoModulesCore
|
||||||
- ExpoModulesCore (1.12.24):
|
- ExpoModulesCore (1.12.24):
|
||||||
- DoubleConversion
|
- DoubleConversion
|
||||||
- glog
|
- glog
|
||||||
@ -2802,7 +2808,9 @@ DEPENDENCIES:
|
|||||||
- expo-dev-launcher (from `../node_modules/expo-dev-launcher`)
|
- expo-dev-launcher (from `../node_modules/expo-dev-launcher`)
|
||||||
- expo-dev-menu (from `../node_modules/expo-dev-menu`)
|
- expo-dev-menu (from `../node_modules/expo-dev-menu`)
|
||||||
- expo-dev-menu-interface (from `../node_modules/expo-dev-menu-interface/ios`)
|
- expo-dev-menu-interface (from `../node_modules/expo-dev-menu-interface/ios`)
|
||||||
|
- ExpoAppleAuthentication (from `../node_modules/expo-apple-authentication/ios`)
|
||||||
- ExpoAsset (from `../node_modules/expo-asset/ios`)
|
- ExpoAsset (from `../node_modules/expo-asset/ios`)
|
||||||
|
- ExpoCalendar (from `../node_modules/expo-calendar/ios`)
|
||||||
- ExpoCamera (from `../node_modules/expo-camera/ios`)
|
- ExpoCamera (from `../node_modules/expo-camera/ios`)
|
||||||
- ExpoCrypto (from `../node_modules/expo-crypto/ios`)
|
- ExpoCrypto (from `../node_modules/expo-crypto/ios`)
|
||||||
- ExpoDevice (from `../node_modules/expo-device/ios`)
|
- ExpoDevice (from `../node_modules/expo-device/ios`)
|
||||||
@ -2811,6 +2819,7 @@ DEPENDENCIES:
|
|||||||
- ExpoHead (from `../node_modules/expo-router/ios`)
|
- ExpoHead (from `../node_modules/expo-router/ios`)
|
||||||
- ExpoImagePicker (from `../node_modules/expo-image-picker/ios`)
|
- ExpoImagePicker (from `../node_modules/expo-image-picker/ios`)
|
||||||
- ExpoKeepAwake (from `../node_modules/expo-keep-awake/ios`)
|
- ExpoKeepAwake (from `../node_modules/expo-keep-awake/ios`)
|
||||||
|
- ExpoLocalization (from `../node_modules/expo-localization/ios`)
|
||||||
- ExpoModulesCore (from `../node_modules/expo-modules-core`)
|
- ExpoModulesCore (from `../node_modules/expo-modules-core`)
|
||||||
- ExpoSystemUI (from `../node_modules/expo-system-ui/ios`)
|
- ExpoSystemUI (from `../node_modules/expo-system-ui/ios`)
|
||||||
- ExpoWebBrowser (from `../node_modules/expo-web-browser/ios`)
|
- ExpoWebBrowser (from `../node_modules/expo-web-browser/ios`)
|
||||||
@ -2956,8 +2965,12 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../node_modules/expo-dev-menu"
|
:path: "../node_modules/expo-dev-menu"
|
||||||
expo-dev-menu-interface:
|
expo-dev-menu-interface:
|
||||||
:path: "../node_modules/expo-dev-menu-interface/ios"
|
:path: "../node_modules/expo-dev-menu-interface/ios"
|
||||||
|
ExpoAppleAuthentication:
|
||||||
|
:path: "../node_modules/expo-apple-authentication/ios"
|
||||||
ExpoAsset:
|
ExpoAsset:
|
||||||
:path: "../node_modules/expo-asset/ios"
|
:path: "../node_modules/expo-asset/ios"
|
||||||
|
ExpoCalendar:
|
||||||
|
:path: "../node_modules/expo-calendar/ios"
|
||||||
ExpoCamera:
|
ExpoCamera:
|
||||||
:path: "../node_modules/expo-camera/ios"
|
:path: "../node_modules/expo-camera/ios"
|
||||||
ExpoCrypto:
|
ExpoCrypto:
|
||||||
@ -2974,6 +2987,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../node_modules/expo-image-picker/ios"
|
:path: "../node_modules/expo-image-picker/ios"
|
||||||
ExpoKeepAwake:
|
ExpoKeepAwake:
|
||||||
:path: "../node_modules/expo-keep-awake/ios"
|
:path: "../node_modules/expo-keep-awake/ios"
|
||||||
|
ExpoLocalization:
|
||||||
|
:path: "../node_modules/expo-localization/ios"
|
||||||
ExpoModulesCore:
|
ExpoModulesCore:
|
||||||
:path: "../node_modules/expo-modules-core"
|
:path: "../node_modules/expo-modules-core"
|
||||||
ExpoSystemUI:
|
ExpoSystemUI:
|
||||||
@ -3142,7 +3157,9 @@ SPEC CHECKSUMS:
|
|||||||
expo-dev-launcher: fe4f2c0a0aa627449eeaec5f9f11e04090f97c40
|
expo-dev-launcher: fe4f2c0a0aa627449eeaec5f9f11e04090f97c40
|
||||||
expo-dev-menu: 5b14897ecce3a8cf9e9cf9109344c2c192a3766a
|
expo-dev-menu: 5b14897ecce3a8cf9e9cf9109344c2c192a3766a
|
||||||
expo-dev-menu-interface: be32c09f1e03833050f0ee290dcc86b3ad0e73e4
|
expo-dev-menu-interface: be32c09f1e03833050f0ee290dcc86b3ad0e73e4
|
||||||
|
ExpoAppleAuthentication: 265219fa0ba1110872079f55f56686b9737b0065
|
||||||
ExpoAsset: 323700f291684f110fb55f0d4022a3362ea9f875
|
ExpoAsset: 323700f291684f110fb55f0d4022a3362ea9f875
|
||||||
|
ExpoCalendar: 135beb39ea3795f854a4ea287a49f74c9203ce51
|
||||||
ExpoCamera: 929be541d1c1319fcf32f9f5d9df8b97804346b5
|
ExpoCamera: 929be541d1c1319fcf32f9f5d9df8b97804346b5
|
||||||
ExpoCrypto: 156078f266bf28f80ecf5e2a9c3a0d6ffce07a1c
|
ExpoCrypto: 156078f266bf28f80ecf5e2a9c3a0d6ffce07a1c
|
||||||
ExpoDevice: fc94f0e42ecdfd897e7590f2874fc64dfa7e9b1c
|
ExpoDevice: fc94f0e42ecdfd897e7590f2874fc64dfa7e9b1c
|
||||||
@ -3151,6 +3168,7 @@ SPEC CHECKSUMS:
|
|||||||
ExpoHead: fcb28a68ed4ba28f177394d2dfb8a0a8824cd103
|
ExpoHead: fcb28a68ed4ba28f177394d2dfb8a0a8824cd103
|
||||||
ExpoImagePicker: 12a420923383ae38dccb069847218f27a3b87816
|
ExpoImagePicker: 12a420923383ae38dccb069847218f27a3b87816
|
||||||
ExpoKeepAwake: 3b8815d9dd1d419ee474df004021c69fdd316d08
|
ExpoKeepAwake: 3b8815d9dd1d419ee474df004021c69fdd316d08
|
||||||
|
ExpoLocalization: f04eeec2e35bed01ab61c72ee1768ec04d093d01
|
||||||
ExpoModulesCore: db3e31e694684f08223d713e89f7648c6d3e04d0
|
ExpoModulesCore: db3e31e694684f08223d713e89f7648c6d3e04d0
|
||||||
ExpoSystemUI: d4f065a016cae6721b324eb659cdee4d4cf0cb26
|
ExpoSystemUI: d4f065a016cae6721b324eb659cdee4d4cf0cb26
|
||||||
ExpoWebBrowser: 7595ccac6938eb65b076385fd23d035db9ecdc8e
|
ExpoWebBrowser: 7595ccac6938eb65b076385fd23d035db9ecdc8e
|
||||||
|
@ -178,7 +178,9 @@
|
|||||||
LastUpgradeCheck = 1130;
|
LastUpgradeCheck = 1130;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
13B07F861A680F5B00A75B9A = {
|
13B07F861A680F5B00A75B9A = {
|
||||||
|
DevelopmentTeam = MV9C3PHV87;
|
||||||
LastSwiftMigration = 1250;
|
LastSwiftMigration = 1250;
|
||||||
|
ProvisioningStyle = Automatic;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -302,6 +304,7 @@
|
|||||||
"${PODS_CONFIGURATION_BUILD_DIR}/EXUpdates/EXUpdates.bundle",
|
"${PODS_CONFIGURATION_BUILD_DIR}/EXUpdates/EXUpdates.bundle",
|
||||||
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoDevice/ExpoDevice_privacy.bundle",
|
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoDevice/ExpoDevice_privacy.bundle",
|
||||||
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle",
|
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle",
|
||||||
|
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoLocalization/ExpoLocalization_privacy.bundle",
|
||||||
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoSystemUI/ExpoSystemUI_privacy.bundle",
|
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoSystemUI/ExpoSystemUI_privacy.bundle",
|
||||||
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseAuth/FirebaseAuth_Privacy.bundle",
|
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseAuth/FirebaseAuth_Privacy.bundle",
|
||||||
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore_Privacy.bundle",
|
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore_Privacy.bundle",
|
||||||
@ -337,6 +340,7 @@
|
|||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXUpdates.bundle",
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXUpdates.bundle",
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoDevice_privacy.bundle",
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoDevice_privacy.bundle",
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle",
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle",
|
||||||
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoLocalization_privacy.bundle",
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoSystemUI_privacy.bundle",
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoSystemUI_privacy.bundle",
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseAuth_Privacy.bundle",
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseAuth_Privacy.bundle",
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCore_Privacy.bundle",
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCore_Privacy.bundle",
|
||||||
@ -421,7 +425,10 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = cally/cally.entitlements;
|
CODE_SIGN_ENTITLEMENTS = cally/cally.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = MV9C3PHV87;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
@ -441,7 +448,7 @@
|
|||||||
);
|
);
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
|
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.cally.app;
|
PRODUCT_BUNDLE_IDENTIFIER = com.cally.app;
|
||||||
PRODUCT_NAME = CallyFamilyPlanner;
|
PRODUCT_NAME = "Cally";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "cally/cally-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "cally/cally-Bridging-Header.h";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
@ -457,7 +464,10 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = cally/cally.entitlements;
|
CODE_SIGN_ENTITLEMENTS = cally/cally.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = MV9C3PHV87;
|
||||||
INFOPLIST_FILE = cally/Info.plist;
|
INFOPLIST_FILE = cally/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
|
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
@ -472,7 +482,7 @@
|
|||||||
);
|
);
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
|
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.cally.app;
|
PRODUCT_BUNDLE_IDENTIFIER = com.cally.app;
|
||||||
PRODUCT_NAME = CallyFamilyPlanner;
|
PRODUCT_NAME = "Cally";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "cally/cally-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "cally/cally-Bridging-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
@ -4,10 +4,12 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>CFBundleAllowMixedLocalizations</key>
|
||||||
|
<true/>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleDisplayName</key>
|
<key>CFBundleDisplayName</key>
|
||||||
<string>Cally - Family Planner</string>
|
<string>Cally.</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
@ -55,12 +57,20 @@
|
|||||||
<key>NSAllowsLocalNetworking</key>
|
<key>NSAllowsLocalNetworking</key>
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
|
<key>NSCalendarsFullAccessUsageDescription</key>
|
||||||
|
<string>The app needs to access your calendar.</string>
|
||||||
|
<key>NSCalendarsUsageDescription</key>
|
||||||
|
<string>The app needs to access your calendar.</string>
|
||||||
<key>NSCameraUsageDescription</key>
|
<key>NSCameraUsageDescription</key>
|
||||||
<string>Allow $(PRODUCT_NAME) to access your camera</string>
|
<string>Allow $(PRODUCT_NAME) to access your camera</string>
|
||||||
<key>NSMicrophoneUsageDescription</key>
|
<key>NSMicrophoneUsageDescription</key>
|
||||||
<string>Allow $(PRODUCT_NAME) to access your microphone</string>
|
<string>Allow $(PRODUCT_NAME) to access your microphone</string>
|
||||||
<key>NSPhotoLibraryUsageDescription</key>
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
<string>Allow $(PRODUCT_NAME) to access your photos</string>
|
<string>Allow $(PRODUCT_NAME) to access your photos</string>
|
||||||
|
<key>NSRemindersFullAccessUsageDescription</key>
|
||||||
|
<string>Allow $(PRODUCT_NAME) to access your reminders</string>
|
||||||
|
<key>NSRemindersUsageDescription</key>
|
||||||
|
<string>Allow $(PRODUCT_NAME) to access your reminders</string>
|
||||||
<key>NSUserActivityTypes</key>
|
<key>NSUserActivityTypes</key>
|
||||||
<array>
|
<array>
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
|
||||||
@ -92,6 +102,12 @@
|
|||||||
<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>
|
<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>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
|
||||||
</array>
|
</array>
|
||||||
<key>UILaunchStoryboardName</key>
|
<key>UILaunchStoryboardName</key>
|
||||||
<string>SplashScreen</string>
|
<string>SplashScreen</string>
|
||||||
|
@ -4,5 +4,9 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>aps-environment</key>
|
<key>aps-environment</key>
|
||||||
<string>development</string>
|
<string>development</string>
|
||||||
|
<key>com.apple.developer.applesignin</key>
|
||||||
|
<array>
|
||||||
|
<string>Default</string>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
@ -44,9 +44,11 @@
|
|||||||
"debounce": "^2.1.1",
|
"debounce": "^2.1.1",
|
||||||
"expo": "~51.0.24",
|
"expo": "~51.0.24",
|
||||||
"expo-app-loading": "^2.1.1",
|
"expo-app-loading": "^2.1.1",
|
||||||
|
"expo-apple-authentication": "~6.4.2",
|
||||||
"expo-auth-session": "^5.5.2",
|
"expo-auth-session": "^5.5.2",
|
||||||
"expo-barcode-scanner": "~13.0.1",
|
"expo-barcode-scanner": "~13.0.1",
|
||||||
"expo-build-properties": "~0.12.4",
|
"expo-build-properties": "~0.12.4",
|
||||||
|
"expo-calendar": "~13.0.5",
|
||||||
"expo-camera": "~15.0.16",
|
"expo-camera": "~15.0.16",
|
||||||
"expo-constants": "~16.0.2",
|
"expo-constants": "~16.0.2",
|
||||||
"expo-dev-client": "~4.0.27",
|
"expo-dev-client": "~4.0.27",
|
||||||
@ -54,6 +56,7 @@
|
|||||||
"expo-font": "~12.0.10",
|
"expo-font": "~12.0.10",
|
||||||
"expo-image-picker": "~15.0.7",
|
"expo-image-picker": "~15.0.7",
|
||||||
"expo-linking": "~6.3.1",
|
"expo-linking": "~6.3.1",
|
||||||
|
"expo-localization": "~15.0.3",
|
||||||
"expo-notifications": "~0.28.18",
|
"expo-notifications": "~0.28.18",
|
||||||
"expo-router": "~3.5.20",
|
"expo-router": "~3.5.20",
|
||||||
"expo-splash-screen": "~0.27.5",
|
"expo-splash-screen": "~0.27.5",
|
||||||
@ -84,7 +87,9 @@
|
|||||||
"react-native-toast-message": "^2.2.1",
|
"react-native-toast-message": "^2.2.1",
|
||||||
"react-native-ui-lib": "^7.27.0",
|
"react-native-ui-lib": "^7.27.0",
|
||||||
"react-native-web": "~0.19.10",
|
"react-native-web": "~0.19.10",
|
||||||
"react-query": "^3.39.3"
|
"react-query": "^3.39.3",
|
||||||
|
"timezonecomplete": "^5.13.1",
|
||||||
|
"tzdata": "^1.0.42"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.20.0",
|
"@babel/core": "^7.20.0",
|
||||||
|
39
yarn.lock
39
yarn.lock
@ -5001,6 +5001,11 @@ expo-app-loading@^2.1.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
expo-splash-screen "~0.17.0"
|
expo-splash-screen "~0.17.0"
|
||||||
|
|
||||||
|
expo-apple-authentication@~6.4.2:
|
||||||
|
version "6.4.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/expo-apple-authentication/-/expo-apple-authentication-6.4.2.tgz#1c2ea4fcbd1de5736483dccd370cdd6b8e3de15d"
|
||||||
|
integrity sha512-X4u1n3Ql1hOpztXHbKNq4I1l4+Ff82gC6RmEeW43Eht7VE6E8PrQBpYKw+JJv8osrCJt7R5O1PZwed6WLN5oig==
|
||||||
|
|
||||||
expo-application@~5.9.0:
|
expo-application@~5.9.0:
|
||||||
version "5.9.1"
|
version "5.9.1"
|
||||||
resolved "https://registry.npmjs.org/expo-application/-/expo-application-5.9.1.tgz"
|
resolved "https://registry.npmjs.org/expo-application/-/expo-application-5.9.1.tgz"
|
||||||
@ -5042,6 +5047,11 @@ expo-build-properties@~0.12.4:
|
|||||||
ajv "^8.11.0"
|
ajv "^8.11.0"
|
||||||
semver "^7.6.0"
|
semver "^7.6.0"
|
||||||
|
|
||||||
|
expo-calendar@~13.0.5:
|
||||||
|
version "13.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/expo-calendar/-/expo-calendar-13.0.5.tgz#cdc85978cb59d99d6fc9a4fcd2c33d56cc7c8008"
|
||||||
|
integrity sha512-Wkk7eHvlyhWz2csxU6guYA2HFcLUfYpmlsdMy4a6bneBmFqIZG/ldnLKq/lcQ+BCrfI3fOULt3aNdF6SlZtLlw==
|
||||||
|
|
||||||
expo-camera@~15.0.16:
|
expo-camera@~15.0.16:
|
||||||
version "15.0.16"
|
version "15.0.16"
|
||||||
resolved "https://registry.npmjs.org/expo-camera/-/expo-camera-15.0.16.tgz"
|
resolved "https://registry.npmjs.org/expo-camera/-/expo-camera-15.0.16.tgz"
|
||||||
@ -5153,6 +5163,13 @@ expo-linking@~6.3.0, expo-linking@~6.3.1:
|
|||||||
expo-constants "~16.0.0"
|
expo-constants "~16.0.0"
|
||||||
invariant "^2.2.4"
|
invariant "^2.2.4"
|
||||||
|
|
||||||
|
expo-localization@~15.0.3:
|
||||||
|
version "15.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/expo-localization/-/expo-localization-15.0.3.tgz#772c89b3ab9c925b7eca6911a11ca33980c2b674"
|
||||||
|
integrity sha512-IfcmlKuKRlowR9qIzL0e+nGHBeNoF7l2GQaOJstc7HZiPjNJ4J1R4D53ZNf483dt7JSkTRJBihdTadOtOEjRdg==
|
||||||
|
dependencies:
|
||||||
|
rtl-detect "^1.0.2"
|
||||||
|
|
||||||
expo-manifests@~0.14.0:
|
expo-manifests@~0.14.0:
|
||||||
version "0.14.3"
|
version "0.14.3"
|
||||||
resolved "https://registry.npmjs.org/expo-manifests/-/expo-manifests-0.14.3.tgz"
|
resolved "https://registry.npmjs.org/expo-manifests/-/expo-manifests-0.14.3.tgz"
|
||||||
@ -9276,6 +9293,11 @@ rimraf@~2.6.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
glob "^7.1.3"
|
glob "^7.1.3"
|
||||||
|
|
||||||
|
rtl-detect@^1.0.2:
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/rtl-detect/-/rtl-detect-1.1.2.tgz#ca7f0330af5c6bb626c15675c642ba85ad6273c6"
|
||||||
|
integrity sha512-PGMBq03+TTG/p/cRB7HCLKJ1MgDIi07+QU1faSjiYRfmY5UsAttV9Hs08jDAHVwcOwmVLcSJkpwyfXszVjWfIQ==
|
||||||
|
|
||||||
run-parallel@^1.1.9:
|
run-parallel@^1.1.9:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz"
|
resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz"
|
||||||
@ -10124,6 +10146,13 @@ through@2:
|
|||||||
resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
|
resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
|
||||||
integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
|
integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
|
||||||
|
|
||||||
|
timezonecomplete@^5.13.1:
|
||||||
|
version "5.13.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/timezonecomplete/-/timezonecomplete-5.13.1.tgz#72c05e82b33013bacc7a38e5d554eafc7914b31f"
|
||||||
|
integrity sha512-41o3TTExXQ03jQML12Tk64b5TYeEy968Umq5vya+08sFWCcYw5fNqrHubD1vj/JGN1NYhFFBCS09rOL3b7nM2w==
|
||||||
|
dependencies:
|
||||||
|
tzdata "1.0.25"
|
||||||
|
|
||||||
tinycolor2@^1.4.1, tinycolor2@^1.4.2:
|
tinycolor2@^1.4.1, tinycolor2@^1.4.2:
|
||||||
version "1.6.0"
|
version "1.6.0"
|
||||||
resolved "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz"
|
resolved "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz"
|
||||||
@ -10313,6 +10342,16 @@ typical@^2.6.0:
|
|||||||
resolved "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz"
|
resolved "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz"
|
||||||
integrity sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg==
|
integrity sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg==
|
||||||
|
|
||||||
|
tzdata@1.0.25:
|
||||||
|
version "1.0.25"
|
||||||
|
resolved "https://registry.yarnpkg.com/tzdata/-/tzdata-1.0.25.tgz#e8839033c05761e04ef552242e779777becb13d0"
|
||||||
|
integrity sha512-yAZ/Tv/tBFIPHJGYrOexxW8Swyjszt7rDhIjnIPSqLaP8mzrr3T7D0w4cxQBtToXnQrlFqkEU0stGC/auz0JcQ==
|
||||||
|
|
||||||
|
tzdata@^1.0.42:
|
||||||
|
version "1.0.42"
|
||||||
|
resolved "https://registry.yarnpkg.com/tzdata/-/tzdata-1.0.42.tgz#4f278809b50c50e9c865e44969aa7e746b165638"
|
||||||
|
integrity sha512-hVA4V8g27yz1YB4Ty4UliwJlWrFOoFrFBYFMd9rKUlRlaF+9Fl3gyzxF/+MQOtCH50pPE+XZ/bYOYkRnBDscVQ==
|
||||||
|
|
||||||
ua-parser-js@^0.7.33:
|
ua-parser-js@^0.7.33:
|
||||||
version "0.7.39"
|
version "0.7.39"
|
||||||
resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.39.tgz"
|
resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.39.tgz"
|
||||||
|
Reference in New Issue
Block a user