fix(calendar): recurring events not working (#4265)
This commit is contained in:
@@ -1,4 +1,7 @@
|
|||||||
import type { Agent } from "https";
|
import type { Agent } from "https";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import timezone from "dayjs/plugin/timezone";
|
||||||
|
import utc from "dayjs/plugin/utc";
|
||||||
import type { RequestInit as NodeFetchRequestInit } from "node-fetch";
|
import type { RequestInit as NodeFetchRequestInit } from "node-fetch";
|
||||||
import * as ical from "node-ical";
|
import * as ical from "node-ical";
|
||||||
import { DAVClient } from "tsdav";
|
import { DAVClient } from "tsdav";
|
||||||
@@ -14,6 +17,9 @@ import type { TestingResult } from "../base/test-connection/test-connection-serv
|
|||||||
import type { ICalendarIntegration } from "../interfaces/calendar/calendar-integration";
|
import type { ICalendarIntegration } from "../interfaces/calendar/calendar-integration";
|
||||||
import type { CalendarEvent } from "../interfaces/calendar/calendar-types";
|
import type { CalendarEvent } from "../interfaces/calendar/calendar-types";
|
||||||
|
|
||||||
|
dayjs.extend(utc);
|
||||||
|
dayjs.extend(timezone);
|
||||||
|
|
||||||
@HandleIntegrationErrors([integrationTsdavHttpErrorHandler])
|
@HandleIntegrationErrors([integrationTsdavHttpErrorHandler])
|
||||||
export class NextcloudIntegration extends Integration implements ICalendarIntegration {
|
export class NextcloudIntegration extends Integration implements ICalendarIntegration {
|
||||||
protected async testingAsync(input: IntegrationTestingInput): Promise<TestingResult> {
|
protected async testingAsync(input: IntegrationTestingInput): Promise<TestingResult> {
|
||||||
@@ -41,47 +47,58 @@ export class NextcloudIntegration extends Integration implements ICalendarIntegr
|
|||||||
)
|
)
|
||||||
).flat();
|
).flat();
|
||||||
|
|
||||||
return calendarEvents.map((event): CalendarEvent => {
|
return calendarEvents
|
||||||
// @ts-expect-error the typescript definitions for this package are wrong
|
.map((event) => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
|
// @ts-expect-error the typescript definitions for this package are wrong
|
||||||
const icalData = ical.default.parseICS(event.data) as ical.CalendarResponse;
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
|
||||||
const veventObject = Object.values(icalData).find((data) => data.type === "VEVENT");
|
const icalData = ical.default.parseICS(event.data) as ical.CalendarResponse;
|
||||||
|
const veventObject = Object.values(icalData).find((data) => data.type === "VEVENT");
|
||||||
|
|
||||||
if (!veventObject) {
|
if (!veventObject) {
|
||||||
throw new Error(`Invalid event data object: ${JSON.stringify(event.data)}. Unable to process the calendar.`);
|
throw new Error(`Invalid event data object: ${JSON.stringify(event.data)}. Unable to process the calendar.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug(`Converting VEVENT event to ${event.etag} from Nextcloud: ${JSON.stringify(veventObject)}`);
|
logger.debug(`Converting VEVENT event to ${event.etag} from Nextcloud: ${JSON.stringify(veventObject)}`);
|
||||||
|
|
||||||
const date = veventObject.start;
|
const eventUrlWithoutHost = new URL(event.url).pathname;
|
||||||
|
const eventSlug = Buffer.from(eventUrlWithoutHost).toString("base64url");
|
||||||
|
|
||||||
const eventUrlWithoutHost = new URL(event.url).pathname;
|
const startDates = veventObject.rrule ? veventObject.rrule.between(start, end) : [veventObject.start];
|
||||||
const dateInMillis = veventObject.start.valueOf();
|
|
||||||
|
|
||||||
const url = this.url(
|
const durationMs = veventObject.end.getTime() - veventObject.start.getTime();
|
||||||
`/apps/calendar/timeGridWeek/now/edit/sidebar/${Buffer.from(eventUrlWithoutHost).toString("base64url")}/${dateInMillis / 1000}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return startDates.map((startDate) => {
|
||||||
title: veventObject.summary,
|
const timezoneOffsetMinutes = veventObject.rrule?.origOptions.tzid
|
||||||
subTitle: null,
|
? dayjs(startDate).tz(veventObject.rrule.origOptions.tzid).utcOffset()
|
||||||
description: veventObject.description,
|
: 0;
|
||||||
startDate: date,
|
const utcStartDate = new Date(startDate.getTime() - timezoneOffsetMinutes * 60 * 1000);
|
||||||
endDate: veventObject.end,
|
const endDate = new Date(utcStartDate.getTime() + durationMs);
|
||||||
image: null,
|
const dateInMillis = utcStartDate.valueOf();
|
||||||
location: veventObject.location || null,
|
|
||||||
indicatorColor: "#ff8600",
|
return {
|
||||||
links: [
|
title: veventObject.summary,
|
||||||
{
|
subTitle: null,
|
||||||
href: url.toString(),
|
description: veventObject.description,
|
||||||
name: "Nextcloud",
|
startDate: utcStartDate,
|
||||||
logo: "/images/apps/nextcloud.svg",
|
endDate,
|
||||||
color: undefined,
|
image: null,
|
||||||
isDark: true,
|
location: veventObject.location || null,
|
||||||
},
|
indicatorColor: "#ff8600",
|
||||||
],
|
links: [
|
||||||
};
|
{
|
||||||
});
|
href: this.url(
|
||||||
|
`/apps/calendar/timeGridWeek/now/edit/sidebar/${eventSlug}/${dateInMillis / 1000}`,
|
||||||
|
).toString(),
|
||||||
|
name: "Nextcloud",
|
||||||
|
logo: "/images/apps/nextcloud.svg",
|
||||||
|
color: undefined,
|
||||||
|
isDark: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.flat();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createCalendarClientAsync(agent?: Agent) {
|
private async createCalendarClientAsync(agent?: Agent) {
|
||||||
|
|||||||
Reference in New Issue
Block a user