tablet event and todo filtering

This commit is contained in:
ivic00
2024-12-08 16:49:39 +01:00
parent f382d403cd
commit 3fe46f1954
7 changed files with 198 additions and 134 deletions

View File

@ -320,20 +320,14 @@ export default function TabLayout() {
name="index" name="index"
options={{ options={{
drawerLabel: "Calendar", drawerLabel: "Calendar",
title: title: "Calendar",
Device.deviceType === DeviceType.TABLET
? "Family Calendar"
: "Calendar",
}} }}
/> />
<Drawer.Screen <Drawer.Screen
name="calendar" name="calendar"
options={{ options={{
drawerLabel: "Calendar", drawerLabel: "Calendar",
title: title: "Calendar",
Device.deviceType === DeviceType.TABLET
? "Family Calendar"
: "Calendar",
drawerItemStyle: { display: "none" }, drawerItemStyle: { display: "none" },
}} }}
/> />

View File

@ -1,4 +1,4 @@
import React from "react"; import React, { useEffect } from "react";
import {RefreshControl, ScrollView, View} from "react-native"; import {RefreshControl, ScrollView, View} from "react-native";
import CalendarPage from "@/components/pages/calendar/CalendarPage"; import CalendarPage from "@/components/pages/calendar/CalendarPage";
import TabletCalendarPage from "@/components/pages/(tablet_pages)/calendar/TabletCalendarPage"; 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 {DeviceType} from "expo-device";
import {useCalSync} from "@/hooks/useCalSync"; import {useCalSync} from "@/hooks/useCalSync";
import {colorMap} from "@/constants/colorMap"; 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() { export default function Screen() {
const isTablet = Device.deviceType === DeviceType.TABLET; const isTablet = Device.deviceType === DeviceType.TABLET;
const {resyncAllCalendars, isSyncing} = useCalSync(); 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 () => { const onRefresh = React.useCallback(async () => {
try { try {

View File

@ -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"; dateKey = "Today";
} else if (isTomorrow(toDo.date)) { } else if (isTomorrow(toDo.date)) {
dateKey = "Tomorrow"; dateKey = "Tomorrow";
@ -49,7 +57,8 @@ const groupToDosByDate = (toDos: IToDo[]) => {
dateKey = "Next 30 Days"; dateKey = "Next 30 Days";
subDateKey = format(toDo.date, "MMM d"); subDateKey = format(toDo.date, "MMM d");
} else { } else {
return groups; dateKey = "Later";
subDateKey = format(toDo.date, "MMM d, yyyy");
} }
if (!groups[dateKey]) { 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]) { if (!groups[dateKey].subgroups[subDateKey]) {
groups[dateKey].subgroups[subDateKey] = []; groups[dateKey].subgroups[subDateKey] = [];
} }
@ -103,14 +112,17 @@ const SingleUserChoreList = ({ user }: { user: UserProfile }) => {
}; };
const noDateToDos = groupedToDos["No Date"]?.items || []; const noDateToDos = groupedToDos["No Date"]?.items || [];
const datedToDos = Object.keys(groupedToDos).filter( const datedToDos = Object.keys(groupedToDos)
(key) => key !== "No Date" .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 renderTodoGroup = (dateKey: string) => {
const isExpanded = expandedGroups[dateKey] || false; 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( const subgroups = Object.entries(groupedToDos[dateKey].subgroups).sort(
([dateA], [dateB]) => { ([dateA], [dateB]) => {
const dateAObj = new Date(dateA); const dateAObj = new Date(dateA);

View File

@ -5,14 +5,16 @@ import TabletContainer from "../tablet_components/TabletContainer";
import SingleUserChoreList from "./SingleUserChoreList"; import SingleUserChoreList from "./SingleUserChoreList";
import { useGetFamilyMembers } from "@/hooks/firebase/useGetFamilyMembers"; import { useGetFamilyMembers } from "@/hooks/firebase/useGetFamilyMembers";
import { ImageBackground, StyleSheet } from "react-native"; import { ImageBackground, StyleSheet } from "react-native";
import { colorMap } from "@/constants/colorMap";
import { ScrollView } from "react-native-gesture-handler"; import { ScrollView } from "react-native-gesture-handler";
import { ProfileType, useAuthContext } from "@/contexts/AuthContext"; import { ProfileType, useAuthContext } from "@/contexts/AuthContext";
import AddChore from "../../todos/AddChore"; import AddChore from "../../todos/AddChore";
import { useAtom } from "jotai";
import { selectedUserAtom } from "@/components/pages/calendar/atoms";
const TabletChoresPage = () => { const TabletChoresPage = () => {
const {data: users} = useGetFamilyMembers(); const {data: users} = useGetFamilyMembers();
const { user: currentUser } = useAuthContext(); const { user: currentUser } = useAuthContext();
const [selectedUser] = useAtom(selectedUserAtom);
const sortedUsers = useMemo(() => { const sortedUsers = useMemo(() => {
return users return users
@ -31,79 +33,77 @@ const TabletChoresPage = () => {
}); });
}, [users, currentUser]); }, [users, currentUser]);
// Function to lock the screen orientation to landscape
const lockScreenOrientation = async () => { const lockScreenOrientation = async () => {
await ScreenOrientation.lockAsync( await ScreenOrientation.lockAsync(
ScreenOrientation.OrientationLock.LANDSCAPE_RIGHT ScreenOrientation.OrientationLock.LANDSCAPE_RIGHT
); );
}; };
useEffect(() => { useEffect(() => {
lockScreenOrientation(); // Lock orientation when the component mounts lockScreenOrientation();
return () => { return () => {
// Optional: Unlock to default when the component unmounts ScreenOrientation.unlockAsync();
ScreenOrientation.unlockAsync(); };
}, []);
const capitalizeFirstLetter = (str: string) => {
if (!str) return "";
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
}; };
}, []);
const capitalizeFirstLetter = (str: string) => { return (
if (!str) return ""; <TabletContainer>
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase(); <ScrollView horizontal>
}; <View row gap-25 padding-25>
{sortedUsers
return ( ?.filter((member) =>
<TabletContainer> !selectedUser ||
<ScrollView horizontal> selectedUser.uid === 'family-view' ||
<View row gap-25 padding-25> selectedUser.uid === member.uid
{sortedUsers )
?.filter((member) => member.userType !== ProfileType.FAMILY_DEVICE) .map((user, index) => (
.map((user, index) => ( <View key={index}>
<View key={index}> <View row centerV>
<View row centerV> {user.pfp ? (
{user.pfp ? ( <ImageBackground
<ImageBackground source={{ uri: user.pfp }}
source={{ uri: user.pfp }} style={styles.pfp}
style={ imageStyle={(user.eventColor && {
styles.pfp borderWidth: 2,
} borderColor: user.eventColor,
imageStyle={(user.eventColor && { }) || undefined}
borderWidth: 2, borderRadius={13.33}
borderColor: user.eventColor, />
}) || ) : (
undefined <View
} center
borderRadius={13.33} style={styles.pfp}
/> backgroundColor={user.eventColor || "#00a8b6"}
) : ( >
<View <Text color="white">
center {user.firstName.at(0)}
style={styles.pfp} {user.lastName.at(0)}
backgroundColor={user.eventColor || "#00a8b6"} </Text>
> </View>
<Text color="white"> )}
{user.firstName.at(0)} <Text style={styles.name} marginL-15>
{user.lastName.at(0)} {user.firstName}
</Text> </Text>
</View> <Text style={[styles.name, { color: "#9b9b9b" }]} marginL-5>
)} ({capitalizeFirstLetter(user.userType)})
<Text style={styles.name} marginL-15> </Text>
{user.firstName} </View>
</Text> <SingleUserChoreList user={user} />
<Text style={[styles.name, { color: "#9b9b9b" }]} marginL-5> </View>
({capitalizeFirstLetter(user.userType)}) ))}
</Text> </View>
</View> </ScrollView>
<SingleUserChoreList user={user} /> <View style={styles.addBtn}>
<AddChore />
</View> </View>
))} </TabletContainer>
</View> );
</ScrollView>
<View style={styles.addBtn}>
<AddChore />
</View>
</TabletContainer>
);
}; };
const styles = StyleSheet.create({ const styles = StyleSheet.create({
@ -118,10 +118,10 @@ const styles = StyleSheet.create({
color: "#2c2c2c", color: "#2c2c2c",
}, },
addBtn: { addBtn: {
position: 'absolute', position: 'absolute',
bottom: 50, bottom: 50,
right: 220 right: 220
} }
}); });
export default TabletChoresPage; export default TabletChoresPage;

View File

@ -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 React, { useEffect, useMemo } from "react";
import { useGetFamilyMembers } from "@/hooks/firebase/useGetFamilyMembers"; import { useGetFamilyMembers } from "@/hooks/firebase/useGetFamilyMembers";
import { ImageBackground, StyleSheet } from "react-native"; import { ImageBackground, StyleSheet } from "react-native";
import { colorMap } from "@/constants/colorMap"; import { colorMap } from "@/constants/colorMap";
import { ProfileType, useAuthContext } from "@/contexts/AuthContext"; 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 UsersList = () => {
const { user: currentUser } = useAuthContext(); const { user: currentUser } = useAuthContext();
const { data: familyMembers, refetch: refetchFamilyMembers } = const { data: familyMembers, refetch: refetchFamilyMembers } =
useGetFamilyMembers(); useGetFamilyMembers();
const [selectedUser, setSelectedUser] = useAtom(selectedUserAtom);
useEffect(() => { useEffect(() => {
refetchFamilyMembers(); refetchFamilyMembers();
}, []); }, []);
const sortedMembers = useMemo(() => { const sortedMembers = useMemo(() => {
return familyMembers const filtered = familyMembers
?.filter((member) => member.userType !== ProfileType.FAMILY_DEVICE) ?.filter((member) => member.userType !== ProfileType.FAMILY_DEVICE);
.sort((a, b) => {
// Current user first const currentUserData = filtered?.find(m => m.uid === currentUser?.uid);
if (a.uid === currentUser?.uid) return -1; const parents = filtered?.filter(m => m.userType === ProfileType.PARENT && m.uid !== currentUser?.uid) || [];
if (b.uid === currentUser?.uid) return 1; 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) || [];
// Then sort by user type priority
const typePriority = { const familyViewOption = {
[ProfileType.PARENT]: 0, uid: 'family-view',
[ProfileType.CHILD]: 1, firstName: 'Family',
[ProfileType.CAREGIVER]: 2, lastName: 'View',
}; userType: 'Family View',
eventColor: colorMap.pink
return typePriority[a.userType] - typePriority[b.userType]; };
});
return currentUserData
? [currentUserData, ...parents, ...children, familyViewOption, ...caregivers]
: [...parents, ...children, familyViewOption, ...caregivers];
}, [familyMembers, currentUser]); }, [familyMembers, currentUser]);
const capitalizeFirstLetter = (str: string) => { const capitalizeFirstLetter = (str: string) => {
@ -39,37 +44,54 @@ const UsersList = () => {
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase(); return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
}; };
useEffect(() => {
console.log(selectedUser);
}, [selectedUser]);
return ( return (
<View centerH paddingT-10 marginB-70> <View centerH paddingT-10 marginB-70>
{sortedMembers?.map((member, index) => ( {sortedMembers?.map((member, index) => (
<React.Fragment key={member.uid}> <TouchableOpacity
{member.pfp ? ( key={member.uid}
<ImageBackground onPress={() => {
key={index} if (member.uid === 'family-view') {
source={{ uri: member.pfp }} setSelectedUser(null);
style={styles.pfp} } else {
borderRadius={200} setSelectedUser(selectedUser?.uid === member.uid ? null : member);
imageStyle={{ borderWidth: 2, borderColor: "red" }} }
/> }}
) : ( style={[
<View styles.memberContainer,
key={index} selectedUser?.uid === member.uid && styles.selectedMember,
style={styles.pfp} ]}
center >
backgroundColor={member.eventColor || colorMap.teal} {member.pfp ? (
> <ImageBackground
<Text color="white"> key={index}
{member.firstName.at(0)} source={{ uri: member.pfp }}
{member.lastName.at(0)} style={styles.pfp}
</Text> borderRadius={200}
</View> imageStyle={{ borderWidth: 2, borderColor: "red" }}
)} />
<Text style={styles.fName}>{member.firstName}</Text> ) : (
<Text style={styles.role}> <View
{capitalizeFirstLetter(member.userType)} key={index}
</Text> style={styles.pfp}
</React.Fragment> center
))} backgroundColor={member.eventColor || colorMap.teal}
>
<Text color="white">
{member.firstName.at(0)}
{member.lastName.at(0)}
</Text>
</View>
)}
<Text style={styles.fName}>{member.firstName}</Text>
<Text style={styles.role}>
{capitalizeFirstLetter(member.userType)}
</Text>
</TouchableOpacity>
))}
</View> </View>
); );
}; };
@ -91,6 +113,13 @@ const styles = StyleSheet.create({
color: "#9b9b9b", color: "#9b9b9b",
marginBottom: 20, marginBottom: 20,
}, },
memberContainer: {
alignItems: "center",
marginBottom: 20,
},
selectedMember: {
opacity: 1,
},
}); });
export default UsersList; export default UsersList;

View File

@ -11,6 +11,7 @@ import {
modeAtom, modeAtom,
selectedDateAtom, selectedDateAtom,
selectedNewEventDateAtom, selectedNewEventDateAtom,
selectedUserAtom,
} from "@/components/pages/calendar/atoms"; } from "@/components/pages/calendar/atoms";
import {useAuthContext} from "@/contexts/AuthContext"; import {useAuthContext} from "@/contexts/AuthContext";
import {CalendarEvent} from "@/components/pages/calendar/interfaces"; import {CalendarEvent} from "@/components/pages/calendar/interfaces";
@ -377,6 +378,9 @@ export const EventCalendar: React.FC<EventCalendarProps> = React.memo(
const [mode, setMode] = useAtom(modeAtom); const [mode, setMode] = useAtom(modeAtom);
const [isFamilyView] = useAtom(isFamilyViewAtom); const [isFamilyView] = useAtom(isFamilyViewAtom);
//tablet view filter
const [selectedUser] = useAtom(selectedUserAtom);
const setEditVisible = useSetAtom(editVisibleAtom); const setEditVisible = useSetAtom(editVisibleAtom);
const [isAllDay, setIsAllDay] = useAtom(isAllDayAtom); const [isAllDay, setIsAllDay] = useAtom(isAllDayAtom);
const setEventForEdit = useSetAtom(eventForEditAtom); const setEventForEdit = useSetAtom(eventForEditAtom);
@ -488,8 +492,17 @@ export const EventCalendar: React.FC<EventCalendarProps> = React.memo(
const startOffset = mode === "month" ? 40 : (mode === "week" || mode === "3days") ? 10 : 1; const startOffset = mode === "month" ? 40 : (mode === "week" || mode === "3days") ? 10 : 1;
const endOffset = 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 = const filteredEvents =
events?.filter( eventsToFilter?.filter(
(event) => (event) =>
event.start && event.start &&
event.end && event.end &&
@ -521,7 +534,7 @@ export const EventCalendar: React.FC<EventCalendarProps> = React.memo(
// console.log("memoizedEvents computation time:", endTime - startTime, "ms"); // console.log("memoizedEvents computation time:", endTime - startTime, "ms");
return {enrichedEvents, filteredEvents}; return {enrichedEvents, filteredEvents};
}, [events, selectedDate, mode]); }, [events, selectedDate, mode, selectedUser]);
const renderCustomDateForMonth = (date: Date) => { const renderCustomDateForMonth = (date: Date) => {
const circleStyle = useMemo<ViewStyle>( const circleStyle = useMemo<ViewStyle>(

View File

@ -18,4 +18,10 @@ export const settingsPageIndex = atom<number>(0);
export const userSettingsView = atom<boolean>(true); export const userSettingsView = atom<boolean>(true);
export const toDosPageIndex = atom<number>(0); export const toDosPageIndex = atom<number>(0);
export const refreshTriggerAtom = atom<boolean>(false); export const refreshTriggerAtom = atom<boolean>(false);
export const refreshEnabledAtom = atom<boolean>(true); export const refreshEnabledAtom = atom<boolean>(true);
export const selectedUserAtom = atom<{
uid: string;
firstName: string;
lastName: string;
eventColor?: string;
} | null>(null);