diff --git a/.idea/git_toolbox_blame.xml b/.idea/git_toolbox_blame.xml
new file mode 100644
index 0000000..7dc1249
--- /dev/null
+++ b/.idea/git_toolbox_blame.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..55001da
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jsLinters/eslint.xml b/.idea/jsLinters/eslint.xml
new file mode 100644
index 0000000..541945b
--- /dev/null
+++ b/.idea/jsLinters/eslint.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
index 22880b8..09a92f3 100644
--- a/android/app/src/main/res/values/strings.xml
+++ b/android/app/src/main/res/values/strings.xml
@@ -1,5 +1,5 @@
- "Cally "
+ \"Cally \"
contain
false
light
diff --git a/app.json b/app.json
index 98db76d..476fa76 100644
--- a/app.json
+++ b/app.json
@@ -13,11 +13,14 @@
"backgroundColor": "#ffffff"
},
"ios": {
- "supportsTablet": false,
+ "supportsTablet": true,
"bundleIdentifier": "com.cally.app",
"googleServicesFile": "./ios/GoogleService-Info.plist",
"buildNumber": "74",
- "usesAppleSignIn": true
+ "usesAppleSignIn": true,
+ "infoPlist": {
+ "ITSAppUsesNonExemptEncryption": false
+ }
},
"android": {
"adaptiveIcon": {
diff --git a/app/(auth)/_layout.tsx b/app/(auth)/_layout.tsx
index 394628a..a89c336 100644
--- a/app/(auth)/_layout.tsx
+++ b/app/(auth)/_layout.tsx
@@ -1,15 +1,9 @@
-import React, { useEffect } from "react";
-import { Drawer } from "expo-router/drawer";
-import { useSignOut } from "@/hooks/firebase/useSignOut";
-import { DrawerContentScrollView } from "@react-navigation/drawer";
-import {
- Button,
- ButtonSize,
- Text,
- TouchableOpacity,
- View,
-} from "react-native-ui-lib";
-import { ImageBackground, StyleSheet } from "react-native";
+import React from "react";
+import {Drawer} from "expo-router/drawer";
+import {useSignOut} from "@/hooks/firebase/useSignOut";
+import {DrawerContentScrollView, DrawerNavigationOptions, DrawerNavigationProp} from "@react-navigation/drawer";
+import {Button, ButtonSize, Text, TouchableOpacity, View,} from "react-native-ui-lib";
+import {ImageBackground, StyleSheet} from "react-native";
import DrawerButton from "@/components/shared/DrawerButton";
import NavGroceryIcon from "@/assets/svgs/NavGroceryIcon";
import NavToDosIcon from "@/assets/svgs/NavToDosIcon";
@@ -17,127 +11,165 @@ import NavBrainDumpIcon from "@/assets/svgs/NavBrainDumpIcon";
import NavCalendarIcon from "@/assets/svgs/NavCalendarIcon";
import NavSettingsIcon from "@/assets/svgs/NavSettingsIcon";
import ViewSwitch from "@/components/pages/(tablet_pages)/ViewSwitch";
-import { useSetAtom } from "jotai";
+import {useSetAtom} from "jotai";
import {
- isFamilyViewAtom,
- settingsPageIndex,
- toDosPageIndex,
- userSettingsView,
+ isFamilyViewAtom,
+ settingsPageIndex,
+ toDosPageIndex,
+ userSettingsView,
} from "@/components/pages/calendar/atoms";
import Ionicons from "@expo/vector-icons/Ionicons";
import * as Device from "expo-device";
-import { DeviceType } from "expo-device";
+import {DeviceType} from "expo-device";
import FeedbackNavIcon from "@/assets/svgs/FeedbackNavIcon";
import DrawerIcon from "@/assets/svgs/DrawerIcon";
+import {RouteProp} from "@react-navigation/core";
+type DrawerParamList = {
+ index: undefined;
+ calendar: undefined;
+ todos: undefined;
+};
+
+type NavigationProp = DrawerNavigationProp;
+
+interface ViewSwitchProps {
+ navigation: NavigationProp;
+}
+
+interface HeaderRightProps {
+ routeName: keyof DrawerParamList;
+ navigation: NavigationProp;
+}
+
+const MemoizedViewSwitch = React.memo(({navigation}) => (
+
+
+
+));
+
+const HeaderRight = React.memo(({routeName, navigation}) => {
+ const showViewSwitch = ["calendar", "todos", "index"].includes(routeName);
+
+ if (Device.deviceType !== DeviceType.TABLET || !showViewSwitch) {
+ return null;
+ }
+
+ return ;
+});
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 (
- ({
+ const screenOptions = ({
+ navigation,
+ route,
+ }: {
+ navigation: DrawerNavigationProp;
+ route: RouteProp;
+ }): DrawerNavigationOptions => ({
headerShown: true,
- headerTitleAlign:
- Device.deviceType === DeviceType.TABLET ? "left" : "center",
+ headerTitleAlign: Device.deviceType === DeviceType.TABLET ? "left" : "center",
headerTitleStyle: {
- fontFamily: "Manrope_600SemiBold",
- fontSize: Device.deviceType === DeviceType.TABLET ? 22 : 17,
+ fontFamily: "Manrope_600SemiBold",
+ fontSize: Device.deviceType === DeviceType.TABLET ? 22 : 17,
},
- headerLeft: (props) => (
-
-
-
+ headerLeft: () => (
+
+
+
),
headerRight: () => {
- // Only show ViewSwitch on calendar and todos pages
- const showViewSwitch = ["calendar", "todos", "index"].includes(
- route.name
- );
- return Device.deviceType === DeviceType.TABLET && showViewSwitch ? (
-
-
-
- ) : null;
+ const showViewSwitch = ["calendar", "todos", "index"].includes(route.name);
+
+ if (Device.deviceType !== DeviceType.TABLET || !showViewSwitch) {
+ return null;
+ }
+
+ return ;
},
drawerStyle: {
- width: Device.deviceType === DeviceType.TABLET ? "30%" : "90%",
- backgroundColor: "#f9f8f7",
- height: "100%",
+ width: Device.deviceType === DeviceType.TABLET ? "30%" : "90%",
+ backgroundColor: "#f9f8f7",
+ height: "100%",
},
- })}
- drawerContent={(props) => {
- 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={}
- />
- {
- props.navigation.navigate("notifications");
- setPageIndex(0);
- setToDosIndex(0);
- setUserView(true);
- setIsFamilyView(false);
- }}
- icon={
-
- }
- />
-
-
-
+
+
+ );
+ }}
+ >
+
-
- );
- }}
- >
-
-
-
-
-
-
-
-
-
-
- );
+
+
+
+
+
+
+
+
+
+ );
}
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)/calendar/index.tsx b/app/(auth)/calendar/index.tsx
index 0dcaa52..0a5987a 100644
--- a/app/(auth)/calendar/index.tsx
+++ b/app/(auth)/calendar/index.tsx
@@ -1,105 +1,86 @@
-import React, { useState } from "react";
-import { ScrollView, RefreshControl, View } from "react-native";
-import { useAtom } from "jotai";
+import React from "react";
+import {RefreshControl, ScrollView, View} from "react-native";
import CalendarPage from "@/components/pages/calendar/CalendarPage";
-import { refreshTriggerAtom } from "@/components/pages/calendar/atoms";
-import { colorMap } from "@/constants/colorMap";
import TabletCalendarPage from "@/components/pages/(tablet_pages)/calendar/TabletCalendarPage";
-import { DeviceType } from "expo-device";
import * as Device from "expo-device";
-import { useCalSync } from "@/hooks/useCalSync";
-import Toast from "react-native-toast-message";
+import {DeviceType} from "expo-device";
+import {useCalSync} from "@/hooks/useCalSync";
+import {colorMap} from "@/constants/colorMap";
export default function Screen() {
- const [refreshing, setRefreshing] = useState(false);
- const [shouldRefresh, setShouldRefresh] = useAtom(refreshTriggerAtom);
+ const isTablet = Device.deviceType === DeviceType.TABLET;
+ const {resyncAllCalendars, isSyncing} = useCalSync();
- const isTablet: boolean = Device.deviceType === DeviceType.TABLET;
+ const onRefresh = React.useCallback(async () => {
+ try {
+ await resyncAllCalendars();
+ } catch (error) {
+ console.error("Refresh failed:", error);
+ }
+ }, [resyncAllCalendars]);
- const {
- isConnectedToGoogle,
- isConnectedToMicrosoft,
- isConnectedToApple,
- resyncAllCalendars,
- isSyncing,
- } = useCalSync();
-
- const onRefresh = React.useCallback(async () => {
- setRefreshing(true);
-
- const minimumDelay = new Promise((resolve) => setTimeout(resolve, 1000));
-
- try {
- if (isConnectedToGoogle || isConnectedToMicrosoft || isConnectedToApple) {
- await Promise.all([resyncAllCalendars(), minimumDelay]);
- } else {
- await minimumDelay;
- }
- } catch (error) {
- console.error("Refresh failed:", error);
- } finally {
- setRefreshing(false);
- setShouldRefresh((prev) => !prev);
- }
- }, [
- resyncAllCalendars,
- isConnectedToGoogle,
- isConnectedToMicrosoft,
- isConnectedToApple,
- ]);
-
- return (
-
-
- {Device.deviceType === DeviceType.TABLET ? (
-
- ) : (
-
- )}
-
-
-
- }
- bounces={true}
- showsVerticalScrollIndicator={false}
- pointerEvents={refreshing || isSyncing ? "auto" : "none"}
- />
-
- );
-}
+ style={isTablet ? {
+ position: "absolute",
+ left: "50%",
+ transform: [{translateX: -20}],
+ } : undefined}
+ />
+ );
+
+ if (isTablet) {
+ return (
+
+
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/app/(auth)/todos/index.tsx b/app/(auth)/todos/index.tsx
index 33103a5..cda7d94 100644
--- a/app/(auth)/todos/index.tsx
+++ b/app/(auth)/todos/index.tsx
@@ -1,25 +1,16 @@
import TabletChoresPage from "@/components/pages/(tablet_pages)/chores/TabletChoresPage";
-import AddChore from "@/components/pages/todos/AddChore";
-import ProgressCard from "@/components/pages/todos/ProgressCard";
-import ToDoItem from "@/components/pages/todos/ToDoItem";
-import ToDosList from "@/components/pages/todos/ToDosList";
import ToDosPage from "@/components/pages/todos/ToDosPage";
-import HeaderTemplate from "@/components/shared/HeaderTemplate";
-import { useAuthContext } from "@/contexts/AuthContext";
-import { ToDosContextProvider, useToDosContext } from "@/contexts/ToDosContext";
-import { AntDesign } from "@expo/vector-icons";
-import { ScrollView } from "react-native-gesture-handler";
-import { Button, ButtonSize, View, Text, Constants } from "react-native-ui-lib";
+import {ToDosContextProvider} from "@/contexts/ToDosContext";
import * as Device from "expo-device";
export default function Screen() {
- return (
-
- {Device.deviceType === Device.DeviceType.TABLET ? (
-
- ) : (
-
- )}
-
- );
+ return (
+
+ {Device.deviceType === Device.DeviceType.TABLET ? (
+
+ ) : (
+
+ )}
+
+ );
}
diff --git a/components/pages/(tablet_pages)/ViewSwitch.tsx b/components/pages/(tablet_pages)/ViewSwitch.tsx
index b450364..0aef1e2 100644
--- a/components/pages/(tablet_pages)/ViewSwitch.tsx
+++ b/components/pages/(tablet_pages)/ViewSwitch.tsx
@@ -1,89 +1,68 @@
import { SegmentedControl, View } from "react-native-ui-lib";
-import React, { memo, useCallback, useEffect, useRef, useState } from "react";
+import React, { memo, useCallback } from "react";
import { StyleSheet } from "react-native";
import { NavigationProp, useNavigationState } from "@react-navigation/native";
-const ViewSwitch = memo(function ViewSwitch({
- navigation,
-}: {
- navigation: any;
-}) {
- const isNavigating = useRef(false);
- const navigationState = useNavigationState((state) => state);
- const [selectedIndex, setSelectedIndex] = useState(
- navigationState.index === 6 ? 1 : 0
- );
+interface ViewSwitchProps {
+ navigation: NavigationProp;
+}
- // Sync the selected index with navigation state
- useEffect(() => {
- const newIndex = navigationState.index === 6 ? 1 : 0;
- if (selectedIndex !== newIndex) {
- setSelectedIndex(newIndex);
- }
- isNavigating.current = false;
- }, [navigationState.index]);
+const ViewSwitch = memo(function ViewSwitch({ navigation }: ViewSwitchProps) {
+ const currentIndex = useNavigationState((state) => state.index === 6 ? 1 : 0);
- const handleSegmentChange = useCallback(
- (index: number) => {
- if (isNavigating.current) return;
- if (index === selectedIndex) return;
+ const handleSegmentChange = useCallback(
+ (index: number) => {
+ if (index === currentIndex) return;
+ navigation.navigate(index === 0 ? "calendar" : "todos");
+ },
+ [navigation, currentIndex]
+ );
- isNavigating.current = true;
- setSelectedIndex(index);
-
- // Delay navigation slightly to allow the segment control to update
- requestAnimationFrame(() => {
- navigation.navigate(index === 0 ? "calendar" : "todos");
- });
- console.log(selectedIndex)
- },
- [navigation, selectedIndex]
- );
-
- return (
-
-
-
- );
+ return (
+
+
+
+ );
});
-export default ViewSwitch;
const styles = StyleSheet.create({
- container: {
- borderRadius: 30,
- backgroundColor: "#ebebeb",
- shadowColor: "#000",
- shadowOffset: { width: 0, height: 0 },
- shadowOpacity: 0,
- shadowRadius: 0,
- elevation: 0,
- },
- segmentContainer: {
- height: 44,
- width: 220,
- },
- segment: {
- borderRadius: 50,
- borderWidth: 0,
- height: 44,
- },
- labelStyle: {
- fontSize: 16,
- fontFamily: "Manrope_600SemiBold",
- },
+ container: {
+ borderRadius: 30,
+ backgroundColor: "#ebebeb",
+ shadowColor: "#000",
+ shadowOffset: { width: 0, height: 0 },
+ shadowOpacity: 0,
+ shadowRadius: 0,
+ elevation: 0,
+ },
+ segmentContainer: {
+ height: 44,
+ width: 220,
+ },
+ segment: {
+ borderRadius: 50,
+ borderWidth: 0,
+ height: 44,
+ },
+ labelStyle: {
+ fontSize: 16,
+ fontFamily: "Manrope_600SemiBold",
+ },
});
+
+export default ViewSwitch;
\ No newline at end of file
diff --git a/components/pages/(tablet_pages)/chores/TabletChoresPage.tsx b/components/pages/(tablet_pages)/chores/TabletChoresPage.tsx
index f628245..657c3a2 100644
--- a/components/pages/(tablet_pages)/chores/TabletChoresPage.tsx
+++ b/components/pages/(tablet_pages)/chores/TabletChoresPage.tsx
@@ -1,9 +1,7 @@
-import React, { useEffect } from "react";
-import { View, Text } from "react-native-ui-lib";
+import React, {useEffect} from "react";
+import {Text, View} from "react-native-ui-lib";
import * as ScreenOrientation from "expo-screen-orientation";
import TabletContainer from "../tablet_components/TabletContainer";
-import ToDosPage from "../../todos/ToDosPage";
-import ToDosList from "../../todos/ToDosList";
import SingleUserChoreList from "./SingleUserChoreList";
import { useGetFamilyMembers } from "@/hooks/firebase/useGetFamilyMembers";
import { ImageBackground, StyleSheet } from "react-native";
@@ -12,13 +10,13 @@ import { ScrollView } from "react-native-gesture-handler";
import { ProfileType } from "@/contexts/AuthContext";
const TabletChoresPage = () => {
- const { data: users } = useGetFamilyMembers();
- // Function to lock the screen orientation to landscape
- const lockScreenOrientation = async () => {
- await ScreenOrientation.lockAsync(
- ScreenOrientation.OrientationLock.LANDSCAPE_RIGHT
- );
- };
+ const {data: users} = useGetFamilyMembers();
+ // Function to lock the screen orientation to landscape
+ const lockScreenOrientation = async () => {
+ await ScreenOrientation.lockAsync(
+ ScreenOrientation.OrientationLock.LANDSCAPE_RIGHT
+ );
+ };
useEffect(() => {
lockScreenOrientation(); // Lock orientation when the component mounts
@@ -34,7 +32,7 @@ const TabletChoresPage = () => {
{users
- ?.filter(member => member.userType !== ProfileType.FAMILY_DEVICE)
+ ?.filter((member) => member.userType !== ProfileType.FAMILY_DEVICE)
.map((user, index) => (
@@ -81,16 +79,16 @@ const TabletChoresPage = () => {
};
const styles = StyleSheet.create({
- pfp: {
- width: 46.74,
- aspectRatio: 1,
- borderRadius: 13.33,
- },
- name: {
- fontFamily: "Manrope_600SemiBold",
- fontSize: 22.43,
- color: "#2c2c2c",
- },
+ pfp: {
+ width: 46.74,
+ aspectRatio: 1,
+ borderRadius: 13.33,
+ },
+ name: {
+ fontFamily: "Manrope_600SemiBold",
+ fontSize: 22.43,
+ color: "#2c2c2c",
+ },
});
export default TabletChoresPage;
diff --git a/components/pages/brain_dump/DumpList.tsx b/components/pages/brain_dump/DumpList.tsx
index d8b7611..6f2f435 100644
--- a/components/pages/brain_dump/DumpList.tsx
+++ b/components/pages/brain_dump/DumpList.tsx
@@ -3,7 +3,6 @@ import React from "react";
import { useBrainDumpContext } from "@/contexts/DumpContext";
import { FlatList } from "react-native";
import BrainDumpItem from "./DumpItem";
-import LinearGradient from "react-native-linear-gradient";
const DumpList = (props: { searchText: string }) => {
const { brainDumps } = useBrainDumpContext();
diff --git a/components/pages/calendar/CalendarHeader.tsx b/components/pages/calendar/CalendarHeader.tsx
index c77de27..0fb1e97 100644
--- a/components/pages/calendar/CalendarHeader.tsx
+++ b/components/pages/calendar/CalendarHeader.tsx
@@ -1,131 +1,153 @@
-import React, { memo } from "react";
-import {
- Button,
- Picker,
- PickerModes,
- SegmentedControl,
- Text,
- View,
-} from "react-native-ui-lib";
-import { MaterialIcons } from "@expo/vector-icons";
-import { modeMap, months } from "./constants";
-import { StyleSheet } from "react-native";
-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";
+import React, {memo} from "react";
+import {Button, Picker, PickerModes, SegmentedControl, Text, View} from "react-native-ui-lib";
+import {MaterialIcons} from "@expo/vector-icons";
+import {months} from "./constants";
+import {StyleSheet} from "react-native";
+import {useAtom} from "jotai";
+import {modeAtom, selectedDateAtom} from "@/components/pages/calendar/atoms";
+import {format, isSameDay} from "date-fns";
+import * as Device from "expo-device";
+import {Mode} from "react-native-big-calendar";
+import { FontAwesome5 } from '@expo/vector-icons';
export const CalendarHeader = memo(() => {
- const [selectedDate, setSelectedDate] = useAtom(selectedDateAtom);
- const [mode, setMode] = useAtom(modeAtom);
- const { profileData } = useAuthContext();
+ const [selectedDate, setSelectedDate] = useAtom(selectedDateAtom);
+ const [mode, setMode] = useAtom(modeAtom);
+ const isTablet = Device.deviceType === Device.DeviceType.TABLET;
- const handleSegmentChange = (index: number) => {
- const selectedMode = modeMap.get(index);
- if (selectedMode) {
- setTimeout(() => {
- setMode(selectedMode as "day" | "week" | "month");
- }, 150);
- }
- };
+ const segments = isTablet
+ ? [{label: "D"}, {label: "W"}, {label: "M"}]
+ : [{label: "D"}, {label: "3D"}, {label: "M"}];
- const handleMonthChange = (month: string) => {
- const currentDay = selectedDate.getDate();
- const currentYear = selectedDate.getFullYear();
- const newMonthIndex = months.indexOf(month);
+ const handleSegmentChange = (index: number) => {
+ let selectedMode: Mode;
+ if (isTablet) {
+ selectedMode = ["day", "week", "month"][index] as Mode;
+ } else {
+ selectedMode = ["day", "3days", "month"][index] as Mode;
+ }
- const updatedDate = new Date(currentYear, newMonthIndex, currentDay);
- setSelectedDate(updatedDate);
- };
+ if (selectedMode) {
+ setTimeout(() => {
+ setMode(selectedMode as "day" | "week" | "month" | "3days");
+ }, 150);
+ }
+ };
- const isSelectedDateToday = isSameDay(selectedDate, new Date());
+ const handleMonthChange = (month: string) => {
+ const currentDay = selectedDate.getDate();
+ const currentYear = selectedDate.getFullYear();
+ const newMonthIndex = months.indexOf(month);
- return (
-
-
-
- {selectedDate.getFullYear()}
-
- handleMonthChange(itemValue as string)}
- trailingAccessory={}
- topBarProps={{
- title: selectedDate.getFullYear().toString(),
- titleStyle: { fontFamily: "Manrope_500Medium", fontSize: 17 },
- }}
- >
- {months.map((month) => (
-
- ))}
-
-
+ const updatedDate = new Date(currentYear, newMonthIndex, currentDay);
+ setSelectedDate(updatedDate);
+ };
-
- {!isSelectedDateToday && (
- {
+ if (isTablet) {
+ switch (mode) {
+ case "day": return 0;
+ case "week": return 1;
+ case "month": return 2;
+ default: return 1;
+ }
+ } else {
+ switch (mode) {
+ case "day": return 0;
+ case "3days": return 1;
+ case "month": return 2;
+ default: return 1;
+ }
+ }
+ };
+
+ return (
+ {
- setSelectedDate(new Date());
- }}
- />
- )}
+ >
+
+
+ {selectedDate.getFullYear()}
+
+ handleMonthChange(itemValue as string)}
+ trailingAccessory={}
+ topBarProps={{
+ title: selectedDate.getFullYear().toString(),
+ titleStyle: {fontFamily: "Manrope_500Medium", fontSize: 17},
+ }}
+ >
+ {months.map((month) => (
+
+ ))}
+
+
-
-
+
+ setSelectedDate(new Date())}
+ >
+
+ {format(new Date(), "d")}
+
+
+
+
+
-
-
- );
+ );
});
const styles = StyleSheet.create({
- segmentslblStyle: {
- fontSize: 12,
- fontFamily: "Manrope_600SemiBold",
- },
-});
+ segmentslblStyle: {
+ fontSize: 12,
+ fontFamily: "Manrope_600SemiBold",
+ },
+ todayButton: {
+ backgroundColor: "transparent",
+ borderWidth: 0,
+ height: 30,
+ width: 30,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ todayDate: {
+ position: 'absolute',
+ fontSize: 12,
+ fontFamily: "Manrope_600SemiBold",
+ color: "#5f6368",
+ top: '30%',
+ },
+});
\ No newline at end of file
diff --git a/components/pages/calendar/CalendarPage.tsx b/components/pages/calendar/CalendarPage.tsx
index 005dbbe..c09e337 100644
--- a/components/pages/calendar/CalendarPage.tsx
+++ b/components/pages/calendar/CalendarPage.tsx
@@ -1,6 +1,5 @@
import React from "react";
import { View } from "react-native-ui-lib";
-import HeaderTemplate from "@/components/shared/HeaderTemplate";
import { InnerCalendar } from "@/components/pages/calendar/InnerCalendar";
export default function CalendarPage() {
diff --git a/components/pages/calendar/EventCalendar.tsx b/components/pages/calendar/EventCalendar.tsx
index eeb40c1..b95f612 100644
--- a/components/pages/calendar/EventCalendar.tsx
+++ b/components/pages/calendar/EventCalendar.tsx
@@ -1,61 +1,397 @@
-import React, { useCallback, useEffect, useMemo, useState } from "react";
-import { Calendar } from "react-native-big-calendar";
-import { ActivityIndicator, ScrollView, StyleSheet, View, ViewStyle } from "react-native";
-import { useGetEvents } from "@/hooks/firebase/useGetEvents";
-import { useAtom, useAtomValue, useSetAtom } from "jotai";
+import React, {useCallback, useEffect, useMemo, useState} from "react";
+import {Calendar} from "react-native-big-calendar";
+import {ActivityIndicator, StyleSheet, TouchableOpacity, View, ViewStyle} from "react-native";
+import {useGetEvents} from "@/hooks/firebase/useGetEvents";
+import {useAtom, useSetAtom} from "jotai";
import {
editVisibleAtom,
eventForEditAtom,
- isAllDayAtom, isFamilyViewAtom,
+ isAllDayAtom,
+ isFamilyViewAtom,
modeAtom,
- refreshTriggerAtom,
selectedDateAtom,
selectedNewEventDateAtom,
} from "@/components/pages/calendar/atoms";
-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 {useAuthContext} from "@/contexts/AuthContext";
+import {CalendarEvent} from "@/components/pages/calendar/interfaces";
+import {Text} from "react-native-ui-lib";
+import {addDays, compareAsc, format, isWithinInterval, subDays} from "date-fns";
import {useCalSync} from "@/hooks/useCalSync";
import {useSyncEvents} from "@/hooks/useSyncOnScroll";
import {colorMap} from "@/constants/colorMap";
+import {useGetFamilyMembers} from "@/hooks/firebase/useGetFamilyMembers";
+import CachedImage from "expo-cached-image";
interface EventCalendarProps {
- calendarHeight: number;
- // WAS USED FOR SCROLLABLE CALENDARS, PERFORMANCE WAS NOT OPTIMAL
- calendarWidth: number;
+ calendarHeight: number;
+ // WAS USED FOR SCROLLABLE CALENDARS, PERFORMANCE WAS NOT OPTIMAL
+ calendarWidth: number;
}
const getTotalMinutes = () => {
- const date = new Date();
- return Math.abs(date.getUTCHours() * 60 + date.getUTCMinutes() - 200);
+ const date = new Date();
+ return Math.abs(date.getUTCHours() * 60 + date.getUTCMinutes() - 200);
};
+
+const processEventsForSideBySide = (events: CalendarEvent[]) => {
+ if (!events) return [];
+
+ // Group events by day and time slot
+ const timeSlots: { [key: string]: CalendarEvent[] } = {};
+
+ events.forEach(event => {
+ const startDate = new Date(event.start);
+ const endDate = new Date(event.end);
+
+ // If it's an all-day event, mark it and add it directly
+ if (event.allDay) {
+ const key = `${startDate.toISOString().split('T')[0]}-allday`;
+ if (!timeSlots[key]) timeSlots[key] = [];
+ timeSlots[key].push({
+ ...event,
+ isAllDayEvent: true,
+ width: 1,
+ xPos: 0
+ });
+ return;
+ }
+
+ // Handle multi-day events
+ if (startDate.toDateString() !== endDate.toDateString()) {
+ // Create array of dates between start and end
+ const dates = [];
+ let currentDate = new Date(startDate);
+
+ while (currentDate <= endDate) {
+ dates.push(new Date(currentDate));
+ currentDate.setDate(currentDate.getDate() + 1);
+ }
+
+ // Create segments for each day
+ dates.forEach((date, index) => {
+ const isFirstDay = index === 0;
+ const isLastDay = index === dates.length - 1;
+
+ let segmentStart, segmentEnd;
+
+ if (isFirstDay) {
+ // First day: use original start time to end of day
+ segmentStart = new Date(startDate);
+ segmentEnd = new Date(date);
+ segmentEnd.setHours(23, 59, 59);
+ } else if (isLastDay) {
+ // Last day: use start of day to original end time
+ segmentStart = new Date(date);
+ segmentStart.setHours(0, 0, 0);
+ segmentEnd = new Date(endDate);
+ } else {
+ // Middle days: full day
+ segmentStart = new Date(date);
+ segmentStart.setHours(0, 0, 0);
+ segmentEnd = new Date(date);
+ segmentEnd.setHours(23, 59, 59);
+ }
+
+ const key = `${segmentStart.toISOString().split('T')[0]}-${segmentStart.getHours()}`;
+ if (!timeSlots[key]) timeSlots[key] = [];
+
+ timeSlots[key].push({
+ ...event,
+ start: segmentStart,
+ end: segmentEnd,
+ isMultiDaySegment: true,
+ isFirstDay,
+ isLastDay,
+ originalStart: startDate,
+ originalEnd: endDate,
+ allDay: true // Mark multi-day events as all-day events
+ });
+ });
+ } else {
+ // Regular event
+ const key = `${startDate.toISOString().split('T')[0]}-${startDate.getHours()}`;
+ if (!timeSlots[key]) timeSlots[key] = [];
+ timeSlots[key].push(event);
+ }
+ });
+
+ // Process all time slots
+ return Object.values(timeSlots).flatMap(slotEvents => {
+ // Sort events by start time
+ slotEvents.sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime());
+
+ // Find overlapping events (only for non-all-day events)
+ return slotEvents.map((event, index) => {
+ // If it's an all-day or multi-day event, return as is
+ if (event.allDay || event.isMultiDaySegment) {
+ return {
+ ...event,
+ width: 1,
+ xPos: 0
+ };
+ }
+
+ // Handle regular events
+ const overlappingEvents = slotEvents.filter((otherEvent, otherIndex) => {
+ if (index === otherIndex || otherEvent.allDay || otherEvent.isMultiDaySegment) return false;
+ const eventStart = new Date(event.start);
+ const eventEnd = new Date(event.end);
+ const otherStart = new Date(otherEvent.start);
+ const otherEnd = new Date(otherEvent.end);
+ return (eventStart < otherEnd && eventEnd > otherStart);
+ });
+
+ const total = overlappingEvents.length + 1;
+ const position = index % total;
+
+ return {
+ ...event,
+ width: 1 / total,
+ xPos: position / total
+ };
+ });
+ });
+};
+
+const renderEvent = (event: CalendarEvent & {
+ width: number;
+ xPos: number;
+ isMultiDaySegment?: boolean;
+ isFirstDay?: boolean;
+ isLastDay?: boolean;
+ originalStart?: Date;
+ originalEnd?: Date;
+ isAllDayEvent?: boolean;
+ allDay?: boolean;
+ eventColor?: string;
+ attendees?: string[];
+ creatorId?: string;
+ pfp?: string;
+ firstName?: string;
+ lastName?: string;
+ notes?: string;
+ hideHours?: boolean;
+}, props: any) => {
+ const {data: familyMembers} = useGetFamilyMembers();
+
+ const attendees = useMemo(() => {
+ if (!familyMembers || !event.attendees) return event?.creatorId ? [event?.creatorId] : [];
+ return familyMembers.filter(member => event?.attendees?.includes(member?.uid!));
+ }, [familyMembers, event.attendees]);
+
+ if (event.allDay && !!event.isMultiDaySegment) {
+ return (
+
+
+ {event.title}
+ {event.isMultiDaySegment &&
+ ` (${format(new Date(event.start), 'MMM d')} - ${format(new Date(event.end), 'MMM d')})`
+ }
+
+
+ );
+ }
+
+ const originalStyle = Array.isArray(props.style) ? props.style[0] : props.style;
+
+ console.log('Rendering event:', {
+ title: event.title,
+ start: event.start,
+ end: event.end,
+ width: event.width,
+ xPos: event.xPos,
+ isMultiDaySegment: event.isMultiDaySegment
+ });
+
+
+ // Ensure we have Date objects
+ const startDate = event.start instanceof Date ? event.start : new Date(event.start);
+ const endDate = event.end instanceof Date ? event.end : new Date(event.end);
+
+ const hourHeight = props.hourHeight || 60;
+ const startHour = startDate.getHours();
+ const startMinutes = startDate.getMinutes();
+ const topPosition = (startHour + startMinutes / 60) * hourHeight;
+
+ const endHour = endDate.getHours();
+ const endMinutes = endDate.getMinutes();
+ const duration = (endHour + endMinutes / 60) - (startHour + startMinutes / 60);
+ const height = duration * hourHeight;
+
+ const formatTime = (date: Date) => {
+ const hours = date.getHours();
+ const minutes = date.getMinutes();
+ const ampm = hours >= 12 ? 'pm' : 'am';
+ const formattedHours = hours % 12 || 12;
+ const formattedMinutes = minutes.toString().padStart(2, '0');
+ return `${formattedHours}:${formattedMinutes}${ampm}`;
+ };
+
+ const timeString = event.isMultiDaySegment
+ ? event.isFirstDay
+ ? `${formatTime(startDate)} → 12:00PM`
+ : event.isLastDay
+ ? `12:00am → ${formatTime(endDate)}`
+ : 'All day'
+ : `${formatTime(startDate)} - ${formatTime(endDate)}`;
+
+ return (
+
+
+
+
+ {event.title}
+
+
+ {timeString}
+
+
+
+ {/* Attendees Section */}
+ {attendees?.length > 0 && (
+
+ {attendees?.filter(x=>x?.firstName).slice(0, 5).map((attendee, index) => (
+
+ {attendee.pfp ? (
+
+ ) : (
+
+
+ {attendee?.firstName?.at(0)}
+ {attendee?.lastName?.at(0)}
+
+
+ )}
+
+ ))}
+ {attendees.length > 3 && (
+
+
+ +{attendees.length - 3}
+
+
+ )}
+
+ )}
+
+
+ );
+};
+
+
export const EventCalendar: React.FC = React.memo(
- ({ calendarHeight }) => {
- const { data: events, isLoading, refetch } = useGetEvents();
- const { profileData, user } = useAuthContext();
- const [selectedDate, setSelectedDate] = useAtom(selectedDateAtom);
- const [mode, setMode] = useAtom(modeAtom);
- const [isFamilyView] = useAtom(isFamilyViewAtom);
+ ({calendarHeight}) => {
+ const {data: events, isLoading} = useGetEvents();
+ const {profileData, user} = useAuthContext();
+ const [selectedDate, setSelectedDate] = useAtom(selectedDateAtom);
+ const [mode, setMode] = useAtom(modeAtom);
+ const [isFamilyView] = useAtom(isFamilyViewAtom);
- const setEditVisible = useSetAtom(editVisibleAtom);
- const [isAllDay, setIsAllDay] = useAtom(isAllDayAtom);
- const setEventForEdit = useSetAtom(eventForEditAtom);
- const shouldRefresh = useAtomValue(refreshTriggerAtom);
- const setSelectedNewEndDate = useSetAtom(selectedNewEventDateAtom);
+ const setEditVisible = useSetAtom(editVisibleAtom);
+ const [isAllDay, setIsAllDay] = useAtom(isAllDayAtom);
+ const setEventForEdit = useSetAtom(eventForEditAtom);
+ const setSelectedNewEndDate = useSetAtom(selectedNewEventDateAtom);
- const {isSyncing} = useSyncEvents()
- const [offsetMinutes, setOffsetMinutes] = useState(getTotalMinutes());
- useCalSync()
+ const {isSyncing} = useSyncEvents()
+ const [offsetMinutes, setOffsetMinutes] = useState(getTotalMinutes());
+ useCalSync()
- const todaysDate = new Date();
+ const todaysDate = new Date();
- const handlePressEvent = useCallback(
- (event: CalendarEvent) => {
- if (mode === "day" || mode === "week") {
- setEditVisible(true);
- // console.log({event});
+ const handlePressEvent = useCallback(
+ (event: CalendarEvent) => {
+ if (mode === "day" || mode === "week" || mode === "3days") {
+ setEditVisible(true);
setEventForEdit(event);
} else {
setMode("day");
@@ -65,209 +401,195 @@ export const EventCalendar: React.FC = React.memo(
[setEditVisible, setEventForEdit, mode]
);
- const handlePressCell = useCallback(
- (date: Date) => {
- if (mode === "day" || mode === "week") {
- setSelectedNewEndDate(date);
- } else {
- setMode("day");
- setSelectedDate(date);
- }
- },
- [mode, setSelectedNewEndDate, setSelectedDate]
- );
+ const handlePressCell = useCallback(
+ (date: Date) => {
+ if (mode === "day" || mode === "week" || mode === "3days") {
+ setSelectedNewEndDate(date);
+ } else {
+ setMode("day");
+ setSelectedDate(date);
+ }
+ },
+ [mode, setSelectedNewEndDate, setSelectedDate]
+ );
- const handlePressDayHeader = useCallback(
- (date: Date) => {
- if (mode === "day") {
- setIsAllDay(true);
- setSelectedNewEndDate(date);
- setEditVisible(true);
- }
- if (mode === 'week')
- {
- setSelectedDate(date)
+ const handlePressDayHeader = useCallback(
+ (date: Date) => {
+ if (mode === "day") {
+ setIsAllDay(true);
+ setSelectedNewEndDate(date);
+ setEditVisible(true);
+ }
+ if (mode === 'week' || mode === '3days') {
+ setSelectedDate(date)
+ setMode("day")
+ }
+ },
+ [mode, setSelectedNewEndDate]
+ );
- setMode("day")
- }
- },
- [mode, setSelectedNewEndDate]
- );
+ const handleSwipeEnd = useCallback(
+ (date: Date) => {
+ setSelectedDate(date);
+ },
+ [setSelectedDate]
+ );
- const handleSwipeEnd = useCallback(
- (date: Date) => {
- setSelectedDate(date);
- },
- [setSelectedDate]
- );
+ const memoizedEventCellStyle = useCallback(
+ (event: CalendarEvent) => {
+ let eventColor = event.eventColor;
+ if (!isFamilyView && (event.attendees?.includes(user?.uid!) || event.creatorId! === user?.uid)) {
+ eventColor = profileData?.eventColor ?? colorMap.teal;
+ }
- const memoizedEventCellStyle = useCallback(
- (event: CalendarEvent) => {
- let eventColor = event.eventColor;
- if (!isFamilyView && (event.attendees?.includes(user?.uid) || event.creatorId === user?.uid)) {
- eventColor = profileData?.eventColor ?? colorMap.teal;
- }
+ return {backgroundColor: eventColor, fontSize: 14}
+ },
+ []
+ );
- return { backgroundColor: eventColor , fontSize: 14}
- },
- []
- );
+ const memoizedWeekStartsOn = useMemo(
+ () => (profileData?.firstDayOfWeek === "Mondays" ? 1 : 0),
+ [profileData]
+ );
- const memoizedWeekStartsOn = useMemo(
- () => (profileData?.firstDayOfWeek === "Mondays" ? 1 : 0),
- [profileData]
- );
+ const isSameDate = useCallback((date1: Date, date2: Date) => {
+ return (
+ date1.getDate() === date2.getDate() &&
+ date1.getMonth() === date2.getMonth() &&
+ date1.getFullYear() === date2.getFullYear()
+ );
+ }, []);
- // console.log({memoizedWeekStartsOn, profileData: profileData?.firstDayOfWeek,
+ const dayHeaderColor = useMemo(() => {
+ return isSameDate(todaysDate, selectedDate) ? "white" : "#4d4d4d";
+ }, [selectedDate, mode]);
- const isSameDate = useCallback((date1: Date, date2: Date) => {
- return (
- date1.getDate() === date2.getDate() &&
- date1.getMonth() === date2.getMonth() &&
- date1.getFullYear() === date2.getFullYear()
- );
- }, []);
+ const dateStyle = useMemo(() => {
+ if (mode === "week" || mode === "3days") return undefined;
+ return isSameDate(todaysDate, selectedDate) && mode === "day"
+ ? styles.dayHeader
+ : styles.otherDayHeader;
+ }, [selectedDate, mode]);
- const dayHeaderColor = useMemo(() => {
- return isSameDate(todaysDate, selectedDate) ? "white" : "#4d4d4d";
- }, [selectedDate, mode]);
+ const memoizedHeaderContentStyle = useMemo(() => {
+ if (mode === "day") {
+ return styles.dayModeHeader;
+ } else if (mode === "week" || mode === "3days") {
+ return styles.weekModeHeader;
+ } else if (mode === "month") {
+ return styles.monthModeHeader;
+ } else {
+ return {};
+ }
+ }, [mode]);
+ const {enrichedEvents, filteredEvents} = useMemo(() => {
+ const startTime = Date.now();
- const dateStyle = useMemo(() => {
- if (mode === "week") return undefined;
- return isSameDate(todaysDate, selectedDate) && mode === "day"
- ? styles.dayHeader
- : styles.otherDayHeader;
- }, [selectedDate, mode]);
+ const startOffset = mode === "month" ? 40 : (mode === "week" || mode === "3days") ? 10 : 1;
+ const endOffset = mode === "month" ? 40 : (mode === "week" || mode === "3days") ? 10 : 1;
- const memoizedHeaderContentStyle = useMemo(() => {
- if (mode === "day") {
- return styles.dayModeHeader;
- } else if (mode === "week") {
- return styles.weekModeHeader;
- } else if (mode === "month") {
- return styles.monthModeHeader;
- } else {
- return {};
- }
- }, [mode]);
+ const filteredEvents =
+ events?.filter(
+ (event) =>
+ event.start &&
+ event.end &&
+ isWithinInterval(event.start, {
+ start: subDays(selectedDate, startOffset),
+ end: addDays(selectedDate, endOffset),
+ }) &&
+ isWithinInterval(event.end, {
+ start: subDays(selectedDate, startOffset),
+ end: addDays(selectedDate, endOffset),
+ })
+ ) ?? [];
- const { enrichedEvents, filteredEvents } = useMemo(() => {
- const startTime = Date.now(); // Start timer
+ const enrichedEvents = filteredEvents.reduce((acc, event) => {
+ const dateKey = event.start.toISOString().split("T")[0];
+ acc[dateKey] = acc[dateKey] || [];
+ acc[dateKey].push({
+ ...event,
+ overlapPosition: false,
+ overlapCount: 0,
+ });
- const startOffset = mode === "month" ? 40 : mode === "week" ? 10 : 1;
- const endOffset = mode === "month" ? 40 : mode === "week" ? 10 : 1;
+ acc[dateKey].sort((a: { start: any; }, b: { start: any; }) => compareAsc(a.start, b.start));
- const filteredEvents =
- events?.filter(
- (event) =>
- event.start &&
- event.end &&
- isWithinInterval(event.start, {
- start: subDays(selectedDate, startOffset),
- end: addDays(selectedDate, endOffset),
- }) &&
- isWithinInterval(event.end, {
- start: subDays(selectedDate, startOffset),
- end: addDays(selectedDate, endOffset),
- })
- ) ?? [];
+ return acc;
+ }, {} as Record);
- const enrichedEvents = filteredEvents.reduce((acc, event) => {
- const dateKey = event.start.toISOString().split("T")[0];
- acc[dateKey] = acc[dateKey] || [];
- acc[dateKey].push({
- ...event,
- overlapPosition: false,
- overlapCount: 0,
- });
+ const endTime = Date.now();
+ // console.log("memoizedEvents computation time:", endTime - startTime, "ms");
- acc[dateKey].sort((a, b) => compareAsc(a.start, b.start));
+ return {enrichedEvents, filteredEvents};
+ }, [events, selectedDate, mode]);
- return acc;
- }, {} as Record);
+ const renderCustomDateForMonth = (date: Date) => {
+ const circleStyle = useMemo(
+ () => ({
+ width: 30,
+ height: 30,
+ justifyContent: "center",
+ alignItems: "center",
+ borderRadius: 15,
+ }),
+ []
+ );
- const endTime = Date.now();
- // console.log("memoizedEvents computation time:", endTime - startTime, "ms");
+ const defaultStyle = useMemo(
+ () => ({
+ ...circleStyle,
+ }),
+ [circleStyle]
+ );
- return { enrichedEvents, filteredEvents };
- }, [events, selectedDate, mode]);
+ const currentDateStyle = useMemo(
+ () => ({
+ ...circleStyle,
+ backgroundColor: "#4184f2",
+ }),
+ [circleStyle]
+ );
- const renderCustomDateForMonth = (date: Date) => {
- const circleStyle = useMemo(
- () => ({
- width: 30,
- height: 30,
- justifyContent: "center",
- alignItems: "center",
- borderRadius: 15,
- }),
- []
- );
+ const renderDate = useCallback(
+ (date: Date) => {
+ const isCurrentDate = isSameDate(todaysDate, date);
+ const appliedStyle = isCurrentDate ? currentDateStyle : defaultStyle;
- const defaultStyle = useMemo(
- () => ({
- ...circleStyle,
- }),
- [circleStyle]
- );
+ return (
+
+
+
+ {date.getDate()}
+
+
+
+ );
+ },
+ [todaysDate, currentDateStyle, defaultStyle]
+ );
- const currentDateStyle = useMemo(
- () => ({
- ...circleStyle,
- backgroundColor: "#4184f2",
- }),
- [circleStyle]
- );
+ return renderDate(date);
+ };
- const renderDate = useCallback(
- (date: Date) => {
- const isCurrentDate = isSameDate(todaysDate, date);
- const appliedStyle = isCurrentDate ? currentDateStyle : defaultStyle;
+ const processedEvents = useMemo(() => {
+ return processEventsForSideBySide(filteredEvents);
+ }, [filteredEvents]);
- return (
-
-
-
- {date.getDate()}
-
-
-
- );
- },
- [todaysDate, currentDateStyle, defaultStyle] // dependencies
- );
+ useEffect(() => {
+ setOffsetMinutes(getTotalMinutes());
+ }, [events, mode]);
- return renderDate(date);
- };
-
- useEffect(() => {
- setOffsetMinutes(getTotalMinutes());
- }, [events, mode]);
-
- useEffect(() => {
- refetch()
- .then(() => {
- console.log('✅ Events refreshed successfully');
- })
- .catch((error) => {
- console.error('❌ Events refresh failed:', error);
- });
- }, [shouldRefresh, refetch])
-
-
- if (isLoading) {
- return (
-
- {isSyncing && Syncing...}
+ if (isLoading) {
+ return (
+
+ {isSyncing && Syncing...}
);
}
- // console.log(enrichedEvents, filteredEvents)
-
- return (
- <>
+ return (
+ <>
{isSyncing && (
{isSyncing && Syncing...}
@@ -278,12 +600,13 @@ export const EventCalendar: React.FC = React.memo(
bodyContainerStyle={styles.calHeader}
swipeEnabled
mode={mode}
- // enableEnrichedEvents={true}
sortedMonthView
- // enrichedEventsByDate={enrichedEvents}
events={filteredEvents}
+ // renderEvent={renderEvent}
eventCellStyle={memoizedEventCellStyle}
allDayEventCellStyle={memoizedEventCellStyle}
+ // enableEnrichedEvents={true}
+ // enrichedEventsByDate={enrichedEvents}
onPressEvent={handlePressEvent}
weekStartsOn={memoizedWeekStartsOn}
height={calendarHeight}
@@ -318,14 +641,14 @@ export const EventCalendar: React.FC = React.memo(
dayHeaderHighlightColor={"white"}
showAdjacentMonths
headerContainerStyle={mode !== "month" ? {
- overflow:"hidden",
+ overflow: "hidden",
} : {}}
hourStyle={styles.hourStyle}
- onPressDateHeader={handlePressDayHeader}
+ onPressDateHeader={handlePressDayHeader}
ampm
// renderCustomDateForMonth={renderCustomDateForMonth}
/>
-
+
>
);
@@ -381,4 +704,16 @@ const styles = StyleSheet.create({
fontSize: 12,
fontFamily: "Manrope_500Medium",
},
+ eventCell: {
+ flex: 1,
+ borderRadius: 4,
+ padding: 4,
+ height: '100%',
+ justifyContent: 'center',
+ },
+ eventTitle: {
+ color: 'white',
+ fontSize: 12,
+ fontFamily: "PlusJakartaSans_500Medium",
+ },
});
diff --git a/components/pages/calendar/ManuallyAddEventModal.tsx b/components/pages/calendar/ManuallyAddEventModal.tsx
index 3a92335..077a3e9 100644
--- a/components/pages/calendar/ManuallyAddEventModal.tsx
+++ b/components/pages/calendar/ManuallyAddEventModal.tsx
@@ -39,6 +39,7 @@ import BinIcon from "@/assets/svgs/BinIcon";
import DeleteEventDialog from "./DeleteEventDialog";
import { useDeleteEvent } from "@/hooks/firebase/useDeleteEvent";
import AddPersonIcon from "@/assets/svgs/AddPersonIcon";
+import {addHours, startOfHour, startOfMinute} from "date-fns";
const daysOfWeek = [
{ label: "Monday", value: "monday" },
@@ -86,7 +87,6 @@ export const ManuallyAddEventModal = () => {
if(allDayAtom === true) setIsAllDay(true);
}, [allDayAtom])
-
const [startTime, setStartTime] = useState(() => {
const date = initialDate ?? new Date();
if (
@@ -104,27 +104,11 @@ export const ManuallyAddEventModal = () => {
const [endTime, setEndTime] = useState(() => {
if (editEvent?.end) {
- const date = new Date(editEvent.end);
- date.setSeconds(0, 0);
- return date;
+ return new Date(editEvent.end);
}
const baseDate = editEvent?.end ?? initialDate ?? new Date();
- const date = new Date(baseDate);
-
- if (
- date.getMinutes() > 0 ||
- date.getSeconds() > 0 ||
- date.getMilliseconds() > 0
- ) {
- date.setHours(date.getHours() + 1);
- }
- date.setMinutes(0);
- date.setSeconds(0);
- date.setMilliseconds(0);
-
- date.setHours(date.getHours() + 1);
- return date;
+ return addHours(startOfHour(baseDate), 1);
});
const [startDate, setStartDate] = useState(initialDate ?? new Date());
const [endDate, setEndDate] = useState(
@@ -161,38 +145,17 @@ export const ManuallyAddEventModal = () => {
setIsPrivate(editEvent?.private || false);
setStartTime(() => {
- const date = new Date(initialDate ?? new Date());
- const minutes = date.getMinutes();
- date.setMinutes(0, 0, 0);
- if (minutes >= 30) {
- date.setHours(date.getHours() + 1);
- }
+ const date = initialDate ?? new Date();
+ date.setSeconds(0, 0);
return date;
});
-
setEndTime(() => {
if (editEvent?.end) {
- const date = new Date(editEvent.end);
- date.setSeconds(0, 0);
- return date;
+ return startOfMinute(new Date(editEvent.end));
}
const baseDate = editEvent?.end ?? initialDate ?? new Date();
- const date = new Date(baseDate);
-
- if (
- date.getMinutes() > 0 ||
- date.getSeconds() > 0 ||
- date.getMilliseconds() > 0
- ) {
- date.setHours(date.getHours() + 1);
- }
- date.setMinutes(0);
- date.setSeconds(0);
- date.setMilliseconds(0);
-
- date.setHours(date.getHours() + 1);
- return date;
+ return addHours(startOfHour(baseDate), 1);
});
setStartDate(initialDate ?? new Date());
@@ -260,7 +223,7 @@ export const ManuallyAddEventModal = () => {
startDate: finalStartDate,
endDate: finalEndDate,
allDay: isAllDay,
- attendees: selectedAttendees,
+ attendees: selectedAttendees,
notes: details,
location: location
};
@@ -282,10 +245,10 @@ export const ManuallyAddEventModal = () => {
Alert.alert('Alert', 'Title field cannot be empty');
return false;
}
- if (!selectedAttendees || selectedAttendees?.length === 0) {
- Alert.alert('Alert', 'Cannot have an event without any attendees');
- return false;
- }
+ // if (!selectedAttendees || selectedAttendees?.length === 0) {
+ // Alert.alert('Alert', 'Cannot have an event without any attendees');
+ // return false;
+ // }
return true;
}
diff --git a/components/pages/calendar/atoms.ts b/components/pages/calendar/atoms.ts
index a2095dc..82769ca 100644
--- a/components/pages/calendar/atoms.ts
+++ b/components/pages/calendar/atoms.ts
@@ -1,15 +1,21 @@
import { atom } from "jotai";
+import * as Device from "expo-device";
import { CalendarEvent } from "@/components/pages/calendar/interfaces";
+const getDefaultMode = () => {
+ const isTablet = Device.deviceType === Device.DeviceType.TABLET;
+ return isTablet ? "week" : "3days";
+};
+
export const editVisibleAtom = atom(false);
export const isAllDayAtom = atom(false);
export const eventForEditAtom = atom(undefined);
export const isFamilyViewAtom = atom(false);
-export const modeAtom = atom<"week" | "month" | "day">("week");
+export const modeAtom = atom<"week" | "month" | "day" | "3days">(getDefaultMode());
export const selectedDateAtom = atom(new Date());
export const selectedNewEventDateAtom = atom(undefined);
export const settingsPageIndex = atom(0);
export const userSettingsView = atom(true);
export const toDosPageIndex = atom(0);
export const refreshTriggerAtom = atom(false);
-export const refreshEnabledAtom = atom(true);
+export const refreshEnabledAtom = atom(true);
\ No newline at end of file
diff --git a/components/pages/calendar/constants.ts b/components/pages/calendar/constants.ts
index eb5eefc..653b54c 100644
--- a/components/pages/calendar/constants.ts
+++ b/components/pages/calendar/constants.ts
@@ -1,7 +1,8 @@
export const modeMap = new Map([
[0, "day"],
- [1, "week"],
- [2, "month"],
+ [1, "3days"],
+ [2, "week"],
+ [3, "month"]
]);
export const months = [
diff --git a/components/pages/calendar/interfaces.ts b/components/pages/calendar/interfaces.ts
index 8ac7f7d..e6aa4ab 100644
--- a/components/pages/calendar/interfaces.ts
+++ b/components/pages/calendar/interfaces.ts
@@ -1,8 +1,9 @@
export interface CalendarEvent {
id?: number | string; // Unique identifier for the event
user?: string;
+ creatorId?: string;
title: string; // Event title or name
- description?: string; // Optional description for the event
+ description?: string; // Optiional description for the event
start: Date; // Start date and time of the event
end: Date; // End date and time of the event
location?: string; // Optional event location
diff --git a/components/pages/feedback/AddFeedback.tsx b/components/pages/feedback/AddFeedback.tsx
index 9b2c816..6544e73 100644
--- a/components/pages/feedback/AddFeedback.tsx
+++ b/components/pages/feedback/AddFeedback.tsx
@@ -11,7 +11,6 @@ import { PanningDirectionsEnum } from "react-native-ui-lib/src/incubator/panView
import { Dimensions, Platform, StyleSheet } from "react-native";
import DropModalIcon from "@/assets/svgs/DropModalIcon";
-import { useBrainDumpContext } from "@/contexts/DumpContext";
import KeyboardManager from "react-native-keyboard-manager";
import { useFeedbackContext } from "@/contexts/FeedbackContext";
diff --git a/components/pages/feedback/EditFeedback.tsx b/components/pages/feedback/EditFeedback.tsx
index 385f659..005196c 100644
--- a/components/pages/feedback/EditFeedback.tsx
+++ b/components/pages/feedback/EditFeedback.tsx
@@ -14,9 +14,6 @@ import PenIcon from "@/assets/svgs/PenIcon";
import BinIcon from "@/assets/svgs/BinIcon";
import DropModalIcon from "@/assets/svgs/DropModalIcon";
import CloseXIcon from "@/assets/svgs/CloseXIcon";
-import NavCalendarIcon from "@/assets/svgs/NavCalendarIcon";
-import NavToDosIcon from "@/assets/svgs/NavToDosIcon";
-import RemindersIcon from "@/assets/svgs/RemindersIcon";
import MenuIcon from "@/assets/svgs/MenuIcon";
import { IFeedback, useFeedbackContext } from "@/contexts/FeedbackContext";
import FeedbackDialog from "./FeedbackDialog";
diff --git a/components/pages/grocery/AddGroceryItem.tsx b/components/pages/grocery/AddGroceryItem.tsx
index c177d2c..dcbba22 100644
--- a/components/pages/grocery/AddGroceryItem.tsx
+++ b/components/pages/grocery/AddGroceryItem.tsx
@@ -2,7 +2,6 @@ import {Dimensions, StyleSheet} from "react-native";
import React from "react";
import {Button, View,} from "react-native-ui-lib";
import {useGroceryContext} from "@/contexts/GroceryContext";
-import {FontAwesome6} from "@expo/vector-icons";
import PlusIcon from "@/assets/svgs/PlusIcon";
const { width } = Dimensions.get("screen");
diff --git a/components/pages/main/SignInPage.tsx b/components/pages/main/SignInPage.tsx
index 781e6a0..0ab9825 100644
--- a/components/pages/main/SignInPage.tsx
+++ b/components/pages/main/SignInPage.tsx
@@ -26,6 +26,20 @@ import { DeviceType } from "expo-device";
if (Platform.OS === "ios") KeyboardManager.setEnableAutoToolbar(true);
+const firebaseAuthErrors: { [key: string]: string } = {
+ 'auth/invalid-email': 'Please enter a valid email address',
+ 'auth/user-disabled': 'This account has been disabled. Please contact support',
+ 'auth/user-not-found': 'No account found with this email address',
+ 'auth/wrong-password': 'Incorrect password. Please try again',
+ 'auth/email-already-in-use': 'An account with this email already exists',
+ 'auth/operation-not-allowed': 'This login method is not enabled. Please contact support',
+ 'auth/weak-password': 'Password should be at least 6 characters',
+ 'auth/invalid-credential': 'Invalid login credentials. Please try again',
+ 'auth/network-request-failed': 'Network error. Please check your internet connection',
+ 'auth/too-many-requests': 'Too many failed login attempts. Please try again later',
+ 'auth/invalid-login-credentials': 'Invalid email or password. Please try again',
+};
+
const SignInPage = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
@@ -56,21 +70,25 @@ const SignInPage = () => {
const router = useRouter();
- const handleSignIn = async () => {
- await signIn({ email, password });
- if (!isError) {
- Toast.show({
- type: "success",
- text1: "Login successful!",
- });
- } else {
- Toast.show({
- type: "error",
- text1: "Error logging in",
- text2: `${error}`,
- });
- }
- };
+ const handleSignIn = async () => {
+ try {
+ await signIn({ email, password });
+ Toast.show({
+ type: "success",
+ text1: "Login successful!",
+ });
+ } catch (error: any) {
+ const errorCode = error?.code || 'unknown-error';
+
+ const errorMessage = firebaseAuthErrors[errorCode] || 'An unexpected error occurred. Please try again';
+
+ Toast.show({
+ type: "error",
+ text1: "Error logging in",
+ text2: errorMessage,
+ });
+ }
+ };
return (
@@ -146,11 +164,11 @@ const SignInPage = () => {
backgroundColor="#fd1775"
/>
- {isError && (
- {`${
- error?.toString()?.split("]")?.[1]
- }`}
- )}
+ {isError && (
+
+ {firebaseAuthErrors[error?.code] || 'An unexpected error occurred. Please try again'}
+
+ )}
Don't have an account?
diff --git a/components/pages/notifications/NotificationsPage.tsx b/components/pages/notifications/NotificationsPage.tsx
index ea0d43e..3dd5926 100644
--- a/components/pages/notifications/NotificationsPage.tsx
+++ b/components/pages/notifications/NotificationsPage.tsx
@@ -1,15 +1,123 @@
-import {FlatList, StyleSheet} from "react-native";
-import React from "react";
+import {ActivityIndicator, Animated, FlatList, StyleSheet} from "react-native";
+import React, {useCallback, useState} from "react";
import {Card, Text, View} from "react-native-ui-lib";
import HeaderTemplate from "@/components/shared/HeaderTemplate";
-import {useGetNotifications} from "@/hooks/firebase/useGetNotifications";
+import {Notification, useGetNotifications} from "@/hooks/firebase/useGetNotifications";
import {formatDistanceToNow} from "date-fns";
+import {useRouter} from "expo-router";
+import {useSetAtom} from "jotai";
+import {modeAtom, selectedDateAtom} from "@/components/pages/calendar/atoms";
+import {Swipeable} from 'react-native-gesture-handler';
+import {useDeleteNotification} from "@/hooks/firebase/useDeleteNotification";
-const NotificationsPage = () => {
- const {data: notifications} = useGetNotifications()
+interface NotificationItemProps {
+ item: Notification;
+ onDelete: (id: string) => void;
+ onPress: () => void;
+ isDeleting: boolean;
+}
- console.log(notifications?.[0])
+const NotificationItem: React.FC = React.memo(({
+ item,
+ onDelete,
+ onPress,
+ isDeleting
+ }) => {
+ const renderRightActions = useCallback((
+ progress: Animated.AnimatedInterpolation,
+ dragX: Animated.AnimatedInterpolation
+ ) => {
+ const trans = dragX.interpolate({
+ inputRange: [-100, 0],
+ outputRange: [0, 100],
+ extrapolate: 'clamp'
+ });
+ return (
+
+ Delete
+
+ );
+ }, []);
+
+ return (
+ onDelete(item.id)}
+ overshootRight={false}
+ enabled={!isDeleting}
+ >
+
+ {isDeleting && (
+
+
+
+ )}
+ {item.content}
+
+
+ {formatDistanceToNow(new Date(item.timestamp), {addSuffix: true})}
+
+
+ {item.timestamp.toLocaleDateString()}
+
+
+
+
+ );
+});
+
+const NotificationsPage: React.FC = () => {
+ const setSelectedDate = useSetAtom(selectedDateAtom);
+ const setMode = useSetAtom(modeAtom);
+ const {data: notifications} = useGetNotifications();
+ const deleteNotification = useDeleteNotification();
+ const {push} = useRouter();
+ const [deletingIds, setDeletingIds] = useState>(new Set());
+
+ const goToEventDay = useCallback((notification: Notification) => () => {
+ if (notification?.date) {
+ setSelectedDate(notification.date);
+ setMode("day")
+ }
+ push({pathname: "/calendar"});
+ }, [push, setSelectedDate]);
+
+ const handleDelete = useCallback((notificationId: string) => {
+ setDeletingIds(prev => new Set(prev).add(notificationId));
+ deleteNotification.mutate(notificationId, {
+ onSettled: () => {
+ setDeletingIds(prev => {
+ const newSet = new Set(prev);
+ newSet.delete(notificationId);
+ return newSet;
+ });
+ }
+ });
+ }, [deleteNotification]);
+
+ const renderNotificationItem = useCallback(({item}: { item: Notification }) => (
+
+ ), [handleDelete, goToEventDay, deletingIds]);
return (
@@ -18,40 +126,57 @@ const NotificationsPage = () => {
- See your notifications here.
-
- }
- />
+ >
+
+ See your notifications here.
+
+
-
- {item.content}
-
- {formatDistanceToNow(new Date(item.timestamp), {addSuffix: true})}
- {item.timestamp.toLocaleDateString()}
-
- }/>
-
+ item.id}
+ />
+
);
};
const styles = StyleSheet.create({
- searchField: {
- borderWidth: 0.7,
- borderColor: "#9b9b9b",
- borderRadius: 15,
- height: 42,
- paddingLeft: 10,
- marginVertical: 20,
+ listContainer: {
+ paddingBottom: 10,
+ paddingHorizontal: 25,
+ },
+ card: {
+ width: '100%',
+ backgroundColor: 'white',
+ },
+ subtitle: {
+ fontFamily: "Manrope_400Regular",
+ fontSize: 14,
+ },
+ deleteAction: {
+ backgroundColor: '#FF3B30',
+ justifyContent: 'center',
+ alignItems: 'flex-end',
+ paddingRight: 30,
+ marginBottom: 10,
+ width: 100,
+ borderRadius: 10,
+ },
+ deleteActionText: {
+ color: 'white',
+ fontWeight: '600',
+ },
+ loadingOverlay: {
+ ...StyleSheet.absoluteFillObject,
+ backgroundColor: 'rgba(255, 255, 255, 0.8)',
+ justifyContent: 'center',
+ alignItems: 'center',
+ zIndex: 1,
},
});
-export default NotificationsPage;
+export default NotificationsPage
diff --git a/components/pages/onboarding/OnboardingFlow.tsx b/components/pages/onboarding/OnboardingFlow.tsx
index 69b01d2..103a055 100644
--- a/components/pages/onboarding/OnboardingFlow.tsx
+++ b/components/pages/onboarding/OnboardingFlow.tsx
@@ -1,91 +1,88 @@
-import { Image } from "react-native";
-import React, { useRef } from "react";
-import { View, Text, Button, TextField } from "react-native-ui-lib";
+import {Image, StyleSheet} from "react-native";
+import React, {useRef} from "react";
+import {Button, Text, TextField, View} from "react-native-ui-lib";
import Onboarding from "react-native-onboarding-swiper";
-import { StyleSheet } from "react-native";
-import { useAuthContext } from "@/contexts/AuthContext";
-import { useSignUp } from "@/hooks/firebase/useSignUp";
-const OnboardingFlow = () => {
- const onboardingRef = useRef(null);
- const { mutateAsync: signUp } = useSignUp();
- return (
-
- ),
- title: Welcome to Cally,
- subtitle: (
-
- Lightening Mental Loads, One Family at a Time
- onboardingRef?.current?.goToPage(1, true)}
- />
-
- ),
- },
- {
- backgroundColor: "#f9f8f7",
- title: Get started with Cally,
- image: (
-
- ),
- subtitle: (
-
-
- {/**/}
- {/**/}
-
-
- {
- console.log("Onboarding Done");
- }}
- />
-
-
- ),
- },
- ]}
- />
- );
+const OnboardingFlow = () => {
+ const onboardingRef = useRef(null);
+
+ return (
+
+ ),
+ title: Welcome to Cally,
+ subtitle: (
+
+ Lightening Mental Loads, One Family at a Time
+ onboardingRef?.current?.goToPage(1, true)}
+ />
+
+ ),
+ },
+ {
+ backgroundColor: "#f9f8f7",
+ title: Get started with Cally,
+ image: (
+
+ ),
+ subtitle: (
+
+
+ {/**/}
+ {/**/}
+
+
+ {
+ console.log("Onboarding Done");
+ }}
+ />
+
+
+ ),
+ },
+ ]}
+ />
+ );
};
export default OnboardingFlow;
const styles = StyleSheet.create({
- textfield: {
- backgroundColor: "white",
- marginVertical: 10,
- padding: 30,
- height: 45,
- borderRadius: 50,
- },
+ textfield: {
+ backgroundColor: "white",
+ marginVertical: 10,
+ padding: 30,
+ height: 45,
+ borderRadius: 50,
+ },
});
diff --git a/components/pages/settings/user_components/DeleteProfileDialogs.tsx b/components/pages/settings/user_components/DeleteProfileDialogs.tsx
index e6acb52..63f5c41 100644
--- a/components/pages/settings/user_components/DeleteProfileDialogs.tsx
+++ b/components/pages/settings/user_components/DeleteProfileDialogs.tsx
@@ -2,7 +2,6 @@ import React, { useState } from "react";
import { Dialog, Button, Text, View } from "react-native-ui-lib";
import { StyleSheet } from "react-native";
import { Feather } from "@expo/vector-icons";
-import {useDeleteUser} from "@/hooks/firebase/useDeleteUser";
interface ConfirmationDialogProps {
visible: boolean;
diff --git a/components/pages/settings/user_settings_views/MyGroup.tsx b/components/pages/settings/user_settings_views/MyGroup.tsx
index 581ded9..76d253a 100644
--- a/components/pages/settings/user_settings_views/MyGroup.tsx
+++ b/components/pages/settings/user_settings_views/MyGroup.tsx
@@ -33,8 +33,6 @@ import KeyboardManager, {
} from "react-native-keyboard-manager";
import { ScrollView } from "react-native-gesture-handler";
import { useUploadProfilePicture } from "@/hooks/useUploadProfilePicture";
-import { ImagePickerAsset } from "expo-image-picker";
-import MenuDotsIcon from "@/assets/svgs/MenuDotsIcon";
import UserOptions from "./UserOptions";
import { colorMap } from "@/constants/colorMap";
diff --git a/components/pages/settings/user_settings_views/MyProfile.tsx b/components/pages/settings/user_settings_views/MyProfile.tsx
index 87fc963..6385b9b 100644
--- a/components/pages/settings/user_settings_views/MyProfile.tsx
+++ b/components/pages/settings/user_settings_views/MyProfile.tsx
@@ -270,35 +270,35 @@ const MyProfile = () => {
handleChangeColor(colorMap.pink)}>
{selectedColor == colorMap.pink && (
-
+
)}
handleChangeColor(colorMap.orange)}>
{selectedColor == colorMap.orange && (
-
+
)}
handleChangeColor(colorMap.green)}>
{selectedColor == colorMap.green && (
-
+
)}
handleChangeColor(colorMap.teal)}>
{selectedColor == colorMap.teal && (
-
+
)}
handleChangeColor(colorMap.purple)}>
{selectedColor == colorMap.purple && (
-
+
)}
@@ -307,35 +307,35 @@ const MyProfile = () => {
handleChangeColor(colorMap.navy)}>
{selectedColor == colorMap.navy && (
-
+
)}
handleChangeColor(colorMap.red)}>
{selectedColor == colorMap.red && (
-
+
)}
- handleChangeColor(colorMap.gray)}>
-
- {selectedColor == colorMap.gray && (
-
+ handleChangeColor(colorMap.indigo)}>
+
+ {selectedColor == colorMap.indigo && (
+
)}
- handleChangeColor(colorMap.yellow)}>
-
- {selectedColor == colorMap.yellow && (
-
+ handleChangeColor(colorMap.emerald)}>
+
+ {selectedColor == colorMap.emerald && (
+
)}
- handleChangeColor(colorMap.sky)}>
-
- {selectedColor == colorMap.sky && (
-
+ handleChangeColor(colorMap.violet)}>
+
+ {selectedColor == colorMap.violet && (
+
)}
diff --git a/components/pages/settings/user_settings_views/UserMenu.tsx b/components/pages/settings/user_settings_views/UserMenu.tsx
index 63560c0..5d270f2 100644
--- a/components/pages/settings/user_settings_views/UserMenu.tsx
+++ b/components/pages/settings/user_settings_views/UserMenu.tsx
@@ -9,7 +9,6 @@ import {
import QRCode from "react-native-qrcode-svg";
import { PanningDirectionsEnum } from "react-native-ui-lib/src/components/panningViews/panningProvider";
import Ionicons from "@expo/vector-icons/Ionicons";
-import { useGetFamilyMembers } from "@/hooks/firebase/useGetFamilyMembers";
import { UserProfile } from "@/hooks/firebase/types/profileTypes";
import { StyleSheet } from "react-native";
import { ProfileType, useAuthContext } from "@/contexts/AuthContext";
diff --git a/components/pages/todos/AddChore.tsx b/components/pages/todos/AddChore.tsx
index 2237240..2030be4 100644
--- a/components/pages/todos/AddChore.tsx
+++ b/components/pages/todos/AddChore.tsx
@@ -1,68 +1,66 @@
-import { Dimensions, StyleSheet } from "react-native";
-import React, { useState } from "react";
-import { Button, ButtonSize, Text, View } from "react-native-ui-lib";
-import { AntDesign } from "@expo/vector-icons";
-import LinearGradient from "react-native-linear-gradient";
+import {StyleSheet} from "react-native";
+import React, {useState} from "react";
+import {Button, ButtonSize, Text, View} from "react-native-ui-lib";
import AddChoreDialog from "./AddChoreDialog";
import PlusIcon from "@/assets/svgs/PlusIcon";
const AddChore = () => {
- const [isVisible, setIsVisible] = useState(false);
+ const [isVisible, setIsVisible] = useState(false);
- return (
-
-
- setIsVisible(!isVisible)}
+ return (
+
-
-
- Create new to do
-
-
-
- {isVisible && (
-
- )}
-
- );
+
+ setIsVisible(!isVisible)}
+ >
+
+
+ Create new to do
+
+
+
+ {isVisible && (
+
+ )}
+
+ );
};
export default AddChore;
const styles = StyleSheet.create({
- gradient: {
- height: 150,
- position: "absolute",
- bottom: 0,
- width: "100%",
- justifyContent: "center",
- alignItems: "center",
- },
- buttonContainer: {
- width: "100%",
- alignItems: "center",
- },
- button: {
- backgroundColor: "rgb(253, 23, 117)",
- height: 53.26,
- borderRadius: 30,
- width: 335,
- },
+ gradient: {
+ height: 150,
+ position: "absolute",
+ bottom: 0,
+ width: "100%",
+ justifyContent: "center",
+ alignItems: "center",
+ },
+ buttonContainer: {
+ width: "100%",
+ alignItems: "center",
+ },
+ button: {
+ backgroundColor: "rgb(253, 23, 117)",
+ height: 53.26,
+ borderRadius: 30,
+ width: 335,
+ },
});
diff --git a/components/pages/todos/ProgressCard.tsx b/components/pages/todos/ProgressCard.tsx
index c82d1aa..d73d8c9 100644
--- a/components/pages/todos/ProgressCard.tsx
+++ b/components/pages/todos/ProgressCard.tsx
@@ -1,44 +1,43 @@
-import { View, Text, Button } from "react-native-ui-lib";
+import {Text, View} from "react-native-ui-lib";
import React from "react";
-import { Fontisto } from "@expo/vector-icons";
-import { ProgressBar } from "react-native-ui-lib/src/components/progressBar";
-import { useToDosContext } from "@/contexts/ToDosContext";
+import {ProgressBar} from "react-native-ui-lib/src/components/progressBar";
+import {useToDosContext} from "@/contexts/ToDosContext";
import FireworksOrangeIcon from "@/assets/svgs/FireworksOrangeIcon";
-const ProgressCard = ({children}: {children?: React.ReactNode}) => {
- const { maxPoints } = useToDosContext();
- return (
-
-
-
-
- You have earned XX points this week!{" "}
-
-
-
-
- 0
- {maxPoints}
-
-
- {children}
-
-
- );
+const ProgressCard = ({children}: { children?: React.ReactNode }) => {
+ const {maxPoints} = useToDosContext();
+ return (
+
+
+
+
+ You have earned XX points this week!{" "}
+
+
+
+
+ 0
+ {maxPoints}
+
+
+ {children}
+
+
+ );
};
export default ProgressCard;
diff --git a/components/pages/todos/family-chores/FamilyChart.tsx b/components/pages/todos/family-chores/FamilyChart.tsx
index 7737adc..87add82 100644
--- a/components/pages/todos/family-chores/FamilyChart.tsx
+++ b/components/pages/todos/family-chores/FamilyChart.tsx
@@ -1,5 +1,4 @@
import React from "react";
-import { View } from "react-native";
import { BarChart } from "react-native-gifted-charts";
const FamilyChart = () => {
diff --git a/components/pages/todos/user-chores/UserChart.tsx b/components/pages/todos/user-chores/UserChart.tsx
index 5ef602b..eeed824 100644
--- a/components/pages/todos/user-chores/UserChart.tsx
+++ b/components/pages/todos/user-chores/UserChart.tsx
@@ -1,5 +1,4 @@
import React from "react";
-import { View } from "react-native";
import { BarChart } from "react-native-gifted-charts";
const UserChart = () => {
diff --git a/components/pages/todos/user-chores/UserChoresProgress.tsx b/components/pages/todos/user-chores/UserChoresProgress.tsx
index f4cec19..8cc04f3 100644
--- a/components/pages/todos/user-chores/UserChoresProgress.tsx
+++ b/components/pages/todos/user-chores/UserChoresProgress.tsx
@@ -1,191 +1,181 @@
-import {
- View,
- Text,
- ProgressBar,
- Button,
- ButtonSize,
- Modal,
- Dialog,
- TouchableOpacity,
-} from "react-native-ui-lib";
-import React, { useState } from "react";
-import { StyleSheet } from "react-native";
+import {Button, ButtonSize, Dialog, ProgressBar, Text, TouchableOpacity, View,} from "react-native-ui-lib";
+import React, {useState} from "react";
+import {StyleSheet} from "react-native";
import UserChart from "./UserChart";
import ProgressCard from "../ProgressCard";
-import { AntDesign, Feather, Ionicons } from "@expo/vector-icons";
-import { ScrollView } from "react-native-gesture-handler";
-import { PanViewDirectionsEnum } from "react-native-ui-lib/src/incubator/panView";
+import {AntDesign, Ionicons} from "@expo/vector-icons";
+import {ScrollView} from "react-native-gesture-handler";
import FireworksOrangeIcon from "@/assets/svgs/FireworksOrangeIcon";
const UserChoresProgress = ({
- setPageIndex,
-}: {
- setPageIndex: (value: number) => void;
+ setPageIndex,
+ }: {
+ setPageIndex: (value: number) => void;
}) => {
- const [modalVisible, setModalVisible] = useState(false);
- return (
-
-
- setPageIndex(0)}>
-
-
-
- Return to To Do's
-
-
-
-
-
- Your To Do's Progress Report
-
-
-
-
- Daily Goal
-
-
-
-
-
- Points Earned This Week
-
-
-
-
-
-
-
- Total Reward Points
-
- setModalVisible(true)}
- labelStyle={{
- fontSize: 13,
- fontFamily: "Manrope_400Regular",
- }}
- iconSource={() => (
-
- )}
- />
-
-
-
-
- (false);
+ return (
+
+
- You have 1200 points saved!
-
-
-
-
- 0
- 5000
-
+ setPageIndex(0)}>
+
+
+
+ Return to To Do's
+
+
+
+
+
+ Your To Do's Progress Report
+
+
+
+
+ Daily Goal
+
+
+
+
+
+ Points Earned This Week
+
+
+
+
+
+
+
+ Total Reward Points
+
+ setModalVisible(true)}
+ labelStyle={{
+ fontSize: 13,
+ fontFamily: "Manrope_400Regular",
+ }}
+ iconSource={() => (
+
+ )}
+ />
+
+
+
+
+
+ You have 1200 points saved!
+
+
+
+
+ 0
+ 5000
+
+
+
+
-
-