diff --git a/__test__/features/Events/EventRepetition.test.tsx b/__test__/features/Events/EventRepetition.test.tsx index 5131ed6..dc29944 100644 --- a/__test__/features/Events/EventRepetition.test.tsx +++ b/__test__/features/Events/EventRepetition.test.tsx @@ -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( { 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"); }); }); diff --git a/src/components/Event/eventHandlers/eventHandlers.ts b/src/components/Event/eventHandlers/eventHandlers.ts index b252abe..8951aec 100644 --- a/src/components/Event/eventHandlers/eventHandlers.ts +++ b/src/components/Event/eventHandlers/eventHandlers.ts @@ -8,7 +8,10 @@ import { deleteEventAsync, } from "../../../features/Calendars/CalendarSlice"; import { Calendars } from "../../../features/Calendars/CalendarTypes"; -import { getEvent } from "../../../features/Events/EventApi"; +import { + getEvent, + updateSeriesPartstat, +} from "../../../features/Events/EventApi"; import { CalendarEvent } from "../../../features/Events/EventsTypes"; import { userData } from "../../../features/User/userDataTypes"; import { getCalendarRange } from "../../../utils/dateUtils"; @@ -33,20 +36,11 @@ export async function handleRSVP( if (typeOfAction === "solo") { dispatch(updateEventInstanceAsync({ cal: calendar, event: newEvent })); } else if (typeOfAction === "all") { - const master = await getEvent(newEvent, true); const calendarRange = getCalendarRange(new Date(event.start)); - dispatch( - updateSeriesAsync({ - cal: calendar, - event: { - ...master, - attendee: event.attendee?.map((a) => - a.cal_address === user.userData.email ? { ...a, partstat: rsvp } : a - ), - }, - }) - ); + // Update PARTSTAT on ALL VEVENTs (master + exceptions) + await updateSeriesPartstat(event, user.userData.email, rsvp); + if (calendars) { await refreshCalendars(dispatch, calendars, calendarRange); } diff --git a/src/features/Events/EventApi.ts b/src/features/Events/EventApi.ts index c28c3a6..d018621 100644 --- a/src/features/Events/EventApi.ts +++ b/src/features/Events/EventApi.ts @@ -126,6 +126,50 @@ export const deleteEventInstance = async ( return putEvent(seriesEvent, calOwnerEmail); }; +export const updateSeriesPartstat = async ( + event: CalendarEvent, + attendeeEmail: string, + partstat: string +) => { + const vevents = await getAllRecurrentEvent(event); + + // Update PARTSTAT in ALL VEVENTs (master + exceptions) + const updatedVevents = vevents.map((vevent: any[]) => { + const properties = vevent[1]; + const updatedProperties = properties.map((prop: any[]) => { + // Find ATTENDEE properties + if (prop[0] === "attendee") { + const calAddress = prop[3]; + // Check if this is the target attendee + if (calAddress.toLowerCase().includes(attendeeEmail.toLowerCase())) { + // Update PARTSTAT parameter + const params = { ...prop[1], partstat: partstat }; + return [prop[0], params, prop[2], prop[3]]; + } + } + return prop; + }); + return [vevent[0], updatedProperties, vevent[2]]; + }); + + const timezoneData = TIMEZONES.zones[event.timezone]; + const vtimezone = makeTimezone(timezoneData, event); + + const newJCal = [ + "vcalendar", + [], + [...updatedVevents, vtimezone.component.jCal], + ]; + + return api(`dav${event.URL}`, { + method: "PUT", + body: JSON.stringify(newJCal), + headers: { + "content-type": "text/calendar; charset=utf-8", + }, + }); +}; + export const updateSeries = async ( event: CalendarEvent, calOwnerEmail?: string,