diff --git a/app/(auth)/_layout.tsx b/app/(auth)/_layout.tsx index aa94045..f5ac187 100644 --- a/app/(auth)/_layout.tsx +++ b/app/(auth)/_layout.tsx @@ -320,20 +320,14 @@ export default function TabLayout() { name="index" options={{ drawerLabel: "Calendar", - title: - Device.deviceType === DeviceType.TABLET - ? "Family Calendar" - : "Calendar", + title: "Calendar", }} /> diff --git a/app/(auth)/calendar/index.tsx b/app/(auth)/calendar/index.tsx index b93837c..d1152d9 100644 --- a/app/(auth)/calendar/index.tsx +++ b/app/(auth)/calendar/index.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect } from "react"; import {RefreshControl, ScrollView, View} from "react-native"; import CalendarPage from "@/components/pages/calendar/CalendarPage"; import TabletCalendarPage from "@/components/pages/(tablet_pages)/calendar/TabletCalendarPage"; @@ -6,10 +6,20 @@ import * as Device from "expo-device"; import {DeviceType} from "expo-device"; import {useCalSync} from "@/hooks/useCalSync"; import {colorMap} from "@/constants/colorMap"; +import { useSetAtom } from "jotai"; +import { selectedUserAtom } from "@/components/pages/calendar/atoms"; +import { useAuthContext } from "@/contexts/AuthContext"; export default function Screen() { const isTablet = Device.deviceType === DeviceType.TABLET; const {resyncAllCalendars, isSyncing} = useCalSync(); + const setSelectedUser = useSetAtom(selectedUserAtom); + const {profileData} = useAuthContext() + + useEffect(() => { + if(!isTablet && profileData) setSelectedUser({firstName: profileData.firstName, lastName: profileData.lastName, eventColor: profileData.eventColor}) + }, []) + const onRefresh = React.useCallback(async () => { try { diff --git a/components/pages/(tablet_pages)/chores/SingleUserChoreList.tsx b/components/pages/(tablet_pages)/chores/SingleUserChoreList.tsx index 8bdb044..38f246f 100644 --- a/components/pages/(tablet_pages)/chores/SingleUserChoreList.tsx +++ b/components/pages/(tablet_pages)/chores/SingleUserChoreList.tsx @@ -39,7 +39,15 @@ const groupToDosByDate = (toDos: IToDo[]) => { }); }; - if (toDo.date === null || isToday(toDo.date)) { + const isOverdue = (date: Date) => { + const today = new Date(); + today.setHours(0, 0, 0, 0); + return date < today; + }; + + if (isOverdue(toDo.date) && !toDo.done) { + dateKey = "Overdue"; + } else if (toDo.date === null || isToday(toDo.date)) { dateKey = "Today"; } else if (isTomorrow(toDo.date)) { dateKey = "Tomorrow"; @@ -49,7 +57,8 @@ const groupToDosByDate = (toDos: IToDo[]) => { dateKey = "Next 30 Days"; subDateKey = format(toDo.date, "MMM d"); } else { - return groups; + dateKey = "Later"; + subDateKey = format(toDo.date, "MMM d, yyyy"); } if (!groups[dateKey]) { @@ -59,7 +68,7 @@ const groupToDosByDate = (toDos: IToDo[]) => { }; } - if (dateKey === "Next 30 Days" && subDateKey) { + if ((dateKey === "Next 30 Days" || dateKey === "Later") && subDateKey) { if (!groups[dateKey].subgroups[subDateKey]) { groups[dateKey].subgroups[subDateKey] = []; } @@ -103,14 +112,17 @@ const SingleUserChoreList = ({ user }: { user: UserProfile }) => { }; const noDateToDos = groupedToDos["No Date"]?.items || []; - const datedToDos = Object.keys(groupedToDos).filter( - (key) => key !== "No Date" - ); + const datedToDos = Object.keys(groupedToDos) + .filter((key) => key !== "No Date") + .sort((a, b) => { + const order = ["Overdue", "Today", "Tomorrow", "Next 7 Days", "Next 30 Days", "Later"]; + return order.indexOf(a) - order.indexOf(b); + }); const renderTodoGroup = (dateKey: string) => { const isExpanded = expandedGroups[dateKey] || false; - if (dateKey === "Next 30 Days") { + if (dateKey === "Next 30 Days" || dateKey === "Later") { const subgroups = Object.entries(groupedToDos[dateKey].subgroups).sort( ([dateA], [dateB]) => { const dateAObj = new Date(dateA); diff --git a/components/pages/(tablet_pages)/chores/TabletChoresPage.tsx b/components/pages/(tablet_pages)/chores/TabletChoresPage.tsx index 160f79a..6d99bf5 100644 --- a/components/pages/(tablet_pages)/chores/TabletChoresPage.tsx +++ b/components/pages/(tablet_pages)/chores/TabletChoresPage.tsx @@ -5,14 +5,16 @@ import TabletContainer from "../tablet_components/TabletContainer"; import SingleUserChoreList from "./SingleUserChoreList"; import { useGetFamilyMembers } from "@/hooks/firebase/useGetFamilyMembers"; import { ImageBackground, StyleSheet } from "react-native"; -import { colorMap } from "@/constants/colorMap"; import { ScrollView } from "react-native-gesture-handler"; import { ProfileType, useAuthContext } from "@/contexts/AuthContext"; import AddChore from "../../todos/AddChore"; +import { useAtom } from "jotai"; +import { selectedUserAtom } from "@/components/pages/calendar/atoms"; const TabletChoresPage = () => { const {data: users} = useGetFamilyMembers(); const { user: currentUser } = useAuthContext(); + const [selectedUser] = useAtom(selectedUserAtom); const sortedUsers = useMemo(() => { return users @@ -31,79 +33,77 @@ const TabletChoresPage = () => { }); }, [users, currentUser]); - // 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 + useEffect(() => { + lockScreenOrientation(); - return () => { - // Optional: Unlock to default when the component unmounts - ScreenOrientation.unlockAsync(); + return () => { + ScreenOrientation.unlockAsync(); + }; + }, []); + + const capitalizeFirstLetter = (str: string) => { + if (!str) return ""; + return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase(); }; - }, []); - const capitalizeFirstLetter = (str: string) => { - if (!str) return ""; - return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase(); - }; - - return ( - - - - {sortedUsers - ?.filter((member) => member.userType !== ProfileType.FAMILY_DEVICE) - .map((user, index) => ( - - - {user.pfp ? ( - - ) : ( - - - {user.firstName.at(0)} - {user.lastName.at(0)} - - - )} - - {user.firstName} - - - ({capitalizeFirstLetter(user.userType)}) - - - + return ( + + + + {sortedUsers + ?.filter((member) => + !selectedUser || + selectedUser.uid === 'family-view' || + selectedUser.uid === member.uid + ) + .map((user, index) => ( + + + {user.pfp ? ( + + ) : ( + + + {user.firstName.at(0)} + {user.lastName.at(0)} + + + )} + + {user.firstName} + + + ({capitalizeFirstLetter(user.userType)}) + + + + + ))} + + + + - ))} - - - - - - - ); + + ); }; const styles = StyleSheet.create({ @@ -118,10 +118,10 @@ const styles = StyleSheet.create({ color: "#2c2c2c", }, addBtn: { - position: 'absolute', - bottom: 50, - right: 220 + position: 'absolute', + bottom: 50, + right: 220 } }); -export default TabletChoresPage; +export default TabletChoresPage; \ No newline at end of file diff --git a/components/pages/(tablet_pages)/tablet_components/UsersList.tsx b/components/pages/(tablet_pages)/tablet_components/UsersList.tsx index bf81cb1..706e0d5 100644 --- a/components/pages/(tablet_pages)/tablet_components/UsersList.tsx +++ b/components/pages/(tablet_pages)/tablet_components/UsersList.tsx @@ -1,37 +1,42 @@ -import { View, Text } from "react-native-ui-lib"; +import { View, Text, TouchableOpacity } from "react-native-ui-lib"; import React, { useEffect, useMemo } from "react"; import { useGetFamilyMembers } from "@/hooks/firebase/useGetFamilyMembers"; import { ImageBackground, StyleSheet } from "react-native"; import { colorMap } from "@/constants/colorMap"; import { ProfileType, useAuthContext } from "@/contexts/AuthContext"; -import { ScrollView } from "react-native-gesture-handler"; +import { useAtom } from "jotai"; +import { selectedUserAtom } from "@/components/pages/calendar/atoms"; const UsersList = () => { const { user: currentUser } = useAuthContext(); const { data: familyMembers, refetch: refetchFamilyMembers } = useGetFamilyMembers(); + const [selectedUser, setSelectedUser] = useAtom(selectedUserAtom); useEffect(() => { refetchFamilyMembers(); }, []); const sortedMembers = useMemo(() => { - return familyMembers - ?.filter((member) => member.userType !== ProfileType.FAMILY_DEVICE) - .sort((a, b) => { - // Current user first - if (a.uid === currentUser?.uid) return -1; - if (b.uid === currentUser?.uid) return 1; - - // Then sort by user type priority - const typePriority = { - [ProfileType.PARENT]: 0, - [ProfileType.CHILD]: 1, - [ProfileType.CAREGIVER]: 2, - }; - - return typePriority[a.userType] - typePriority[b.userType]; - }); + const filtered = familyMembers + ?.filter((member) => member.userType !== ProfileType.FAMILY_DEVICE); + + const currentUserData = filtered?.find(m => m.uid === currentUser?.uid); + const parents = filtered?.filter(m => m.userType === ProfileType.PARENT && m.uid !== currentUser?.uid) || []; + const children = filtered?.filter(m => m.userType === ProfileType.CHILD && m.uid !== currentUser?.uid) || []; + const caregivers = filtered?.filter(m => m.userType === ProfileType.CAREGIVER && m.uid !== currentUser?.uid) || []; + + const familyViewOption = { + uid: 'family-view', + firstName: 'Family', + lastName: 'View', + userType: 'Family View', + eventColor: colorMap.pink + }; + + return currentUserData + ? [currentUserData, ...parents, ...children, familyViewOption, ...caregivers] + : [...parents, ...children, familyViewOption, ...caregivers]; }, [familyMembers, currentUser]); const capitalizeFirstLetter = (str: string) => { @@ -39,37 +44,54 @@ const UsersList = () => { return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase(); }; + useEffect(() => { + console.log(selectedUser); + }, [selectedUser]); + return ( - {sortedMembers?.map((member, index) => ( - - {member.pfp ? ( - - ) : ( - - - {member.firstName.at(0)} - {member.lastName.at(0)} - - - )} - {member.firstName} - - {capitalizeFirstLetter(member.userType)} - - - ))} + {sortedMembers?.map((member, index) => ( + { + if (member.uid === 'family-view') { + setSelectedUser(null); + } else { + setSelectedUser(selectedUser?.uid === member.uid ? null : member); + } + }} + style={[ + styles.memberContainer, + selectedUser?.uid === member.uid && styles.selectedMember, + ]} + > + {member.pfp ? ( + + ) : ( + + + {member.firstName.at(0)} + {member.lastName.at(0)} + + + )} + {member.firstName} + + {capitalizeFirstLetter(member.userType)} + + + ))} ); }; @@ -91,6 +113,13 @@ const styles = StyleSheet.create({ color: "#9b9b9b", marginBottom: 20, }, + memberContainer: { + alignItems: "center", + marginBottom: 20, + }, + selectedMember: { + opacity: 1, + }, }); export default UsersList; diff --git a/components/pages/calendar/EventCalendar.tsx b/components/pages/calendar/EventCalendar.tsx index b95f612..ff3b0ce 100644 --- a/components/pages/calendar/EventCalendar.tsx +++ b/components/pages/calendar/EventCalendar.tsx @@ -11,6 +11,7 @@ import { modeAtom, selectedDateAtom, selectedNewEventDateAtom, + selectedUserAtom, } from "@/components/pages/calendar/atoms"; import {useAuthContext} from "@/contexts/AuthContext"; import {CalendarEvent} from "@/components/pages/calendar/interfaces"; @@ -377,6 +378,9 @@ export const EventCalendar: React.FC = React.memo( const [mode, setMode] = useAtom(modeAtom); const [isFamilyView] = useAtom(isFamilyViewAtom); + //tablet view filter + const [selectedUser] = useAtom(selectedUserAtom); + const setEditVisible = useSetAtom(editVisibleAtom); const [isAllDay, setIsAllDay] = useAtom(isAllDayAtom); const setEventForEdit = useSetAtom(eventForEditAtom); @@ -488,8 +492,17 @@ export const EventCalendar: React.FC = React.memo( const startOffset = mode === "month" ? 40 : (mode === "week" || mode === "3days") ? 10 : 1; const endOffset = mode === "month" ? 40 : (mode === "week" || mode === "3days") ? 10 : 1; + let eventsToFilter = events; + + if (selectedUser) { + eventsToFilter = events?.filter(event => + event.attendees?.includes(selectedUser.uid) || + event.creatorId === selectedUser.uid + ); + } + const filteredEvents = - events?.filter( + eventsToFilter?.filter( (event) => event.start && event.end && @@ -521,7 +534,7 @@ export const EventCalendar: React.FC = React.memo( // console.log("memoizedEvents computation time:", endTime - startTime, "ms"); return {enrichedEvents, filteredEvents}; - }, [events, selectedDate, mode]); + }, [events, selectedDate, mode, selectedUser]); const renderCustomDateForMonth = (date: Date) => { const circleStyle = useMemo( diff --git a/components/pages/calendar/atoms.ts b/components/pages/calendar/atoms.ts index 82769ca..79e6993 100644 --- a/components/pages/calendar/atoms.ts +++ b/components/pages/calendar/atoms.ts @@ -18,4 +18,10 @@ 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); \ No newline at end of file +export const refreshEnabledAtom = atom(true); +export const selectedUserAtom = atom<{ + uid: string; + firstName: string; + lastName: string; + eventColor?: string; + } | null>(null); \ No newline at end of file