mirror of
https://github.com/urosran/cally.git
synced 2025-11-26 00:24:53 +00:00
tablet event and todo filtering
This commit is contained in:
@ -320,20 +320,14 @@ export default function TabLayout() {
|
||||
name="index"
|
||||
options={{
|
||||
drawerLabel: "Calendar",
|
||||
title:
|
||||
Device.deviceType === DeviceType.TABLET
|
||||
? "Family Calendar"
|
||||
: "Calendar",
|
||||
title: "Calendar",
|
||||
}}
|
||||
/>
|
||||
<Drawer.Screen
|
||||
name="calendar"
|
||||
options={{
|
||||
drawerLabel: "Calendar",
|
||||
title:
|
||||
Device.deviceType === DeviceType.TABLET
|
||||
? "Family Calendar"
|
||||
: "Calendar",
|
||||
title: "Calendar",
|
||||
drawerItemStyle: { display: "none" },
|
||||
}}
|
||||
/>
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 (
|
||||
<TabletContainer>
|
||||
<ScrollView horizontal>
|
||||
<View row gap-25 padding-25>
|
||||
{sortedUsers
|
||||
?.filter((member) => member.userType !== ProfileType.FAMILY_DEVICE)
|
||||
.map((user, index) => (
|
||||
<View key={index}>
|
||||
<View row centerV>
|
||||
{user.pfp ? (
|
||||
<ImageBackground
|
||||
source={{ uri: user.pfp }}
|
||||
style={
|
||||
styles.pfp
|
||||
}
|
||||
imageStyle={(user.eventColor && {
|
||||
borderWidth: 2,
|
||||
borderColor: user.eventColor,
|
||||
}) ||
|
||||
undefined
|
||||
}
|
||||
borderRadius={13.33}
|
||||
/>
|
||||
) : (
|
||||
<View
|
||||
center
|
||||
style={styles.pfp}
|
||||
backgroundColor={user.eventColor || "#00a8b6"}
|
||||
>
|
||||
<Text color="white">
|
||||
{user.firstName.at(0)}
|
||||
{user.lastName.at(0)}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
<Text style={styles.name} marginL-15>
|
||||
{user.firstName}
|
||||
</Text>
|
||||
<Text style={[styles.name, { color: "#9b9b9b" }]} marginL-5>
|
||||
({capitalizeFirstLetter(user.userType)})
|
||||
</Text>
|
||||
</View>
|
||||
<SingleUserChoreList user={user} />
|
||||
return (
|
||||
<TabletContainer>
|
||||
<ScrollView horizontal>
|
||||
<View row gap-25 padding-25>
|
||||
{sortedUsers
|
||||
?.filter((member) =>
|
||||
!selectedUser ||
|
||||
selectedUser.uid === 'family-view' ||
|
||||
selectedUser.uid === member.uid
|
||||
)
|
||||
.map((user, index) => (
|
||||
<View key={index}>
|
||||
<View row centerV>
|
||||
{user.pfp ? (
|
||||
<ImageBackground
|
||||
source={{ uri: user.pfp }}
|
||||
style={styles.pfp}
|
||||
imageStyle={(user.eventColor && {
|
||||
borderWidth: 2,
|
||||
borderColor: user.eventColor,
|
||||
}) || undefined}
|
||||
borderRadius={13.33}
|
||||
/>
|
||||
) : (
|
||||
<View
|
||||
center
|
||||
style={styles.pfp}
|
||||
backgroundColor={user.eventColor || "#00a8b6"}
|
||||
>
|
||||
<Text color="white">
|
||||
{user.firstName.at(0)}
|
||||
{user.lastName.at(0)}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
<Text style={styles.name} marginL-15>
|
||||
{user.firstName}
|
||||
</Text>
|
||||
<Text style={[styles.name, { color: "#9b9b9b" }]} marginL-5>
|
||||
({capitalizeFirstLetter(user.userType)})
|
||||
</Text>
|
||||
</View>
|
||||
<SingleUserChoreList user={user} />
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</ScrollView>
|
||||
<View style={styles.addBtn}>
|
||||
<AddChore />
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</ScrollView>
|
||||
<View style={styles.addBtn}>
|
||||
<AddChore />
|
||||
</View>
|
||||
</TabletContainer>
|
||||
);
|
||||
</TabletContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
@ -118,9 +118,9 @@ const styles = StyleSheet.create({
|
||||
color: "#2c2c2c",
|
||||
},
|
||||
addBtn: {
|
||||
position: 'absolute',
|
||||
bottom: 50,
|
||||
right: 220
|
||||
position: 'absolute',
|
||||
bottom: 50,
|
||||
right: 220
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -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;
|
||||
const filtered = familyMembers
|
||||
?.filter((member) => member.userType !== ProfileType.FAMILY_DEVICE);
|
||||
|
||||
// Then sort by user type priority
|
||||
const typePriority = {
|
||||
[ProfileType.PARENT]: 0,
|
||||
[ProfileType.CHILD]: 1,
|
||||
[ProfileType.CAREGIVER]: 2,
|
||||
};
|
||||
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) || [];
|
||||
|
||||
return typePriority[a.userType] - typePriority[b.userType];
|
||||
});
|
||||
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 (
|
||||
<View centerH paddingT-10 marginB-70>
|
||||
{sortedMembers?.map((member, index) => (
|
||||
<React.Fragment key={member.uid}>
|
||||
{member.pfp ? (
|
||||
<ImageBackground
|
||||
key={index}
|
||||
source={{ uri: member.pfp }}
|
||||
style={styles.pfp}
|
||||
borderRadius={200}
|
||||
imageStyle={{ borderWidth: 2, borderColor: "red" }}
|
||||
/>
|
||||
) : (
|
||||
<View
|
||||
key={index}
|
||||
style={styles.pfp}
|
||||
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>
|
||||
</React.Fragment>
|
||||
))}
|
||||
{sortedMembers?.map((member, index) => (
|
||||
<TouchableOpacity
|
||||
key={member.uid}
|
||||
onPress={() => {
|
||||
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 ? (
|
||||
<ImageBackground
|
||||
key={index}
|
||||
source={{ uri: member.pfp }}
|
||||
style={styles.pfp}
|
||||
borderRadius={200}
|
||||
imageStyle={{ borderWidth: 2, borderColor: "red" }}
|
||||
/>
|
||||
) : (
|
||||
<View
|
||||
key={index}
|
||||
style={styles.pfp}
|
||||
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>
|
||||
);
|
||||
};
|
||||
@ -91,6 +113,13 @@ const styles = StyleSheet.create({
|
||||
color: "#9b9b9b",
|
||||
marginBottom: 20,
|
||||
},
|
||||
memberContainer: {
|
||||
alignItems: "center",
|
||||
marginBottom: 20,
|
||||
},
|
||||
selectedMember: {
|
||||
opacity: 1,
|
||||
},
|
||||
});
|
||||
|
||||
export default UsersList;
|
||||
|
||||
@ -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<EventCalendarProps> = 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<EventCalendarProps> = 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<EventCalendarProps> = 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<ViewStyle>(
|
||||
|
||||
@ -19,3 +19,9 @@ export const userSettingsView = atom<boolean>(true);
|
||||
export const toDosPageIndex = atom<number>(0);
|
||||
export const refreshTriggerAtom = atom<boolean>(false);
|
||||
export const refreshEnabledAtom = atom<boolean>(true);
|
||||
export const selectedUserAtom = atom<{
|
||||
uid: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
eventColor?: string;
|
||||
} | null>(null);
|
||||
Reference in New Issue
Block a user