diff --git a/lib/__tests__/calendar-rsvp.test.ts b/lib/__tests__/calendar-rsvp.test.ts index 0f395b720..d650d7e2a 100644 --- a/lib/__tests__/calendar-rsvp.test.ts +++ b/lib/__tests__/calendar-rsvp.test.ts @@ -60,6 +60,13 @@ describe('answerInvitation', () => { expect(req.store.rsvpEvent).toHaveBeenCalledWith(req.client, 'new', 'p2', 'declined', { imip: 'mailto:alice@example.org' }); }); + it('finds the new copy on the server when the store doesn’t hold it', async () => { + const base = request(); + const req = request({ store: { ...base.store, events: () => [] }, findImported: async () => ({ id: 'srv', uid: 'u1', participants: { p2: me } } as never) } as never); + expect(await answerInvitation(req, 'accepted')).toBe('answered'); + expect(req.store.rsvpEvent).toHaveBeenCalledWith(req.client, 'srv', 'p2', 'accepted', { imip: 'mailto:alice@example.org' }); + }); + it('says when a plugin stopped it, when it only added the event, and when it can\'t', async () => { expect(await answerInvitation(request({ store: { ...request().store, importEvents: vi.fn(async () => null) } } as never), 'accepted')).toBe('stopped'); expect(await answerInvitation(request({ myParticipant: null, store: { ...request().store, events: () => [{ id: 'new', uid: 'u1', participants: {} } as never] } } as never), 'accepted')).toBe('added'); diff --git a/lib/calendar-rsvp.ts b/lib/calendar-rsvp.ts index b2d52c15f..edb679482 100644 --- a/lib/calendar-rsvp.ts +++ b/lib/calendar-rsvp.ts @@ -44,6 +44,11 @@ export interface RsvpRequest { answerOptions?: { sentBy: string }; /** The stored copy with its participants loaded (or the shared account's copy). */ resolveExisting: () => Promise; + /** + * The copy an import just made, from the server, when the store doesn't + * hold it (an event outside the range the calendar has loaded). + */ + findImported?: (uid: string) => Promise; } /** @@ -113,7 +118,7 @@ export async function answerInvitation(req: RsvpRequest, status: RsvpStatus): Pr if (imported <= 0) return 'failed'; const newEvent = store.events().find( (e) => e.uid === parsedEvent.uid && (sharedMailAccount ? e.accountId === sharedMailAccount : !e.isShared), - ); + ) ?? (parsedEvent.uid && req.findImported ? await req.findImported(parsedEvent.uid) : null); const participant = myParticipant || (newEvent ? findParticipantByEmails(newEvent, actingEmails) : null); if (!newEvent || !participant) return 'added'; return (await rsvp(newEvent.id, participant.id)) ? 'answered' : 'stopped'; diff --git a/lib/plugin-sandbox/assistant/c.calendar.ts b/lib/plugin-sandbox/assistant/c.calendar.ts index 9c76f72de..0e87f5aff 100644 --- a/lib/plugin-sandbox/assistant/c.calendar.ts +++ b/lib/plugin-sandbox/assistant/c.calendar.ts @@ -100,6 +100,7 @@ export async function doCalendarRespond(emailIdArg: unknown, statusArg: unknown) actingEmails: emails, calendarId, sharedMailAccount: null, + findImported: (uid) => ownCopy(client, uid), resolveExisting: async () => { if (!existing) return null; if (existing.participants && Object.keys(existing.participants).length > 0) return existing;