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, modeAtom, selectedDateAtom, selectedNewEventDateAtom, selectedUserAtom, } 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, format, isWithinInterval, subDays} from "date-fns"; import {useCalSync} from "@/hooks/useCalSync"; import {useSyncEvents} from "@/hooks/useSyncOnScroll"; import {colorMap, getEventTextColor} from "@/constants/colorMap"; import {useGetFamilyMembers} from "@/hooks/firebase/useGetFamilyMembers"; import CachedImage from "expo-cached-image"; import { DeviceType } from "expo-device"; import * as Device from "expo-device" interface EventCalendarProps { 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); }; export const MonthCalendar: React.FC = React.memo( ({calendarHeight}) => { const {data: events, isLoading} = useGetEvents(); const {profileData, user} = useAuthContext(); const [selectedDate, setSelectedDate] = useAtom(selectedDateAtom); 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); const setSelectedNewEndDate = useSetAtom(selectedNewEventDateAtom); const {isSyncing} = useSyncEvents() const [offsetMinutes, setOffsetMinutes] = useState(getTotalMinutes()); useCalSync() const todaysDate = new Date(); const handlePressEvent = useCallback( (event: CalendarEvent) => { if (mode === "day" || mode === "week" || mode === "3days") { setEditVisible(true); setEventForEdit(event); } else { setMode("day"); setSelectedDate(event.start); } }, [setEditVisible, setEventForEdit, mode] ); const handlePressCell = useCallback( (date: Date) => { date && setSelectedDate(date); setTimeout(() => { setMode("day"); }, 100) }, [mode, setSelectedNewEndDate, setSelectedDate] ); 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] ); 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; } return {backgroundColor: eventColor, fontSize: 14, color: getEventTextColor(event?.eventColor)} }, [] ); 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() ); }, []); const dayHeaderColor = useMemo(() => { return isSameDate(todaysDate, selectedDate) ? "white" : "#4d4d4d"; }, [selectedDate, mode]); const dateStyle = useMemo(() => { if (mode === "week" || mode === "3days") return undefined; return isSameDate(todaysDate, selectedDate) && mode === "day" ? styles.dayHeader : styles.otherDayHeader; }, [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 {filteredEvents} = useMemo(() => { 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 && Device.deviceType === DeviceType.TABLET) { eventsToFilter = events?.filter(event => event.attendees?.includes(selectedUser.uid) || event.creatorId === selectedUser.uid ); } const filteredEvents = eventsToFilter?.filter( (event) => event?.start instanceof Date && event?.end instanceof Date && isWithinInterval(event.start, { start: subDays(selectedDate, startOffset), end: addDays(selectedDate, endOffset), }) && isWithinInterval(event.end, { start: subDays(selectedDate, startOffset), end: addDays(selectedDate, endOffset), }) ) ?? []; return {filteredEvents}; }, [events, selectedDate, mode, selectedUser]); useEffect(() => { setOffsetMinutes(getTotalMinutes()); }, [events, mode]); if (isLoading || !events) { return ( {isSyncing && Syncing...} ); } return ( <> {isSyncing && ( {isSyncing && Syncing...} )} {Device.deviceType === DeviceType.TABLET && } ); } ); const styles = StyleSheet.create({ segmentslblStyle: { fontSize: 12, fontFamily: "Manrope_600SemiBold", }, calHeader: { borderWidth: 0, paddingBottom: 0, }, dayModeHeader: { alignSelf: "flex-start", justifyContent: "space-between", alignContent: "center", width: 38, right: 42, height: 13, }, weekModeHeader: {}, monthModeHeader: {}, loadingContainer: { flex: 1, justifyContent: "center", alignItems: "center", position: "absolute", width: "100%", height: "100%", zIndex: 100, backgroundColor: "rgba(255, 255, 255, 0.9)", }, dayHeader: { backgroundColor: "#4184f2", aspectRatio: 1, borderRadius: 100, alignItems: "center", justifyContent: "center", }, otherDayHeader: { backgroundColor: "transparent", color: "#919191", aspectRatio: 1, borderRadius: 100, alignItems: "center", justifyContent: "center", }, hourStyle: { color: "#5f6368", fontSize: 12, fontFamily: "Manrope_500Medium", }, eventCell: { flex: 1, borderRadius: 4, padding: 4, height: '100%', justifyContent: 'center', }, eventTitle: { color: 'white', fontSize: 12, fontFamily: "PlusJakartaSans_500Medium", }, });