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 {addDays, isWithinInterval, subDays} 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] ); 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 memoizedEvents = useMemo(() => { const startOffset = mode === "month" ? 40 : mode === "week" ? 10 : 1; const endOffset = mode === "month" ? 40 : mode === "week" ? 10 : 1; return 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) }) ) ?? []; }, [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 ( ); } 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", }, });