Skip to content

Commit

Permalink
remove altered instance exclusions before exporting calendar events
Browse files Browse the repository at this point in the history
this client-side fix makes invitations work for google calendar

#5707
  • Loading branch information
ganthern committed Aug 3, 2023
1 parent e0716ef commit bbf68fb
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 15 deletions.
2 changes: 1 addition & 1 deletion src/calendar/date/CalendarInvites.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ export async function replyToEventInvitation(
responseModel?.addRecipient(RecipientField.TO, previousMail.sender)

try {
await notificationModel.send(eventClone, { responseModel, inviteModel: null, cancelModel: null, updateModel: null })
await notificationModel.send(eventClone, [], { responseModel, inviteModel: null, cancelModel: null, updateModel: null })
} catch (e) {
if (e instanceof UserError) {
await Dialog.message(() => e.message)
Expand Down
4 changes: 3 additions & 1 deletion src/calendar/date/eventeditor/CalendarEventModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,10 @@ export async function makeCalendarEventModel(
description: new SanitizedTextViewModel(initializationEvent.description, htmlSanitizer, uiUpdateCallback),
})

const recurrenceIds = async (uid?: string) =>
uid == null ? [] : (await calendarModel.getEventsByUid(uid))?.alteredInstances.map((i) => i.recurrenceId) ?? []
const notificationModel = new CalendarNotificationModel(notificationSender, logins)
const applyStrategies = new CalendarEventApplyStrategies(calendarModel, logins, notificationModel, showProgress, zone)
const applyStrategies = new CalendarEventApplyStrategies(calendarModel, logins, notificationModel, recurrenceIds, showProgress, zone)
const progenitor = () => calendarModel.resolveCalendarEventProgenitor(cleanInitialValues)
const strategy = await selectStrategy(makeEditModels, applyStrategies, operation, progenitor, createCalendarEvent(initialValues), cleanInitialValues)
return strategy && new CalendarEventModel(strategy, eventType, operation, logins.getUserController(), notificationSender, entityClient, calendars)
Expand Down
28 changes: 17 additions & 11 deletions src/calendar/date/eventeditor/CalendarEventModelStrategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export class CalendarEventApplyStrategies {
private readonly calendarModel: CalendarModel,
private readonly logins: LoginController,
private readonly notificationModel: CalendarNotificationModel,
private readonly lazyRecurrenceIds: (uid?: string | null) => Promise<Array<Date>>,
private readonly showProgress: ShowProgressCallback = identity,
private readonly zone: string,
) {}
Expand All @@ -55,7 +56,7 @@ export class CalendarEventApplyStrategies {

await this.showProgress(
(async () => {
await this.notificationModel.send(newEvent, sendModels)
await this.notificationModel.send(newEvent, [], sendModels)
await this.calendarModel.createEvent(newEvent, newAlarms, this.zone, groupRoot)
})(),
)
Expand All @@ -77,7 +78,8 @@ export class CalendarEventApplyStrategies {
const { groupRoot } = calendar
await this.showProgress(
(async () => {
await this.notificationModel.send(newEvent, sendModels)
const recurrenceIds: Array<Date> = await this.lazyRecurrenceIds(uid)
await this.notificationModel.send(newEvent, recurrenceIds, sendModels)
await this.calendarModel.updateEvent(newEvent, newAlarms, this.zone, groupRoot, existingEvent)
const invalidateAlteredInstances = newEvent.repeatRule && newEvent.repeatRule.excludedDates.length === 0

Expand All @@ -102,7 +104,7 @@ export class CalendarEventApplyStrategies {
sendModels.cancelModel = sendModels.updateModel
sendModels.updateModel = null
sendModels.inviteModel = null
await this.notificationModel.send(occurrence, sendModels)
await this.notificationModel.send(occurrence, [], sendModels)
await this.calendarModel.deleteEvent(occurrence)
} else {
const { newEvent, newAlarms, sendModels } = assembleEditResultAndAssignFromExisting(
Expand All @@ -115,7 +117,7 @@ export class CalendarEventApplyStrategies {
newEvent.endTime = DateTime.fromJSDate(newEvent.startTime, { zone: this.zone }).plus(newDuration).toJSDate()
// altered instances never have a repeat rule
newEvent.repeatRule = null
await this.notificationModel.send(newEvent, sendModels)
await this.notificationModel.send(newEvent, [], sendModels)
await this.calendarModel.updateEvent(newEvent, newAlarms, this.zone, groupRoot, occurrence)
}
}
Expand All @@ -142,7 +144,7 @@ export class CalendarEventApplyStrategies {
editModels,
CalendarOperation.EditThis,
)
await this.notificationModel.send(newEvent, sendModels)
await this.notificationModel.send(newEvent, [], sendModels)

// OLD: but we need to update the existing one as well, to add an exclusion for the original instance that we edited.
editModelsForProgenitor.whoModel.shouldSendUpdates = true
Expand All @@ -152,7 +154,9 @@ export class CalendarEventApplyStrategies {
sendModels: progenitorSendModels,
newAlarms: progenitorAlarms,
} = assembleEditResultAndAssignFromExisting(progenitor, editModelsForProgenitor, CalendarOperation.EditAll)
await this.notificationModel.send(newProgenitor, progenitorSendModels)
const recurrenceIds = await this.lazyRecurrenceIds(progenitor.uid)
recurrenceIds.push(existingInstance.startTime)
await this.notificationModel.send(newProgenitor, recurrenceIds, progenitorSendModels)
await this.calendarModel.updateEvent(newProgenitor, progenitorAlarms, this.zone, calendar.groupRoot, progenitor)

// NEW
Expand All @@ -167,7 +171,7 @@ export class CalendarEventApplyStrategies {
const { groupRoot } = calendar
await this.showProgress(
(async () => {
await this.notificationModel.send(newEvent, sendModels)
await this.notificationModel.send(newEvent, [], sendModels)
await this.calendarModel.updateEvent(newEvent, newAlarms, this.zone, groupRoot, existingInstance)
})(),
)
Expand All @@ -186,13 +190,13 @@ export class CalendarEventApplyStrategies {
const { sendModels } = assembleEditResultAndAssignFromExisting(occurrence, editModels, CalendarOperation.DeleteAll)
sendModels.cancelModel = sendModels.updateModel
sendModels.updateModel = null
await this.notificationModel.send(occurrence, sendModels)
await this.notificationModel.send(occurrence, [], sendModels)
}
}

sendModels.cancelModel = sendModels.updateModel
sendModels.updateModel = null
await this.notificationModel.send(existingEvent, sendModels)
await this.notificationModel.send(existingEvent, [], sendModels)
if (existingEvent.uid != null) {
await this.calendarModel.deleteEventsByUid(existingEvent.uid)
}
Expand All @@ -214,7 +218,9 @@ export class CalendarEventApplyStrategies {
editModelsForProgenitor,
CalendarOperation.DeleteThis,
)
await this.notificationModel.send(newEvent, sendModels)
const recurrenceIds = await this.lazyRecurrenceIds(progenitor.uid)
recurrenceIds.push(existingInstance.startTime)
await this.notificationModel.send(newEvent, recurrenceIds, sendModels)
await this.calendarModel.updateEvent(newEvent, newAlarms, this.zone, calendar.groupRoot, progenitor)
})(),
)
Expand All @@ -228,7 +234,7 @@ export class CalendarEventApplyStrategies {
sendModels.updateModel = null
await this.showProgress(
(async () => {
await this.notificationModel.send(existingAlteredInstance, sendModels)
await this.notificationModel.send(existingAlteredInstance, [], sendModels)
await this.calendarModel.deleteEvent(existingAlteredInstance)
})(),
)
Expand Down
12 changes: 10 additions & 2 deletions src/calendar/date/eventeditor/CalendarNotificationModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,27 @@ export class CalendarNotificationModel {
*
* will modify the attendee list of newEvent if invites/cancellations are sent.
*/
async send(event: CalendarEvent, sendModels: CalendarNotificationSendModels): Promise<void> {
async send(event: CalendarEvent, recurrenceIds: Array<Date>, sendModels: CalendarNotificationSendModels): Promise<void> {
if (sendModels.updateModel == null && sendModels.cancelModel == null && sendModels.inviteModel == null && sendModels.responseModel == null) {
return
}
if (!(await hasPlanWithInvites(this.loginController))) {
const { getAvailablePlansWithCalendarInvites } = await import("../../../subscription/SubscriptionUtils.js")
throw new UpgradeRequiredError("upgradeRequired_msg", await getAvailablePlansWithCalendarInvites())
}
// we need to exclude the exclusions that are only there because of altered instances specifically
// so google calendar handles our invitations
const recurrenceTimes = recurrenceIds.map((date) => date.getTime())
const originalExclusions = event.repeatRule?.excludedDates ?? []
const filteredExclusions = originalExclusions.filter(({ date }) => !recurrenceTimes.includes(date.getTime()))
if (event.repeatRule != null) event.repeatRule.excludedDates = filteredExclusions

const invitePromise = sendModels.inviteModel != null ? this.sendInvites(event, sendModels.inviteModel) : Promise.resolve()
const cancelPromise = sendModels.cancelModel != null ? this.sendCancellation(event, sendModels.cancelModel) : Promise.resolve()
const updatePromise = sendModels.updateModel != null ? this.sendUpdates(event, sendModels.updateModel) : Promise.resolve()
const responsePromise = sendModels.responseModel != null ? this.respondToOrganizer(event, sendModels.responseModel) : Promise.resolve()
return await Promise.all([invitePromise, cancelPromise, updatePromise, responsePromise]).then()
await Promise.all([invitePromise, cancelPromise, updatePromise, responsePromise])
if (event.repeatRule != null) event.repeatRule.excludedDates = originalExclusions
}

/**
Expand Down

0 comments on commit bbf68fb

Please sign in to comment.