fix: directly delete recurring events when converting to non-recurring
- Use direct API call to delete recurring events instead of going through Redux store - Construct event URL directly for more reliable deletion - Add error handling and small delay to ensure complete deletion - Clean up all instances from store after deletion
This commit is contained in:
committed by
Benoit TELLIER
parent
2ba80fcdb6
commit
40f0179be6
@@ -345,13 +345,13 @@ export default function EventFormFields({
|
|||||||
label="All day"
|
label="All day"
|
||||||
/>
|
/>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={showRepeat}
|
checked={showRepeat}
|
||||||
disabled={typeOfAction === "solo"}
|
disabled={typeOfAction === "solo"}
|
||||||
onChange={() => {
|
onChange={() => {
|
||||||
const newShowRepeat = !showRepeat;
|
const newShowRepeat = !showRepeat;
|
||||||
setShowRepeat(newShowRepeat);
|
setShowRepeat(newShowRepeat);
|
||||||
if (newShowRepeat) {
|
if (newShowRepeat) {
|
||||||
setRepetition({
|
setRepetition({
|
||||||
freq: "daily",
|
freq: "daily",
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ import {
|
|||||||
import { Calendars } from "../Calendars/CalendarTypes";
|
import { Calendars } from "../Calendars/CalendarTypes";
|
||||||
import { userAttendee } from "../User/userDataTypes";
|
import { userAttendee } from "../User/userDataTypes";
|
||||||
import { getEvent } from "./EventApi";
|
import { getEvent } from "./EventApi";
|
||||||
import { formatLocalDateTime } from "./EventModal";
|
import { formatLocalDateTime } from "../../components/Event/EventFormFields";
|
||||||
import { CalendarEvent, RepetitionObject } from "./EventsTypes";
|
import { CalendarEvent, RepetitionObject } from "./EventsTypes";
|
||||||
|
|
||||||
export default function EventDisplayModal({
|
export default function EventDisplayModal({
|
||||||
@@ -128,7 +128,7 @@ export default function EventDisplayModal({
|
|||||||
onClose({}, "backdropClick");
|
onClose({}, "backdropClick");
|
||||||
}
|
}
|
||||||
setRepetition(event?.repetition ?? ({} as RepetitionObject));
|
setRepetition(event?.repetition ?? ({} as RepetitionObject));
|
||||||
}, [open, eventId, dispatch, onClose, event]);
|
}, [open, eventId, dispatch, onClose, event, calendar]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchMasterEvent = async () => {
|
const fetchMasterEvent = async () => {
|
||||||
const masterEvent = await getEvent(event);
|
const masterEvent = await getEvent(event);
|
||||||
@@ -160,8 +160,8 @@ export default function EventDisplayModal({
|
|||||||
calId,
|
calId,
|
||||||
title,
|
title,
|
||||||
URL: event.URL ?? `/calendars/${calId}/${newEventUID}.ics`,
|
URL: event.URL ?? `/calendars/${calId}/${newEventUID}.ics`,
|
||||||
start: new Date(start),
|
start: start,
|
||||||
end: new Date(end),
|
end: end,
|
||||||
allday,
|
allday,
|
||||||
uid: event.uid ?? newEventUID,
|
uid: event.uid ?? newEventUID,
|
||||||
description,
|
description,
|
||||||
@@ -523,7 +523,7 @@ export default function EventDisplayModal({
|
|||||||
{isOwn && (
|
{isOwn && (
|
||||||
<RepeatEvent
|
<RepeatEvent
|
||||||
repetition={repetition}
|
repetition={repetition}
|
||||||
eventStart={event.start}
|
eventStart={new Date(event.start)}
|
||||||
setRepetition={setRepetition}
|
setRepetition={setRepetition}
|
||||||
isOwn={isOwn && typeOfAction !== "solo"}
|
isOwn={isOwn && typeOfAction !== "solo"}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import { addVideoConferenceToDescription } from "../../utils/videoConferenceUtil
|
|||||||
import EventFormFields, {
|
import EventFormFields, {
|
||||||
formatDateTimeInTimezone,
|
formatDateTimeInTimezone,
|
||||||
} from "../../components/Event/EventFormFields";
|
} from "../../components/Event/EventFormFields";
|
||||||
import { getEvent } from "./EventApi";
|
import { getEvent, deleteEvent } from "./EventApi";
|
||||||
import { refreshCalendars } from "../../components/Event/utils/eventUtils";
|
import { refreshCalendars } from "../../components/Event/utils/eventUtils";
|
||||||
import { getCalendarRange } from "../../utils/dateUtils";
|
import { getCalendarRange } from "../../utils/dateUtils";
|
||||||
|
|
||||||
@@ -346,66 +346,90 @@ function EventUpdateModal({
|
|||||||
dispatch(removeEvent({ calendarUid: calId, eventUid: event.uid }));
|
dispatch(removeEvent({ calendarUid: calId, eventUid: event.uid }));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle recurrence instances
|
// Handle recurrence instances
|
||||||
const [, recurrenceId] = event.uid.split("/");
|
const [, recurrenceId] = event.uid.split("/");
|
||||||
|
|
||||||
// Execute API calls in background based on typeOfAction
|
// Execute API calls in background based on typeOfAction
|
||||||
if (recurrenceId) {
|
if (recurrenceId) {
|
||||||
if (typeOfAction === "solo") {
|
if (typeOfAction === "solo") {
|
||||||
// Update just this instance
|
// Update just this instance
|
||||||
dispatch(
|
|
||||||
updateEventInstanceAsync({
|
|
||||||
cal: targetCalendar,
|
|
||||||
event: { ...newEvent, recurrenceId },
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else if (typeOfAction === "all") {
|
|
||||||
// Update all instances
|
|
||||||
|
|
||||||
// Special case: When converting recurring event to non-recurring
|
|
||||||
if (event.repetition?.freq && !repetition.freq) {
|
|
||||||
// For repeat -> no-repeat, create a new non-repeating event
|
|
||||||
dispatch(
|
dispatch(
|
||||||
putEventAsync({
|
updateEventInstanceAsync({
|
||||||
cal: targetCalendar,
|
|
||||||
newEvent: {
|
|
||||||
...newEvent,
|
|
||||||
uid: crypto.randomUUID(), // Generate new ID for the single event
|
|
||||||
URL: `/calendars/${newCalId || calId}/${crypto.randomUUID()}.ics`,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// Delete the old repeating series
|
|
||||||
const baseUID = event.uid.split("/")[0];
|
|
||||||
Object.keys(targetCalendar.events).forEach((eventId) => {
|
|
||||||
if (eventId.split("/")[0] === baseUID) {
|
|
||||||
dispatch(removeEvent({ calendarUid: calId, eventUid: eventId }));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Normal update for recurring events
|
|
||||||
dispatch(
|
|
||||||
updateSeriesAsync({
|
|
||||||
cal: targetCalendar,
|
cal: targetCalendar,
|
||||||
event: { ...newEvent, recurrenceId },
|
event: { ...newEvent, recurrenceId },
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
} else if (typeOfAction === "all") {
|
||||||
|
// Update all instances
|
||||||
|
|
||||||
|
// Special case: When converting recurring event to non-recurring
|
||||||
|
if (event.repetition?.freq && !repetition.freq) {
|
||||||
|
// For repeat -> no-repeat, create a new non-repeating event
|
||||||
|
dispatch(
|
||||||
|
putEventAsync({
|
||||||
|
cal: targetCalendar,
|
||||||
|
newEvent: {
|
||||||
|
...newEvent,
|
||||||
|
uid: crypto.randomUUID(), // Generate new ID for the single event
|
||||||
|
URL: `/calendars/${newCalId || calId}/${crypto.randomUUID()}.ics`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Delete the old repeating series - we need to be more direct and forceful
|
||||||
|
const baseUID = event.uid.split("/")[0];
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Directly use the deleteEvent API without going through the store
|
||||||
|
const eventBaseURL = `/calendars/${calId}/${baseUID}.ics`;
|
||||||
|
await deleteEvent(eventBaseURL);
|
||||||
|
console.log(
|
||||||
|
"Deleted master event via direct API call:",
|
||||||
|
eventBaseURL
|
||||||
|
);
|
||||||
|
|
||||||
|
// Force a small delay to ensure deletion completes
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||||
|
|
||||||
|
// Make sure to clean up any instances in the store
|
||||||
|
Object.keys(targetCalendar.events).forEach((eventId) => {
|
||||||
|
if (eventId.split("/")[0] === baseUID) {
|
||||||
|
dispatch(
|
||||||
|
removeEvent({ calendarUid: calId, eventUid: eventId })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Failed to delete recurring event:", err);
|
||||||
|
// Even if deletion fails, continue with creating the new event
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Normal update for recurring events
|
||||||
|
dispatch(
|
||||||
|
updateSeriesAsync({
|
||||||
|
cal: targetCalendar,
|
||||||
|
event: { ...newEvent, recurrenceId },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh calendars to ensure all instances are updated
|
||||||
|
const calendarRange = getCalendarRange(new Date(start));
|
||||||
|
await refreshCalendars(
|
||||||
|
dispatch,
|
||||||
|
Object.values(calendarsList),
|
||||||
|
calendarRange
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
// Refresh calendars to ensure all instances are updated
|
// Non-recurring event
|
||||||
const calendarRange = getCalendarRange(new Date(start));
|
dispatch(
|
||||||
await refreshCalendars(dispatch, Object.values(calendarsList), calendarRange);
|
putEventAsync({
|
||||||
|
cal: targetCalendar,
|
||||||
|
newEvent,
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Non-recurring event
|
|
||||||
dispatch(
|
|
||||||
putEventAsync({
|
|
||||||
cal: targetCalendar,
|
|
||||||
newEvent,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle calendar change
|
// Handle calendar change
|
||||||
if (newCalId !== calId) {
|
if (newCalId !== calId) {
|
||||||
|
|||||||
Reference in New Issue
Block a user