fix: preserve recurrence exceptions when RSVP to recurring events (#234)

When an attendee accepts/rejects/tentative a recurring event with
exceptions, only update the PARTSTAT without removing exception events.

Add removeOverrides: false parameter to updateSeriesAsync call in
handleRSVP function to preserve all recurrence exceptions.

Fixes: Recurrence exceptions being deleted when accepting/rejecting
all events in a recurring series

* fix: update PARTSTAT on all VEVENTs when RSVP to recurring events

When an attendee accepts/rejects/tentative a recurring event with exceptions,
update PARTSTAT on ALL VEVENTs (master + exceptions) to reflect the correct
attendance status across the entire series.

Changes:
- Add updateSeriesPartstat() in EventApi.ts to update PARTSTAT on all VEVENTs
- Update handleRSVP() to use new function instead of updateSeriesAsync
- Preserve exception times while updating PARTSTAT consistently
- Update test to verify new function is called correctly

Fixes: Exception events not accepting when user chooses 'Accept all events'
This commit is contained in:
lenhanphung
2025-10-23 10:36:34 +07:00
committed by GitHub
parent 1c8826facc
commit 8681e88390
3 changed files with 58 additions and 27 deletions
@@ -472,18 +472,10 @@ describe("Recurrence Event Behavior Tests", () => {
expect(updatedEvent.attendee[0].partstat).toBe("ACCEPTED");
});
it("calls updateSeriesAsync when accepting all instances", async () => {
const getEventSpy = jest.spyOn(EventApi, "getEvent").mockResolvedValue({
...basePreloadedState.calendars.list["667037022b752d0026472254/cal1"]
.events["recurring-base/20250315T100000"],
uid: "recurring-base",
} as any);
it("calls updateSeriesPartstat when accepting all instances", async () => {
const spy = jest
.spyOn(eventThunks, "updateSeriesAsync")
.mockImplementation((payload) => {
return () => Promise.resolve() as any;
});
.spyOn(EventApi, "updateSeriesPartstat")
.mockResolvedValue({} as any);
renderWithProviders(
<EventPreviewModal
@@ -507,12 +499,13 @@ describe("Recurrence Event Behavior Tests", () => {
fireEvent.click(screen.getByRole("button", { name: /Ok/i }));
await waitFor(() => {
expect(getEventSpy).toHaveBeenCalled();
expect(spy).toHaveBeenCalled();
});
const updatedEvent = spy.mock.calls[0][0].event;
expect(updatedEvent.attendee[0].partstat).toBe("ACCEPTED");
const callArgs = spy.mock.calls[0];
expect(callArgs[0].uid).toBe("recurring-base/20250315T100000");
expect(callArgs[1]).toBe("test@test.com");
expect(callArgs[2]).toBe("ACCEPTED");
});
});