diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 53f6067..6c2d8b6 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -29,6 +29,7 @@ + diff --git a/app.json b/app.json index aa988ac..728d2b6 100644 --- a/app.json +++ b/app.json @@ -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", @@ -16,7 +16,7 @@ "supportsTablet": true, "bundleIdentifier": "com.cally.app", "googleServicesFile": "./ios/GoogleService-Info.plist", - "buildNumber": "14" + "buildNumber": "15" }, "android": { "adaptiveIcon": { diff --git a/calendar-integration/google-calendar-utils.js b/calendar-integration/google-calendar-utils.js new file mode 100644 index 0000000..428cb42 --- /dev/null +++ b/calendar-integration/google-calendar-utils.js @@ -0,0 +1,51 @@ +export async function fetchGoogleCalendarEvents(token, startDate, endDate) { + const response = await fetch( + `https://www.googleapis.com/calendar/v3/calendars/primary/events?single_events=true&time_min=${startDate}&time_max=${endDate}`, + { + headers: { + Authorization: `Bearer ${token}`, + }, + }, + ); + + const data = await response.json(); + const googleEvents = []; + data.items?.forEach((item) => { + let isAllDay = false; + const start = item.start; + let startDateTime; + if (start !== undefined) { + if (start.dateTime) { + const stringDate = start.dateTime; + startDateTime = new Date(stringDate); + } else { + const stringDate = start.date; + startDateTime = new Date(stringDate); + isAllDay = true; + } + } + + const end = item.end; + let endDateTime; + if (end !== undefined) { + if (end.dateTime) { + const stringDate = end.dateTime; + endDateTime = new Date(stringDate); + } else { + const stringDate = end.date; + endDateTime = new Date(stringDate); + } + } + + const googleEvent = { + id: item.id, + title: item.summary, + startDate: startDateTime, + endDate: endDateTime, + allDay: isAllDay, + }; + googleEvents.push(googleEvent); + }); + + return googleEvents; +} diff --git a/calendar-integration/microsoft-calendar-utils.js b/calendar-integration/microsoft-calendar-utils.js new file mode 100644 index 0000000..b459a29 --- /dev/null +++ b/calendar-integration/microsoft-calendar-utils.js @@ -0,0 +1,44 @@ +export async function fetchMicrosoftCalendarEvents(token, startDate, endDate) { + const response = await fetch( + `https://graph.microsoft.com/v1.0/me/calendar/calendarView?startDateTime=${startDate}&endDateTime=${endDate}`, + { + headers: { + Authorization: `Bearer ${token}`, + }, + }, + ); + + const data = await response.json(); + + console.log(data, startDate, endDate) + + const microsoftEvents = []; + data?.value?.forEach((item) => { + const start = item.start; + let startDateTime; + if (start !== undefined) { + const stringDate = start.dateTime; + startDateTime = new Date(stringDate); + } + + const end = item.end; + let endDateTime; + if (end !== undefined) { + const stringDate = end.dateTime; + endDateTime = new Date(stringDate); + } + + const microsoftEvent = { + id: item.uid, + title: item.subject, + startDate: startDateTime, + endDate: endDateTime, + allDay: item.isAllDay, + }; + + + microsoftEvents.push(microsoftEvent); + }); + + return microsoftEvents; +} diff --git a/components/pages/calendar/AddEventDialog.tsx b/components/pages/calendar/AddEventDialog.tsx index 368ced8..2731d95 100644 --- a/components/pages/calendar/AddEventDialog.tsx +++ b/components/pages/calendar/AddEventDialog.tsx @@ -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); diff --git a/components/pages/settings/CalendarSettingsPage.tsx b/components/pages/settings/CalendarSettingsPage.tsx index 4048dda..845e698 100644 --- a/components/pages/settings/CalendarSettingsPage.tsx +++ b/components/pages/settings/CalendarSettingsPage.tsx @@ -1,169 +1,349 @@ -import { AntDesign, Ionicons } from "@expo/vector-icons"; -import React, { useState } from "react"; -import { Button, View, Text, Checkbox } from "react-native-ui-lib"; -import { StyleSheet } from "react-native"; -import { colorMap } from "@/contexts/SettingsContext"; -import { ScrollView, TouchableOpacity } from "react-native-gesture-handler"; -import GoogleIcon from "@/assets/svgs/GoogleIcon"; -import AppleIcon from "@/assets/svgs/AppleIcon"; -import OutlookIcon from "@/assets/svgs/OutlookIcon"; -import CloseXIcon from "@/assets/svgs/CloseXIcon"; +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 +}); + +const GoogleLogin = async () => { + return await GoogleSignin.signIn(); +}; + +const microsoftConfig = { + 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" +}; const CalendarSettingsPage = (props: { - setSelectedPage: (page: number) => void; + setSelectedPage: (page: number) => void; }) => { - const [selectedColor, setSelectedColor] = useState(colorMap.pink); - const [startDate, setStartDate] = useState(true); - return ( - - - props.setSelectedPage(0)}> - - - - Return to main settings - - - - Calendar settings - - - Event Color Preference - - - setSelectedColor(colorMap.pink)}> - - {selectedColor == colorMap.pink && ( - - )} - - - setSelectedColor(colorMap.orange)}> - - {selectedColor == colorMap.orange && ( - - )} - - - setSelectedColor(colorMap.green)}> - - {selectedColor == colorMap.green && ( - - )} - - - setSelectedColor(colorMap.teal)}> - - {selectedColor == colorMap.teal && ( - - )} - - - setSelectedColor(colorMap.purple)}> - - {selectedColor == colorMap.purple && ( - - )} - - - - - - Weekly Start Date - - setStartDate(true)} - /> - - Sundays - - {" "} - (default) - - - - - setStartDate(false)} - /> - - Mondays - - - + const [selectedColor, setSelectedColor] = useState(colorMap.pink); + const [startDate, setStartDate] = useState(true); + const {profileData} = useAuthContext(); - - Add Calendar - + const {mutateAsync: createEventFromProvider} = useCreateEventFromProvider(); + const {mutateAsync: updateUserData} = useUpdateUserData(); -