diff --git a/__test__/components/Calendar.test.tsx b/__test__/components/Calendar.test.tsx index ed3dd23..f699d55 100644 --- a/__test__/components/Calendar.test.tsx +++ b/__test__/components/Calendar.test.tsx @@ -135,7 +135,7 @@ describe("CalendarSelection", () => { }, }, }, - pending: false, + pending: true, }, }; it("renders calendars", async () => { @@ -340,7 +340,7 @@ describe("calendar Availability search", () => { events: {}, }, }, - pending: false, + pending: true, templist: {}, }, }; @@ -495,12 +495,20 @@ describe("calendar Availability search", () => { }, 15000); it("BUGFIX: can untoggle all calendar.personal", async () => { + jest + .spyOn(calendarDetailThunks, "getCalendarDetailAsync") + .mockImplementation( + () => + ({ + type: "getCalendarDetailAsync", + unwrap: () => Promise.resolve({}), + }) as any + ); await act(async () => renderWithProviders(, { user: preloadedState.user, calendars: { list: { "user1/cal1": preloadedState.calendars.list["user1/cal1"] }, - pending: false, }, }) ); @@ -572,8 +580,8 @@ describe("calendar Availability search", () => { calendarApi.changeView("dayGridMonth"); fireEvent.click(screen.getByTestId("ChevronRightIcon")); }); - expect(spy).toHaveBeenCalledTimes(4); - const callArgs = spy.mock.calls[3][0]; + expect(spy).toHaveBeenCalledTimes(2); + const callArgs = spy.mock.calls[1][0]; expect(callArgs.calId).toBe("user1/cal1"); const startDate = new Date( @@ -621,10 +629,13 @@ describe("calendar Availability search", () => { const spy = jest .spyOn(calendarDetailThunks, "getCalendarDetailAsync") - .mockImplementation(() => ({ - type: "getCalendarDetailAsync", - unwrap: () => Promise.resolve({}), - })) as any; + .mockImplementation( + () => + ({ + type: "getCalendarDetailAsync", + unwrap: () => Promise.resolve({}), + }) as any + ); await act(async () => { renderWithProviders( @@ -675,10 +686,13 @@ describe("calendar Availability search", () => { const spy = jest .spyOn(calendarDetailThunks, "getCalendarDetailAsync") - .mockImplementation(() => ({ - type: "getCalendarDetailAsync", - unwrap: () => Promise.resolve({}), - })) as any; + .mockImplementation( + () => + ({ + type: "getCalendarDetailAsync", + unwrap: () => Promise.resolve({}), + }) as any + ); await act(async () => { renderWithProviders( @@ -708,10 +722,13 @@ describe("calendar Availability search", () => { it("does not make duplicate API calls for same calendar and range", async () => { const spy = jest .spyOn(calendarDetailThunks, "getCalendarDetailAsync") - .mockImplementation(() => ({ - type: "getCalendarDetailAsync", - unwrap: () => Promise.resolve({}), - })) as any; + .mockImplementation( + () => + ({ + type: "getCalendarDetailAsync", + unwrap: () => Promise.resolve({}), + }) as any + ); await act(async () => { renderWithProviders( diff --git a/src/components/Calendar/Calendar.tsx b/src/components/Calendar/Calendar.tsx index ea3324d..00887d6 100644 --- a/src/components/Calendar/Calendar.tsx +++ b/src/components/Calendar/Calendar.tsx @@ -13,6 +13,7 @@ import { } from "@/utils/dateUtils"; import { extractEventBaseUuid } from "@/utils/extractEventBaseUuid"; import { setSelectedCalendars as setSelectedCalendarsToStorage } from "@/utils/storage/setSelectedCalendars"; +import { useSelectedCalendars } from "@/utils/storage/useSelectedCalendars"; import { browserDefaultTimeZone } from "@/utils/timezone"; import { CalendarApi, DateSelectArg } from "@fullcalendar/core"; import frLocale from "@fullcalendar/core/locales/fr"; @@ -76,11 +77,14 @@ export default function CalendarApp({ (state) => state.settings.hideDeclinedEvents ); const calendars = useAppSelector((state) => state.calendars.list); + const isPending = useAppSelector((state) => state.calendars.pending); const displayWeekNumbers = useAppSelector( (state) => state.settings.displayWeekNumbers ); const tempcalendars = useAppSelector((state) => state.calendars.templist); - const [selectedCalendars, setSelectedCalendars] = useState([]); + const storedCalendars = useSelectedCalendars(); + const [selectedCalendars, setSelectedCalendars] = + useState(storedCalendars); const calendarLightSignature = useMemo(() => { return Object.values(calendars || {}) @@ -253,6 +257,8 @@ export default function CalendarApp({ }, [rangeKey]); useEffect(() => { + if (isPending) return; + if (!rangeKey || sortedSelectedCalendars.length === 0) { activeLoadCompletedRef.current = true; setActiveLoadCompleted(true); @@ -320,8 +326,7 @@ export default function CalendarApp({ }, [dispatch, rangeKey, sortedSelectedCalendars, rangeStart, rangeEnd]); useEffect(() => { - if (!rangeKey || !activeLoadCompleted) return; - + if (!rangeKey || !activeLoadCompleted || isPending) return; const hiddenCalendars = calendarIds .filter((id) => !selectedCalendars.includes(id)) .filter((id) => { @@ -355,6 +360,7 @@ export default function CalendarApp({ rangeStart, rangeEnd, activeLoadCompleted, + isPending, ]); const calendarsWithClearedCache = useMemo(() => { @@ -369,6 +375,7 @@ export default function CalendarApp({ const processedCacheClearRef = useRef>({}); useEffect(() => { + if (isPending) return; calendarsWithClearedCache.forEach(({ id, cleared }) => { if (processedCacheClearRef.current[id] === cleared) { return; @@ -397,7 +404,14 @@ export default function CalendarApp({ prefetchedCalendarsRef.current[id] = ""; }); }); - }, [calendarsWithClearedCache, dispatch, rangeKey, rangeStart, rangeEnd]); + }, [ + calendarsWithClearedCache, + dispatch, + rangeKey, + rangeStart, + rangeEnd, + isPending, + ]); useEffect(() => { const currentIds = new Set(tempCalendarIds); diff --git a/src/features/Calendars/CalendarSlice.ts b/src/features/Calendars/CalendarSlice.ts index 17353e0..83b32fb 100644 --- a/src/features/Calendars/CalendarSlice.ts +++ b/src/features/Calendars/CalendarSlice.ts @@ -28,7 +28,7 @@ const CalendarSlice = createSlice({ initialState: { list: {} as Record, templist: {} as Record, - pending: false, + pending: true, error: null as string | null, } as { list: Record; diff --git a/src/utils/storage/useSelectedCalendars.ts b/src/utils/storage/useSelectedCalendars.ts index 7689f0d..180324c 100644 --- a/src/utils/storage/useSelectedCalendars.ts +++ b/src/utils/storage/useSelectedCalendars.ts @@ -2,6 +2,7 @@ import { useEffect, useState } from "react"; export function useSelectedCalendars(): string[] { const [calendars, setCalendars] = useState(() => { + if (typeof window === "undefined") return []; try { return JSON.parse(localStorage.getItem("selectedCalendars") ?? "[]"); } catch { @@ -10,6 +11,8 @@ export function useSelectedCalendars(): string[] { }); useEffect(() => { + if (typeof window === "undefined") return; + const onStorage = (e: StorageEvent) => { if (e.key === "selectedCalendars") { try { diff --git a/src/websocket/WebSocketGate.tsx b/src/websocket/WebSocketGate.tsx index 6cefb3d..cb73e00 100644 --- a/src/websocket/WebSocketGate.tsx +++ b/src/websocket/WebSocketGate.tsx @@ -17,6 +17,7 @@ export function WebSocketGate() { ); const [isSocketOpen, setIsSocketOpen] = useState(false); + const isPending = useAppSelector((state) => state.calendars.pending); const calendarList = useSelectedCalendars(); const tempCalendarList = Object.keys( @@ -73,13 +74,15 @@ export function WebSocketGate() { // Register using a diff with previous calendars useEffect(() => { + if (isPending) return; + syncCalendarRegistrations( isSocketOpen, socketRef, calendarList, previousCalendarListRef ); - }, [isSocketOpen, calendarList]); + }, [isSocketOpen, calendarList, isPending]); useEffect(() => { syncCalendarRegistrations(