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({ - 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 - - - {isVisible && ( - - )} - - ); + + + + {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 - -