diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index 9b747bd..f927c65 100755
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -25,7 +25,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 20.x
- cache: npm
+ cache: yarn
- name: Setup Expo and EAS
uses: expo/expo-github-action@v8
@@ -34,7 +34,7 @@ jobs:
token: ${{ secrets.EXPO_TOKEN }}
- name: Install dependencies
- run: npm ci --legacy-peer-deps
+ run: yarn install --immutable
- name: Prebuild, Build and Submit
- run: npm run prebuild-build-submit-ios-cicd
+ run: yarn prebuild-build-submit-ios-cicd
\ No newline at end of file
diff --git a/app.json b/app.json
index 7413002..98db76d 100644
--- a/app.json
+++ b/app.json
@@ -13,10 +13,10 @@
"backgroundColor": "#ffffff"
},
"ios": {
- "supportsTablet": true,
+ "supportsTablet": false,
"bundleIdentifier": "com.cally.app",
"googleServicesFile": "./ios/GoogleService-Info.plist",
- "buildNumber": "60",
+ "buildNumber": "74",
"usesAppleSignIn": true
},
"android": {
diff --git a/app/(auth)/_layout.tsx b/app/(auth)/_layout.tsx
index dceffc1..824e01d 100644
--- a/app/(auth)/_layout.tsx
+++ b/app/(auth)/_layout.tsx
@@ -1,120 +1,108 @@
import React from "react";
-import { Drawer } from "expo-router/drawer";
-import { useSignOut } from "@/hooks/firebase/useSignOut";
-import {
- DrawerContentScrollView,
- DrawerItem,
- DrawerItemList,
-} from "@react-navigation/drawer";
-import { Button, View, Text, ButtonSize } from "react-native-ui-lib";
-import { Dimensions, ImageBackground, StyleSheet } from "react-native";
-import Feather from "@expo/vector-icons/Feather";
+import {Drawer} from "expo-router/drawer";
+import {useSignOut} from "@/hooks/firebase/useSignOut";
+import {DrawerContentScrollView,} from "@react-navigation/drawer";
+import {Button, ButtonSize, Text, View} from "react-native-ui-lib";
+import {ImageBackground, StyleSheet} from "react-native";
import DrawerButton from "@/components/shared/DrawerButton";
-import {
- AntDesign,
- FontAwesome6,
- MaterialCommunityIcons,
- Octicons,
-} from "@expo/vector-icons";
-import MenuIcon from "@/assets/svgs/MenuIcon";
-import { router } from "expo-router";
import NavGroceryIcon from "@/assets/svgs/NavGroceryIcon";
import NavToDosIcon from "@/assets/svgs/NavToDosIcon";
import NavBrainDumpIcon from "@/assets/svgs/NavBrainDumpIcon";
import NavCalendarIcon from "@/assets/svgs/NavCalendarIcon";
import NavSettingsIcon from "@/assets/svgs/NavSettingsIcon";
-import { useAtom, useSetAtom } from "jotai";
-import {
- isFamilyViewAtom,
- settingsPageIndex,
- toDosPageIndex,
- userSettingsView,
-} from "@/components/pages/calendar/atoms";
import FeedbackNavIcon from "@/assets/svgs/FeedbackNavIcon";
+import {MaterialIcons} from "@expo/vector-icons";
+import {useSetAtom} from "jotai";
+import {
+ isFamilyViewAtom,
+ settingsPageIndex,
+ toDosPageIndex,
+ userSettingsView,
+} from "@/components/pages/calendar/atoms";
+import Ionicons from "@expo/vector-icons/Ionicons";
export default function TabLayout() {
- const { mutateAsync: signOut } = useSignOut();
- const setIsFamilyView = useSetAtom(isFamilyViewAtom);
- const setPageIndex = useSetAtom(settingsPageIndex);
- const setUserView = useSetAtom(userSettingsView);
- const setToDosIndex = useSetAtom(toDosPageIndex);
+ const {mutateAsync: signOut} = useSignOut();
+ const setIsFamilyView = useSetAtom(isFamilyViewAtom);
+ const setPageIndex = useSetAtom(settingsPageIndex);
+ const setUserView = useSetAtom(userSettingsView);
+ const setToDosIndex = useSetAtom(toDosPageIndex);
- return (
- {
- return (
-
-
-
- Welcome to Cally
-
-
-
- {
- props.navigation.navigate("calendar");
- setPageIndex(0);
- setToDosIndex(0);
- setUserView(true);
- setIsFamilyView(false);
- }}
- icon={}
- />
- {
- props.navigation.navigate("grocery");
- setPageIndex(0);
- setToDosIndex(0);
- setUserView(true);
- setIsFamilyView(false);
- }}
- icon={}
- />
- {
- props.navigation.navigate("feedback");
- setPageIndex(0);
- setToDosIndex(0);
- setUserView(true);
- setIsFamilyView(false);
- }}
- icon={}
- />
-
-
- {/* {
+ return (
+
+
+
+ Welcome to Cally
+
+
+
+ {
+ props.navigation.navigate("calendar");
+ setPageIndex(0);
+ setToDosIndex(0);
+ setUserView(true);
+ setIsFamilyView(false);
+ }}
+ icon={}
+ />
+ {
+ props.navigation.navigate("grocery");
+ setPageIndex(0);
+ setToDosIndex(0);
+ setUserView(true);
+ setIsFamilyView(false);
+ }}
+ icon={}/>
+ {
+ props.navigation.navigate("feedback");
+ setPageIndex(0);
+ setToDosIndex(0);
+ setUserView(true);
+ setIsFamilyView(false);
+ }}
+ icon={}
+ />
+
+
+ {/*
}
/>*/}
- {
- props.navigation.navigate("todos");
- setPageIndex(0);
- setToDosIndex(0);
- setUserView(true);
- setIsFamilyView(false);
- }}
- icon={}
- />
- {
- props.navigation.navigate("brain_dump");
- setPageIndex(0);
- setToDosIndex(0);
- setUserView(true);
- setIsFamilyView(false);
- }}
- icon={}
- />
- {/* signOut()} />*/}
-
-
-
+
+
+ );
+ }}
+ >
+
-
- );
- }}
- >
-
-
-
-
-
-
-
-
-
- );
+
+
+
+
+
+
+
+
+
+ );
}
const styles = StyleSheet.create({
- signOut: { fontFamily: "Poppins_500Medium", fontSize: 15 },
- label: { fontFamily: "Poppins_400Medium", fontSize: 15 },
- title: {
- fontSize: 26.13,
- fontFamily: "Manrope_600SemiBold",
- color: "#262627",
- },
+ signOut: {fontFamily: "Poppins_500Medium", fontSize: 15},
+ label: {fontFamily: "Poppins_400Medium", fontSize: 15},
+ title: {
+ fontSize: 26.13,
+ fontFamily: "Manrope_600SemiBold",
+ color: "#262627",
+ },
});
diff --git a/app/(auth)/brain_dump/index.tsx b/app/(auth)/brain_dump/index.tsx
index 5bf6ae7..5d68a19 100644
--- a/app/(auth)/brain_dump/index.tsx
+++ b/app/(auth)/brain_dump/index.tsx
@@ -1,14 +1,13 @@
+import {BrainDumpProvider} from "@/contexts/DumpContext";
+import {View} from "react-native-ui-lib";
import BrainDumpPage from "@/components/pages/brain_dump/BrainDumpPage";
-import { BrainDumpProvider } from "@/contexts/DumpContext";
-import { ScrollView } from "react-native-gesture-handler";
-import { View } from "react-native-ui-lib";
export default function Screen() {
- return (
-
-
-
-
-
- );
+ return (
+
+
+
+
+
+ );
}
diff --git a/app/(auth)/notifications/_layout.tsx b/app/(auth)/notifications/_layout.tsx
new file mode 100644
index 0000000..0990eb8
--- /dev/null
+++ b/app/(auth)/notifications/_layout.tsx
@@ -0,0 +1,5 @@
+import {Stack} from "expo-router";
+
+export default function StackLayout () {
+ return
+}
\ No newline at end of file
diff --git a/app/(auth)/notifications/index.tsx b/app/(auth)/notifications/index.tsx
new file mode 100644
index 0000000..3ada521
--- /dev/null
+++ b/app/(auth)/notifications/index.tsx
@@ -0,0 +1,7 @@
+import NotificationsPage from "@/components/pages/notifications/NotificationsPage";
+
+export default function Screen() {
+ return (
+
+ );
+}
diff --git a/babel.config.js b/babel.config.js
index 8248109..5ca28ee 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -2,7 +2,7 @@ module.exports = function (api) {
api.cache(true);
return {
presets: [
- 'babel-preset-expo'
+ 'babel-preset-expo',
]
};
};
diff --git a/calendar-integration/google-calendar-utils.js b/calendar-integration/google-calendar-utils.js
index c68b7b1..59709c5 100644
--- a/calendar-integration/google-calendar-utils.js
+++ b/calendar-integration/google-calendar-utils.js
@@ -1,55 +1,64 @@
export async function fetchGoogleCalendarEvents(token, email, familyId, 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 googleEvents = [];
+ let pageToken = null;
+ do {
+ const url = new URL(`https://www.googleapis.com/calendar/v3/calendars/primary/events`);
+ url.searchParams.set('singleEvents', 'true');
+ url.searchParams.set('timeMin', startDate);
+ url.searchParams.set('timeMax', endDate);
+ if (pageToken) url.searchParams.set('pageToken', pageToken);
- const data = await response.json();
+ const response = await fetch(url.toString(), {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ });
- 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 data = await response.json();
- 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);
- }
- }
+ if (!response.ok) {
+ throw new Error(`Error fetching events: ${data.error?.message || response.statusText}`);
+ }
- const googleEvent = {
- id: item.id,
- title: item.summary ?? "",
- startDate: startDateTime,
- endDate: endDateTime,
- allDay: isAllDay,
- familyId,
- email
- };
- googleEvents.push(googleEvent);
- });
+ data.items?.forEach((item) => {
+ let isAllDay = false;
+ let startDateTime, endDateTime;
- return {googleEvents, success: response.ok};
-}
+ if (item.start) {
+ if (item.start.dateTime) {
+ startDateTime = new Date(item.start.dateTime);
+ } else if (item.start.date) {
+ startDateTime = new Date(item.start.date);
+ isAllDay = true;
+ }
+ }
+
+ if (item.end) {
+ if (item.end.dateTime) {
+ endDateTime = new Date(item.end.dateTime);
+ } else if (item.end.date) {
+ endDateTime = new Date(item.end.date);
+ isAllDay = true;
+ }
+ }
+
+ const googleEvent = {
+ id: item.id,
+ title: item.summary || "",
+ startDate: startDateTime,
+ endDate: endDateTime,
+ allDay: isAllDay,
+ familyId,
+ email,
+ };
+
+ googleEvents.push(googleEvent);
+ });
+
+ // Prepare for the next page if it exists
+ pageToken = data.nextPageToken;
+ } while (pageToken);
+
+ return { googleEvents, success: true };
+}
\ No newline at end of file
diff --git a/components/pages/calendar/CalendarHeader.tsx b/components/pages/calendar/CalendarHeader.tsx
index 403e63f..c77de27 100644
--- a/components/pages/calendar/CalendarHeader.tsx
+++ b/components/pages/calendar/CalendarHeader.tsx
@@ -14,6 +14,7 @@ import { useAtom } from "jotai";
import { modeAtom, selectedDateAtom } from "@/components/pages/calendar/atoms";
import { format, isSameDay } from "date-fns";
import { useAuthContext } from "@/contexts/AuthContext";
+import {useIsMutating} from "react-query";
export const CalendarHeader = memo(() => {
const [selectedDate, setSelectedDate] = useAtom(selectedDateAtom);
diff --git a/components/pages/calendar/CalendarPage.tsx b/components/pages/calendar/CalendarPage.tsx
index eddd7d5..0118f6c 100644
--- a/components/pages/calendar/CalendarPage.tsx
+++ b/components/pages/calendar/CalendarPage.tsx
@@ -11,7 +11,7 @@ export default function CalendarPage() {
paddingT-0
>
diff --git a/components/pages/calendar/EventCalendar.tsx b/components/pages/calendar/EventCalendar.tsx
index ada0db9..3f39399 100644
--- a/components/pages/calendar/EventCalendar.tsx
+++ b/components/pages/calendar/EventCalendar.tsx
@@ -1,6 +1,6 @@
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { Calendar } from "react-native-big-calendar";
-import { ActivityIndicator, StyleSheet, View, ViewStyle } from "react-native";
+import { ActivityIndicator, ScrollView, StyleSheet, View, ViewStyle } from "react-native";
import { useGetEvents } from "@/hooks/firebase/useGetEvents";
import { useAtom, useSetAtom } from "jotai";
import {
@@ -15,6 +15,8 @@ import { useAuthContext } from "@/contexts/AuthContext";
import { CalendarEvent } from "@/components/pages/calendar/interfaces";
import { Text } from "react-native-ui-lib";
import { addDays, compareAsc, isWithinInterval, subDays } from "date-fns";
+import {useCalSync} from "@/hooks/useCalSync";
+import {useSyncEvents} from "@/hooks/useSyncOnScroll";
interface EventCalendarProps {
calendarHeight: number;
@@ -39,7 +41,9 @@ export const EventCalendar: React.FC = React.memo(
const setEventForEdit = useSetAtom(eventForEditAtom);
const setSelectedNewEndDate = useSetAtom(selectedNewEventDateAtom);
- const [offsetMinutes, setOffsetMinutes] = useState(getTotalMinutes());
+ const {isSyncing} = useSyncEvents()
+ const [offsetMinutes, setOffsetMinutes] = useState(getTotalMinutes());
+ useCalSync()
const todaysDate = new Date();
@@ -47,15 +51,15 @@ export const EventCalendar: React.FC = React.memo(
(event: CalendarEvent) => {
if (mode === "day" || mode === "week") {
setEditVisible(true);
- console.log({ event });
- setEventForEdit(event);
- } else {
- setMode("day");
- setSelectedDate(event.start);
- }
- },
- [setEditVisible, setEventForEdit, mode]
- );
+ // console.log({event});
+ setEventForEdit(event);
+ } else {
+ setMode("day");
+ setSelectedDate(event.start);
+ }
+ },
+ [setEditVisible, setEventForEdit, mode]
+ );
const handlePressCell = useCallback(
(date: Date) => {
@@ -94,7 +98,7 @@ export const EventCalendar: React.FC = React.memo(
);
const memoizedEventCellStyle = useCallback(
- (event: CalendarEvent) => ({ backgroundColor: event.eventColor }),
+ (event: CalendarEvent) => ({ backgroundColor: event.eventColor , fontSize: 14}),
[]
);
@@ -103,9 +107,7 @@ export const EventCalendar: React.FC = React.memo(
[profileData]
);
- console.log({
- memoizedWeekStartsOn,
- profileData: profileData?.firstDayOfWeek,
+ // console.log({memoizedWeekStartsOn, profileData: profileData?.firstDayOfWeek,
});
const isSameDate = useCallback((date1: Date, date2: Date) => {
@@ -175,11 +177,7 @@ export const EventCalendar: React.FC = React.memo(
}, {} as Record);
const endTime = Date.now();
- console.log(
- "memoizedEvents computation time:",
- endTime - startTime,
- "ms"
- );
+ // console.log("memoizedEvents computation time:", endTime - startTime, "ms");
return { enrichedEvents, filteredEvents };
}, [events, selectedDate, mode]);
@@ -239,106 +237,123 @@ export const EventCalendar: React.FC = React.memo(
if (isLoading) {
return (
-
-
- );
- }
+ {isSyncing && Syncing...}
+
+
+ );
+ }
// console.log(enrichedEvents, filteredEvents)
return (
-
- );
- }
+ <>
+ {isSyncing && (
+
+ {isSyncing && Syncing...}
+
+
+ )}
+
+ >
+
+ );
+ }
);
const styles = StyleSheet.create({
- segmentslblStyle: {
- fontSize: 12,
- fontFamily: "Manrope_600SemiBold",
- },
- calHeader: {
- borderWidth: 0,
- },
- dayModeHeader: {
- alignSelf: "flex-start",
- justifyContent: "space-between",
- alignContent: "center",
- width: 38,
- right: 42,
- height: 13,
- },
- weekModeHeader: {},
- monthModeHeader: {},
- loadingContainer: {
- flex: 1,
- justifyContent: "center",
- alignItems: "center",
- },
- dayHeader: {
- backgroundColor: "#4184f2",
- aspectRatio: 1,
- borderRadius: 100,
- alignItems: "center",
- justifyContent: "center",
- },
- otherDayHeader: {
- backgroundColor: "transparent",
- color: "#919191",
- aspectRatio: 1,
- borderRadius: 100,
- alignItems: "center",
- justifyContent: "center",
- },
- hourStyle: {
- color: "#5f6368",
- fontSize: 12,
- fontFamily: "Manrope_500Medium",
- },
+ segmentslblStyle: {
+ fontSize: 12,
+ fontFamily: "Manrope_600SemiBold",
+ },
+ calHeader: {
+ borderWidth: 0,
+ },
+ dayModeHeader: {
+ alignSelf: "flex-start",
+ justifyContent: "space-between",
+ alignContent: "center",
+ width: 38,
+ right: 42,
+ height: 13,
+ },
+ weekModeHeader: {},
+ monthModeHeader: {},
+ loadingContainer: {
+ flex: 1,
+ justifyContent: "center",
+ alignItems: "center",
+ position: "absolute",
+ width: "100%",
+ height: "100%",
+ zIndex: 100,
+ backgroundColor: "rgba(255, 255, 255, 0.9)",
+ },
+ dayHeader: {
+ backgroundColor: "#4184f2",
+ aspectRatio: 1,
+ borderRadius: 100,
+ alignItems: "center",
+ justifyContent: "center",
+ },
+ otherDayHeader: {
+ backgroundColor: "transparent",
+ color: "#919191",
+ aspectRatio: 1,
+ borderRadius: 100,
+ alignItems: "center",
+ justifyContent: "center",
+ },
+ hourStyle: {
+ color: "#5f6368",
+ fontSize: 12,
+ fontFamily: "Manrope_500Medium",
+ },
});
diff --git a/components/pages/notifications/NotificationsPage.tsx b/components/pages/notifications/NotificationsPage.tsx
new file mode 100644
index 0000000..af1622b
--- /dev/null
+++ b/components/pages/notifications/NotificationsPage.tsx
@@ -0,0 +1,60 @@
+import {FlatList, ScrollView, StyleSheet} from "react-native";
+import React from "react";
+import {Card, Text, View} from "react-native-ui-lib";
+import HeaderTemplate from "@/components/shared/HeaderTemplate";
+import {useGetNotifications} from "@/hooks/firebase/useGetNotifications";
+import {formatDistanceToNow} from "date-fns";
+
+const NotificationsPage = () => {
+ const {data: notifications} = useGetNotifications()
+
+ console.log(notifications?.[0]?.timestamp)
+
+
+ return (
+
+
+
+
+
+ See your notifications here.
+
+ }
+ />
+
+
+ {item.content}
+
+ {formatDistanceToNow(new Date(item.timestamp), { addSuffix: true })}
+ {item.timestamp.toLocaleDateString()}
+
+ }/>
+
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ searchField: {
+ borderWidth: 0.7,
+ borderColor: "#9b9b9b",
+ borderRadius: 15,
+ height: 42,
+ paddingLeft: 10,
+ marginVertical: 20,
+ },
+});
+
+export default NotificationsPage;
diff --git a/components/pages/settings/CalendarSettingsPage.tsx b/components/pages/settings/CalendarSettingsPage.tsx
index 68b7bd5..842719b 100644
--- a/components/pages/settings/CalendarSettingsPage.tsx
+++ b/components/pages/settings/CalendarSettingsPage.tsx
@@ -142,7 +142,7 @@ const CalendarSettingsPage = () => {
- Add Calendar
+ Add Calendars
= ({
onConfirm,
}) => {
const [confirmationDialog, setConfirmationDialog] = useState(false);
+
return (
<>