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