fix: recurring events edit all instances issues

- Fix master event detection in getEvent() to find VEVENT without recurrence-id
- Add removeOverrides parameter to updateSeries() to control override deletion
- Fetch master event when editing 'all events' to preserve original start date
- Combine master's date with form's time when updating all instances
- Detect time changes separately from date changes to preserve optimistic updates
- Only remove overrides when date/time/timezone/repeat rules change
- Keep overrides when only properties (title, description, etc) change

Fixes:
1. Old UI not cleared when editing all events
2. Start date not preserved when editing from different instance
3. Solo overrides incorrectly removed when only properties changed
4. Optimistic updates lost when editing recurring events
This commit is contained in:
lenhanphung
2025-10-14 12:46:32 +07:00
committed by Benoit TELLIER
parent 5017c1bd6b
commit af47923478
3 changed files with 180 additions and 29 deletions
+38 -5
View File
@@ -15,8 +15,27 @@ export async function getEvent(event: CalendarEvent, isMaster?: boolean) {
const eventical = ICAL.parse(eventData);
let targetVevent;
if (isMaster) {
// Find master VEVENT (the one without recurrence-id)
const vevents = eventical[2].filter(
([name]: string[]) => name === "vevent"
);
targetVevent = vevents.find(
([, props]: [string, any[]]) =>
!props.find(([k]: string[]) => k.toLowerCase() === "recurrence-id")
);
if (!targetVevent) {
// Fallback to first VEVENT if no master found
targetVevent = eventical[2][1];
}
} else {
// For non-master, use first VEVENT as before
targetVevent = eventical[2][1];
}
const eventjson = parseCalendarEvent(
eventical[2][1][1],
targetVevent[1],
event.color ?? "",
event.calId,
event.URL
@@ -109,7 +128,8 @@ export const deleteEventInstance = async (
export const updateSeries = async (
event: CalendarEvent,
calOwnerEmail?: string
calOwnerEmail?: string,
removeOverrides: boolean = true
) => {
const vevents = await getAllRecurrentEvent(event);
const masterIndex = vevents.findIndex(
@@ -119,7 +139,7 @@ export const updateSeries = async (
if (masterIndex === -1) {
throw new Error("No master VEVENT found for this series");
}
const rrule = vevents[0][1].find(([k]: string[]) => k === "rrule");
const rrule = vevents[masterIndex][1].find(([k]: string[]) => k === "rrule");
const tzid = event.timezone;
@@ -128,12 +148,25 @@ export const updateSeries = async (
if (!newRrule) {
updatedMaster[1].push(rrule);
}
vevents[masterIndex] = updatedMaster;
const timezoneData = TIMEZONES.zones[event.timezone];
const vtimezone = makeTimezone(timezoneData, event);
const newJCal = ["vcalendar", [], [...vevents, vtimezone.component.jCal]];
let finalVevents;
if (removeOverrides) {
// When date/time/timezone/repeat rules changed, remove all override instances
finalVevents = [updatedMaster];
} else {
// When only properties changed, keep override instances
vevents[masterIndex] = updatedMaster;
finalVevents = vevents;
}
const newJCal = [
"vcalendar",
[],
[...finalVevents, vtimezone.component.jCal],
];
return api(`dav${event.URL}`, {
method: "PUT",
body: JSON.stringify(newJCal),