[#475] fixed calendars selection persistence (#479)

This commit is contained in:
Camille Moussu
2026-01-26 11:14:42 +01:00
committed by GitHub
parent 7058f1c2d6
commit 1fc85e141e
5 changed files with 60 additions and 23 deletions
+34 -17
View File
@@ -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(<CalendarTestWrapper />, {
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(
+18 -4
View File
@@ -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<string[]>([]);
const storedCalendars = useSelectedCalendars();
const [selectedCalendars, setSelectedCalendars] =
useState<string[]>(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<Record<string, number>>({});
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);
+1 -1
View File
@@ -28,7 +28,7 @@ const CalendarSlice = createSlice({
initialState: {
list: {} as Record<string, Calendar>,
templist: {} as Record<string, Calendar>,
pending: false,
pending: true,
error: null as string | null,
} as {
list: Record<string, Calendar>;
@@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
export function useSelectedCalendars(): string[] {
const [calendars, setCalendars] = useState<string[]>(() => {
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 {
+4 -1
View File
@@ -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(