fix: handle synthetic ID errors when updating calendar events with fallback to destroy and recreate
This commit is contained in:
@@ -169,7 +169,30 @@ export const useCalendarStore = create<CalendarStore>()(
|
|||||||
}
|
}
|
||||||
cleanUpdates.calendarIds = remapped;
|
cleanUpdates.calendarIds = remapped;
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
await client.updateCalendarEvent(realId, cleanUpdates, sendSchedulingMessages, targetAccountId);
|
await client.updateCalendarEvent(realId, cleanUpdates, sendSchedulingMessages, targetAccountId);
|
||||||
|
} catch (updateError) {
|
||||||
|
// Stalwart doesn't support updating events created via CalDAV (e.g. by Thunderbird).
|
||||||
|
// These events have "synthetic" JMAP IDs. Work around by destroying and recreating.
|
||||||
|
const message = updateError instanceof Error ? updateError.message : '';
|
||||||
|
if (message.toLowerCase().includes('synthetic') && storeEvent) {
|
||||||
|
debug.log('Event has synthetic ID, falling back to destroy+recreate');
|
||||||
|
// Merge existing event with updates, strip server-computed fields
|
||||||
|
const { id: _id, originalId: _oi, originalCalendarIds: _oc,
|
||||||
|
accountId: _ai, accountName: _an, isShared: _is,
|
||||||
|
utcStart: _us, utcEnd: _ue, isDraft: _dr, isOrigin: _io,
|
||||||
|
created: _cr, updated: _up, ...baseEvent } = storeEvent;
|
||||||
|
const mergedEvent: Partial<CalendarEvent> = { ...baseEvent, ...cleanUpdates };
|
||||||
|
// Delete the old event without sending cancellations (we're recreating it)
|
||||||
|
await client.deleteCalendarEvent(realId, false, targetAccountId);
|
||||||
|
const created = await client.createCalendarEvent(mergedEvent, sendSchedulingMessages, targetAccountId);
|
||||||
|
set((state) => ({
|
||||||
|
events: state.events.map(e => e.id === id ? created : e),
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw updateError;
|
||||||
|
}
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
events: state.events.map(e => e.id === id ? { ...e, ...updates } : e),
|
events: state.events.map(e => e.id === id ? { ...e, ...updates } : e),
|
||||||
}));
|
}));
|
||||||
@@ -202,12 +225,40 @@ export const useCalendarStore = create<CalendarStore>()(
|
|||||||
if (replyTo) {
|
if (replyTo) {
|
||||||
patch.replyTo = replyTo;
|
patch.replyTo = replyTo;
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
await client.updateCalendarEvent(
|
await client.updateCalendarEvent(
|
||||||
realId,
|
realId,
|
||||||
patch as unknown as Partial<CalendarEvent>,
|
patch as unknown as Partial<CalendarEvent>,
|
||||||
true,
|
true,
|
||||||
targetAccountId
|
targetAccountId
|
||||||
);
|
);
|
||||||
|
} catch (updateError) {
|
||||||
|
// Fallback for CalDAV-created events with synthetic IDs
|
||||||
|
const message = updateError instanceof Error ? updateError.message : '';
|
||||||
|
if (message.toLowerCase().includes('synthetic') && storeEvent) {
|
||||||
|
debug.log('RSVP: Event has synthetic ID, falling back to destroy+recreate');
|
||||||
|
const { id: _id, originalId: _oi, originalCalendarIds: _oc,
|
||||||
|
accountId: _ai, accountName: _an, isShared: _is,
|
||||||
|
utcStart: _us, utcEnd: _ue, isDraft: _dr, isOrigin: _io,
|
||||||
|
created: _cr, updated: _up, ...baseEvent } = storeEvent;
|
||||||
|
const updatedParticipants = storeEvent.participants ? {
|
||||||
|
...storeEvent.participants,
|
||||||
|
[participantId]: { ...storeEvent.participants[participantId], participationStatus: status },
|
||||||
|
} : storeEvent.participants;
|
||||||
|
const mergedEvent: Partial<CalendarEvent> = {
|
||||||
|
...baseEvent,
|
||||||
|
participants: updatedParticipants as Record<string, CalendarParticipant> | null,
|
||||||
|
...(replyTo ? { replyTo } : {}),
|
||||||
|
};
|
||||||
|
await client.deleteCalendarEvent(realId, false, targetAccountId);
|
||||||
|
const created = await client.createCalendarEvent(mergedEvent, true, targetAccountId);
|
||||||
|
set((state) => ({
|
||||||
|
events: state.events.map(e => e.id === eventId ? created : e),
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw updateError;
|
||||||
|
}
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
events: state.events.map(e => {
|
events: state.events.map(e => {
|
||||||
if (e.id !== eventId || !e.participants?.[participantId]) return e;
|
if (e.id !== eventId || !e.participants?.[participantId]) return e;
|
||||||
|
|||||||
Reference in New Issue
Block a user