import React, {useCallback, useEffect, useMemo, useState} from "react"; import {Calendar} from "react-native-big-calendar"; import {ActivityIndicator, StyleSheet, View, ViewStyle} from "react-native"; import {useGetEvents} from "@/hooks/firebase/useGetEvents"; import {useAtom, useSetAtom} from "jotai"; import { editVisibleAtom, eventForEditAtom, modeAtom, 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 { isWithinInterval, subDays, addDays, compareAsc } from "date-fns"; 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 EventCalendar: React.FC = React.memo( ({calendarHeight}) => { const {data: events, isLoading} = useGetEvents(); const {profileData} = useAuthContext(); const [selectedDate, setSelectedDate] = useAtom(selectedDateAtom); const [mode, setMode] = useAtom(modeAtom); const setEditVisible = useSetAtom(editVisibleAtom); const setEventForEdit = useSetAtom(eventForEditAtom); const setSelectedNewEndDate = useSetAtom(selectedNewEventDateAtom); const [isRendering, setIsRendering] = useState(true); const [offsetMinutes, setOffsetMinutes] = useState(getTotalMinutes()); const todaysDate = new Date(); useEffect(() => { if (events && mode) { setIsRendering(true); const timeout = setTimeout(() => { setIsRendering(false); }, 10); return () => clearTimeout(timeout); } }, [events, mode]); const handlePressEvent = useCallback( (event: CalendarEvent) => { if (mode === "day" || mode === "week") { setEditVisible(true); console.log({event}); setEventForEdit(event); } else { setMode("day"); setSelectedDate(event.start); } }, [setEditVisible, setEventForEdit, mode] ); const handlePressCell = useCallback( (date: Date) => { if (mode === "day" || mode === "week") { setSelectedNewEndDate(date); } else { setMode("day"); setSelectedDate(date); } }, [mode, setSelectedNewEndDate, setSelectedDate] ); const handleSwipeEnd = useCallback( (date: Date) => { setSelectedDate(date); }, [setSelectedDate] ); const memoizedEventCellStyle = useCallback( (event: CalendarEvent) => ({backgroundColor: event.eventColor}), [] ); const memoizedWeekStartsOn = useMemo( () => (profileData?.firstDayOfWeek === "Mondays" ? 1 : 0), [profileData] ); console.log({memoizedWeekStartsOn, profileData: profileData?.firstDayOfWeek}) 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") 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") { return styles.weekModeHeader; } else if (mode === "month") { return styles.monthModeHeader; } else { return {}; } }, [mode]); const { enrichedEvents, filteredEvents } = useMemo(() => { const startTime = Date.now(); // Start timer const startOffset = mode === "month" ? 40 : mode === "week" ? 10 : 1; const endOffset = mode === "month" ? 40 : mode === "week" ? 10 : 1; 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.reduce((acc, event) => { const dateKey = event.start.toISOString().split('T')[0]; acc[dateKey] = acc[dateKey] || []; acc[dateKey].push({ ...event, overlapPosition: false, overlapCount: 0 }); // Sort events for this dateKey from oldest to newest by event.start acc[dateKey].sort((a, b) => compareAsc(a.start, b.start)); return acc; }, {} as Record); const endTime = Date.now(); // End timer console.log("memoizedEvents computation time:", endTime - startTime, "ms"); return { enrichedEvents, filteredEvents }; }, [events, selectedDate, mode]); const renderCustomDateForMonth = (date: Date) => { const circleStyle = useMemo( () => ({ width: 30, height: 30, justifyContent: "center", alignItems: "center", borderRadius: 15, }), [] ); const defaultStyle = useMemo( () => ({ ...circleStyle, }), [circleStyle] ); const currentDateStyle = useMemo( () => ({ ...circleStyle, backgroundColor: "#4184f2", }), [circleStyle] ); const renderDate = useCallback( (date: Date) => { const isCurrentDate = isSameDate(todaysDate, date); const appliedStyle = isCurrentDate ? currentDateStyle : defaultStyle; return ( {date.getDate()} ); }, [todaysDate, currentDateStyle, defaultStyle] // dependencies ); return renderDate(date); }; useEffect(() => { setOffsetMinutes(getTotalMinutes()); }, [events, mode]); if (isLoading || isRendering) { return ( ); } // console.log(enrichedEvents, filteredEvents) return ( ); } ); const styles = StyleSheet.create({ segmentslblStyle: { fontSize: 12, fontFamily: "Manrope_600SemiBold", }, calHeader: { borderWidth: 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", }, 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", }, });